diff options
author | neksard <neksard@yandex-team.ru> | 2022-02-10 16:45:33 +0300 |
---|---|---|
committer | Daniil Cherednik <dcherednik@yandex-team.ru> | 2022-02-10 16:45:33 +0300 |
commit | 1d9c550e7c38e051d7961f576013a482003a70d9 (patch) | |
tree | b2cc84ee7850122e7ccf51d0ea21e4fa7e7a5685 /contrib/libs/nghttp2/lib | |
parent | 8f7cf138264e0caa318144bf8a2c950e0b0a8593 (diff) | |
download | ydb-1d9c550e7c38e051d7961f576013a482003a70d9.tar.gz |
Restoring authorship annotation for <neksard@yandex-team.ru>. Commit 2 of 2.
Diffstat (limited to 'contrib/libs/nghttp2/lib')
46 files changed, 25188 insertions, 25188 deletions
diff --git a/contrib/libs/nghttp2/lib/includes/nghttp2/nghttp2.h b/contrib/libs/nghttp2/lib/includes/nghttp2/nghttp2.h index 164104770d..04321a652f 100644 --- a/contrib/libs/nghttp2/lib/includes/nghttp2/nghttp2.h +++ b/contrib/libs/nghttp2/lib/includes/nghttp2/nghttp2.h @@ -1,60 +1,60 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2013, 2014 Tatsuhiro Tsujikawa - * - * 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_H -#define NGHTTP2_H - -/* Define WIN32 when build target is Win32 API (borrowed from - libcurl) */ -#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32) +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013, 2014 Tatsuhiro Tsujikawa + * + * 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_H +#define NGHTTP2_H + +/* Define WIN32 when build target is Win32 API (borrowed from + libcurl) */ +#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32) # define WIN32 -#endif - +#endif + /* Compatibility for non-Clang compilers */ #ifndef __has_declspec_attribute # define __has_declspec_attribute(x) 0 #endif -#ifdef __cplusplus -extern "C" { -#endif - -#include <stdlib.h> -#if defined(_MSC_VER) && (_MSC_VER < 1800) -/* MSVC < 2013 does not have inttypes.h because it is not C99 - compliant. See compiler macros and version number in - https://sourceforge.net/p/predef/wiki/Compilers/ */ +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdlib.h> +#if defined(_MSC_VER) && (_MSC_VER < 1800) +/* MSVC < 2013 does not have inttypes.h because it is not C99 + compliant. See compiler macros and version number in + https://sourceforge.net/p/predef/wiki/Compilers/ */ # include <stdint.h> -#else /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */ +#else /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */ # include <inttypes.h> -#endif /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */ -#include <sys/types.h> -#include <stdarg.h> - -#include <nghttp2/nghttp2ver.h> - -#ifdef NGHTTP2_STATICLIB +#endif /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */ +#include <sys/types.h> +#include <stdarg.h> + +#include <nghttp2/nghttp2ver.h> + +#ifdef NGHTTP2_STATICLIB # define NGHTTP2_EXTERN #elif defined(WIN32) || (__has_declspec_attribute(dllexport) && \ __has_declspec_attribute(dllimport)) @@ -70,165 +70,165 @@ extern "C" { # define NGHTTP2_EXTERN # endif /* !BUILDING_NGHTTP2 */ #endif /* !defined(WIN32) */ - -/** - * @macro - * - * The protocol version identification string of this library - * supports. This identifier is used if HTTP/2 is used over TLS. - */ -#define NGHTTP2_PROTO_VERSION_ID "h2" -/** - * @macro - * - * The length of :macro:`NGHTTP2_PROTO_VERSION_ID`. - */ -#define NGHTTP2_PROTO_VERSION_ID_LEN 2 - -/** - * @macro - * - * The serialized form of ALPN protocol identifier this library - * supports. Notice that first byte is the length of following - * protocol identifier. This is the same wire format of `TLS ALPN - * extension <https://tools.ietf.org/html/rfc7301>`_. This is useful - * to process incoming ALPN tokens in wire format. - */ -#define NGHTTP2_PROTO_ALPN "\x2h2" - -/** - * @macro - * - * The length of :macro:`NGHTTP2_PROTO_ALPN`. - */ -#define NGHTTP2_PROTO_ALPN_LEN (sizeof(NGHTTP2_PROTO_ALPN) - 1) - -/** - * @macro - * - * The protocol version identification string of this library - * supports. This identifier is used if HTTP/2 is used over cleartext - * TCP. - */ -#define NGHTTP2_CLEARTEXT_PROTO_VERSION_ID "h2c" - -/** - * @macro - * - * The length of :macro:`NGHTTP2_CLEARTEXT_PROTO_VERSION_ID`. - */ -#define NGHTTP2_CLEARTEXT_PROTO_VERSION_ID_LEN 3 - -struct nghttp2_session; -/** - * @struct - * - * The primary structure to hold the resources needed for a HTTP/2 - * session. The details of this structure are intentionally hidden - * from the public API. - */ -typedef struct nghttp2_session nghttp2_session; - -/** - * @macro - * - * The age of :type:`nghttp2_info` - */ -#define NGHTTP2_VERSION_AGE 1 - -/** - * @struct - * - * This struct is what `nghttp2_version()` returns. It holds - * information about the particular nghttp2 version. - */ -typedef struct { - /** - * Age of this struct. This instance of nghttp2 sets it to - * :macro:`NGHTTP2_VERSION_AGE` but a future version may bump it and - * add more struct fields at the bottom - */ - int age; - /** - * the :macro:`NGHTTP2_VERSION_NUM` number (since age ==1) - */ - int version_num; - /** - * points to the :macro:`NGHTTP2_VERSION` string (since age ==1) - */ - const char *version_str; - /** - * points to the :macro:`NGHTTP2_PROTO_VERSION_ID` string this - * instance implements (since age ==1) - */ - const char *proto_str; - /* -------- the above fields all exist when age == 1 */ -} nghttp2_info; - -/** - * @macro - * - * The default weight of stream dependency. - */ -#define NGHTTP2_DEFAULT_WEIGHT 16 - -/** - * @macro - * - * The maximum weight of stream dependency. - */ -#define NGHTTP2_MAX_WEIGHT 256 - -/** - * @macro - * - * The minimum weight of stream dependency. - */ -#define NGHTTP2_MIN_WEIGHT 1 - -/** - * @macro - * - * The maximum window size - */ -#define NGHTTP2_MAX_WINDOW_SIZE ((int32_t)((1U << 31) - 1)) - -/** - * @macro - * - * The initial window size for stream level flow control. - */ -#define NGHTTP2_INITIAL_WINDOW_SIZE ((1 << 16) - 1) -/** - * @macro - * - * The initial window size for connection level flow control. - */ -#define NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE ((1 << 16) - 1) - -/** - * @macro - * - * The default header table size. - */ -#define NGHTTP2_DEFAULT_HEADER_TABLE_SIZE (1 << 12) - -/** - * @macro - * - * The client magic string, which is the first 24 bytes byte string of - * client connection preface. - */ -#define NGHTTP2_CLIENT_MAGIC "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n" - -/** - * @macro - * - * The length of :macro:`NGHTTP2_CLIENT_MAGIC`. - */ -#define NGHTTP2_CLIENT_MAGIC_LEN 24 - -/** + +/** + * @macro + * + * The protocol version identification string of this library + * supports. This identifier is used if HTTP/2 is used over TLS. + */ +#define NGHTTP2_PROTO_VERSION_ID "h2" +/** + * @macro + * + * The length of :macro:`NGHTTP2_PROTO_VERSION_ID`. + */ +#define NGHTTP2_PROTO_VERSION_ID_LEN 2 + +/** + * @macro + * + * The serialized form of ALPN protocol identifier this library + * supports. Notice that first byte is the length of following + * protocol identifier. This is the same wire format of `TLS ALPN + * extension <https://tools.ietf.org/html/rfc7301>`_. This is useful + * to process incoming ALPN tokens in wire format. + */ +#define NGHTTP2_PROTO_ALPN "\x2h2" + +/** + * @macro + * + * The length of :macro:`NGHTTP2_PROTO_ALPN`. + */ +#define NGHTTP2_PROTO_ALPN_LEN (sizeof(NGHTTP2_PROTO_ALPN) - 1) + +/** + * @macro + * + * The protocol version identification string of this library + * supports. This identifier is used if HTTP/2 is used over cleartext + * TCP. + */ +#define NGHTTP2_CLEARTEXT_PROTO_VERSION_ID "h2c" + +/** + * @macro + * + * The length of :macro:`NGHTTP2_CLEARTEXT_PROTO_VERSION_ID`. + */ +#define NGHTTP2_CLEARTEXT_PROTO_VERSION_ID_LEN 3 + +struct nghttp2_session; +/** + * @struct + * + * The primary structure to hold the resources needed for a HTTP/2 + * session. The details of this structure are intentionally hidden + * from the public API. + */ +typedef struct nghttp2_session nghttp2_session; + +/** + * @macro + * + * The age of :type:`nghttp2_info` + */ +#define NGHTTP2_VERSION_AGE 1 + +/** + * @struct + * + * This struct is what `nghttp2_version()` returns. It holds + * information about the particular nghttp2 version. + */ +typedef struct { + /** + * Age of this struct. This instance of nghttp2 sets it to + * :macro:`NGHTTP2_VERSION_AGE` but a future version may bump it and + * add more struct fields at the bottom + */ + int age; + /** + * the :macro:`NGHTTP2_VERSION_NUM` number (since age ==1) + */ + int version_num; + /** + * points to the :macro:`NGHTTP2_VERSION` string (since age ==1) + */ + const char *version_str; + /** + * points to the :macro:`NGHTTP2_PROTO_VERSION_ID` string this + * instance implements (since age ==1) + */ + const char *proto_str; + /* -------- the above fields all exist when age == 1 */ +} nghttp2_info; + +/** + * @macro + * + * The default weight of stream dependency. + */ +#define NGHTTP2_DEFAULT_WEIGHT 16 + +/** + * @macro + * + * The maximum weight of stream dependency. + */ +#define NGHTTP2_MAX_WEIGHT 256 + +/** + * @macro + * + * The minimum weight of stream dependency. + */ +#define NGHTTP2_MIN_WEIGHT 1 + +/** + * @macro + * + * The maximum window size + */ +#define NGHTTP2_MAX_WINDOW_SIZE ((int32_t)((1U << 31) - 1)) + +/** + * @macro + * + * The initial window size for stream level flow control. + */ +#define NGHTTP2_INITIAL_WINDOW_SIZE ((1 << 16) - 1) +/** + * @macro + * + * The initial window size for connection level flow control. + */ +#define NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE ((1 << 16) - 1) + +/** + * @macro + * + * The default header table size. + */ +#define NGHTTP2_DEFAULT_HEADER_TABLE_SIZE (1 << 12) + +/** + * @macro + * + * The client magic string, which is the first 24 bytes byte string of + * client connection preface. + */ +#define NGHTTP2_CLIENT_MAGIC "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n" + +/** + * @macro + * + * The length of :macro:`NGHTTP2_CLIENT_MAGIC`. + */ +#define NGHTTP2_CLIENT_MAGIC_LEN 24 + +/** * @macro * * The default max number of settings per SETTINGS frame @@ -236,176 +236,176 @@ typedef struct { #define NGHTTP2_DEFAULT_MAX_SETTINGS 32 /** - * @enum - * - * Error codes used in this library. The code range is [-999, -500], - * inclusive. The following values are defined: - */ -typedef enum { - /** - * Invalid argument passed. - */ - NGHTTP2_ERR_INVALID_ARGUMENT = -501, - /** - * Out of buffer space. - */ - NGHTTP2_ERR_BUFFER_ERROR = -502, - /** - * The specified protocol version is not supported. - */ - NGHTTP2_ERR_UNSUPPORTED_VERSION = -503, - /** - * Used as a return value from :type:`nghttp2_send_callback`, - * :type:`nghttp2_recv_callback` and - * :type:`nghttp2_send_data_callback` to indicate that the operation - * would block. - */ - NGHTTP2_ERR_WOULDBLOCK = -504, - /** - * General protocol error - */ - NGHTTP2_ERR_PROTO = -505, - /** - * The frame is invalid. - */ - NGHTTP2_ERR_INVALID_FRAME = -506, - /** - * The peer performed a shutdown on the connection. - */ - NGHTTP2_ERR_EOF = -507, - /** - * Used as a return value from - * :func:`nghttp2_data_source_read_callback` to indicate that data - * transfer is postponed. See - * :func:`nghttp2_data_source_read_callback` for details. - */ - NGHTTP2_ERR_DEFERRED = -508, - /** - * Stream ID has reached the maximum value. Therefore no stream ID - * is available. - */ - NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE = -509, - /** - * The stream is already closed; or the stream ID is invalid. - */ - NGHTTP2_ERR_STREAM_CLOSED = -510, - /** - * RST_STREAM has been added to the outbound queue. The stream is - * in closing state. - */ - NGHTTP2_ERR_STREAM_CLOSING = -511, - /** - * The transmission is not allowed for this stream (e.g., a frame - * with END_STREAM flag set has already sent). - */ - NGHTTP2_ERR_STREAM_SHUT_WR = -512, - /** - * The stream ID is invalid. - */ - NGHTTP2_ERR_INVALID_STREAM_ID = -513, - /** - * The state of the stream is not valid (e.g., DATA cannot be sent - * to the stream if response HEADERS has not been sent). - */ - NGHTTP2_ERR_INVALID_STREAM_STATE = -514, - /** - * Another DATA frame has already been deferred. - */ - NGHTTP2_ERR_DEFERRED_DATA_EXIST = -515, - /** - * Starting new stream is not allowed (e.g., GOAWAY has been sent - * and/or received). - */ - NGHTTP2_ERR_START_STREAM_NOT_ALLOWED = -516, - /** - * GOAWAY has already been sent. - */ - NGHTTP2_ERR_GOAWAY_ALREADY_SENT = -517, - /** - * The received frame contains the invalid header block (e.g., There - * are duplicate header names; or the header names are not encoded - * in US-ASCII character set and not lower cased; or the header name - * is zero-length string; or the header value contains multiple - * in-sequence NUL bytes). - */ - NGHTTP2_ERR_INVALID_HEADER_BLOCK = -518, - /** - * Indicates that the context is not suitable to perform the - * requested operation. - */ - NGHTTP2_ERR_INVALID_STATE = -519, - /** - * The user callback function failed due to the temporal error. - */ - NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE = -521, - /** - * The length of the frame is invalid, either too large or too small. - */ - NGHTTP2_ERR_FRAME_SIZE_ERROR = -522, - /** - * Header block inflate/deflate error. - */ - NGHTTP2_ERR_HEADER_COMP = -523, - /** - * Flow control error - */ - NGHTTP2_ERR_FLOW_CONTROL = -524, - /** - * Insufficient buffer size given to function. - */ - NGHTTP2_ERR_INSUFF_BUFSIZE = -525, - /** - * Callback was paused by the application - */ - NGHTTP2_ERR_PAUSE = -526, - /** - * There are too many in-flight SETTING frame and no more - * transmission of SETTINGS is allowed. - */ - NGHTTP2_ERR_TOO_MANY_INFLIGHT_SETTINGS = -527, - /** - * The server push is disabled. - */ - NGHTTP2_ERR_PUSH_DISABLED = -528, - /** - * DATA or HEADERS frame for a given stream has been already - * submitted and has not been fully processed yet. Application - * should wait for the transmission of the previously submitted - * frame before submitting another. - */ - NGHTTP2_ERR_DATA_EXIST = -529, - /** - * The current session is closing due to a connection error or - * `nghttp2_session_terminate_session()` is called. - */ - NGHTTP2_ERR_SESSION_CLOSING = -530, - /** - * Invalid HTTP header field was received and stream is going to be - * closed. - */ - NGHTTP2_ERR_HTTP_HEADER = -531, - /** - * Violation in HTTP messaging rule. - */ - NGHTTP2_ERR_HTTP_MESSAGING = -532, - /** - * Stream was refused. - */ - NGHTTP2_ERR_REFUSED_STREAM = -533, - /** - * Unexpected internal error, but recovered. - */ - NGHTTP2_ERR_INTERNAL = -534, - /** - * Indicates that a processing was canceled. - */ - NGHTTP2_ERR_CANCEL = -535, - /** - * When a local endpoint expects to receive SETTINGS frame, it - * receives an other type of frame. - */ - NGHTTP2_ERR_SETTINGS_EXPECTED = -536, - /** + * @enum + * + * Error codes used in this library. The code range is [-999, -500], + * inclusive. The following values are defined: + */ +typedef enum { + /** + * Invalid argument passed. + */ + NGHTTP2_ERR_INVALID_ARGUMENT = -501, + /** + * Out of buffer space. + */ + NGHTTP2_ERR_BUFFER_ERROR = -502, + /** + * The specified protocol version is not supported. + */ + NGHTTP2_ERR_UNSUPPORTED_VERSION = -503, + /** + * Used as a return value from :type:`nghttp2_send_callback`, + * :type:`nghttp2_recv_callback` and + * :type:`nghttp2_send_data_callback` to indicate that the operation + * would block. + */ + NGHTTP2_ERR_WOULDBLOCK = -504, + /** + * General protocol error + */ + NGHTTP2_ERR_PROTO = -505, + /** + * The frame is invalid. + */ + NGHTTP2_ERR_INVALID_FRAME = -506, + /** + * The peer performed a shutdown on the connection. + */ + NGHTTP2_ERR_EOF = -507, + /** + * Used as a return value from + * :func:`nghttp2_data_source_read_callback` to indicate that data + * transfer is postponed. See + * :func:`nghttp2_data_source_read_callback` for details. + */ + NGHTTP2_ERR_DEFERRED = -508, + /** + * Stream ID has reached the maximum value. Therefore no stream ID + * is available. + */ + NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE = -509, + /** + * The stream is already closed; or the stream ID is invalid. + */ + NGHTTP2_ERR_STREAM_CLOSED = -510, + /** + * RST_STREAM has been added to the outbound queue. The stream is + * in closing state. + */ + NGHTTP2_ERR_STREAM_CLOSING = -511, + /** + * The transmission is not allowed for this stream (e.g., a frame + * with END_STREAM flag set has already sent). + */ + NGHTTP2_ERR_STREAM_SHUT_WR = -512, + /** + * The stream ID is invalid. + */ + NGHTTP2_ERR_INVALID_STREAM_ID = -513, + /** + * The state of the stream is not valid (e.g., DATA cannot be sent + * to the stream if response HEADERS has not been sent). + */ + NGHTTP2_ERR_INVALID_STREAM_STATE = -514, + /** + * Another DATA frame has already been deferred. + */ + NGHTTP2_ERR_DEFERRED_DATA_EXIST = -515, + /** + * Starting new stream is not allowed (e.g., GOAWAY has been sent + * and/or received). + */ + NGHTTP2_ERR_START_STREAM_NOT_ALLOWED = -516, + /** + * GOAWAY has already been sent. + */ + NGHTTP2_ERR_GOAWAY_ALREADY_SENT = -517, + /** + * The received frame contains the invalid header block (e.g., There + * are duplicate header names; or the header names are not encoded + * in US-ASCII character set and not lower cased; or the header name + * is zero-length string; or the header value contains multiple + * in-sequence NUL bytes). + */ + NGHTTP2_ERR_INVALID_HEADER_BLOCK = -518, + /** + * Indicates that the context is not suitable to perform the + * requested operation. + */ + NGHTTP2_ERR_INVALID_STATE = -519, + /** + * The user callback function failed due to the temporal error. + */ + NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE = -521, + /** + * The length of the frame is invalid, either too large or too small. + */ + NGHTTP2_ERR_FRAME_SIZE_ERROR = -522, + /** + * Header block inflate/deflate error. + */ + NGHTTP2_ERR_HEADER_COMP = -523, + /** + * Flow control error + */ + NGHTTP2_ERR_FLOW_CONTROL = -524, + /** + * Insufficient buffer size given to function. + */ + NGHTTP2_ERR_INSUFF_BUFSIZE = -525, + /** + * Callback was paused by the application + */ + NGHTTP2_ERR_PAUSE = -526, + /** + * There are too many in-flight SETTING frame and no more + * transmission of SETTINGS is allowed. + */ + NGHTTP2_ERR_TOO_MANY_INFLIGHT_SETTINGS = -527, + /** + * The server push is disabled. + */ + NGHTTP2_ERR_PUSH_DISABLED = -528, + /** + * DATA or HEADERS frame for a given stream has been already + * submitted and has not been fully processed yet. Application + * should wait for the transmission of the previously submitted + * frame before submitting another. + */ + NGHTTP2_ERR_DATA_EXIST = -529, + /** + * The current session is closing due to a connection error or + * `nghttp2_session_terminate_session()` is called. + */ + NGHTTP2_ERR_SESSION_CLOSING = -530, + /** + * Invalid HTTP header field was received and stream is going to be + * closed. + */ + NGHTTP2_ERR_HTTP_HEADER = -531, + /** + * Violation in HTTP messaging rule. + */ + NGHTTP2_ERR_HTTP_MESSAGING = -532, + /** + * Stream was refused. + */ + NGHTTP2_ERR_REFUSED_STREAM = -533, + /** + * Unexpected internal error, but recovered. + */ + NGHTTP2_ERR_INTERNAL = -534, + /** + * Indicates that a processing was canceled. + */ + NGHTTP2_ERR_CANCEL = -535, + /** + * When a local endpoint expects to receive SETTINGS frame, it + * receives an other type of frame. + */ + NGHTTP2_ERR_SETTINGS_EXPECTED = -536, + /** * When a local endpoint receives too many settings entries * in a single SETTINGS frame. */ @@ -417,477 +417,477 @@ typedef enum { * error code, it must stop using that :type:`nghttp2_session` * object and only allowed operation for that object is deallocate * it using `nghttp2_session_del()`. - */ - NGHTTP2_ERR_FATAL = -900, - /** - * Out of memory. This is a fatal error. - */ - NGHTTP2_ERR_NOMEM = -901, - /** - * The user callback function failed. This is a fatal error. - */ - NGHTTP2_ERR_CALLBACK_FAILURE = -902, - /** - * Invalid client magic (see :macro:`NGHTTP2_CLIENT_MAGIC`) was - * received and further processing is not possible. - */ - NGHTTP2_ERR_BAD_CLIENT_MAGIC = -903, - /** - * Possible flooding by peer was detected in this HTTP/2 session. - * Flooding is measured by how many PING and SETTINGS frames with - * ACK flag set are queued for transmission. These frames are - * response for the peer initiated frames, and peer can cause memory - * exhaustion on server side to send these frames forever and does - * not read network. - */ - NGHTTP2_ERR_FLOODED = -904 -} nghttp2_error; - -/** - * @struct - * - * The object representing single contiguous buffer. - */ -typedef struct { - /** - * The pointer to the buffer. - */ - uint8_t *base; - /** - * The length of the buffer. - */ - size_t len; -} nghttp2_vec; - -struct nghttp2_rcbuf; - -/** - * @struct - * - * The object representing reference counted buffer. The details of - * this structure are intentionally hidden from the public API. - */ -typedef struct nghttp2_rcbuf nghttp2_rcbuf; - -/** - * @function - * - * Increments the reference count of |rcbuf| by 1. - */ -NGHTTP2_EXTERN void nghttp2_rcbuf_incref(nghttp2_rcbuf *rcbuf); - -/** - * @function - * - * Decrements the reference count of |rcbuf| by 1. If the reference - * count becomes zero, the object pointed by |rcbuf| will be freed. - * In this case, application must not use |rcbuf| again. - */ -NGHTTP2_EXTERN void nghttp2_rcbuf_decref(nghttp2_rcbuf *rcbuf); - -/** - * @function - * - * Returns the underlying buffer managed by |rcbuf|. - */ -NGHTTP2_EXTERN nghttp2_vec nghttp2_rcbuf_get_buf(nghttp2_rcbuf *rcbuf); - -/** - * @function - * - * Returns nonzero if the underlying buffer is statically allocated, - * and 0 otherwise. This can be useful for language bindings that wish - * to avoid creating duplicate strings for these buffers. - */ -NGHTTP2_EXTERN int nghttp2_rcbuf_is_static(const nghttp2_rcbuf *rcbuf); - -/** - * @enum - * - * The flags for header field name/value pair. - */ -typedef enum { - /** - * No flag set. - */ - NGHTTP2_NV_FLAG_NONE = 0, - /** - * Indicates that this name/value pair must not be indexed ("Literal - * Header Field never Indexed" representation must be used in HPACK - * encoding). Other implementation calls this bit as "sensitive". - */ - NGHTTP2_NV_FLAG_NO_INDEX = 0x01, - /** - * This flag is set solely by application. If this flag is set, the - * library does not make a copy of header field name. This could - * improve performance. - */ - NGHTTP2_NV_FLAG_NO_COPY_NAME = 0x02, - /** - * This flag is set solely by application. If this flag is set, the - * library does not make a copy of header field value. This could - * improve performance. - */ - NGHTTP2_NV_FLAG_NO_COPY_VALUE = 0x04 -} nghttp2_nv_flag; - -/** - * @struct - * - * The name/value pair, which mainly used to represent header fields. - */ -typedef struct { - /** - * The |name| byte string. If this struct is presented from library - * (e.g., :type:`nghttp2_on_frame_recv_callback`), |name| is - * guaranteed to be NULL-terminated. For some callbacks - * (:type:`nghttp2_before_frame_send_callback`, - * :type:`nghttp2_on_frame_send_callback`, and - * :type:`nghttp2_on_frame_not_send_callback`), it may not be - * NULL-terminated if header field is passed from application with + */ + NGHTTP2_ERR_FATAL = -900, + /** + * Out of memory. This is a fatal error. + */ + NGHTTP2_ERR_NOMEM = -901, + /** + * The user callback function failed. This is a fatal error. + */ + NGHTTP2_ERR_CALLBACK_FAILURE = -902, + /** + * Invalid client magic (see :macro:`NGHTTP2_CLIENT_MAGIC`) was + * received and further processing is not possible. + */ + NGHTTP2_ERR_BAD_CLIENT_MAGIC = -903, + /** + * Possible flooding by peer was detected in this HTTP/2 session. + * Flooding is measured by how many PING and SETTINGS frames with + * ACK flag set are queued for transmission. These frames are + * response for the peer initiated frames, and peer can cause memory + * exhaustion on server side to send these frames forever and does + * not read network. + */ + NGHTTP2_ERR_FLOODED = -904 +} nghttp2_error; + +/** + * @struct + * + * The object representing single contiguous buffer. + */ +typedef struct { + /** + * The pointer to the buffer. + */ + uint8_t *base; + /** + * The length of the buffer. + */ + size_t len; +} nghttp2_vec; + +struct nghttp2_rcbuf; + +/** + * @struct + * + * The object representing reference counted buffer. The details of + * this structure are intentionally hidden from the public API. + */ +typedef struct nghttp2_rcbuf nghttp2_rcbuf; + +/** + * @function + * + * Increments the reference count of |rcbuf| by 1. + */ +NGHTTP2_EXTERN void nghttp2_rcbuf_incref(nghttp2_rcbuf *rcbuf); + +/** + * @function + * + * Decrements the reference count of |rcbuf| by 1. If the reference + * count becomes zero, the object pointed by |rcbuf| will be freed. + * In this case, application must not use |rcbuf| again. + */ +NGHTTP2_EXTERN void nghttp2_rcbuf_decref(nghttp2_rcbuf *rcbuf); + +/** + * @function + * + * Returns the underlying buffer managed by |rcbuf|. + */ +NGHTTP2_EXTERN nghttp2_vec nghttp2_rcbuf_get_buf(nghttp2_rcbuf *rcbuf); + +/** + * @function + * + * Returns nonzero if the underlying buffer is statically allocated, + * and 0 otherwise. This can be useful for language bindings that wish + * to avoid creating duplicate strings for these buffers. + */ +NGHTTP2_EXTERN int nghttp2_rcbuf_is_static(const nghttp2_rcbuf *rcbuf); + +/** + * @enum + * + * The flags for header field name/value pair. + */ +typedef enum { + /** + * No flag set. + */ + NGHTTP2_NV_FLAG_NONE = 0, + /** + * Indicates that this name/value pair must not be indexed ("Literal + * Header Field never Indexed" representation must be used in HPACK + * encoding). Other implementation calls this bit as "sensitive". + */ + NGHTTP2_NV_FLAG_NO_INDEX = 0x01, + /** + * This flag is set solely by application. If this flag is set, the + * library does not make a copy of header field name. This could + * improve performance. + */ + NGHTTP2_NV_FLAG_NO_COPY_NAME = 0x02, + /** + * This flag is set solely by application. If this flag is set, the + * library does not make a copy of header field value. This could + * improve performance. + */ + NGHTTP2_NV_FLAG_NO_COPY_VALUE = 0x04 +} nghttp2_nv_flag; + +/** + * @struct + * + * The name/value pair, which mainly used to represent header fields. + */ +typedef struct { + /** + * The |name| byte string. If this struct is presented from library + * (e.g., :type:`nghttp2_on_frame_recv_callback`), |name| is + * guaranteed to be NULL-terminated. For some callbacks + * (:type:`nghttp2_before_frame_send_callback`, + * :type:`nghttp2_on_frame_send_callback`, and + * :type:`nghttp2_on_frame_not_send_callback`), it may not be + * NULL-terminated if header field is passed from application with * the flag :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME`). * When application is constructing this struct, |name| is not * required to be NULL-terminated. - */ - uint8_t *name; - /** - * The |value| byte string. If this struct is presented from - * library (e.g., :type:`nghttp2_on_frame_recv_callback`), |value| - * is guaranteed to be NULL-terminated. For some callbacks - * (:type:`nghttp2_before_frame_send_callback`, - * :type:`nghttp2_on_frame_send_callback`, and - * :type:`nghttp2_on_frame_not_send_callback`), it may not be - * NULL-terminated if header field is passed from application with + */ + uint8_t *name; + /** + * The |value| byte string. If this struct is presented from + * library (e.g., :type:`nghttp2_on_frame_recv_callback`), |value| + * is guaranteed to be NULL-terminated. For some callbacks + * (:type:`nghttp2_before_frame_send_callback`, + * :type:`nghttp2_on_frame_send_callback`, and + * :type:`nghttp2_on_frame_not_send_callback`), it may not be + * NULL-terminated if header field is passed from application with * the flag :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_VALUE`). * When application is constructing this struct, |value| is not * required to be NULL-terminated. - */ - uint8_t *value; - /** - * The length of the |name|, excluding terminating NULL. - */ - size_t namelen; - /** - * The length of the |value|, excluding terminating NULL. - */ - size_t valuelen; - /** - * Bitwise OR of one or more of :type:`nghttp2_nv_flag`. - */ - uint8_t flags; -} nghttp2_nv; - -/** - * @enum - * - * The frame types in HTTP/2 specification. - */ -typedef enum { - /** - * The DATA frame. - */ - NGHTTP2_DATA = 0, - /** - * The HEADERS frame. - */ - NGHTTP2_HEADERS = 0x01, - /** - * The PRIORITY frame. - */ - NGHTTP2_PRIORITY = 0x02, - /** - * The RST_STREAM frame. - */ - NGHTTP2_RST_STREAM = 0x03, - /** - * The SETTINGS frame. - */ - NGHTTP2_SETTINGS = 0x04, - /** - * The PUSH_PROMISE frame. - */ - NGHTTP2_PUSH_PROMISE = 0x05, - /** - * The PING frame. - */ - NGHTTP2_PING = 0x06, - /** - * The GOAWAY frame. - */ - NGHTTP2_GOAWAY = 0x07, - /** - * The WINDOW_UPDATE frame. - */ - NGHTTP2_WINDOW_UPDATE = 0x08, - /** - * The CONTINUATION frame. This frame type won't be passed to any - * callbacks because the library processes this frame type and its - * preceding HEADERS/PUSH_PROMISE as a single frame. - */ - NGHTTP2_CONTINUATION = 0x09, - /** - * The ALTSVC frame, which is defined in `RFC 7383 - * <https://tools.ietf.org/html/rfc7838#section-4>`_. - */ + */ + uint8_t *value; + /** + * The length of the |name|, excluding terminating NULL. + */ + size_t namelen; + /** + * The length of the |value|, excluding terminating NULL. + */ + size_t valuelen; + /** + * Bitwise OR of one or more of :type:`nghttp2_nv_flag`. + */ + uint8_t flags; +} nghttp2_nv; + +/** + * @enum + * + * The frame types in HTTP/2 specification. + */ +typedef enum { + /** + * The DATA frame. + */ + NGHTTP2_DATA = 0, + /** + * The HEADERS frame. + */ + NGHTTP2_HEADERS = 0x01, + /** + * The PRIORITY frame. + */ + NGHTTP2_PRIORITY = 0x02, + /** + * The RST_STREAM frame. + */ + NGHTTP2_RST_STREAM = 0x03, + /** + * The SETTINGS frame. + */ + NGHTTP2_SETTINGS = 0x04, + /** + * The PUSH_PROMISE frame. + */ + NGHTTP2_PUSH_PROMISE = 0x05, + /** + * The PING frame. + */ + NGHTTP2_PING = 0x06, + /** + * The GOAWAY frame. + */ + NGHTTP2_GOAWAY = 0x07, + /** + * The WINDOW_UPDATE frame. + */ + NGHTTP2_WINDOW_UPDATE = 0x08, + /** + * The CONTINUATION frame. This frame type won't be passed to any + * callbacks because the library processes this frame type and its + * preceding HEADERS/PUSH_PROMISE as a single frame. + */ + NGHTTP2_CONTINUATION = 0x09, + /** + * The ALTSVC frame, which is defined in `RFC 7383 + * <https://tools.ietf.org/html/rfc7838#section-4>`_. + */ NGHTTP2_ALTSVC = 0x0a, /** * The ORIGIN frame, which is defined by `RFC 8336 * <https://tools.ietf.org/html/rfc8336>`_. */ NGHTTP2_ORIGIN = 0x0c -} nghttp2_frame_type; - -/** - * @enum - * - * The flags for HTTP/2 frames. This enum defines all flags for all - * frames. - */ -typedef enum { - /** - * No flag set. - */ - NGHTTP2_FLAG_NONE = 0, - /** - * The END_STREAM flag. - */ - NGHTTP2_FLAG_END_STREAM = 0x01, - /** - * The END_HEADERS flag. - */ - NGHTTP2_FLAG_END_HEADERS = 0x04, - /** - * The ACK flag. - */ - NGHTTP2_FLAG_ACK = 0x01, - /** - * The PADDED flag. - */ - NGHTTP2_FLAG_PADDED = 0x08, - /** - * The PRIORITY flag. - */ - NGHTTP2_FLAG_PRIORITY = 0x20 -} nghttp2_flag; - -/** - * @enum - * The SETTINGS ID. - */ -typedef enum { - /** - * SETTINGS_HEADER_TABLE_SIZE - */ - NGHTTP2_SETTINGS_HEADER_TABLE_SIZE = 0x01, - /** - * SETTINGS_ENABLE_PUSH - */ - NGHTTP2_SETTINGS_ENABLE_PUSH = 0x02, - /** - * SETTINGS_MAX_CONCURRENT_STREAMS - */ - NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS = 0x03, - /** - * SETTINGS_INITIAL_WINDOW_SIZE - */ - NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE = 0x04, - /** - * SETTINGS_MAX_FRAME_SIZE - */ - NGHTTP2_SETTINGS_MAX_FRAME_SIZE = 0x05, - /** - * SETTINGS_MAX_HEADER_LIST_SIZE - */ +} nghttp2_frame_type; + +/** + * @enum + * + * The flags for HTTP/2 frames. This enum defines all flags for all + * frames. + */ +typedef enum { + /** + * No flag set. + */ + NGHTTP2_FLAG_NONE = 0, + /** + * The END_STREAM flag. + */ + NGHTTP2_FLAG_END_STREAM = 0x01, + /** + * The END_HEADERS flag. + */ + NGHTTP2_FLAG_END_HEADERS = 0x04, + /** + * The ACK flag. + */ + NGHTTP2_FLAG_ACK = 0x01, + /** + * The PADDED flag. + */ + NGHTTP2_FLAG_PADDED = 0x08, + /** + * The PRIORITY flag. + */ + NGHTTP2_FLAG_PRIORITY = 0x20 +} nghttp2_flag; + +/** + * @enum + * The SETTINGS ID. + */ +typedef enum { + /** + * SETTINGS_HEADER_TABLE_SIZE + */ + NGHTTP2_SETTINGS_HEADER_TABLE_SIZE = 0x01, + /** + * SETTINGS_ENABLE_PUSH + */ + NGHTTP2_SETTINGS_ENABLE_PUSH = 0x02, + /** + * SETTINGS_MAX_CONCURRENT_STREAMS + */ + NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS = 0x03, + /** + * SETTINGS_INITIAL_WINDOW_SIZE + */ + NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE = 0x04, + /** + * SETTINGS_MAX_FRAME_SIZE + */ + NGHTTP2_SETTINGS_MAX_FRAME_SIZE = 0x05, + /** + * SETTINGS_MAX_HEADER_LIST_SIZE + */ NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE = 0x06, /** * SETTINGS_ENABLE_CONNECT_PROTOCOL * (`RFC 8441 <https://tools.ietf.org/html/rfc8441>`_) */ NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL = 0x08 -} nghttp2_settings_id; -/* Note: If we add SETTINGS, update the capacity of - NGHTTP2_INBOUND_NUM_IV as well */ - -/** - * @macro - * - * .. warning:: - * - * Deprecated. The initial max concurrent streams is 0xffffffffu. - * - * Default maximum number of incoming concurrent streams. Use - * `nghttp2_submit_settings()` with +} nghttp2_settings_id; +/* Note: If we add SETTINGS, update the capacity of + NGHTTP2_INBOUND_NUM_IV as well */ + +/** + * @macro + * + * .. warning:: + * + * Deprecated. The initial max concurrent streams is 0xffffffffu. + * + * Default maximum number of incoming concurrent streams. Use + * `nghttp2_submit_settings()` with * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS` * to change the maximum number of incoming concurrent streams. - * - * .. note:: - * - * The maximum number of outgoing concurrent streams is 100 by - * default. - */ -#define NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS ((1U << 31) - 1) - -/** - * @enum - * The status codes for the RST_STREAM and GOAWAY frames. - */ -typedef enum { - /** - * No errors. - */ - NGHTTP2_NO_ERROR = 0x00, - /** - * PROTOCOL_ERROR - */ - NGHTTP2_PROTOCOL_ERROR = 0x01, - /** - * INTERNAL_ERROR - */ - NGHTTP2_INTERNAL_ERROR = 0x02, - /** - * FLOW_CONTROL_ERROR - */ - NGHTTP2_FLOW_CONTROL_ERROR = 0x03, - /** - * SETTINGS_TIMEOUT - */ - NGHTTP2_SETTINGS_TIMEOUT = 0x04, - /** - * STREAM_CLOSED - */ - NGHTTP2_STREAM_CLOSED = 0x05, - /** - * FRAME_SIZE_ERROR - */ - NGHTTP2_FRAME_SIZE_ERROR = 0x06, - /** - * REFUSED_STREAM - */ - NGHTTP2_REFUSED_STREAM = 0x07, - /** - * CANCEL - */ - NGHTTP2_CANCEL = 0x08, - /** - * COMPRESSION_ERROR - */ - NGHTTP2_COMPRESSION_ERROR = 0x09, - /** - * CONNECT_ERROR - */ - NGHTTP2_CONNECT_ERROR = 0x0a, - /** - * ENHANCE_YOUR_CALM - */ - NGHTTP2_ENHANCE_YOUR_CALM = 0x0b, - /** - * INADEQUATE_SECURITY - */ - NGHTTP2_INADEQUATE_SECURITY = 0x0c, - /** - * HTTP_1_1_REQUIRED - */ - NGHTTP2_HTTP_1_1_REQUIRED = 0x0d -} nghttp2_error_code; - -/** - * @struct - * The frame header. - */ -typedef struct { - /** - * The length field of this frame, excluding frame header. - */ - size_t length; - /** - * The stream identifier (aka, stream ID) - */ - int32_t stream_id; - /** - * The type of this frame. See `nghttp2_frame_type`. - */ - uint8_t type; - /** - * The flags. - */ - uint8_t flags; - /** - * Reserved bit in frame header. Currently, this is always set to 0 - * and application should not expect something useful in here. - */ - uint8_t reserved; -} nghttp2_frame_hd; - -/** - * @union - * - * This union represents the some kind of data source passed to - * :type:`nghttp2_data_source_read_callback`. - */ -typedef union { - /** - * The integer field, suitable for a file descriptor. - */ - int fd; - /** - * The pointer to an arbitrary object. - */ - void *ptr; -} nghttp2_data_source; - -/** - * @enum - * - * The flags used to set in |data_flags| output parameter in - * :type:`nghttp2_data_source_read_callback`. - */ -typedef enum { - /** - * No flag set. - */ - NGHTTP2_DATA_FLAG_NONE = 0, - /** - * Indicates EOF was sensed. - */ - NGHTTP2_DATA_FLAG_EOF = 0x01, - /** - * Indicates that END_STREAM flag must not be set even if - * NGHTTP2_DATA_FLAG_EOF is set. Usually this flag is used to send - * trailer fields with `nghttp2_submit_request()` or - * `nghttp2_submit_response()`. - */ - NGHTTP2_DATA_FLAG_NO_END_STREAM = 0x02, - /** - * Indicates that application will send complete DATA frame in - * :type:`nghttp2_send_data_callback`. - */ - NGHTTP2_DATA_FLAG_NO_COPY = 0x04 -} nghttp2_data_flag; - -/** - * @functypedef - * - * Callback function invoked when the library wants to read data from - * the |source|. The read data is sent in the stream |stream_id|. - * The implementation of this function must read at most |length| - * bytes of data from |source| (or possibly other places) and store - * them in |buf| and return number of data stored in |buf|. If EOF is + * + * .. note:: + * + * The maximum number of outgoing concurrent streams is 100 by + * default. + */ +#define NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS ((1U << 31) - 1) + +/** + * @enum + * The status codes for the RST_STREAM and GOAWAY frames. + */ +typedef enum { + /** + * No errors. + */ + NGHTTP2_NO_ERROR = 0x00, + /** + * PROTOCOL_ERROR + */ + NGHTTP2_PROTOCOL_ERROR = 0x01, + /** + * INTERNAL_ERROR + */ + NGHTTP2_INTERNAL_ERROR = 0x02, + /** + * FLOW_CONTROL_ERROR + */ + NGHTTP2_FLOW_CONTROL_ERROR = 0x03, + /** + * SETTINGS_TIMEOUT + */ + NGHTTP2_SETTINGS_TIMEOUT = 0x04, + /** + * STREAM_CLOSED + */ + NGHTTP2_STREAM_CLOSED = 0x05, + /** + * FRAME_SIZE_ERROR + */ + NGHTTP2_FRAME_SIZE_ERROR = 0x06, + /** + * REFUSED_STREAM + */ + NGHTTP2_REFUSED_STREAM = 0x07, + /** + * CANCEL + */ + NGHTTP2_CANCEL = 0x08, + /** + * COMPRESSION_ERROR + */ + NGHTTP2_COMPRESSION_ERROR = 0x09, + /** + * CONNECT_ERROR + */ + NGHTTP2_CONNECT_ERROR = 0x0a, + /** + * ENHANCE_YOUR_CALM + */ + NGHTTP2_ENHANCE_YOUR_CALM = 0x0b, + /** + * INADEQUATE_SECURITY + */ + NGHTTP2_INADEQUATE_SECURITY = 0x0c, + /** + * HTTP_1_1_REQUIRED + */ + NGHTTP2_HTTP_1_1_REQUIRED = 0x0d +} nghttp2_error_code; + +/** + * @struct + * The frame header. + */ +typedef struct { + /** + * The length field of this frame, excluding frame header. + */ + size_t length; + /** + * The stream identifier (aka, stream ID) + */ + int32_t stream_id; + /** + * The type of this frame. See `nghttp2_frame_type`. + */ + uint8_t type; + /** + * The flags. + */ + uint8_t flags; + /** + * Reserved bit in frame header. Currently, this is always set to 0 + * and application should not expect something useful in here. + */ + uint8_t reserved; +} nghttp2_frame_hd; + +/** + * @union + * + * This union represents the some kind of data source passed to + * :type:`nghttp2_data_source_read_callback`. + */ +typedef union { + /** + * The integer field, suitable for a file descriptor. + */ + int fd; + /** + * The pointer to an arbitrary object. + */ + void *ptr; +} nghttp2_data_source; + +/** + * @enum + * + * The flags used to set in |data_flags| output parameter in + * :type:`nghttp2_data_source_read_callback`. + */ +typedef enum { + /** + * No flag set. + */ + NGHTTP2_DATA_FLAG_NONE = 0, + /** + * Indicates EOF was sensed. + */ + NGHTTP2_DATA_FLAG_EOF = 0x01, + /** + * Indicates that END_STREAM flag must not be set even if + * NGHTTP2_DATA_FLAG_EOF is set. Usually this flag is used to send + * trailer fields with `nghttp2_submit_request()` or + * `nghttp2_submit_response()`. + */ + NGHTTP2_DATA_FLAG_NO_END_STREAM = 0x02, + /** + * Indicates that application will send complete DATA frame in + * :type:`nghttp2_send_data_callback`. + */ + NGHTTP2_DATA_FLAG_NO_COPY = 0x04 +} nghttp2_data_flag; + +/** + * @functypedef + * + * Callback function invoked when the library wants to read data from + * the |source|. The read data is sent in the stream |stream_id|. + * The implementation of this function must read at most |length| + * bytes of data from |source| (or possibly other places) and store + * them in |buf| and return number of data stored in |buf|. If EOF is * reached, set :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_EOF` flag * in |*data_flags|. - * - * Sometime it is desirable to avoid copying data into |buf| and let - * application to send data directly. To achieve this, set + * + * Sometime it is desirable to avoid copying data into |buf| and let + * application to send data directly. To achieve this, set * :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_NO_COPY` to * |*data_flags| (and possibly other flags, just like when we do * copy), and return the number of bytes to send without copying data * into |buf|. The library, seeing * :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_NO_COPY`, will invoke - * :type:`nghttp2_send_data_callback`. The application must send - * complete DATA frame in that callback. - * - * If this callback is set by `nghttp2_submit_request()`, - * `nghttp2_submit_response()` or `nghttp2_submit_headers()` and - * `nghttp2_submit_data()` with flag parameter + * :type:`nghttp2_send_data_callback`. The application must send + * complete DATA frame in that callback. + * + * If this callback is set by `nghttp2_submit_request()`, + * `nghttp2_submit_response()` or `nghttp2_submit_headers()` and + * `nghttp2_submit_data()` with flag parameter * :enum:`nghttp2_flag.NGHTTP2_FLAG_END_STREAM` set, and * :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_EOF` flag is set to * |*data_flags|, DATA frame will have END_STREAM flag set. Usually, @@ -898,26 +898,26 @@ typedef enum { * with :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_EOF` to signal the * library not to set END_STREAM in DATA frame. Then application can * use `nghttp2_submit_trailer()` to send trailer fields. - * `nghttp2_submit_trailer()` can be called inside this callback. - * - * If the application wants to postpone DATA frames (e.g., - * asynchronous I/O, or reading data blocks for long time), it is + * `nghttp2_submit_trailer()` can be called inside this callback. + * + * If the application wants to postpone DATA frames (e.g., + * asynchronous I/O, or reading data blocks for long time), it is * achieved by returning :enum:`nghttp2_error.NGHTTP2_ERR_DEFERRED` * without reading any data in this invocation. The library removes * DATA frame from the outgoing queue temporarily. To move back * deferred DATA frame to outgoing queue, call * `nghttp2_session_resume_data()`. - * - * By default, |length| is limited to 16KiB at maximum. If peer - * allows larger frames, application can enlarge transmission buffer - * size. See :type:`nghttp2_data_source_read_length_callback` for - * more details. - * - * If the application just wants to return from - * `nghttp2_session_send()` or `nghttp2_session_mem_send()` without + * + * By default, |length| is limited to 16KiB at maximum. If peer + * allows larger frames, application can enlarge transmission buffer + * size. See :type:`nghttp2_data_source_read_length_callback` for + * more details. + * + * If the application just wants to return from + * `nghttp2_session_send()` or `nghttp2_session_mem_send()` without * sending anything, return :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE`. - * - * In case of error, there are 2 choices. Returning + * + * In case of error, there are 2 choices. Returning * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will * close the stream by issuing RST_STREAM with * :enum:`nghttp2_error_code.NGHTTP2_INTERNAL_ERROR`. If a different @@ -926,436 +926,436 @@ typedef enum { * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. * Returning :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` will * signal the entire session failure. - */ -typedef ssize_t (*nghttp2_data_source_read_callback)( - nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t length, - uint32_t *data_flags, nghttp2_data_source *source, void *user_data); - -/** - * @struct - * - * This struct represents the data source and the way to read a chunk - * of data from it. - */ -typedef struct { - /** - * The data source. - */ - nghttp2_data_source source; - /** - * The callback function to read a chunk of data from the |source|. - */ - nghttp2_data_source_read_callback read_callback; -} nghttp2_data_provider; - -/** - * @struct - * - * The DATA frame. The received data is delivered via - * :type:`nghttp2_on_data_chunk_recv_callback`. - */ -typedef struct { - nghttp2_frame_hd hd; - /** - * The length of the padding in this frame. This includes PAD_HIGH - * and PAD_LOW. - */ - size_t padlen; -} nghttp2_data; - -/** - * @enum - * - * The category of HEADERS, which indicates the role of the frame. In - * HTTP/2 spec, request, response, push response and other arbitrary - * headers (e.g., trailer fields) are all called just HEADERS. To - * give the application the role of incoming HEADERS frame, we define - * several categories. - */ -typedef enum { - /** - * The HEADERS frame is opening new stream, which is analogous to - * SYN_STREAM in SPDY. - */ - NGHTTP2_HCAT_REQUEST = 0, - /** - * The HEADERS frame is the first response headers, which is - * analogous to SYN_REPLY in SPDY. - */ - NGHTTP2_HCAT_RESPONSE = 1, - /** - * The HEADERS frame is the first headers sent against reserved - * stream. - */ - NGHTTP2_HCAT_PUSH_RESPONSE = 2, - /** - * The HEADERS frame which does not apply for the above categories, - * which is analogous to HEADERS in SPDY. If non-final response - * (e.g., status 1xx) is used, final response HEADERS frame will be - * categorized here. - */ - NGHTTP2_HCAT_HEADERS = 3 -} nghttp2_headers_category; - -/** - * @struct - * - * The structure to specify stream dependency. - */ -typedef struct { - /** - * The stream ID of the stream to depend on. Specifying 0 makes - * stream not depend any other stream. - */ - int32_t stream_id; - /** - * The weight of this dependency. - */ - int32_t weight; - /** - * nonzero means exclusive dependency - */ - uint8_t exclusive; -} nghttp2_priority_spec; - -/** - * @struct - * - * The HEADERS frame. It has the following members: - */ -typedef struct { - /** - * The frame header. - */ - nghttp2_frame_hd hd; - /** - * The length of the padding in this frame. This includes PAD_HIGH - * and PAD_LOW. - */ - size_t padlen; - /** - * The priority specification - */ - nghttp2_priority_spec pri_spec; - /** - * The name/value pairs. - */ - nghttp2_nv *nva; - /** - * The number of name/value pairs in |nva|. - */ - size_t nvlen; - /** - * The category of this HEADERS frame. - */ - nghttp2_headers_category cat; -} nghttp2_headers; - -/** - * @struct - * - * The PRIORITY frame. It has the following members: - */ -typedef struct { - /** - * The frame header. - */ - nghttp2_frame_hd hd; - /** - * The priority specification. - */ - nghttp2_priority_spec pri_spec; -} nghttp2_priority; - -/** - * @struct - * - * The RST_STREAM frame. It has the following members: - */ -typedef struct { - /** - * The frame header. - */ - nghttp2_frame_hd hd; - /** - * The error code. See :type:`nghttp2_error_code`. - */ - uint32_t error_code; -} nghttp2_rst_stream; - -/** - * @struct - * - * The SETTINGS ID/Value pair. It has the following members: - */ -typedef struct { - /** - * The SETTINGS ID. See :type:`nghttp2_settings_id`. - */ - int32_t settings_id; - /** - * The value of this entry. - */ - uint32_t value; -} nghttp2_settings_entry; - -/** - * @struct - * - * The SETTINGS frame. It has the following members: - */ -typedef struct { - /** - * The frame header. - */ - nghttp2_frame_hd hd; - /** - * The number of SETTINGS ID/Value pairs in |iv|. - */ - size_t niv; - /** - * The pointer to the array of SETTINGS ID/Value pair. - */ - nghttp2_settings_entry *iv; -} nghttp2_settings; - -/** - * @struct - * - * The PUSH_PROMISE frame. It has the following members: - */ -typedef struct { - /** - * The frame header. - */ - nghttp2_frame_hd hd; - /** - * The length of the padding in this frame. This includes PAD_HIGH - * and PAD_LOW. - */ - size_t padlen; - /** - * The name/value pairs. - */ - nghttp2_nv *nva; - /** - * The number of name/value pairs in |nva|. - */ - size_t nvlen; - /** - * The promised stream ID - */ - int32_t promised_stream_id; - /** - * Reserved bit. Currently this is always set to 0 and application - * should not expect something useful in here. - */ - uint8_t reserved; -} nghttp2_push_promise; - -/** - * @struct - * - * The PING frame. It has the following members: - */ -typedef struct { - /** - * The frame header. - */ - nghttp2_frame_hd hd; - /** - * The opaque data - */ - uint8_t opaque_data[8]; -} nghttp2_ping; - -/** - * @struct - * - * The GOAWAY frame. It has the following members: - */ -typedef struct { - /** - * The frame header. - */ - nghttp2_frame_hd hd; - /** - * The last stream stream ID. - */ - int32_t last_stream_id; - /** - * The error code. See :type:`nghttp2_error_code`. - */ - uint32_t error_code; - /** - * The additional debug data - */ - uint8_t *opaque_data; - /** - * The length of |opaque_data| member. - */ - size_t opaque_data_len; - /** - * Reserved bit. Currently this is always set to 0 and application - * should not expect something useful in here. - */ - uint8_t reserved; -} nghttp2_goaway; - -/** - * @struct - * - * The WINDOW_UPDATE frame. It has the following members: - */ -typedef struct { - /** - * The frame header. - */ - nghttp2_frame_hd hd; - /** - * The window size increment. - */ - int32_t window_size_increment; - /** - * Reserved bit. Currently this is always set to 0 and application - * should not expect something useful in here. - */ - uint8_t reserved; -} nghttp2_window_update; - -/** - * @struct - * - * The extension frame. It has following members: - */ -typedef struct { - /** - * The frame header. - */ - nghttp2_frame_hd hd; - /** - * The pointer to extension payload. The exact pointer type is - * determined by hd.type. - * - * Currently, no extension is supported. This is a place holder for - * the future extensions. - */ - void *payload; -} nghttp2_extension; - -/** - * @union - * - * This union includes all frames to pass them to various function - * calls as nghttp2_frame type. The CONTINUATION frame is omitted - * from here because the library deals with it internally. - */ -typedef union { - /** - * The frame header, which is convenient to inspect frame header. - */ - nghttp2_frame_hd hd; - /** - * The DATA frame. - */ - nghttp2_data data; - /** - * The HEADERS frame. - */ - nghttp2_headers headers; - /** - * The PRIORITY frame. - */ - nghttp2_priority priority; - /** - * The RST_STREAM frame. - */ - nghttp2_rst_stream rst_stream; - /** - * The SETTINGS frame. - */ - nghttp2_settings settings; - /** - * The PUSH_PROMISE frame. - */ - nghttp2_push_promise push_promise; - /** - * The PING frame. - */ - nghttp2_ping ping; - /** - * The GOAWAY frame. - */ - nghttp2_goaway goaway; - /** - * The WINDOW_UPDATE frame. - */ - nghttp2_window_update window_update; - /** - * The extension frame. - */ - nghttp2_extension ext; -} nghttp2_frame; - -/** - * @functypedef - * - * Callback function invoked when |session| wants to send data to the - * remote peer. The implementation of this function must send at most - * |length| bytes of data stored in |data|. The |flags| is currently - * not used and always 0. It must return the number of bytes sent if - * it succeeds. If it cannot send any single byte without blocking, + */ +typedef ssize_t (*nghttp2_data_source_read_callback)( + nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t length, + uint32_t *data_flags, nghttp2_data_source *source, void *user_data); + +/** + * @struct + * + * This struct represents the data source and the way to read a chunk + * of data from it. + */ +typedef struct { + /** + * The data source. + */ + nghttp2_data_source source; + /** + * The callback function to read a chunk of data from the |source|. + */ + nghttp2_data_source_read_callback read_callback; +} nghttp2_data_provider; + +/** + * @struct + * + * The DATA frame. The received data is delivered via + * :type:`nghttp2_on_data_chunk_recv_callback`. + */ +typedef struct { + nghttp2_frame_hd hd; + /** + * The length of the padding in this frame. This includes PAD_HIGH + * and PAD_LOW. + */ + size_t padlen; +} nghttp2_data; + +/** + * @enum + * + * The category of HEADERS, which indicates the role of the frame. In + * HTTP/2 spec, request, response, push response and other arbitrary + * headers (e.g., trailer fields) are all called just HEADERS. To + * give the application the role of incoming HEADERS frame, we define + * several categories. + */ +typedef enum { + /** + * The HEADERS frame is opening new stream, which is analogous to + * SYN_STREAM in SPDY. + */ + NGHTTP2_HCAT_REQUEST = 0, + /** + * The HEADERS frame is the first response headers, which is + * analogous to SYN_REPLY in SPDY. + */ + NGHTTP2_HCAT_RESPONSE = 1, + /** + * The HEADERS frame is the first headers sent against reserved + * stream. + */ + NGHTTP2_HCAT_PUSH_RESPONSE = 2, + /** + * The HEADERS frame which does not apply for the above categories, + * which is analogous to HEADERS in SPDY. If non-final response + * (e.g., status 1xx) is used, final response HEADERS frame will be + * categorized here. + */ + NGHTTP2_HCAT_HEADERS = 3 +} nghttp2_headers_category; + +/** + * @struct + * + * The structure to specify stream dependency. + */ +typedef struct { + /** + * The stream ID of the stream to depend on. Specifying 0 makes + * stream not depend any other stream. + */ + int32_t stream_id; + /** + * The weight of this dependency. + */ + int32_t weight; + /** + * nonzero means exclusive dependency + */ + uint8_t exclusive; +} nghttp2_priority_spec; + +/** + * @struct + * + * The HEADERS frame. It has the following members: + */ +typedef struct { + /** + * The frame header. + */ + nghttp2_frame_hd hd; + /** + * The length of the padding in this frame. This includes PAD_HIGH + * and PAD_LOW. + */ + size_t padlen; + /** + * The priority specification + */ + nghttp2_priority_spec pri_spec; + /** + * The name/value pairs. + */ + nghttp2_nv *nva; + /** + * The number of name/value pairs in |nva|. + */ + size_t nvlen; + /** + * The category of this HEADERS frame. + */ + nghttp2_headers_category cat; +} nghttp2_headers; + +/** + * @struct + * + * The PRIORITY frame. It has the following members: + */ +typedef struct { + /** + * The frame header. + */ + nghttp2_frame_hd hd; + /** + * The priority specification. + */ + nghttp2_priority_spec pri_spec; +} nghttp2_priority; + +/** + * @struct + * + * The RST_STREAM frame. It has the following members: + */ +typedef struct { + /** + * The frame header. + */ + nghttp2_frame_hd hd; + /** + * The error code. See :type:`nghttp2_error_code`. + */ + uint32_t error_code; +} nghttp2_rst_stream; + +/** + * @struct + * + * The SETTINGS ID/Value pair. It has the following members: + */ +typedef struct { + /** + * The SETTINGS ID. See :type:`nghttp2_settings_id`. + */ + int32_t settings_id; + /** + * The value of this entry. + */ + uint32_t value; +} nghttp2_settings_entry; + +/** + * @struct + * + * The SETTINGS frame. It has the following members: + */ +typedef struct { + /** + * The frame header. + */ + nghttp2_frame_hd hd; + /** + * The number of SETTINGS ID/Value pairs in |iv|. + */ + size_t niv; + /** + * The pointer to the array of SETTINGS ID/Value pair. + */ + nghttp2_settings_entry *iv; +} nghttp2_settings; + +/** + * @struct + * + * The PUSH_PROMISE frame. It has the following members: + */ +typedef struct { + /** + * The frame header. + */ + nghttp2_frame_hd hd; + /** + * The length of the padding in this frame. This includes PAD_HIGH + * and PAD_LOW. + */ + size_t padlen; + /** + * The name/value pairs. + */ + nghttp2_nv *nva; + /** + * The number of name/value pairs in |nva|. + */ + size_t nvlen; + /** + * The promised stream ID + */ + int32_t promised_stream_id; + /** + * Reserved bit. Currently this is always set to 0 and application + * should not expect something useful in here. + */ + uint8_t reserved; +} nghttp2_push_promise; + +/** + * @struct + * + * The PING frame. It has the following members: + */ +typedef struct { + /** + * The frame header. + */ + nghttp2_frame_hd hd; + /** + * The opaque data + */ + uint8_t opaque_data[8]; +} nghttp2_ping; + +/** + * @struct + * + * The GOAWAY frame. It has the following members: + */ +typedef struct { + /** + * The frame header. + */ + nghttp2_frame_hd hd; + /** + * The last stream stream ID. + */ + int32_t last_stream_id; + /** + * The error code. See :type:`nghttp2_error_code`. + */ + uint32_t error_code; + /** + * The additional debug data + */ + uint8_t *opaque_data; + /** + * The length of |opaque_data| member. + */ + size_t opaque_data_len; + /** + * Reserved bit. Currently this is always set to 0 and application + * should not expect something useful in here. + */ + uint8_t reserved; +} nghttp2_goaway; + +/** + * @struct + * + * The WINDOW_UPDATE frame. It has the following members: + */ +typedef struct { + /** + * The frame header. + */ + nghttp2_frame_hd hd; + /** + * The window size increment. + */ + int32_t window_size_increment; + /** + * Reserved bit. Currently this is always set to 0 and application + * should not expect something useful in here. + */ + uint8_t reserved; +} nghttp2_window_update; + +/** + * @struct + * + * The extension frame. It has following members: + */ +typedef struct { + /** + * The frame header. + */ + nghttp2_frame_hd hd; + /** + * The pointer to extension payload. The exact pointer type is + * determined by hd.type. + * + * Currently, no extension is supported. This is a place holder for + * the future extensions. + */ + void *payload; +} nghttp2_extension; + +/** + * @union + * + * This union includes all frames to pass them to various function + * calls as nghttp2_frame type. The CONTINUATION frame is omitted + * from here because the library deals with it internally. + */ +typedef union { + /** + * The frame header, which is convenient to inspect frame header. + */ + nghttp2_frame_hd hd; + /** + * The DATA frame. + */ + nghttp2_data data; + /** + * The HEADERS frame. + */ + nghttp2_headers headers; + /** + * The PRIORITY frame. + */ + nghttp2_priority priority; + /** + * The RST_STREAM frame. + */ + nghttp2_rst_stream rst_stream; + /** + * The SETTINGS frame. + */ + nghttp2_settings settings; + /** + * The PUSH_PROMISE frame. + */ + nghttp2_push_promise push_promise; + /** + * The PING frame. + */ + nghttp2_ping ping; + /** + * The GOAWAY frame. + */ + nghttp2_goaway goaway; + /** + * The WINDOW_UPDATE frame. + */ + nghttp2_window_update window_update; + /** + * The extension frame. + */ + nghttp2_extension ext; +} nghttp2_frame; + +/** + * @functypedef + * + * Callback function invoked when |session| wants to send data to the + * remote peer. The implementation of this function must send at most + * |length| bytes of data stored in |data|. The |flags| is currently + * not used and always 0. It must return the number of bytes sent if + * it succeeds. If it cannot send any single byte without blocking, * it must return :enum:`nghttp2_error.NGHTTP2_ERR_WOULDBLOCK`. For * other errors, it must return * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. The - * |user_data| pointer is the third argument passed in to the call to - * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. - * - * This callback is required if the application uses - * `nghttp2_session_send()` to send data to the remote endpoint. If - * the application uses solely `nghttp2_session_mem_send()` instead, - * this callback function is unnecessary. - * - * To set this callback to :type:`nghttp2_session_callbacks`, use - * `nghttp2_session_callbacks_set_send_callback()`. - * - * .. note:: - * - * The |length| may be very small. If that is the case, and - * application disables Nagle algorithm (``TCP_NODELAY``), then just - * writing |data| to the network stack leads to very small packet, - * and it is very inefficient. An application should be responsible - * to buffer up small chunks of data as necessary to avoid this - * situation. - */ -typedef ssize_t (*nghttp2_send_callback)(nghttp2_session *session, - const uint8_t *data, size_t length, - int flags, void *user_data); - -/** - * @functypedef - * + * |user_data| pointer is the third argument passed in to the call to + * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. + * + * This callback is required if the application uses + * `nghttp2_session_send()` to send data to the remote endpoint. If + * the application uses solely `nghttp2_session_mem_send()` instead, + * this callback function is unnecessary. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_send_callback()`. + * + * .. note:: + * + * The |length| may be very small. If that is the case, and + * application disables Nagle algorithm (``TCP_NODELAY``), then just + * writing |data| to the network stack leads to very small packet, + * and it is very inefficient. An application should be responsible + * to buffer up small chunks of data as necessary to avoid this + * situation. + */ +typedef ssize_t (*nghttp2_send_callback)(nghttp2_session *session, + const uint8_t *data, size_t length, + int flags, void *user_data); + +/** + * @functypedef + * * Callback function invoked when * :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_NO_COPY` is used in * :type:`nghttp2_data_source_read_callback` to send complete DATA * frame. - * - * The |frame| is a DATA frame to send. The |framehd| is the - * serialized frame header (9 bytes). The |length| is the length of - * application data to send (this does not include padding). The - * |source| is the same pointer passed to - * :type:`nghttp2_data_source_read_callback`. - * - * The application first must send frame header |framehd| of length 9 - * bytes. If ``frame->data.padlen > 0``, send 1 byte of value - * ``frame->data.padlen - 1``. Then send exactly |length| bytes of - * application data. Finally, if ``frame->data.padlen > 1``, send - * ``frame->data.padlen - 1`` bytes of zero as padding. - * - * The application has to send complete DATA frame in this callback. - * If all data were written successfully, return 0. - * - * If it cannot send any data at all, just return + * + * The |frame| is a DATA frame to send. The |framehd| is the + * serialized frame header (9 bytes). The |length| is the length of + * application data to send (this does not include padding). The + * |source| is the same pointer passed to + * :type:`nghttp2_data_source_read_callback`. + * + * The application first must send frame header |framehd| of length 9 + * bytes. If ``frame->data.padlen > 0``, send 1 byte of value + * ``frame->data.padlen - 1``. Then send exactly |length| bytes of + * application data. Finally, if ``frame->data.padlen > 1``, send + * ``frame->data.padlen - 1`` bytes of zero as padding. + * + * The application has to send complete DATA frame in this callback. + * If all data were written successfully, return 0. + * + * If it cannot send any data at all, just return * :enum:`nghttp2_error.NGHTTP2_ERR_WOULDBLOCK`; the library will call * this callback with the same parameters later (It is recommended to * send complete DATA frame at once in this function to deal with @@ -1367,27 +1367,27 @@ typedef ssize_t (*nghttp2_send_callback)(nghttp2_session *session, * :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE`. If application decided to * reset this stream, return * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`, then - * the library will send RST_STREAM with INTERNAL_ERROR as error code. - * The application can also return + * the library will send RST_STREAM with INTERNAL_ERROR as error code. + * The application can also return * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`, which will * result in connection closure. Returning any other value is treated * as :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` is returned. - */ -typedef int (*nghttp2_send_data_callback)(nghttp2_session *session, - nghttp2_frame *frame, - const uint8_t *framehd, size_t length, - nghttp2_data_source *source, - void *user_data); - -/** - * @functypedef - * - * Callback function invoked when |session| wants to receive data from - * the remote peer. The implementation of this function must read at - * most |length| bytes of data and store it in |buf|. The |flags| is - * currently not used and always 0. It must return the number of - * bytes written in |buf| if it succeeds. If it cannot read any - * single byte without blocking, it must return + */ +typedef int (*nghttp2_send_data_callback)(nghttp2_session *session, + nghttp2_frame *frame, + const uint8_t *framehd, size_t length, + nghttp2_data_source *source, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked when |session| wants to receive data from + * the remote peer. The implementation of this function must read at + * most |length| bytes of data and store it in |buf|. The |flags| is + * currently not used and always 0. It must return the number of + * bytes written in |buf| if it succeeds. If it cannot read any + * single byte without blocking, it must return * :enum:`nghttp2_error.NGHTTP2_ERR_WOULDBLOCK`. If it gets EOF * before it reads any single byte, it must return * :enum:`nghttp2_error.NGHTTP2_ERR_EOF`. For other errors, it must @@ -1395,336 +1395,336 @@ typedef int (*nghttp2_send_data_callback)(nghttp2_session *session, * Returning 0 is treated as * :enum:`nghttp2_error.NGHTTP2_ERR_WOULDBLOCK`. The |user_data| * pointer is the third argument passed in to the call to - * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. - * - * This callback is required if the application uses - * `nghttp2_session_recv()` to receive data from the remote endpoint. - * If the application uses solely `nghttp2_session_mem_recv()` - * instead, this callback function is unnecessary. - * - * To set this callback to :type:`nghttp2_session_callbacks`, use - * `nghttp2_session_callbacks_set_recv_callback()`. - */ -typedef ssize_t (*nghttp2_recv_callback)(nghttp2_session *session, uint8_t *buf, - size_t length, int flags, - void *user_data); - -/** - * @functypedef - * - * Callback function invoked by `nghttp2_session_recv()` and - * `nghttp2_session_mem_recv()` when a frame is received. The - * |user_data| pointer is the third argument passed in to the call to - * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. - * - * If frame is HEADERS or PUSH_PROMISE, the ``nva`` and ``nvlen`` - * member of their data structure are always ``NULL`` and 0 - * respectively. The header name/value pairs are emitted via - * :type:`nghttp2_on_header_callback`. - * - * For HEADERS, PUSH_PROMISE and DATA frames, this callback may be - * called after stream is closed (see - * :type:`nghttp2_on_stream_close_callback`). The application should - * check that stream is still alive using its own stream management or - * :func:`nghttp2_session_get_stream_user_data()`. - * - * Only HEADERS and DATA frame can signal the end of incoming data. - * If ``frame->hd.flags & NGHTTP2_FLAG_END_STREAM`` is nonzero, the - * |frame| is the last frame from the remote peer in this stream. - * - * This callback won't be called for CONTINUATION frames. - * HEADERS/PUSH_PROMISE + CONTINUATIONs are treated as single frame. - * - * The implementation of this function must return 0 if it succeeds. - * If nonzero value is returned, it is treated as fatal error and - * `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions + * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. + * + * This callback is required if the application uses + * `nghttp2_session_recv()` to receive data from the remote endpoint. + * If the application uses solely `nghttp2_session_mem_recv()` + * instead, this callback function is unnecessary. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_recv_callback()`. + */ +typedef ssize_t (*nghttp2_recv_callback)(nghttp2_session *session, uint8_t *buf, + size_t length, int flags, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked by `nghttp2_session_recv()` and + * `nghttp2_session_mem_recv()` when a frame is received. The + * |user_data| pointer is the third argument passed in to the call to + * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. + * + * If frame is HEADERS or PUSH_PROMISE, the ``nva`` and ``nvlen`` + * member of their data structure are always ``NULL`` and 0 + * respectively. The header name/value pairs are emitted via + * :type:`nghttp2_on_header_callback`. + * + * For HEADERS, PUSH_PROMISE and DATA frames, this callback may be + * called after stream is closed (see + * :type:`nghttp2_on_stream_close_callback`). The application should + * check that stream is still alive using its own stream management or + * :func:`nghttp2_session_get_stream_user_data()`. + * + * Only HEADERS and DATA frame can signal the end of incoming data. + * If ``frame->hd.flags & NGHTTP2_FLAG_END_STREAM`` is nonzero, the + * |frame| is the last frame from the remote peer in this stream. + * + * This callback won't be called for CONTINUATION frames. + * HEADERS/PUSH_PROMISE + CONTINUATIONs are treated as single frame. + * + * The implementation of this function must return 0 if it succeeds. + * If nonzero value is returned, it is treated as fatal error and + * `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions * immediately return * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. - * - * To set this callback to :type:`nghttp2_session_callbacks`, use - * `nghttp2_session_callbacks_set_on_frame_recv_callback()`. - */ -typedef int (*nghttp2_on_frame_recv_callback)(nghttp2_session *session, - const nghttp2_frame *frame, - void *user_data); - -/** - * @functypedef - * - * Callback function invoked by `nghttp2_session_recv()` and - * `nghttp2_session_mem_recv()` when an invalid non-DATA frame is - * received. The error is indicated by the |lib_error_code|, which is - * one of the values defined in :type:`nghttp2_error`. When this - * callback function is invoked, the library automatically submits - * either RST_STREAM or GOAWAY frame. The |user_data| pointer is the - * third argument passed in to the call to - * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. - * - * If frame is HEADERS or PUSH_PROMISE, the ``nva`` and ``nvlen`` - * member of their data structure are always ``NULL`` and 0 - * respectively. - * - * The implementation of this function must return 0 if it succeeds. - * If nonzero is returned, it is treated as fatal error and - * `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_on_frame_recv_callback()`. + */ +typedef int (*nghttp2_on_frame_recv_callback)(nghttp2_session *session, + const nghttp2_frame *frame, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked by `nghttp2_session_recv()` and + * `nghttp2_session_mem_recv()` when an invalid non-DATA frame is + * received. The error is indicated by the |lib_error_code|, which is + * one of the values defined in :type:`nghttp2_error`. When this + * callback function is invoked, the library automatically submits + * either RST_STREAM or GOAWAY frame. The |user_data| pointer is the + * third argument passed in to the call to + * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. + * + * If frame is HEADERS or PUSH_PROMISE, the ``nva`` and ``nvlen`` + * member of their data structure are always ``NULL`` and 0 + * respectively. + * + * The implementation of this function must return 0 if it succeeds. + * If nonzero is returned, it is treated as fatal error and + * `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions * immediately return * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. - * - * To set this callback to :type:`nghttp2_session_callbacks`, use - * `nghttp2_session_callbacks_set_on_invalid_frame_recv_callback()`. - */ -typedef int (*nghttp2_on_invalid_frame_recv_callback)( - nghttp2_session *session, const nghttp2_frame *frame, int lib_error_code, - void *user_data); - -/** - * @functypedef - * - * Callback function invoked when a chunk of data in DATA frame is - * received. The |stream_id| is the stream ID this DATA frame belongs - * to. The |flags| is the flags of DATA frame which this data chunk - * is contained. ``(flags & NGHTTP2_FLAG_END_STREAM) != 0`` does not - * necessarily mean this chunk of data is the last one in the stream. - * You should use :type:`nghttp2_on_frame_recv_callback` to know all - * data frames are received. The |user_data| pointer is the third - * argument passed in to the call to `nghttp2_session_client_new()` or - * `nghttp2_session_server_new()`. - * - * If the application uses `nghttp2_session_mem_recv()`, it can return + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_on_invalid_frame_recv_callback()`. + */ +typedef int (*nghttp2_on_invalid_frame_recv_callback)( + nghttp2_session *session, const nghttp2_frame *frame, int lib_error_code, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked when a chunk of data in DATA frame is + * received. The |stream_id| is the stream ID this DATA frame belongs + * to. The |flags| is the flags of DATA frame which this data chunk + * is contained. ``(flags & NGHTTP2_FLAG_END_STREAM) != 0`` does not + * necessarily mean this chunk of data is the last one in the stream. + * You should use :type:`nghttp2_on_frame_recv_callback` to know all + * data frames are received. The |user_data| pointer is the third + * argument passed in to the call to `nghttp2_session_client_new()` or + * `nghttp2_session_server_new()`. + * + * If the application uses `nghttp2_session_mem_recv()`, it can return * :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE` to make * `nghttp2_session_mem_recv()` return without processing further * input bytes. The memory by pointed by the |data| is retained until - * `nghttp2_session_mem_recv()` or `nghttp2_session_recv()` is called. - * The application must retain the input bytes which was used to - * produce the |data| parameter, because it may refer to the memory - * region included in the input bytes. - * - * The implementation of this function must return 0 if it succeeds. - * If nonzero is returned, it is treated as fatal error, and - * `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions + * `nghttp2_session_mem_recv()` or `nghttp2_session_recv()` is called. + * The application must retain the input bytes which was used to + * produce the |data| parameter, because it may refer to the memory + * region included in the input bytes. + * + * The implementation of this function must return 0 if it succeeds. + * If nonzero is returned, it is treated as fatal error, and + * `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions * immediately return * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. - * - * To set this callback to :type:`nghttp2_session_callbacks`, use - * `nghttp2_session_callbacks_set_on_data_chunk_recv_callback()`. - */ -typedef int (*nghttp2_on_data_chunk_recv_callback)(nghttp2_session *session, - uint8_t flags, - int32_t stream_id, - const uint8_t *data, - size_t len, void *user_data); - -/** - * @functypedef - * - * Callback function invoked just before the non-DATA frame |frame| is - * sent. The |user_data| pointer is the third argument passed in to - * the call to `nghttp2_session_client_new()` or - * `nghttp2_session_server_new()`. - * - * The implementation of this function must return 0 if it succeeds. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_on_data_chunk_recv_callback()`. + */ +typedef int (*nghttp2_on_data_chunk_recv_callback)(nghttp2_session *session, + uint8_t flags, + int32_t stream_id, + const uint8_t *data, + size_t len, void *user_data); + +/** + * @functypedef + * + * Callback function invoked just before the non-DATA frame |frame| is + * sent. The |user_data| pointer is the third argument passed in to + * the call to `nghttp2_session_client_new()` or + * `nghttp2_session_server_new()`. + * + * The implementation of this function must return 0 if it succeeds. * It can also return :enum:`nghttp2_error.NGHTTP2_ERR_CANCEL` to * cancel the transmission of the given frame. - * - * If there is a fatal error while executing this callback, the + * + * If there is a fatal error while executing this callback, the * implementation should return * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`, which makes * `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions * immediately return * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. - * - * If the other value is returned, it is treated as if + * + * If the other value is returned, it is treated as if * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` is returned. * But the implementation should not rely on this since the library * may define new return value to extend its capability. - * - * To set this callback to :type:`nghttp2_session_callbacks`, use - * `nghttp2_session_callbacks_set_before_frame_send_callback()`. - */ -typedef int (*nghttp2_before_frame_send_callback)(nghttp2_session *session, - const nghttp2_frame *frame, - void *user_data); - -/** - * @functypedef - * - * Callback function invoked after the frame |frame| is sent. The - * |user_data| pointer is the third argument passed in to the call to - * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. - * - * The implementation of this function must return 0 if it succeeds. - * If nonzero is returned, it is treated as fatal error and - * `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_before_frame_send_callback()`. + */ +typedef int (*nghttp2_before_frame_send_callback)(nghttp2_session *session, + const nghttp2_frame *frame, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked after the frame |frame| is sent. The + * |user_data| pointer is the third argument passed in to the call to + * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. + * + * The implementation of this function must return 0 if it succeeds. + * If nonzero is returned, it is treated as fatal error and + * `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions * immediately return * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. - * - * To set this callback to :type:`nghttp2_session_callbacks`, use - * `nghttp2_session_callbacks_set_on_frame_send_callback()`. - */ -typedef int (*nghttp2_on_frame_send_callback)(nghttp2_session *session, - const nghttp2_frame *frame, - void *user_data); - -/** - * @functypedef - * - * Callback function invoked after the non-DATA frame |frame| is not - * sent because of the error. The error is indicated by the - * |lib_error_code|, which is one of the values defined in - * :type:`nghttp2_error`. The |user_data| pointer is the third - * argument passed in to the call to `nghttp2_session_client_new()` or - * `nghttp2_session_server_new()`. - * - * The implementation of this function must return 0 if it succeeds. - * If nonzero is returned, it is treated as fatal error and - * `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_on_frame_send_callback()`. + */ +typedef int (*nghttp2_on_frame_send_callback)(nghttp2_session *session, + const nghttp2_frame *frame, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked after the non-DATA frame |frame| is not + * sent because of the error. The error is indicated by the + * |lib_error_code|, which is one of the values defined in + * :type:`nghttp2_error`. The |user_data| pointer is the third + * argument passed in to the call to `nghttp2_session_client_new()` or + * `nghttp2_session_server_new()`. + * + * The implementation of this function must return 0 if it succeeds. + * If nonzero is returned, it is treated as fatal error and + * `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions * immediately return * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. - * - * `nghttp2_session_get_stream_user_data()` can be used to get - * associated data. - * - * To set this callback to :type:`nghttp2_session_callbacks`, use - * `nghttp2_session_callbacks_set_on_frame_not_send_callback()`. - */ -typedef int (*nghttp2_on_frame_not_send_callback)(nghttp2_session *session, - const nghttp2_frame *frame, - int lib_error_code, - void *user_data); - -/** - * @functypedef - * - * Callback function invoked when the stream |stream_id| is closed. - * The reason of closure is indicated by the |error_code|. The - * |error_code| is usually one of :enum:`nghttp2_error_code`, but that - * is not guaranteed. The stream_user_data, which was specified in - * `nghttp2_submit_request()` or `nghttp2_submit_headers()`, is still - * available in this function. The |user_data| pointer is the third - * argument passed in to the call to `nghttp2_session_client_new()` or - * `nghttp2_session_server_new()`. - * - * This function is also called for a stream in reserved state. - * - * The implementation of this function must return 0 if it succeeds. - * If nonzero is returned, it is treated as fatal error and - * `nghttp2_session_recv()`, `nghttp2_session_mem_recv()`, - * `nghttp2_session_send()`, and `nghttp2_session_mem_send()` + * + * `nghttp2_session_get_stream_user_data()` can be used to get + * associated data. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_on_frame_not_send_callback()`. + */ +typedef int (*nghttp2_on_frame_not_send_callback)(nghttp2_session *session, + const nghttp2_frame *frame, + int lib_error_code, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked when the stream |stream_id| is closed. + * The reason of closure is indicated by the |error_code|. The + * |error_code| is usually one of :enum:`nghttp2_error_code`, but that + * is not guaranteed. The stream_user_data, which was specified in + * `nghttp2_submit_request()` or `nghttp2_submit_headers()`, is still + * available in this function. The |user_data| pointer is the third + * argument passed in to the call to `nghttp2_session_client_new()` or + * `nghttp2_session_server_new()`. + * + * This function is also called for a stream in reserved state. + * + * The implementation of this function must return 0 if it succeeds. + * If nonzero is returned, it is treated as fatal error and + * `nghttp2_session_recv()`, `nghttp2_session_mem_recv()`, + * `nghttp2_session_send()`, and `nghttp2_session_mem_send()` * functions immediately return * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. - * - * To set this callback to :type:`nghttp2_session_callbacks`, use - * `nghttp2_session_callbacks_set_on_stream_close_callback()`. - */ -typedef int (*nghttp2_on_stream_close_callback)(nghttp2_session *session, - int32_t stream_id, - uint32_t error_code, - void *user_data); - -/** - * @functypedef - * - * Callback function invoked when the reception of header block in - * HEADERS or PUSH_PROMISE is started. Each header name/value pair - * will be emitted by :type:`nghttp2_on_header_callback`. - * - * The ``frame->hd.flags`` may not have + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_on_stream_close_callback()`. + */ +typedef int (*nghttp2_on_stream_close_callback)(nghttp2_session *session, + int32_t stream_id, + uint32_t error_code, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked when the reception of header block in + * HEADERS or PUSH_PROMISE is started. Each header name/value pair + * will be emitted by :type:`nghttp2_on_header_callback`. + * + * The ``frame->hd.flags`` may not have * :enum:`nghttp2_flag.NGHTTP2_FLAG_END_HEADERS` flag set, which * indicates that one or more CONTINUATION frames are involved. But * the application does not need to care about that because the header * name/value pairs are emitted transparently regardless of * CONTINUATION frames. - * - * The server applications probably create an object to store - * information about new stream if ``frame->hd.type == - * NGHTTP2_HEADERS`` and ``frame->headers.cat == - * NGHTTP2_HCAT_REQUEST``. If |session| is configured as server side, - * ``frame->headers.cat`` is either ``NGHTTP2_HCAT_REQUEST`` - * containing request headers or ``NGHTTP2_HCAT_HEADERS`` containing - * trailer fields and never get PUSH_PROMISE in this callback. - * - * For the client applications, ``frame->hd.type`` is either - * ``NGHTTP2_HEADERS`` or ``NGHTTP2_PUSH_PROMISE``. In case of - * ``NGHTTP2_HEADERS``, ``frame->headers.cat == - * NGHTTP2_HCAT_RESPONSE`` means that it is the first response - * headers, but it may be non-final response which is indicated by 1xx - * status code. In this case, there may be zero or more HEADERS frame - * with ``frame->headers.cat == NGHTTP2_HCAT_HEADERS`` which has - * non-final response code and finally client gets exactly one HEADERS - * frame with ``frame->headers.cat == NGHTTP2_HCAT_HEADERS`` - * containing final response headers (non-1xx status code). The - * trailer fields also has ``frame->headers.cat == - * NGHTTP2_HCAT_HEADERS`` which does not contain any status code. - * + * + * The server applications probably create an object to store + * information about new stream if ``frame->hd.type == + * NGHTTP2_HEADERS`` and ``frame->headers.cat == + * NGHTTP2_HCAT_REQUEST``. If |session| is configured as server side, + * ``frame->headers.cat`` is either ``NGHTTP2_HCAT_REQUEST`` + * containing request headers or ``NGHTTP2_HCAT_HEADERS`` containing + * trailer fields and never get PUSH_PROMISE in this callback. + * + * For the client applications, ``frame->hd.type`` is either + * ``NGHTTP2_HEADERS`` or ``NGHTTP2_PUSH_PROMISE``. In case of + * ``NGHTTP2_HEADERS``, ``frame->headers.cat == + * NGHTTP2_HCAT_RESPONSE`` means that it is the first response + * headers, but it may be non-final response which is indicated by 1xx + * status code. In this case, there may be zero or more HEADERS frame + * with ``frame->headers.cat == NGHTTP2_HCAT_HEADERS`` which has + * non-final response code and finally client gets exactly one HEADERS + * frame with ``frame->headers.cat == NGHTTP2_HCAT_HEADERS`` + * containing final response headers (non-1xx status code). The + * trailer fields also has ``frame->headers.cat == + * NGHTTP2_HCAT_HEADERS`` which does not contain any status code. + * * Returning * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will * close the stream (promised stream if frame is PUSH_PROMISE) by * issuing RST_STREAM with * :enum:`nghttp2_error_code.NGHTTP2_INTERNAL_ERROR`. In this case, - * :type:`nghttp2_on_header_callback` and - * :type:`nghttp2_on_frame_recv_callback` will not be invoked. If a - * different error code is desirable, use - * `nghttp2_submit_rst_stream()` with a desired error code and then + * :type:`nghttp2_on_header_callback` and + * :type:`nghttp2_on_frame_recv_callback` will not be invoked. If a + * different error code is desirable, use + * `nghttp2_submit_rst_stream()` with a desired error code and then * return :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. * Again, use ``frame->push_promise.promised_stream_id`` as stream_id * parameter in `nghttp2_submit_rst_stream()` if frame is * PUSH_PROMISE. - * - * The implementation of this function must return 0 if it succeeds. + * + * The implementation of this function must return 0 if it succeeds. * It can return * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` to - * reset the stream (promised stream if frame is PUSH_PROMISE). For - * critical errors, it must return + * reset the stream (promised stream if frame is PUSH_PROMISE). For + * critical errors, it must return * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. If the other * value is returned, it is treated as if * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` is returned. If * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` is returned, - * `nghttp2_session_mem_recv()` function will immediately return + * `nghttp2_session_mem_recv()` function will immediately return * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. - * - * To set this callback to :type:`nghttp2_session_callbacks`, use - * `nghttp2_session_callbacks_set_on_begin_headers_callback()`. - */ -typedef int (*nghttp2_on_begin_headers_callback)(nghttp2_session *session, - const nghttp2_frame *frame, - void *user_data); - -/** - * @functypedef - * - * Callback function invoked when a header name/value pair is received - * for the |frame|. The |name| of length |namelen| is header name. - * The |value| of length |valuelen| is header value. The |flags| is - * bitwise OR of one or more of :type:`nghttp2_nv_flag`. - * + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_on_begin_headers_callback()`. + */ +typedef int (*nghttp2_on_begin_headers_callback)(nghttp2_session *session, + const nghttp2_frame *frame, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked when a header name/value pair is received + * for the |frame|. The |name| of length |namelen| is header name. + * The |value| of length |valuelen| is header value. The |flags| is + * bitwise OR of one or more of :type:`nghttp2_nv_flag`. + * * If :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_INDEX` is set in * |flags|, the receiver must not index this name/value pair when * forwarding it to the next hop. More specifically, "Literal Header * Field never Indexed" representation must be used in HPACK encoding. - * - * When this callback is invoked, ``frame->hd.type`` is either + * + * When this callback is invoked, ``frame->hd.type`` is either * :enum:`nghttp2_frame_type.NGHTTP2_HEADERS` or * :enum:`nghttp2_frame_type.NGHTTP2_PUSH_PROMISE`. After all header * name/value pairs are processed with this callback, and no error has * been detected, :type:`nghttp2_on_frame_recv_callback` will be * invoked. If there is an error in decompression, - * :type:`nghttp2_on_frame_recv_callback` for the |frame| will not be - * invoked. - * - * Both |name| and |value| are guaranteed to be NULL-terminated. The - * |namelen| and |valuelen| do not include terminal NULL. If - * `nghttp2_option_set_no_http_messaging()` is used with nonzero - * value, NULL character may be included in |name| or |value| before - * terminating NULL. - * - * Please note that unless `nghttp2_option_set_no_http_messaging()` is - * used, nghttp2 library does perform validation against the |name| - * and the |value| using `nghttp2_check_header_name()` and - * `nghttp2_check_header_value()`. In addition to this, nghttp2 - * performs validation based on HTTP Messaging rule, which is briefly - * explained in :ref:`http-messaging` section. - * - * If the application uses `nghttp2_session_mem_recv()`, it can return + * :type:`nghttp2_on_frame_recv_callback` for the |frame| will not be + * invoked. + * + * Both |name| and |value| are guaranteed to be NULL-terminated. The + * |namelen| and |valuelen| do not include terminal NULL. If + * `nghttp2_option_set_no_http_messaging()` is used with nonzero + * value, NULL character may be included in |name| or |value| before + * terminating NULL. + * + * Please note that unless `nghttp2_option_set_no_http_messaging()` is + * used, nghttp2 library does perform validation against the |name| + * and the |value| using `nghttp2_check_header_name()` and + * `nghttp2_check_header_value()`. In addition to this, nghttp2 + * performs validation based on HTTP Messaging rule, which is briefly + * explained in :ref:`http-messaging` section. + * + * If the application uses `nghttp2_session_mem_recv()`, it can return * :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE` to make * `nghttp2_session_mem_recv()` return without processing further * input bytes. The memory pointed by |frame|, |name| and |value| @@ -1732,22 +1732,22 @@ typedef int (*nghttp2_on_begin_headers_callback)(nghttp2_session *session, * `nghttp2_session_recv()` is called. The application must retain * the input bytes which was used to produce these parameters, because * it may refer to the memory region included in the input bytes. - * + * * Returning * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will * close the stream (promised stream if frame is PUSH_PROMISE) by * issuing RST_STREAM with * :enum:`nghttp2_error_code.NGHTTP2_INTERNAL_ERROR`. In this case, - * :type:`nghttp2_on_header_callback` and - * :type:`nghttp2_on_frame_recv_callback` will not be invoked. If a - * different error code is desirable, use - * `nghttp2_submit_rst_stream()` with a desired error code and then + * :type:`nghttp2_on_header_callback` and + * :type:`nghttp2_on_frame_recv_callback` will not be invoked. If a + * different error code is desirable, use + * `nghttp2_submit_rst_stream()` with a desired error code and then * return :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. * Again, use ``frame->push_promise.promised_stream_id`` as stream_id * parameter in `nghttp2_submit_rst_stream()` if frame is * PUSH_PROMISE. - * - * The implementation of this function must return 0 if it succeeds. + * + * The implementation of this function must return 0 if it succeeds. * It may return :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE` or * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. For * other critical failures, it must return @@ -1755,788 +1755,788 @@ typedef int (*nghttp2_on_begin_headers_callback)(nghttp2_session *session, * nonzero value is returned, it is treated as * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. If * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` is returned, - * `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions + * `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions * immediately return * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. - * - * To set this callback to :type:`nghttp2_session_callbacks`, use - * `nghttp2_session_callbacks_set_on_header_callback()`. - * - * .. warning:: - * - * Application should properly limit the total buffer size to store - * incoming header fields. Without it, peer may send large number - * of header fields or large header fields to cause out of memory in - * local endpoint. Due to how HPACK works, peer can do this - * effectively without using much memory on their own. - */ -typedef int (*nghttp2_on_header_callback)(nghttp2_session *session, - const nghttp2_frame *frame, - const uint8_t *name, size_t namelen, - const uint8_t *value, size_t valuelen, - uint8_t flags, void *user_data); - -/** - * @functypedef - * - * Callback function invoked when a header name/value pair is received - * for the |frame|. The |name| is header name. The |value| is header - * value. The |flags| is bitwise OR of one or more of - * :type:`nghttp2_nv_flag`. - * - * This callback behaves like :type:`nghttp2_on_header_callback`, - * except that |name| and |value| are stored in reference counted - * buffer. If application wishes to keep these references without - * copying them, use `nghttp2_rcbuf_incref()` to increment their - * reference count. It is the application's responsibility to call - * `nghttp2_rcbuf_decref()` if they called `nghttp2_rcbuf_incref()` so - * as not to leak memory. If the |session| is created by - * `nghttp2_session_server_new3()` or `nghttp2_session_client_new3()`, - * the function to free memory is the one belongs to the mem - * parameter. As long as this free function alives, |name| and - * |value| can live after |session| was destroyed. - */ -typedef int (*nghttp2_on_header_callback2)(nghttp2_session *session, - const nghttp2_frame *frame, - nghttp2_rcbuf *name, - nghttp2_rcbuf *value, uint8_t flags, - void *user_data); - -/** - * @functypedef - * - * Callback function invoked when a invalid header name/value pair is - * received for the |frame|. - * - * The parameter and behaviour are similar to - * :type:`nghttp2_on_header_callback`. The difference is that this - * callback is only invoked when a invalid header name/value pair is - * received which is treated as stream error if this callback is not - * set. Only invalid regular header field are passed to this - * callback. In other words, invalid pseudo header field is not - * passed to this callback. Also header fields which includes upper - * cased latter are also treated as error without passing them to this - * callback. - * - * This callback is only considered if HTTP messaging validation is - * turned on (which is on by default, see - * `nghttp2_option_set_no_http_messaging()`). - * - * With this callback, application inspects the incoming invalid - * field, and it also can reset stream from this callback by returning + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_on_header_callback()`. + * + * .. warning:: + * + * Application should properly limit the total buffer size to store + * incoming header fields. Without it, peer may send large number + * of header fields or large header fields to cause out of memory in + * local endpoint. Due to how HPACK works, peer can do this + * effectively without using much memory on their own. + */ +typedef int (*nghttp2_on_header_callback)(nghttp2_session *session, + const nghttp2_frame *frame, + const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, + uint8_t flags, void *user_data); + +/** + * @functypedef + * + * Callback function invoked when a header name/value pair is received + * for the |frame|. The |name| is header name. The |value| is header + * value. The |flags| is bitwise OR of one or more of + * :type:`nghttp2_nv_flag`. + * + * This callback behaves like :type:`nghttp2_on_header_callback`, + * except that |name| and |value| are stored in reference counted + * buffer. If application wishes to keep these references without + * copying them, use `nghttp2_rcbuf_incref()` to increment their + * reference count. It is the application's responsibility to call + * `nghttp2_rcbuf_decref()` if they called `nghttp2_rcbuf_incref()` so + * as not to leak memory. If the |session| is created by + * `nghttp2_session_server_new3()` or `nghttp2_session_client_new3()`, + * the function to free memory is the one belongs to the mem + * parameter. As long as this free function alives, |name| and + * |value| can live after |session| was destroyed. + */ +typedef int (*nghttp2_on_header_callback2)(nghttp2_session *session, + const nghttp2_frame *frame, + nghttp2_rcbuf *name, + nghttp2_rcbuf *value, uint8_t flags, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked when a invalid header name/value pair is + * received for the |frame|. + * + * The parameter and behaviour are similar to + * :type:`nghttp2_on_header_callback`. The difference is that this + * callback is only invoked when a invalid header name/value pair is + * received which is treated as stream error if this callback is not + * set. Only invalid regular header field are passed to this + * callback. In other words, invalid pseudo header field is not + * passed to this callback. Also header fields which includes upper + * cased latter are also treated as error without passing them to this + * callback. + * + * This callback is only considered if HTTP messaging validation is + * turned on (which is on by default, see + * `nghttp2_option_set_no_http_messaging()`). + * + * With this callback, application inspects the incoming invalid + * field, and it also can reset stream from this callback by returning * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. By * default, the error code is * :enum:`nghttp2_error_code.NGHTTP2_PROTOCOL_ERROR`. To change the * error code, call `nghttp2_submit_rst_stream()` with the error code * of choice in addition to returning * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. - * - * If 0 is returned, the header field is ignored, and the stream is - * not reset. - */ -typedef int (*nghttp2_on_invalid_header_callback)( - nghttp2_session *session, const nghttp2_frame *frame, const uint8_t *name, - size_t namelen, const uint8_t *value, size_t valuelen, uint8_t flags, - void *user_data); - -/** - * @functypedef - * - * Callback function invoked when a invalid header name/value pair is - * received for the |frame|. - * - * The parameter and behaviour are similar to - * :type:`nghttp2_on_header_callback2`. The difference is that this - * callback is only invoked when a invalid header name/value pair is - * received which is silently ignored if this callback is not set. - * Only invalid regular header field are passed to this callback. In - * other words, invalid pseudo header field is not passed to this - * callback. Also header fields which includes upper cased latter are - * also treated as error without passing them to this callback. - * - * This callback is only considered if HTTP messaging validation is - * turned on (which is on by default, see - * `nghttp2_option_set_no_http_messaging()`). - * - * With this callback, application inspects the incoming invalid - * field, and it also can reset stream from this callback by returning + * + * If 0 is returned, the header field is ignored, and the stream is + * not reset. + */ +typedef int (*nghttp2_on_invalid_header_callback)( + nghttp2_session *session, const nghttp2_frame *frame, const uint8_t *name, + size_t namelen, const uint8_t *value, size_t valuelen, uint8_t flags, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked when a invalid header name/value pair is + * received for the |frame|. + * + * The parameter and behaviour are similar to + * :type:`nghttp2_on_header_callback2`. The difference is that this + * callback is only invoked when a invalid header name/value pair is + * received which is silently ignored if this callback is not set. + * Only invalid regular header field are passed to this callback. In + * other words, invalid pseudo header field is not passed to this + * callback. Also header fields which includes upper cased latter are + * also treated as error without passing them to this callback. + * + * This callback is only considered if HTTP messaging validation is + * turned on (which is on by default, see + * `nghttp2_option_set_no_http_messaging()`). + * + * With this callback, application inspects the incoming invalid + * field, and it also can reset stream from this callback by returning * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. By * default, the error code is * :enum:`nghttp2_error_code.NGHTTP2_INTERNAL_ERROR`. To change the * error code, call `nghttp2_submit_rst_stream()` with the error code * of choice in addition to returning * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. - */ -typedef int (*nghttp2_on_invalid_header_callback2)( - nghttp2_session *session, const nghttp2_frame *frame, nghttp2_rcbuf *name, - nghttp2_rcbuf *value, uint8_t flags, void *user_data); - -/** - * @functypedef - * - * Callback function invoked when the library asks application how - * many padding bytes are required for the transmission of the - * |frame|. The application must choose the total length of payload - * including padded bytes in range [frame->hd.length, max_payloadlen], - * inclusive. Choosing number not in this range will be treated as + */ +typedef int (*nghttp2_on_invalid_header_callback2)( + nghttp2_session *session, const nghttp2_frame *frame, nghttp2_rcbuf *name, + nghttp2_rcbuf *value, uint8_t flags, void *user_data); + +/** + * @functypedef + * + * Callback function invoked when the library asks application how + * many padding bytes are required for the transmission of the + * |frame|. The application must choose the total length of payload + * including padded bytes in range [frame->hd.length, max_payloadlen], + * inclusive. Choosing number not in this range will be treated as * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. Returning - * ``frame->hd.length`` means no padding is added. Returning + * ``frame->hd.length`` means no padding is added. Returning * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` will make - * `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions + * `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions * immediately return * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. - * - * To set this callback to :type:`nghttp2_session_callbacks`, use - * `nghttp2_session_callbacks_set_select_padding_callback()`. - */ -typedef ssize_t (*nghttp2_select_padding_callback)(nghttp2_session *session, - const nghttp2_frame *frame, - size_t max_payloadlen, - void *user_data); - -/** - * @functypedef - * - * Callback function invoked when library wants to get max length of - * data to send data to the remote peer. The implementation of this - * function should return a value in the following range. [1, - * min(|session_remote_window_size|, |stream_remote_window_size|, - * |remote_max_frame_size|)]. If a value greater than this range is - * returned than the max allow value will be used. Returning a value - * smaller than this range is treated as + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_select_padding_callback()`. + */ +typedef ssize_t (*nghttp2_select_padding_callback)(nghttp2_session *session, + const nghttp2_frame *frame, + size_t max_payloadlen, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked when library wants to get max length of + * data to send data to the remote peer. The implementation of this + * function should return a value in the following range. [1, + * min(|session_remote_window_size|, |stream_remote_window_size|, + * |remote_max_frame_size|)]. If a value greater than this range is + * returned than the max allow value will be used. Returning a value + * smaller than this range is treated as * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. The * |frame_type| is provided for future extensibility and identifies * the type of frame (see :type:`nghttp2_frame_type`) for which to get * the length for. Currently supported frame types are: * :enum:`nghttp2_frame_type.NGHTTP2_DATA`. - * - * This callback can be used to control the length in bytes for which - * :type:`nghttp2_data_source_read_callback` is allowed to send to the - * remote endpoint. This callback is optional. Returning + * + * This callback can be used to control the length in bytes for which + * :type:`nghttp2_data_source_read_callback` is allowed to send to the + * remote endpoint. This callback is optional. Returning * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` will signal the * entire session failure. - * - * To set this callback to :type:`nghttp2_session_callbacks`, use - * `nghttp2_session_callbacks_set_data_source_read_length_callback()`. - */ -typedef ssize_t (*nghttp2_data_source_read_length_callback)( - nghttp2_session *session, uint8_t frame_type, int32_t stream_id, - int32_t session_remote_window_size, int32_t stream_remote_window_size, - uint32_t remote_max_frame_size, void *user_data); - -/** - * @functypedef - * - * Callback function invoked when a frame header is received. The - * |hd| points to received frame header. - * - * Unlike :type:`nghttp2_on_frame_recv_callback`, this callback will - * also be called when frame header of CONTINUATION frame is received. - * - * If both :type:`nghttp2_on_begin_frame_callback` and - * :type:`nghttp2_on_begin_headers_callback` are set and HEADERS or - * PUSH_PROMISE is received, :type:`nghttp2_on_begin_frame_callback` - * will be called first. - * - * The implementation of this function must return 0 if it succeeds. - * If nonzero value is returned, it is treated as fatal error and - * `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_data_source_read_length_callback()`. + */ +typedef ssize_t (*nghttp2_data_source_read_length_callback)( + nghttp2_session *session, uint8_t frame_type, int32_t stream_id, + int32_t session_remote_window_size, int32_t stream_remote_window_size, + uint32_t remote_max_frame_size, void *user_data); + +/** + * @functypedef + * + * Callback function invoked when a frame header is received. The + * |hd| points to received frame header. + * + * Unlike :type:`nghttp2_on_frame_recv_callback`, this callback will + * also be called when frame header of CONTINUATION frame is received. + * + * If both :type:`nghttp2_on_begin_frame_callback` and + * :type:`nghttp2_on_begin_headers_callback` are set and HEADERS or + * PUSH_PROMISE is received, :type:`nghttp2_on_begin_frame_callback` + * will be called first. + * + * The implementation of this function must return 0 if it succeeds. + * If nonzero value is returned, it is treated as fatal error and + * `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions * immediately return * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. - * - * To set this callback to :type:`nghttp2_session_callbacks`, use - * `nghttp2_session_callbacks_set_on_begin_frame_callback()`. - */ -typedef int (*nghttp2_on_begin_frame_callback)(nghttp2_session *session, - const nghttp2_frame_hd *hd, - void *user_data); - -/** - * @functypedef - * - * Callback function invoked when chunk of extension frame payload is - * received. The |hd| points to frame header. The received - * chunk is |data| of length |len|. - * - * The implementation of this function must return 0 if it succeeds. - * - * To abort processing this extension frame, return + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_on_begin_frame_callback()`. + */ +typedef int (*nghttp2_on_begin_frame_callback)(nghttp2_session *session, + const nghttp2_frame_hd *hd, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked when chunk of extension frame payload is + * received. The |hd| points to frame header. The received + * chunk is |data| of length |len|. + * + * The implementation of this function must return 0 if it succeeds. + * + * To abort processing this extension frame, return * :enum:`nghttp2_error.NGHTTP2_ERR_CANCEL`. - * - * If fatal error occurred, application should return + * + * If fatal error occurred, application should return * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. In this case, - * `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions + * `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions * immediately return * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. If the other * values are returned, currently they are treated as * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. - */ -typedef int (*nghttp2_on_extension_chunk_recv_callback)( - nghttp2_session *session, const nghttp2_frame_hd *hd, const uint8_t *data, - size_t len, void *user_data); - -/** - * @functypedef - * - * Callback function invoked when library asks the application to - * unpack extension payload from its wire format. The extension - * payload has been passed to the application using - * :type:`nghttp2_on_extension_chunk_recv_callback`. The frame header - * is already unpacked by the library and provided as |hd|. - * - * To receive extension frames, the application must tell desired - * extension frame type to the library using - * `nghttp2_option_set_user_recv_extension_type()`. - * - * The implementation of this function may store the pointer to the - * created object as a result of unpacking in |*payload|, and returns - * 0. The pointer stored in |*payload| is opaque to the library, and - * the library does not own its pointer. |*payload| is initialized as - * ``NULL``. The |*payload| is available as ``frame->ext.payload`` in - * :type:`nghttp2_on_frame_recv_callback`. Therefore if application - * can free that memory inside :type:`nghttp2_on_frame_recv_callback` - * callback. Of course, application has a liberty not ot use - * |*payload|, and do its own mechanism to process extension frames. - * - * To abort processing this extension frame, return + */ +typedef int (*nghttp2_on_extension_chunk_recv_callback)( + nghttp2_session *session, const nghttp2_frame_hd *hd, const uint8_t *data, + size_t len, void *user_data); + +/** + * @functypedef + * + * Callback function invoked when library asks the application to + * unpack extension payload from its wire format. The extension + * payload has been passed to the application using + * :type:`nghttp2_on_extension_chunk_recv_callback`. The frame header + * is already unpacked by the library and provided as |hd|. + * + * To receive extension frames, the application must tell desired + * extension frame type to the library using + * `nghttp2_option_set_user_recv_extension_type()`. + * + * The implementation of this function may store the pointer to the + * created object as a result of unpacking in |*payload|, and returns + * 0. The pointer stored in |*payload| is opaque to the library, and + * the library does not own its pointer. |*payload| is initialized as + * ``NULL``. The |*payload| is available as ``frame->ext.payload`` in + * :type:`nghttp2_on_frame_recv_callback`. Therefore if application + * can free that memory inside :type:`nghttp2_on_frame_recv_callback` + * callback. Of course, application has a liberty not ot use + * |*payload|, and do its own mechanism to process extension frames. + * + * To abort processing this extension frame, return * :enum:`nghttp2_error.NGHTTP2_ERR_CANCEL`. - * - * If fatal error occurred, application should return + * + * If fatal error occurred, application should return * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. In this case, - * `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions + * `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` functions * immediately return * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. If the other * values are returned, currently they are treated as * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. - */ -typedef int (*nghttp2_unpack_extension_callback)(nghttp2_session *session, - void **payload, - const nghttp2_frame_hd *hd, - void *user_data); - -/** - * @functypedef - * - * Callback function invoked when library asks the application to pack - * extension payload in its wire format. The frame header will be - * packed by library. Application must pack payload only. - * ``frame->ext.payload`` is the object passed to - * `nghttp2_submit_extension()` as payload parameter. Application - * must pack extension payload to the |buf| of its capacity |len| - * bytes. The |len| is at least 16KiB. - * - * The implementation of this function should return the number of - * bytes written into |buf| when it succeeds. - * - * To abort processing this extension frame, return + */ +typedef int (*nghttp2_unpack_extension_callback)(nghttp2_session *session, + void **payload, + const nghttp2_frame_hd *hd, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked when library asks the application to pack + * extension payload in its wire format. The frame header will be + * packed by library. Application must pack payload only. + * ``frame->ext.payload`` is the object passed to + * `nghttp2_submit_extension()` as payload parameter. Application + * must pack extension payload to the |buf| of its capacity |len| + * bytes. The |len| is at least 16KiB. + * + * The implementation of this function should return the number of + * bytes written into |buf| when it succeeds. + * + * To abort processing this extension frame, return * :enum:`nghttp2_error.NGHTTP2_ERR_CANCEL`, and - * :type:`nghttp2_on_frame_not_send_callback` will be invoked. - * - * If fatal error occurred, application should return + * :type:`nghttp2_on_frame_not_send_callback` will be invoked. + * + * If fatal error occurred, application should return * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. In this case, - * `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions + * `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions * immediately return * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. If the other * values are returned, currently they are treated as * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. If the return * value is strictly larger than |len|, it is treated as * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. - */ -typedef ssize_t (*nghttp2_pack_extension_callback)(nghttp2_session *session, - uint8_t *buf, size_t len, - const nghttp2_frame *frame, - void *user_data); - -/** - * @functypedef - * - * Callback function invoked when library provides the error message - * intended for human consumption. This callback is solely for - * debugging purpose. The |msg| is typically NULL-terminated string - * of length |len|. |len| does not include the sentinel NULL - * character. - * - * This function is deprecated. The new application should use - * :type:`nghttp2_error_callback2`. - * - * The format of error message may change between nghttp2 library - * versions. The application should not depend on the particular - * format. - * - * Normally, application should return 0 from this callback. If fatal - * error occurred while doing something in this callback, application + */ +typedef ssize_t (*nghttp2_pack_extension_callback)(nghttp2_session *session, + uint8_t *buf, size_t len, + const nghttp2_frame *frame, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked when library provides the error message + * intended for human consumption. This callback is solely for + * debugging purpose. The |msg| is typically NULL-terminated string + * of length |len|. |len| does not include the sentinel NULL + * character. + * + * This function is deprecated. The new application should use + * :type:`nghttp2_error_callback2`. + * + * The format of error message may change between nghttp2 library + * versions. The application should not depend on the particular + * format. + * + * Normally, application should return 0 from this callback. If fatal + * error occurred while doing something in this callback, application * should return :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. * In this case, library will return immediately with return value * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. Currently, if * nonzero value is returned from this callback, they are treated as * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`, but application * should not rely on this details. - */ -typedef int (*nghttp2_error_callback)(nghttp2_session *session, const char *msg, - size_t len, void *user_data); - -/** - * @functypedef - * - * Callback function invoked when library provides the error code, and - * message. This callback is solely for debugging purpose. - * |lib_error_code| is one of error code defined in - * :enum:`nghttp2_error`. The |msg| is typically NULL-terminated - * string of length |len|, and intended for human consumption. |len| - * does not include the sentinel NULL character. - * - * The format of error message may change between nghttp2 library - * versions. The application should not depend on the particular - * format. - * - * Normally, application should return 0 from this callback. If fatal - * error occurred while doing something in this callback, application + */ +typedef int (*nghttp2_error_callback)(nghttp2_session *session, const char *msg, + size_t len, void *user_data); + +/** + * @functypedef + * + * Callback function invoked when library provides the error code, and + * message. This callback is solely for debugging purpose. + * |lib_error_code| is one of error code defined in + * :enum:`nghttp2_error`. The |msg| is typically NULL-terminated + * string of length |len|, and intended for human consumption. |len| + * does not include the sentinel NULL character. + * + * The format of error message may change between nghttp2 library + * versions. The application should not depend on the particular + * format. + * + * Normally, application should return 0 from this callback. If fatal + * error occurred while doing something in this callback, application * should return :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. * In this case, library will return immediately with return value * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. Currently, if * nonzero value is returned from this callback, they are treated as * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`, but application * should not rely on this details. - */ -typedef int (*nghttp2_error_callback2)(nghttp2_session *session, - int lib_error_code, const char *msg, - size_t len, void *user_data); - -struct nghttp2_session_callbacks; - -/** - * @struct - * - * Callback functions for :type:`nghttp2_session`. The details of - * this structure are intentionally hidden from the public API. - */ -typedef struct nghttp2_session_callbacks nghttp2_session_callbacks; - -/** - * @function - * - * Initializes |*callbacks_ptr| with NULL values. - * - * The initialized object can be used when initializing multiple - * :type:`nghttp2_session` objects. - * - * When the application finished using this object, it can use - * `nghttp2_session_callbacks_del()` to free its memory. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * + */ +typedef int (*nghttp2_error_callback2)(nghttp2_session *session, + int lib_error_code, const char *msg, + size_t len, void *user_data); + +struct nghttp2_session_callbacks; + +/** + * @struct + * + * Callback functions for :type:`nghttp2_session`. The details of + * this structure are intentionally hidden from the public API. + */ +typedef struct nghttp2_session_callbacks nghttp2_session_callbacks; + +/** + * @function + * + * Initializes |*callbacks_ptr| with NULL values. + * + * The initialized object can be used when initializing multiple + * :type:`nghttp2_session` objects. + * + * When the application finished using this object, it can use + * `nghttp2_session_callbacks_del()` to free its memory. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` - * Out of memory. - */ -NGHTTP2_EXTERN int -nghttp2_session_callbacks_new(nghttp2_session_callbacks **callbacks_ptr); - -/** - * @function - * - * Frees any resources allocated for |callbacks|. If |callbacks| is - * ``NULL``, this function does nothing. - */ -NGHTTP2_EXTERN void -nghttp2_session_callbacks_del(nghttp2_session_callbacks *callbacks); - -/** - * @function - * - * Sets callback function invoked when a session wants to send data to - * the remote peer. This callback is not necessary if the application - * uses solely `nghttp2_session_mem_send()` to serialize data to - * transmit. - */ -NGHTTP2_EXTERN void nghttp2_session_callbacks_set_send_callback( - nghttp2_session_callbacks *cbs, nghttp2_send_callback send_callback); - -/** - * @function - * - * Sets callback function invoked when the a session wants to receive - * data from the remote peer. This callback is not necessary if the - * application uses solely `nghttp2_session_mem_recv()` to process - * received data. - */ -NGHTTP2_EXTERN void nghttp2_session_callbacks_set_recv_callback( - nghttp2_session_callbacks *cbs, nghttp2_recv_callback recv_callback); - -/** - * @function - * - * Sets callback function invoked by `nghttp2_session_recv()` and - * `nghttp2_session_mem_recv()` when a frame is received. - */ -NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_frame_recv_callback( - nghttp2_session_callbacks *cbs, - nghttp2_on_frame_recv_callback on_frame_recv_callback); - -/** - * @function - * - * Sets callback function invoked by `nghttp2_session_recv()` and - * `nghttp2_session_mem_recv()` when an invalid non-DATA frame is - * received. - */ -NGHTTP2_EXTERN void -nghttp2_session_callbacks_set_on_invalid_frame_recv_callback( - nghttp2_session_callbacks *cbs, - nghttp2_on_invalid_frame_recv_callback on_invalid_frame_recv_callback); - -/** - * @function - * - * Sets callback function invoked when a chunk of data in DATA frame - * is received. - */ -NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_data_chunk_recv_callback( - nghttp2_session_callbacks *cbs, - nghttp2_on_data_chunk_recv_callback on_data_chunk_recv_callback); - -/** - * @function - * - * Sets callback function invoked before a non-DATA frame is sent. - */ -NGHTTP2_EXTERN void nghttp2_session_callbacks_set_before_frame_send_callback( - nghttp2_session_callbacks *cbs, - nghttp2_before_frame_send_callback before_frame_send_callback); - -/** - * @function - * - * Sets callback function invoked after a frame is sent. - */ -NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_frame_send_callback( - nghttp2_session_callbacks *cbs, - nghttp2_on_frame_send_callback on_frame_send_callback); - -/** - * @function - * - * Sets callback function invoked when a non-DATA frame is not sent - * because of an error. - */ -NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_frame_not_send_callback( - nghttp2_session_callbacks *cbs, - nghttp2_on_frame_not_send_callback on_frame_not_send_callback); - -/** - * @function - * - * Sets callback function invoked when the stream is closed. - */ -NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_stream_close_callback( - nghttp2_session_callbacks *cbs, - nghttp2_on_stream_close_callback on_stream_close_callback); - -/** - * @function - * - * Sets callback function invoked when the reception of header block - * in HEADERS or PUSH_PROMISE is started. - */ -NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_begin_headers_callback( - nghttp2_session_callbacks *cbs, - nghttp2_on_begin_headers_callback on_begin_headers_callback); - -/** - * @function - * - * Sets callback function invoked when a header name/value pair is - * received. If both - * `nghttp2_session_callbacks_set_on_header_callback()` and - * `nghttp2_session_callbacks_set_on_header_callback2()` are used to - * set callbacks, the latter has the precedence. - */ -NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_header_callback( - nghttp2_session_callbacks *cbs, - nghttp2_on_header_callback on_header_callback); - -/** - * @function - * - * Sets callback function invoked when a header name/value pair is - * received. - */ -NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_header_callback2( - nghttp2_session_callbacks *cbs, - nghttp2_on_header_callback2 on_header_callback2); - -/** - * @function - * - * Sets callback function invoked when a invalid header name/value - * pair is received. If both - * `nghttp2_session_callbacks_set_on_invalid_header_callback()` and - * `nghttp2_session_callbacks_set_on_invalid_header_callback2()` are - * used to set callbacks, the latter takes the precedence. - */ -NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_invalid_header_callback( - nghttp2_session_callbacks *cbs, - nghttp2_on_invalid_header_callback on_invalid_header_callback); - -/** - * @function - * - * Sets callback function invoked when a invalid header name/value - * pair is received. - */ -NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_invalid_header_callback2( - nghttp2_session_callbacks *cbs, - nghttp2_on_invalid_header_callback2 on_invalid_header_callback2); - -/** - * @function - * - * Sets callback function invoked when the library asks application - * how many padding bytes are required for the transmission of the - * given frame. - */ -NGHTTP2_EXTERN void nghttp2_session_callbacks_set_select_padding_callback( - nghttp2_session_callbacks *cbs, - nghttp2_select_padding_callback select_padding_callback); - -/** - * @function - * - * Sets callback function determine the length allowed in - * :type:`nghttp2_data_source_read_callback`. - */ -NGHTTP2_EXTERN void -nghttp2_session_callbacks_set_data_source_read_length_callback( - nghttp2_session_callbacks *cbs, - nghttp2_data_source_read_length_callback data_source_read_length_callback); - -/** - * @function - * - * Sets callback function invoked when a frame header is received. - */ -NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_begin_frame_callback( - nghttp2_session_callbacks *cbs, - nghttp2_on_begin_frame_callback on_begin_frame_callback); - -/** - * @function - * - * Sets callback function invoked when + * Out of memory. + */ +NGHTTP2_EXTERN int +nghttp2_session_callbacks_new(nghttp2_session_callbacks **callbacks_ptr); + +/** + * @function + * + * Frees any resources allocated for |callbacks|. If |callbacks| is + * ``NULL``, this function does nothing. + */ +NGHTTP2_EXTERN void +nghttp2_session_callbacks_del(nghttp2_session_callbacks *callbacks); + +/** + * @function + * + * Sets callback function invoked when a session wants to send data to + * the remote peer. This callback is not necessary if the application + * uses solely `nghttp2_session_mem_send()` to serialize data to + * transmit. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_send_callback( + nghttp2_session_callbacks *cbs, nghttp2_send_callback send_callback); + +/** + * @function + * + * Sets callback function invoked when the a session wants to receive + * data from the remote peer. This callback is not necessary if the + * application uses solely `nghttp2_session_mem_recv()` to process + * received data. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_recv_callback( + nghttp2_session_callbacks *cbs, nghttp2_recv_callback recv_callback); + +/** + * @function + * + * Sets callback function invoked by `nghttp2_session_recv()` and + * `nghttp2_session_mem_recv()` when a frame is received. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_frame_recv_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_frame_recv_callback on_frame_recv_callback); + +/** + * @function + * + * Sets callback function invoked by `nghttp2_session_recv()` and + * `nghttp2_session_mem_recv()` when an invalid non-DATA frame is + * received. + */ +NGHTTP2_EXTERN void +nghttp2_session_callbacks_set_on_invalid_frame_recv_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_invalid_frame_recv_callback on_invalid_frame_recv_callback); + +/** + * @function + * + * Sets callback function invoked when a chunk of data in DATA frame + * is received. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_data_chunk_recv_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_data_chunk_recv_callback on_data_chunk_recv_callback); + +/** + * @function + * + * Sets callback function invoked before a non-DATA frame is sent. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_before_frame_send_callback( + nghttp2_session_callbacks *cbs, + nghttp2_before_frame_send_callback before_frame_send_callback); + +/** + * @function + * + * Sets callback function invoked after a frame is sent. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_frame_send_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_frame_send_callback on_frame_send_callback); + +/** + * @function + * + * Sets callback function invoked when a non-DATA frame is not sent + * because of an error. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_frame_not_send_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_frame_not_send_callback on_frame_not_send_callback); + +/** + * @function + * + * Sets callback function invoked when the stream is closed. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_stream_close_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_stream_close_callback on_stream_close_callback); + +/** + * @function + * + * Sets callback function invoked when the reception of header block + * in HEADERS or PUSH_PROMISE is started. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_begin_headers_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_begin_headers_callback on_begin_headers_callback); + +/** + * @function + * + * Sets callback function invoked when a header name/value pair is + * received. If both + * `nghttp2_session_callbacks_set_on_header_callback()` and + * `nghttp2_session_callbacks_set_on_header_callback2()` are used to + * set callbacks, the latter has the precedence. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_header_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_header_callback on_header_callback); + +/** + * @function + * + * Sets callback function invoked when a header name/value pair is + * received. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_header_callback2( + nghttp2_session_callbacks *cbs, + nghttp2_on_header_callback2 on_header_callback2); + +/** + * @function + * + * Sets callback function invoked when a invalid header name/value + * pair is received. If both + * `nghttp2_session_callbacks_set_on_invalid_header_callback()` and + * `nghttp2_session_callbacks_set_on_invalid_header_callback2()` are + * used to set callbacks, the latter takes the precedence. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_invalid_header_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_invalid_header_callback on_invalid_header_callback); + +/** + * @function + * + * Sets callback function invoked when a invalid header name/value + * pair is received. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_invalid_header_callback2( + nghttp2_session_callbacks *cbs, + nghttp2_on_invalid_header_callback2 on_invalid_header_callback2); + +/** + * @function + * + * Sets callback function invoked when the library asks application + * how many padding bytes are required for the transmission of the + * given frame. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_select_padding_callback( + nghttp2_session_callbacks *cbs, + nghttp2_select_padding_callback select_padding_callback); + +/** + * @function + * + * Sets callback function determine the length allowed in + * :type:`nghttp2_data_source_read_callback`. + */ +NGHTTP2_EXTERN void +nghttp2_session_callbacks_set_data_source_read_length_callback( + nghttp2_session_callbacks *cbs, + nghttp2_data_source_read_length_callback data_source_read_length_callback); + +/** + * @function + * + * Sets callback function invoked when a frame header is received. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_begin_frame_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_begin_frame_callback on_begin_frame_callback); + +/** + * @function + * + * Sets callback function invoked when * :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_NO_COPY` is used in - * :type:`nghttp2_data_source_read_callback` to avoid data copy. - */ -NGHTTP2_EXTERN void nghttp2_session_callbacks_set_send_data_callback( - nghttp2_session_callbacks *cbs, - nghttp2_send_data_callback send_data_callback); - -/** - * @function - * - * Sets callback function invoked when the library asks the - * application to pack extension frame payload in wire format. - */ -NGHTTP2_EXTERN void nghttp2_session_callbacks_set_pack_extension_callback( - nghttp2_session_callbacks *cbs, - nghttp2_pack_extension_callback pack_extension_callback); - -/** - * @function - * - * Sets callback function invoked when the library asks the - * application to unpack extension frame payload from wire format. - */ -NGHTTP2_EXTERN void nghttp2_session_callbacks_set_unpack_extension_callback( - nghttp2_session_callbacks *cbs, - nghttp2_unpack_extension_callback unpack_extension_callback); - -/** - * @function - * - * Sets callback function invoked when chunk of extension frame - * payload is received. - */ -NGHTTP2_EXTERN void -nghttp2_session_callbacks_set_on_extension_chunk_recv_callback( - nghttp2_session_callbacks *cbs, - nghttp2_on_extension_chunk_recv_callback on_extension_chunk_recv_callback); - -/** - * @function - * - * Sets callback function invoked when library tells error message to - * the application. - * - * This function is deprecated. The new application should use - * `nghttp2_session_callbacks_set_error_callback2()`. - * - * If both :type:`nghttp2_error_callback` and - * :type:`nghttp2_error_callback2` are set, the latter takes - * precedence. - */ -NGHTTP2_EXTERN void nghttp2_session_callbacks_set_error_callback( - nghttp2_session_callbacks *cbs, nghttp2_error_callback error_callback); - -/** - * @function - * - * Sets callback function invoked when library tells error code, and - * message to the application. - * - * If both :type:`nghttp2_error_callback` and - * :type:`nghttp2_error_callback2` are set, the latter takes - * precedence. - */ -NGHTTP2_EXTERN void nghttp2_session_callbacks_set_error_callback2( - nghttp2_session_callbacks *cbs, nghttp2_error_callback2 error_callback2); - -/** - * @functypedef - * - * Custom memory allocator to replace malloc(). The |mem_user_data| - * is the mem_user_data member of :type:`nghttp2_mem` structure. - */ -typedef void *(*nghttp2_malloc)(size_t size, void *mem_user_data); - -/** - * @functypedef - * - * Custom memory allocator to replace free(). The |mem_user_data| is - * the mem_user_data member of :type:`nghttp2_mem` structure. - */ -typedef void (*nghttp2_free)(void *ptr, void *mem_user_data); - -/** - * @functypedef - * - * Custom memory allocator to replace calloc(). The |mem_user_data| - * is the mem_user_data member of :type:`nghttp2_mem` structure. - */ -typedef void *(*nghttp2_calloc)(size_t nmemb, size_t size, void *mem_user_data); - -/** - * @functypedef - * - * Custom memory allocator to replace realloc(). The |mem_user_data| - * is the mem_user_data member of :type:`nghttp2_mem` structure. - */ -typedef void *(*nghttp2_realloc)(void *ptr, size_t size, void *mem_user_data); - -/** - * @struct - * - * Custom memory allocator functions and user defined pointer. The - * |mem_user_data| member is passed to each allocator function. This - * can be used, for example, to achieve per-session memory pool. - * - * In the following example code, ``my_malloc``, ``my_free``, - * ``my_calloc`` and ``my_realloc`` are the replacement of the - * standard allocators ``malloc``, ``free``, ``calloc`` and - * ``realloc`` respectively:: - * - * void *my_malloc_cb(size_t size, void *mem_user_data) { - * return my_malloc(size); - * } - * - * void my_free_cb(void *ptr, void *mem_user_data) { my_free(ptr); } - * - * void *my_calloc_cb(size_t nmemb, size_t size, void *mem_user_data) { - * return my_calloc(nmemb, size); - * } - * - * void *my_realloc_cb(void *ptr, size_t size, void *mem_user_data) { - * return my_realloc(ptr, size); - * } - * - * void session_new() { - * nghttp2_session *session; - * nghttp2_session_callbacks *callbacks; - * nghttp2_mem mem = {NULL, my_malloc_cb, my_free_cb, my_calloc_cb, - * my_realloc_cb}; - * - * ... - * - * nghttp2_session_client_new3(&session, callbacks, NULL, NULL, &mem); - * - * ... - * } - */ -typedef struct { - /** - * An arbitrary user supplied data. This is passed to each - * allocator function. - */ - void *mem_user_data; - /** - * Custom allocator function to replace malloc(). - */ - nghttp2_malloc malloc; - /** - * Custom allocator function to replace free(). - */ - nghttp2_free free; - /** - * Custom allocator function to replace calloc(). - */ - nghttp2_calloc calloc; - /** - * Custom allocator function to replace realloc(). - */ - nghttp2_realloc realloc; -} nghttp2_mem; - -struct nghttp2_option; - -/** - * @struct - * - * Configuration options for :type:`nghttp2_session`. The details of - * this structure are intentionally hidden from the public API. - */ -typedef struct nghttp2_option nghttp2_option; - -/** - * @function - * - * Initializes |*option_ptr| with default values. - * - * When the application finished using this object, it can use - * `nghttp2_option_del()` to free its memory. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * + * :type:`nghttp2_data_source_read_callback` to avoid data copy. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_send_data_callback( + nghttp2_session_callbacks *cbs, + nghttp2_send_data_callback send_data_callback); + +/** + * @function + * + * Sets callback function invoked when the library asks the + * application to pack extension frame payload in wire format. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_pack_extension_callback( + nghttp2_session_callbacks *cbs, + nghttp2_pack_extension_callback pack_extension_callback); + +/** + * @function + * + * Sets callback function invoked when the library asks the + * application to unpack extension frame payload from wire format. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_unpack_extension_callback( + nghttp2_session_callbacks *cbs, + nghttp2_unpack_extension_callback unpack_extension_callback); + +/** + * @function + * + * Sets callback function invoked when chunk of extension frame + * payload is received. + */ +NGHTTP2_EXTERN void +nghttp2_session_callbacks_set_on_extension_chunk_recv_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_extension_chunk_recv_callback on_extension_chunk_recv_callback); + +/** + * @function + * + * Sets callback function invoked when library tells error message to + * the application. + * + * This function is deprecated. The new application should use + * `nghttp2_session_callbacks_set_error_callback2()`. + * + * If both :type:`nghttp2_error_callback` and + * :type:`nghttp2_error_callback2` are set, the latter takes + * precedence. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_error_callback( + nghttp2_session_callbacks *cbs, nghttp2_error_callback error_callback); + +/** + * @function + * + * Sets callback function invoked when library tells error code, and + * message to the application. + * + * If both :type:`nghttp2_error_callback` and + * :type:`nghttp2_error_callback2` are set, the latter takes + * precedence. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_error_callback2( + nghttp2_session_callbacks *cbs, nghttp2_error_callback2 error_callback2); + +/** + * @functypedef + * + * Custom memory allocator to replace malloc(). The |mem_user_data| + * is the mem_user_data member of :type:`nghttp2_mem` structure. + */ +typedef void *(*nghttp2_malloc)(size_t size, void *mem_user_data); + +/** + * @functypedef + * + * Custom memory allocator to replace free(). The |mem_user_data| is + * the mem_user_data member of :type:`nghttp2_mem` structure. + */ +typedef void (*nghttp2_free)(void *ptr, void *mem_user_data); + +/** + * @functypedef + * + * Custom memory allocator to replace calloc(). The |mem_user_data| + * is the mem_user_data member of :type:`nghttp2_mem` structure. + */ +typedef void *(*nghttp2_calloc)(size_t nmemb, size_t size, void *mem_user_data); + +/** + * @functypedef + * + * Custom memory allocator to replace realloc(). The |mem_user_data| + * is the mem_user_data member of :type:`nghttp2_mem` structure. + */ +typedef void *(*nghttp2_realloc)(void *ptr, size_t size, void *mem_user_data); + +/** + * @struct + * + * Custom memory allocator functions and user defined pointer. The + * |mem_user_data| member is passed to each allocator function. This + * can be used, for example, to achieve per-session memory pool. + * + * In the following example code, ``my_malloc``, ``my_free``, + * ``my_calloc`` and ``my_realloc`` are the replacement of the + * standard allocators ``malloc``, ``free``, ``calloc`` and + * ``realloc`` respectively:: + * + * void *my_malloc_cb(size_t size, void *mem_user_data) { + * return my_malloc(size); + * } + * + * void my_free_cb(void *ptr, void *mem_user_data) { my_free(ptr); } + * + * void *my_calloc_cb(size_t nmemb, size_t size, void *mem_user_data) { + * return my_calloc(nmemb, size); + * } + * + * void *my_realloc_cb(void *ptr, size_t size, void *mem_user_data) { + * return my_realloc(ptr, size); + * } + * + * void session_new() { + * nghttp2_session *session; + * nghttp2_session_callbacks *callbacks; + * nghttp2_mem mem = {NULL, my_malloc_cb, my_free_cb, my_calloc_cb, + * my_realloc_cb}; + * + * ... + * + * nghttp2_session_client_new3(&session, callbacks, NULL, NULL, &mem); + * + * ... + * } + */ +typedef struct { + /** + * An arbitrary user supplied data. This is passed to each + * allocator function. + */ + void *mem_user_data; + /** + * Custom allocator function to replace malloc(). + */ + nghttp2_malloc malloc; + /** + * Custom allocator function to replace free(). + */ + nghttp2_free free; + /** + * Custom allocator function to replace calloc(). + */ + nghttp2_calloc calloc; + /** + * Custom allocator function to replace realloc(). + */ + nghttp2_realloc realloc; +} nghttp2_mem; + +struct nghttp2_option; + +/** + * @struct + * + * Configuration options for :type:`nghttp2_session`. The details of + * this structure are intentionally hidden from the public API. + */ +typedef struct nghttp2_option nghttp2_option; + +/** + * @function + * + * Initializes |*option_ptr| with default values. + * + * When the application finished using this object, it can use + * `nghttp2_option_del()` to free its memory. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` - * Out of memory. - */ -NGHTTP2_EXTERN int nghttp2_option_new(nghttp2_option **option_ptr); - -/** - * @function - * - * Frees any resources allocated for |option|. If |option| is - * ``NULL``, this function does nothing. - */ -NGHTTP2_EXTERN void nghttp2_option_del(nghttp2_option *option); - -/** - * @function - * - * This option prevents the library from sending WINDOW_UPDATE for a - * connection automatically. If this option is set to nonzero, the - * library won't send WINDOW_UPDATE for DATA until application calls - * `nghttp2_session_consume()` to indicate the consumed amount of - * data. Don't use `nghttp2_submit_window_update()` for this purpose. - * By default, this option is set to zero. - */ -NGHTTP2_EXTERN void -nghttp2_option_set_no_auto_window_update(nghttp2_option *option, int val); - -/** - * @function - * - * This option sets the SETTINGS_MAX_CONCURRENT_STREAMS value of - * remote endpoint as if it is received in SETTINGS frame. Without + * Out of memory. + */ +NGHTTP2_EXTERN int nghttp2_option_new(nghttp2_option **option_ptr); + +/** + * @function + * + * Frees any resources allocated for |option|. If |option| is + * ``NULL``, this function does nothing. + */ +NGHTTP2_EXTERN void nghttp2_option_del(nghttp2_option *option); + +/** + * @function + * + * This option prevents the library from sending WINDOW_UPDATE for a + * connection automatically. If this option is set to nonzero, the + * library won't send WINDOW_UPDATE for DATA until application calls + * `nghttp2_session_consume()` to indicate the consumed amount of + * data. Don't use `nghttp2_submit_window_update()` for this purpose. + * By default, this option is set to zero. + */ +NGHTTP2_EXTERN void +nghttp2_option_set_no_auto_window_update(nghttp2_option *option, int val); + +/** + * @function + * + * This option sets the SETTINGS_MAX_CONCURRENT_STREAMS value of + * remote endpoint as if it is received in SETTINGS frame. Without * specifying this option, the maximum number of outgoing concurrent * streams is initially limited to 100 to avoid issues when the local * endpoint submits lots of requests before receiving initial SETTINGS @@ -2546,160 +2546,160 @@ nghttp2_option_set_no_auto_window_update(nghttp2_option *option, int val); * initial SETTINGS frame from the remote endpoint, either to the * value advertised in SETTINGS_MAX_CONCURRENT_STREAMS or to the * default value (unlimited) if none was advertised. - */ -NGHTTP2_EXTERN void -nghttp2_option_set_peer_max_concurrent_streams(nghttp2_option *option, - uint32_t val); - -/** - * @function - * - * By default, nghttp2 library, if configured as server, requires - * first 24 bytes of client magic byte string (MAGIC). In most cases, - * this will simplify the implementation of server. But sometimes - * server may want to detect the application protocol based on first - * few bytes on clear text communication. - * - * If this option is used with nonzero |val|, nghttp2 library does not - * handle MAGIC. It still checks following SETTINGS frame. This - * means that applications should deal with MAGIC by themselves. - * - * If this option is not used or used with zero value, if MAGIC does - * not match :macro:`NGHTTP2_CLIENT_MAGIC`, `nghttp2_session_recv()` - * and `nghttp2_session_mem_recv()` will return error + */ +NGHTTP2_EXTERN void +nghttp2_option_set_peer_max_concurrent_streams(nghttp2_option *option, + uint32_t val); + +/** + * @function + * + * By default, nghttp2 library, if configured as server, requires + * first 24 bytes of client magic byte string (MAGIC). In most cases, + * this will simplify the implementation of server. But sometimes + * server may want to detect the application protocol based on first + * few bytes on clear text communication. + * + * If this option is used with nonzero |val|, nghttp2 library does not + * handle MAGIC. It still checks following SETTINGS frame. This + * means that applications should deal with MAGIC by themselves. + * + * If this option is not used or used with zero value, if MAGIC does + * not match :macro:`NGHTTP2_CLIENT_MAGIC`, `nghttp2_session_recv()` + * and `nghttp2_session_mem_recv()` will return error * :enum:`nghttp2_error.NGHTTP2_ERR_BAD_CLIENT_MAGIC`, which is fatal * error. - */ -NGHTTP2_EXTERN void -nghttp2_option_set_no_recv_client_magic(nghttp2_option *option, int val); - -/** - * @function - * - * By default, nghttp2 library enforces subset of HTTP Messaging rules - * described in `HTTP/2 specification, section 8 - * <https://tools.ietf.org/html/rfc7540#section-8>`_. See - * :ref:`http-messaging` section for details. For those applications - * who use nghttp2 library as non-HTTP use, give nonzero to |val| to - * disable this enforcement. Please note that disabling this feature - * does not change the fundamental client and server model of HTTP. - * That is, even if the validation is disabled, only client can send - * requests. - */ -NGHTTP2_EXTERN void nghttp2_option_set_no_http_messaging(nghttp2_option *option, - int val); - -/** - * @function - * - * RFC 7540 does not enforce any limit on the number of incoming - * reserved streams (in RFC 7540 terms, streams in reserved (remote) - * state). This only affects client side, since only server can push - * streams. Malicious server can push arbitrary number of streams, - * and make client's memory exhausted. This option can set the - * maximum number of such incoming streams to avoid possible memory - * exhaustion. If this option is set, and pushed streams are - * automatically closed on reception, without calling user provided - * callback, if they exceed the given limit. The default value is - * 200. If session is configured as server side, this option has no - * effect. Server can control the number of streams to push. - */ -NGHTTP2_EXTERN void -nghttp2_option_set_max_reserved_remote_streams(nghttp2_option *option, - uint32_t val); - -/** - * @function - * - * Sets extension frame type the application is willing to handle with - * user defined callbacks (see - * :type:`nghttp2_on_extension_chunk_recv_callback` and - * :type:`nghttp2_unpack_extension_callback`). The |type| is - * extension frame type, and must be strictly greater than 0x9. - * Otherwise, this function does nothing. The application can call - * this function multiple times to set more than one frame type to - * receive. The application does not have to call this function if it - * just sends extension frames. - */ -NGHTTP2_EXTERN void -nghttp2_option_set_user_recv_extension_type(nghttp2_option *option, - uint8_t type); - -/** - * @function - * - * Sets extension frame type the application is willing to receive - * using builtin handler. The |type| is the extension frame type to - * receive, and must be strictly greater than 0x9. Otherwise, this - * function does nothing. The application can call this function - * multiple times to set more than one frame type to receive. The - * application does not have to call this function if it just sends - * extension frames. - * - * If same frame type is passed to both - * `nghttp2_option_set_builtin_recv_extension_type()` and - * `nghttp2_option_set_user_recv_extension_type()`, the latter takes - * precedence. - */ -NGHTTP2_EXTERN void -nghttp2_option_set_builtin_recv_extension_type(nghttp2_option *option, - uint8_t type); - -/** - * @function - * - * This option prevents the library from sending PING frame with ACK - * flag set automatically when PING frame without ACK flag set is - * received. If this option is set to nonzero, the library won't send - * PING frame with ACK flag set in the response for incoming PING - * frame. The application can send PING frame with ACK flag set using + */ +NGHTTP2_EXTERN void +nghttp2_option_set_no_recv_client_magic(nghttp2_option *option, int val); + +/** + * @function + * + * By default, nghttp2 library enforces subset of HTTP Messaging rules + * described in `HTTP/2 specification, section 8 + * <https://tools.ietf.org/html/rfc7540#section-8>`_. See + * :ref:`http-messaging` section for details. For those applications + * who use nghttp2 library as non-HTTP use, give nonzero to |val| to + * disable this enforcement. Please note that disabling this feature + * does not change the fundamental client and server model of HTTP. + * That is, even if the validation is disabled, only client can send + * requests. + */ +NGHTTP2_EXTERN void nghttp2_option_set_no_http_messaging(nghttp2_option *option, + int val); + +/** + * @function + * + * RFC 7540 does not enforce any limit on the number of incoming + * reserved streams (in RFC 7540 terms, streams in reserved (remote) + * state). This only affects client side, since only server can push + * streams. Malicious server can push arbitrary number of streams, + * and make client's memory exhausted. This option can set the + * maximum number of such incoming streams to avoid possible memory + * exhaustion. If this option is set, and pushed streams are + * automatically closed on reception, without calling user provided + * callback, if they exceed the given limit. The default value is + * 200. If session is configured as server side, this option has no + * effect. Server can control the number of streams to push. + */ +NGHTTP2_EXTERN void +nghttp2_option_set_max_reserved_remote_streams(nghttp2_option *option, + uint32_t val); + +/** + * @function + * + * Sets extension frame type the application is willing to handle with + * user defined callbacks (see + * :type:`nghttp2_on_extension_chunk_recv_callback` and + * :type:`nghttp2_unpack_extension_callback`). The |type| is + * extension frame type, and must be strictly greater than 0x9. + * Otherwise, this function does nothing. The application can call + * this function multiple times to set more than one frame type to + * receive. The application does not have to call this function if it + * just sends extension frames. + */ +NGHTTP2_EXTERN void +nghttp2_option_set_user_recv_extension_type(nghttp2_option *option, + uint8_t type); + +/** + * @function + * + * Sets extension frame type the application is willing to receive + * using builtin handler. The |type| is the extension frame type to + * receive, and must be strictly greater than 0x9. Otherwise, this + * function does nothing. The application can call this function + * multiple times to set more than one frame type to receive. The + * application does not have to call this function if it just sends + * extension frames. + * + * If same frame type is passed to both + * `nghttp2_option_set_builtin_recv_extension_type()` and + * `nghttp2_option_set_user_recv_extension_type()`, the latter takes + * precedence. + */ +NGHTTP2_EXTERN void +nghttp2_option_set_builtin_recv_extension_type(nghttp2_option *option, + uint8_t type); + +/** + * @function + * + * This option prevents the library from sending PING frame with ACK + * flag set automatically when PING frame without ACK flag set is + * received. If this option is set to nonzero, the library won't send + * PING frame with ACK flag set in the response for incoming PING + * frame. The application can send PING frame with ACK flag set using * `nghttp2_submit_ping()` with :enum:`nghttp2_flag.NGHTTP2_FLAG_ACK` * as flags parameter. - */ -NGHTTP2_EXTERN void nghttp2_option_set_no_auto_ping_ack(nghttp2_option *option, - int val); - -/** - * @function - * - * This option sets the maximum length of header block (a set of - * header fields per one HEADERS frame) to send. The length of a - * given set of header fields is calculated using - * `nghttp2_hd_deflate_bound()`. The default value is 64KiB. If - * application attempts to send header fields larger than this limit, - * the transmission of the frame fails with error code + */ +NGHTTP2_EXTERN void nghttp2_option_set_no_auto_ping_ack(nghttp2_option *option, + int val); + +/** + * @function + * + * This option sets the maximum length of header block (a set of + * header fields per one HEADERS frame) to send. The length of a + * given set of header fields is calculated using + * `nghttp2_hd_deflate_bound()`. The default value is 64KiB. If + * application attempts to send header fields larger than this limit, + * the transmission of the frame fails with error code * :enum:`nghttp2_error.NGHTTP2_ERR_FRAME_SIZE_ERROR`. - */ -NGHTTP2_EXTERN void -nghttp2_option_set_max_send_header_block_length(nghttp2_option *option, - size_t val); - -/** - * @function - * - * This option sets the maximum dynamic table size for deflating - * header fields. The default value is 4KiB. In HTTP/2, receiver of - * deflated header block can specify maximum dynamic table size. The - * actual maximum size is the minimum of the size receiver specified - * and this option value. - */ -NGHTTP2_EXTERN void -nghttp2_option_set_max_deflate_dynamic_table_size(nghttp2_option *option, - size_t val); - -/** - * @function - * - * This option prevents the library from retaining closed streams to - * maintain the priority tree. If this option is set to nonzero, - * applications can discard closed stream completely to save memory. - */ -NGHTTP2_EXTERN void nghttp2_option_set_no_closed_streams(nghttp2_option *option, - int val); - -/** - * @function - * + */ +NGHTTP2_EXTERN void +nghttp2_option_set_max_send_header_block_length(nghttp2_option *option, + size_t val); + +/** + * @function + * + * This option sets the maximum dynamic table size for deflating + * header fields. The default value is 4KiB. In HTTP/2, receiver of + * deflated header block can specify maximum dynamic table size. The + * actual maximum size is the minimum of the size receiver specified + * and this option value. + */ +NGHTTP2_EXTERN void +nghttp2_option_set_max_deflate_dynamic_table_size(nghttp2_option *option, + size_t val); + +/** + * @function + * + * This option prevents the library from retaining closed streams to + * maintain the priority tree. If this option is set to nonzero, + * applications can discard closed stream completely to save memory. + */ +NGHTTP2_EXTERN void nghttp2_option_set_no_closed_streams(nghttp2_option *option, + int val); + +/** + * @function + * * This function sets the maximum number of outgoing SETTINGS ACK and * PING ACK frames retained in :type:`nghttp2_session` object. If * more than those frames are retained, the peer is considered to be @@ -2722,1430 +2722,1430 @@ NGHTTP2_EXTERN void nghttp2_option_set_max_settings(nghttp2_option *option, /** * @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 - * supplied data, which will be passed to the callback functions. - * - * The :type:`nghttp2_send_callback` must be specified. If the - * application code uses `nghttp2_session_recv()`, the - * :type:`nghttp2_recv_callback` must be specified. The other members - * of |callbacks| can be ``NULL``. - * - * If this function fails, |*session_ptr| is left untouched. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * + * 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 + * supplied data, which will be passed to the callback functions. + * + * The :type:`nghttp2_send_callback` must be specified. If the + * application code uses `nghttp2_session_recv()`, the + * :type:`nghttp2_recv_callback` must be specified. The other members + * of |callbacks| can be ``NULL``. + * + * If this function fails, |*session_ptr| is left untouched. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` - * Out of memory. - */ -NGHTTP2_EXTERN int -nghttp2_session_client_new(nghttp2_session **session_ptr, - const nghttp2_session_callbacks *callbacks, - void *user_data); - -/** - * @function - * - * Initializes |*session_ptr| for server 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 - * supplied data, which will be passed to the callback functions. - * - * The :type:`nghttp2_send_callback` must be specified. If the - * application code uses `nghttp2_session_recv()`, the - * :type:`nghttp2_recv_callback` must be specified. The other members - * of |callbacks| can be ``NULL``. - * - * If this function fails, |*session_ptr| is left untouched. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * + * Out of memory. + */ +NGHTTP2_EXTERN int +nghttp2_session_client_new(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data); + +/** + * @function + * + * Initializes |*session_ptr| for server 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 + * supplied data, which will be passed to the callback functions. + * + * The :type:`nghttp2_send_callback` must be specified. If the + * application code uses `nghttp2_session_recv()`, the + * :type:`nghttp2_recv_callback` must be specified. The other members + * of |callbacks| can be ``NULL``. + * + * If this function fails, |*session_ptr| is left untouched. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` - * Out of memory. - */ -NGHTTP2_EXTERN int -nghttp2_session_server_new(nghttp2_session **session_ptr, - const nghttp2_session_callbacks *callbacks, - void *user_data); - -/** - * @function - * - * Like `nghttp2_session_client_new()`, but with additional options - * specified in the |option|. - * - * The |option| can be ``NULL`` and the call is equivalent to - * `nghttp2_session_client_new()`. - * - * This function does not take ownership |option|. The application is - * responsible for freeing |option| if it finishes using the object. - * - * The library code does not refer to |option| after this function - * returns. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * + * Out of memory. + */ +NGHTTP2_EXTERN int +nghttp2_session_server_new(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data); + +/** + * @function + * + * Like `nghttp2_session_client_new()`, but with additional options + * specified in the |option|. + * + * The |option| can be ``NULL`` and the call is equivalent to + * `nghttp2_session_client_new()`. + * + * This function does not take ownership |option|. The application is + * responsible for freeing |option| if it finishes using the object. + * + * The library code does not refer to |option| after this function + * returns. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` - * Out of memory. - */ -NGHTTP2_EXTERN int -nghttp2_session_client_new2(nghttp2_session **session_ptr, - const nghttp2_session_callbacks *callbacks, - void *user_data, const nghttp2_option *option); - -/** - * @function - * - * Like `nghttp2_session_server_new()`, but with additional options - * specified in the |option|. - * - * The |option| can be ``NULL`` and the call is equivalent to - * `nghttp2_session_server_new()`. - * - * This function does not take ownership |option|. The application is - * responsible for freeing |option| if it finishes using the object. - * - * The library code does not refer to |option| after this function - * returns. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * + * Out of memory. + */ +NGHTTP2_EXTERN int +nghttp2_session_client_new2(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data, const nghttp2_option *option); + +/** + * @function + * + * Like `nghttp2_session_server_new()`, but with additional options + * specified in the |option|. + * + * The |option| can be ``NULL`` and the call is equivalent to + * `nghttp2_session_server_new()`. + * + * This function does not take ownership |option|. The application is + * responsible for freeing |option| if it finishes using the object. + * + * The library code does not refer to |option| after this function + * returns. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` - * Out of memory. - */ -NGHTTP2_EXTERN int -nghttp2_session_server_new2(nghttp2_session **session_ptr, - const nghttp2_session_callbacks *callbacks, - void *user_data, const nghttp2_option *option); - -/** - * @function - * - * Like `nghttp2_session_client_new2()`, but with additional custom - * memory allocator specified in the |mem|. - * - * The |mem| can be ``NULL`` and the call is equivalent to - * `nghttp2_session_client_new2()`. - * - * This function does not take ownership |mem|. The application is - * responsible for freeing |mem|. - * - * The library code does not refer to |mem| pointer after this - * function returns, so the application can safely free it. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * + * Out of memory. + */ +NGHTTP2_EXTERN int +nghttp2_session_server_new2(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data, const nghttp2_option *option); + +/** + * @function + * + * Like `nghttp2_session_client_new2()`, but with additional custom + * memory allocator specified in the |mem|. + * + * The |mem| can be ``NULL`` and the call is equivalent to + * `nghttp2_session_client_new2()`. + * + * This function does not take ownership |mem|. The application is + * responsible for freeing |mem|. + * + * The library code does not refer to |mem| pointer after this + * function returns, so the application can safely free it. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` - * Out of memory. - */ -NGHTTP2_EXTERN int nghttp2_session_client_new3( - nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks, - void *user_data, const nghttp2_option *option, nghttp2_mem *mem); - -/** - * @function - * - * Like `nghttp2_session_server_new2()`, but with additional custom - * memory allocator specified in the |mem|. - * - * The |mem| can be ``NULL`` and the call is equivalent to - * `nghttp2_session_server_new2()`. - * - * This function does not take ownership |mem|. The application is - * responsible for freeing |mem|. - * - * The library code does not refer to |mem| pointer after this - * function returns, so the application can safely free it. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * + * Out of memory. + */ +NGHTTP2_EXTERN int nghttp2_session_client_new3( + nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks, + void *user_data, const nghttp2_option *option, nghttp2_mem *mem); + +/** + * @function + * + * Like `nghttp2_session_server_new2()`, but with additional custom + * memory allocator specified in the |mem|. + * + * The |mem| can be ``NULL`` and the call is equivalent to + * `nghttp2_session_server_new2()`. + * + * This function does not take ownership |mem|. The application is + * responsible for freeing |mem|. + * + * The library code does not refer to |mem| pointer after this + * function returns, so the application can safely free it. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` - * Out of memory. - */ -NGHTTP2_EXTERN int nghttp2_session_server_new3( - nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks, - void *user_data, const nghttp2_option *option, nghttp2_mem *mem); - -/** - * @function - * - * Frees any resources allocated for |session|. If |session| is - * ``NULL``, this function does nothing. - */ -NGHTTP2_EXTERN void nghttp2_session_del(nghttp2_session *session); - -/** - * @function - * - * Sends pending frames to the remote peer. - * - * This function retrieves the highest prioritized frame from the - * outbound queue and sends it to the remote peer. It does this as + * Out of memory. + */ +NGHTTP2_EXTERN int nghttp2_session_server_new3( + nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks, + void *user_data, const nghttp2_option *option, nghttp2_mem *mem); + +/** + * @function + * + * Frees any resources allocated for |session|. If |session| is + * ``NULL``, this function does nothing. + */ +NGHTTP2_EXTERN void nghttp2_session_del(nghttp2_session *session); + +/** + * @function + * + * Sends pending frames to the remote peer. + * + * This function retrieves the highest prioritized frame from the + * outbound queue and sends it to the remote peer. It does this as * many times as possible until the user callback - * :type:`nghttp2_send_callback` returns + * :type:`nghttp2_send_callback` returns * :enum:`nghttp2_error.NGHTTP2_ERR_WOULDBLOCK`, the outbound queue * becomes empty or flow control is triggered (remote window size * becomes depleted or maximum number of concurrent streams is * reached). This function calls several callback functions which are * passed when initializing the |session|. Here is the simple time * chart which tells when each callback is invoked: - * - * 1. Get the next frame to send from outbound queue. - * - * 2. Prepare transmission of the frame. - * - * 3. If the control frame cannot be sent because some preconditions - * are not met (e.g., request HEADERS cannot be sent after GOAWAY), - * :type:`nghttp2_on_frame_not_send_callback` is invoked. Abort - * the following steps. - * - * 4. If the frame is HEADERS, PUSH_PROMISE or DATA, - * :type:`nghttp2_select_padding_callback` is invoked. - * - * 5. If the frame is request HEADERS, the stream is opened here. - * - * 6. :type:`nghttp2_before_frame_send_callback` is invoked. - * + * + * 1. Get the next frame to send from outbound queue. + * + * 2. Prepare transmission of the frame. + * + * 3. If the control frame cannot be sent because some preconditions + * are not met (e.g., request HEADERS cannot be sent after GOAWAY), + * :type:`nghttp2_on_frame_not_send_callback` is invoked. Abort + * the following steps. + * + * 4. If the frame is HEADERS, PUSH_PROMISE or DATA, + * :type:`nghttp2_select_padding_callback` is invoked. + * + * 5. If the frame is request HEADERS, the stream is opened here. + * + * 6. :type:`nghttp2_before_frame_send_callback` is invoked. + * * 7. If :enum:`nghttp2_error.NGHTTP2_ERR_CANCEL` is returned from - * :type:`nghttp2_before_frame_send_callback`, the current frame - * transmission is canceled, and - * :type:`nghttp2_on_frame_not_send_callback` is invoked. Abort - * the following steps. - * - * 8. :type:`nghttp2_send_callback` is invoked one or more times to - * send the frame. - * - * 9. :type:`nghttp2_on_frame_send_callback` is invoked. - * - * 10. If the transmission of the frame triggers closure of the - * stream, the stream is closed and - * :type:`nghttp2_on_stream_close_callback` is invoked. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * + * :type:`nghttp2_before_frame_send_callback`, the current frame + * transmission is canceled, and + * :type:`nghttp2_on_frame_not_send_callback` is invoked. Abort + * the following steps. + * + * 8. :type:`nghttp2_send_callback` is invoked one or more times to + * send the frame. + * + * 9. :type:`nghttp2_on_frame_send_callback` is invoked. + * + * 10. If the transmission of the frame triggers closure of the + * stream, the stream is closed and + * :type:`nghttp2_on_stream_close_callback` is invoked. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` - * Out of memory. + * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` - * The callback function failed. - */ -NGHTTP2_EXTERN int nghttp2_session_send(nghttp2_session *session); - -/** - * @function - * - * Returns the serialized data to send. - * - * This function behaves like `nghttp2_session_send()` except that it - * does not use :type:`nghttp2_send_callback` to transmit data. - * Instead, it assigns the pointer to the serialized data to the - * |*data_ptr| and returns its length. The other callbacks are called - * in the same way as they are in `nghttp2_session_send()`. - * - * If no data is available to send, this function returns 0. - * - * This function may not return all serialized data in one invocation. - * To get all data, call this function repeatedly until it returns 0 - * or one of negative error codes. - * - * The assigned |*data_ptr| is valid until the next call of - * `nghttp2_session_mem_send()` or `nghttp2_session_send()`. - * - * The caller must send all data before sending the next chunk of - * data. - * - * This function returns the length of the data pointed by the - * |*data_ptr| if it succeeds, or one of the following negative error - * codes: - * + * The callback function failed. + */ +NGHTTP2_EXTERN int nghttp2_session_send(nghttp2_session *session); + +/** + * @function + * + * Returns the serialized data to send. + * + * This function behaves like `nghttp2_session_send()` except that it + * does not use :type:`nghttp2_send_callback` to transmit data. + * Instead, it assigns the pointer to the serialized data to the + * |*data_ptr| and returns its length. The other callbacks are called + * in the same way as they are in `nghttp2_session_send()`. + * + * If no data is available to send, this function returns 0. + * + * This function may not return all serialized data in one invocation. + * To get all data, call this function repeatedly until it returns 0 + * or one of negative error codes. + * + * The assigned |*data_ptr| is valid until the next call of + * `nghttp2_session_mem_send()` or `nghttp2_session_send()`. + * + * The caller must send all data before sending the next chunk of + * data. + * + * This function returns the length of the data pointed by the + * |*data_ptr| if it succeeds, or one of the following negative error + * codes: + * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` - * Out of memory. - * - * .. note:: - * - * This function may produce very small byte string. If that is the - * case, and application disables Nagle algorithm (``TCP_NODELAY``), - * then writing this small chunk leads to very small packet, and it - * is very inefficient. An application should be responsible to - * buffer up small chunks of data as necessary to avoid this - * situation. - */ -NGHTTP2_EXTERN ssize_t nghttp2_session_mem_send(nghttp2_session *session, - const uint8_t **data_ptr); - -/** - * @function - * - * Receives frames from the remote peer. - * - * This function receives as many frames as possible until the user - * callback :type:`nghttp2_recv_callback` returns + * Out of memory. + * + * .. note:: + * + * This function may produce very small byte string. If that is the + * case, and application disables Nagle algorithm (``TCP_NODELAY``), + * then writing this small chunk leads to very small packet, and it + * is very inefficient. An application should be responsible to + * buffer up small chunks of data as necessary to avoid this + * situation. + */ +NGHTTP2_EXTERN ssize_t nghttp2_session_mem_send(nghttp2_session *session, + const uint8_t **data_ptr); + +/** + * @function + * + * Receives frames from the remote peer. + * + * This function receives as many frames as possible until the user + * callback :type:`nghttp2_recv_callback` returns * :enum:`nghttp2_error.NGHTTP2_ERR_WOULDBLOCK`. This function calls * several callback functions which are passed when initializing the - * |session|. Here is the simple time chart which tells when each - * callback is invoked: - * - * 1. :type:`nghttp2_recv_callback` is invoked one or more times to - * receive frame header. - * - * 2. When frame header is received, - * :type:`nghttp2_on_begin_frame_callback` is invoked. - * - * 3. If the frame is DATA frame: - * - * 1. :type:`nghttp2_recv_callback` is invoked to receive DATA - * payload. For each chunk of data, - * :type:`nghttp2_on_data_chunk_recv_callback` is invoked. - * - * 2. If one DATA frame is completely received, - * :type:`nghttp2_on_frame_recv_callback` is invoked. If the - * reception of the frame triggers the closure of the stream, - * :type:`nghttp2_on_stream_close_callback` is invoked. - * - * 4. If the frame is the control frame: - * - * 1. :type:`nghttp2_recv_callback` is invoked one or more times to - * receive whole frame. - * - * 2. If the received frame is valid, then following actions are - * taken. If the frame is either HEADERS or PUSH_PROMISE, - * :type:`nghttp2_on_begin_headers_callback` is invoked. Then - * :type:`nghttp2_on_header_callback` is invoked for each header - * name/value pair. For invalid header field, - * :type:`nghttp2_on_invalid_header_callback` is called. After - * all name/value pairs are emitted successfully, - * :type:`nghttp2_on_frame_recv_callback` is invoked. For other - * frames, :type:`nghttp2_on_frame_recv_callback` is invoked. - * If the reception of the frame triggers the closure of the - * stream, :type:`nghttp2_on_stream_close_callback` is invoked. - * - * 3. If the received frame is unpacked but is interpreted as - * invalid, :type:`nghttp2_on_invalid_frame_recv_callback` is - * invoked. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * + * |session|. Here is the simple time chart which tells when each + * callback is invoked: + * + * 1. :type:`nghttp2_recv_callback` is invoked one or more times to + * receive frame header. + * + * 2. When frame header is received, + * :type:`nghttp2_on_begin_frame_callback` is invoked. + * + * 3. If the frame is DATA frame: + * + * 1. :type:`nghttp2_recv_callback` is invoked to receive DATA + * payload. For each chunk of data, + * :type:`nghttp2_on_data_chunk_recv_callback` is invoked. + * + * 2. If one DATA frame is completely received, + * :type:`nghttp2_on_frame_recv_callback` is invoked. If the + * reception of the frame triggers the closure of the stream, + * :type:`nghttp2_on_stream_close_callback` is invoked. + * + * 4. If the frame is the control frame: + * + * 1. :type:`nghttp2_recv_callback` is invoked one or more times to + * receive whole frame. + * + * 2. If the received frame is valid, then following actions are + * taken. If the frame is either HEADERS or PUSH_PROMISE, + * :type:`nghttp2_on_begin_headers_callback` is invoked. Then + * :type:`nghttp2_on_header_callback` is invoked for each header + * name/value pair. For invalid header field, + * :type:`nghttp2_on_invalid_header_callback` is called. After + * all name/value pairs are emitted successfully, + * :type:`nghttp2_on_frame_recv_callback` is invoked. For other + * frames, :type:`nghttp2_on_frame_recv_callback` is invoked. + * If the reception of the frame triggers the closure of the + * stream, :type:`nghttp2_on_stream_close_callback` is invoked. + * + * 3. If the received frame is unpacked but is interpreted as + * invalid, :type:`nghttp2_on_invalid_frame_recv_callback` is + * invoked. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * * :enum:`nghttp2_error.NGHTTP2_ERR_EOF` - * The remote peer did shutdown on the connection. + * The remote peer did shutdown on the connection. * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` - * Out of memory. + * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` - * The callback function failed. + * The callback function failed. * :enum:`nghttp2_error.NGHTTP2_ERR_BAD_CLIENT_MAGIC` - * Invalid client magic was detected. This error only returns - * when |session| was configured as server and - * `nghttp2_option_set_no_recv_client_magic()` is not used with - * nonzero value. + * Invalid client magic was detected. This error only returns + * when |session| was configured as server and + * `nghttp2_option_set_no_recv_client_magic()` is not used with + * nonzero value. * :enum:`nghttp2_error.NGHTTP2_ERR_FLOODED` - * Flooding was detected in this HTTP/2 session, and it must be - * closed. This is most likely caused by misbehaviour of peer. - */ -NGHTTP2_EXTERN int nghttp2_session_recv(nghttp2_session *session); - -/** - * @function - * - * Processes data |in| as an input from the remote endpoint. The + * Flooding was detected in this HTTP/2 session, and it must be + * closed. This is most likely caused by misbehaviour of peer. + */ +NGHTTP2_EXTERN int nghttp2_session_recv(nghttp2_session *session); + +/** + * @function + * + * Processes data |in| as an input from the remote endpoint. The * |inlen| indicates the number of bytes to receive in the |in|. - * - * This function behaves like `nghttp2_session_recv()` except that it - * does not use :type:`nghttp2_recv_callback` to receive data; the - * |in| is the only data for the invocation of this function. If all - * bytes are processed, this function returns. The other callbacks - * are called in the same way as they are in `nghttp2_session_recv()`. - * - * In the current implementation, this function always tries to + * + * This function behaves like `nghttp2_session_recv()` except that it + * does not use :type:`nghttp2_recv_callback` to receive data; the + * |in| is the only data for the invocation of this function. If all + * bytes are processed, this function returns. The other callbacks + * are called in the same way as they are in `nghttp2_session_recv()`. + * + * In the current implementation, this function always tries to * processes |inlen| bytes of input data unless either an error occurs or * :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE` is returned from - * :type:`nghttp2_on_header_callback` or - * :type:`nghttp2_on_data_chunk_recv_callback`. If + * :type:`nghttp2_on_header_callback` or + * :type:`nghttp2_on_data_chunk_recv_callback`. If * :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE` is used, the return value * includes the number of bytes which was used to produce the data or * frame for the callback. - * - * This function returns the number of processed bytes, or one of the - * following negative error codes: - * + * + * This function returns the number of processed bytes, or one of the + * following negative error codes: + * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` - * Out of memory. + * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` - * The callback function failed. + * The callback function failed. * :enum:`nghttp2_error.NGHTTP2_ERR_BAD_CLIENT_MAGIC` - * Invalid client magic was detected. This error only returns - * when |session| was configured as server and - * `nghttp2_option_set_no_recv_client_magic()` is not used with - * nonzero value. + * Invalid client magic was detected. This error only returns + * when |session| was configured as server and + * `nghttp2_option_set_no_recv_client_magic()` is not used with + * nonzero value. * :enum:`nghttp2_error.NGHTTP2_ERR_FLOODED` - * Flooding was detected in this HTTP/2 session, and it must be - * closed. This is most likely caused by misbehaviour of peer. - */ -NGHTTP2_EXTERN ssize_t nghttp2_session_mem_recv(nghttp2_session *session, - const uint8_t *in, - size_t inlen); - -/** - * @function - * - * Puts back previously deferred DATA frame in the stream |stream_id| - * to the outbound queue. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * + * Flooding was detected in this HTTP/2 session, and it must be + * closed. This is most likely caused by misbehaviour of peer. + */ +NGHTTP2_EXTERN ssize_t nghttp2_session_mem_recv(nghttp2_session *session, + const uint8_t *in, + size_t inlen); + +/** + * @function + * + * Puts back previously deferred DATA frame in the stream |stream_id| + * to the outbound queue. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` - * The stream does not exist; or no deferred data exist. + * The stream does not exist; or no deferred data exist. * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` - * Out of memory. - */ -NGHTTP2_EXTERN int nghttp2_session_resume_data(nghttp2_session *session, - int32_t stream_id); - -/** - * @function - * - * Returns nonzero value if |session| wants to receive data from the - * remote peer. - * - * If both `nghttp2_session_want_read()` and - * `nghttp2_session_want_write()` return 0, the application should - * drop the connection. - */ -NGHTTP2_EXTERN int nghttp2_session_want_read(nghttp2_session *session); - -/** - * @function - * - * Returns nonzero value if |session| wants to send data to the remote - * peer. - * - * If both `nghttp2_session_want_read()` and - * `nghttp2_session_want_write()` return 0, the application should - * drop the connection. - */ -NGHTTP2_EXTERN int nghttp2_session_want_write(nghttp2_session *session); - -/** - * @function - * - * Returns stream_user_data for the stream |stream_id|. The - * stream_user_data is provided by `nghttp2_submit_request()`, - * `nghttp2_submit_headers()` or - * `nghttp2_session_set_stream_user_data()`. Unless it is set using - * `nghttp2_session_set_stream_user_data()`, if the stream is - * initiated by the remote endpoint, stream_user_data is always - * ``NULL``. If the stream does not exist, this function returns - * ``NULL``. - */ -NGHTTP2_EXTERN void * -nghttp2_session_get_stream_user_data(nghttp2_session *session, - int32_t stream_id); - -/** - * @function - * - * Sets the |stream_user_data| to the stream denoted by the - * |stream_id|. If a stream user data is already set to the stream, - * it is replaced with the |stream_user_data|. It is valid to specify - * ``NULL`` in the |stream_user_data|, which nullifies the associated - * data pointer. - * - * It is valid to set the |stream_user_data| to the stream reserved by - * PUSH_PROMISE frame. - * - * This function returns 0 if it succeeds, or one of following - * negative error codes: - * + * Out of memory. + */ +NGHTTP2_EXTERN int nghttp2_session_resume_data(nghttp2_session *session, + int32_t stream_id); + +/** + * @function + * + * Returns nonzero value if |session| wants to receive data from the + * remote peer. + * + * If both `nghttp2_session_want_read()` and + * `nghttp2_session_want_write()` return 0, the application should + * drop the connection. + */ +NGHTTP2_EXTERN int nghttp2_session_want_read(nghttp2_session *session); + +/** + * @function + * + * Returns nonzero value if |session| wants to send data to the remote + * peer. + * + * If both `nghttp2_session_want_read()` and + * `nghttp2_session_want_write()` return 0, the application should + * drop the connection. + */ +NGHTTP2_EXTERN int nghttp2_session_want_write(nghttp2_session *session); + +/** + * @function + * + * Returns stream_user_data for the stream |stream_id|. The + * stream_user_data is provided by `nghttp2_submit_request()`, + * `nghttp2_submit_headers()` or + * `nghttp2_session_set_stream_user_data()`. Unless it is set using + * `nghttp2_session_set_stream_user_data()`, if the stream is + * initiated by the remote endpoint, stream_user_data is always + * ``NULL``. If the stream does not exist, this function returns + * ``NULL``. + */ +NGHTTP2_EXTERN void * +nghttp2_session_get_stream_user_data(nghttp2_session *session, + int32_t stream_id); + +/** + * @function + * + * Sets the |stream_user_data| to the stream denoted by the + * |stream_id|. If a stream user data is already set to the stream, + * it is replaced with the |stream_user_data|. It is valid to specify + * ``NULL`` in the |stream_user_data|, which nullifies the associated + * data pointer. + * + * It is valid to set the |stream_user_data| to the stream reserved by + * PUSH_PROMISE frame. + * + * This function returns 0 if it succeeds, or one of following + * negative error codes: + * * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` - * The stream does not exist - */ -NGHTTP2_EXTERN int -nghttp2_session_set_stream_user_data(nghttp2_session *session, - int32_t stream_id, void *stream_user_data); - -/** - * @function - * - * Sets |user_data| to |session|, overwriting the existing user data - * specified in `nghttp2_session_client_new()`, or - * `nghttp2_session_server_new()`. - */ -NGHTTP2_EXTERN void nghttp2_session_set_user_data(nghttp2_session *session, - void *user_data); - -/** - * @function - * - * Returns the number of frames in the outbound queue. This does not - * include the deferred DATA frames. - */ -NGHTTP2_EXTERN size_t -nghttp2_session_get_outbound_queue_size(nghttp2_session *session); - -/** - * @function - * - * Returns the number of DATA payload in bytes received without - * WINDOW_UPDATE transmission for the stream |stream_id|. The local - * (receive) window size can be adjusted by - * `nghttp2_submit_window_update()`. This function takes into account - * that and returns effective data length. In particular, if the - * local window size is reduced by submitting negative - * window_size_increment with `nghttp2_submit_window_update()`, this - * function returns the number of bytes less than actually received. - * - * This function returns -1 if it fails. - */ -NGHTTP2_EXTERN int32_t nghttp2_session_get_stream_effective_recv_data_length( - nghttp2_session *session, int32_t stream_id); - -/** - * @function - * - * Returns the local (receive) window size for the stream |stream_id|. - * The local window size can be adjusted by - * `nghttp2_submit_window_update()`. This function takes into account - * that and returns effective window size. - * - * This function does not take into account the amount of received - * data from the remote endpoint. Use - * `nghttp2_session_get_stream_local_window_size()` to know the amount - * of data the remote endpoint can send without receiving stream level - * WINDOW_UPDATE frame. Note that each stream is still subject to the - * connection level flow control. - * - * This function returns -1 if it fails. - */ -NGHTTP2_EXTERN int32_t nghttp2_session_get_stream_effective_local_window_size( - nghttp2_session *session, int32_t stream_id); - -/** - * @function - * - * Returns the amount of flow-controlled payload (e.g., DATA) that the - * remote endpoint can send without receiving stream level - * WINDOW_UPDATE frame. It is also subject to the connection level - * flow control. So the actual amount of data to send is - * min(`nghttp2_session_get_stream_local_window_size()`, - * `nghttp2_session_get_local_window_size()`). - * - * This function returns -1 if it fails. - */ -NGHTTP2_EXTERN int32_t nghttp2_session_get_stream_local_window_size( - nghttp2_session *session, int32_t stream_id); - -/** - * @function - * - * Returns the number of DATA payload in bytes received without - * WINDOW_UPDATE transmission for a connection. The local (receive) - * window size can be adjusted by `nghttp2_submit_window_update()`. - * This function takes into account that and returns effective data - * length. In particular, if the local window size is reduced by - * submitting negative window_size_increment with - * `nghttp2_submit_window_update()`, this function returns the number - * of bytes less than actually received. - * - * This function returns -1 if it fails. - */ -NGHTTP2_EXTERN int32_t -nghttp2_session_get_effective_recv_data_length(nghttp2_session *session); - -/** - * @function - * - * Returns the local (receive) window size for a connection. The - * local window size can be adjusted by - * `nghttp2_submit_window_update()`. This function takes into account - * that and returns effective window size. - * - * This function does not take into account the amount of received - * data from the remote endpoint. Use - * `nghttp2_session_get_local_window_size()` to know the amount of - * data the remote endpoint can send without receiving - * connection-level WINDOW_UPDATE frame. Note that each stream is - * still subject to the stream level flow control. - * - * This function returns -1 if it fails. - */ -NGHTTP2_EXTERN int32_t -nghttp2_session_get_effective_local_window_size(nghttp2_session *session); - -/** - * @function - * - * Returns the amount of flow-controlled payload (e.g., DATA) that the - * remote endpoint can send without receiving connection level - * WINDOW_UPDATE frame. Note that each stream is still subject to the - * stream level flow control (see - * `nghttp2_session_get_stream_local_window_size()`). - * - * This function returns -1 if it fails. - */ -NGHTTP2_EXTERN int32_t -nghttp2_session_get_local_window_size(nghttp2_session *session); - -/** - * @function - * - * Returns the remote window size for a given stream |stream_id|. - * - * This is the amount of flow-controlled payload (e.g., DATA) that the - * local endpoint can send without stream level WINDOW_UPDATE. There - * is also connection level flow control, so the effective size of - * payload that the local endpoint can actually send is - * min(`nghttp2_session_get_stream_remote_window_size()`, - * `nghttp2_session_get_remote_window_size()`). - * - * This function returns -1 if it fails. - */ -NGHTTP2_EXTERN int32_t nghttp2_session_get_stream_remote_window_size( - nghttp2_session *session, int32_t stream_id); - -/** - * @function - * - * Returns the remote window size for a connection. - * - * This function always succeeds. - */ -NGHTTP2_EXTERN int32_t -nghttp2_session_get_remote_window_size(nghttp2_session *session); - -/** - * @function - * - * Returns 1 if local peer half closed the given stream |stream_id|. - * Returns 0 if it did not. Returns -1 if no such stream exists. - */ -NGHTTP2_EXTERN int -nghttp2_session_get_stream_local_close(nghttp2_session *session, - int32_t stream_id); - -/** - * @function - * - * Returns 1 if remote peer half closed the given stream |stream_id|. - * Returns 0 if it did not. Returns -1 if no such stream exists. - */ -NGHTTP2_EXTERN int -nghttp2_session_get_stream_remote_close(nghttp2_session *session, - int32_t stream_id); - -/** - * @function - * - * Returns the current dynamic table size of HPACK inflater, including - * the overhead 32 bytes per entry described in RFC 7541. - */ -NGHTTP2_EXTERN size_t -nghttp2_session_get_hd_inflate_dynamic_table_size(nghttp2_session *session); - -/** - * @function - * - * Returns the current dynamic table size of HPACK deflater including - * the overhead 32 bytes per entry described in RFC 7541. - */ -NGHTTP2_EXTERN size_t -nghttp2_session_get_hd_deflate_dynamic_table_size(nghttp2_session *session); - -/** - * @function - * - * Signals the session so that the connection should be terminated. - * - * The last stream ID is the minimum value between the stream ID of a - * stream for which :type:`nghttp2_on_frame_recv_callback` was called - * most recently and the last stream ID we have sent to the peer - * previously. - * - * The |error_code| is the error code of this GOAWAY frame. The - * pre-defined error code is one of :enum:`nghttp2_error_code`. - * - * After the transmission, both `nghttp2_session_want_read()` and - * `nghttp2_session_want_write()` return 0. - * - * This function should be called when the connection should be - * terminated after sending GOAWAY. If the remaining streams should - * be processed after GOAWAY, use `nghttp2_submit_goaway()` instead. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * + * The stream does not exist + */ +NGHTTP2_EXTERN int +nghttp2_session_set_stream_user_data(nghttp2_session *session, + int32_t stream_id, void *stream_user_data); + +/** + * @function + * + * Sets |user_data| to |session|, overwriting the existing user data + * specified in `nghttp2_session_client_new()`, or + * `nghttp2_session_server_new()`. + */ +NGHTTP2_EXTERN void nghttp2_session_set_user_data(nghttp2_session *session, + void *user_data); + +/** + * @function + * + * Returns the number of frames in the outbound queue. This does not + * include the deferred DATA frames. + */ +NGHTTP2_EXTERN size_t +nghttp2_session_get_outbound_queue_size(nghttp2_session *session); + +/** + * @function + * + * Returns the number of DATA payload in bytes received without + * WINDOW_UPDATE transmission for the stream |stream_id|. The local + * (receive) window size can be adjusted by + * `nghttp2_submit_window_update()`. This function takes into account + * that and returns effective data length. In particular, if the + * local window size is reduced by submitting negative + * window_size_increment with `nghttp2_submit_window_update()`, this + * function returns the number of bytes less than actually received. + * + * This function returns -1 if it fails. + */ +NGHTTP2_EXTERN int32_t nghttp2_session_get_stream_effective_recv_data_length( + nghttp2_session *session, int32_t stream_id); + +/** + * @function + * + * Returns the local (receive) window size for the stream |stream_id|. + * The local window size can be adjusted by + * `nghttp2_submit_window_update()`. This function takes into account + * that and returns effective window size. + * + * This function does not take into account the amount of received + * data from the remote endpoint. Use + * `nghttp2_session_get_stream_local_window_size()` to know the amount + * of data the remote endpoint can send without receiving stream level + * WINDOW_UPDATE frame. Note that each stream is still subject to the + * connection level flow control. + * + * This function returns -1 if it fails. + */ +NGHTTP2_EXTERN int32_t nghttp2_session_get_stream_effective_local_window_size( + nghttp2_session *session, int32_t stream_id); + +/** + * @function + * + * Returns the amount of flow-controlled payload (e.g., DATA) that the + * remote endpoint can send without receiving stream level + * WINDOW_UPDATE frame. It is also subject to the connection level + * flow control. So the actual amount of data to send is + * min(`nghttp2_session_get_stream_local_window_size()`, + * `nghttp2_session_get_local_window_size()`). + * + * This function returns -1 if it fails. + */ +NGHTTP2_EXTERN int32_t nghttp2_session_get_stream_local_window_size( + nghttp2_session *session, int32_t stream_id); + +/** + * @function + * + * Returns the number of DATA payload in bytes received without + * WINDOW_UPDATE transmission for a connection. The local (receive) + * window size can be adjusted by `nghttp2_submit_window_update()`. + * This function takes into account that and returns effective data + * length. In particular, if the local window size is reduced by + * submitting negative window_size_increment with + * `nghttp2_submit_window_update()`, this function returns the number + * of bytes less than actually received. + * + * This function returns -1 if it fails. + */ +NGHTTP2_EXTERN int32_t +nghttp2_session_get_effective_recv_data_length(nghttp2_session *session); + +/** + * @function + * + * Returns the local (receive) window size for a connection. The + * local window size can be adjusted by + * `nghttp2_submit_window_update()`. This function takes into account + * that and returns effective window size. + * + * This function does not take into account the amount of received + * data from the remote endpoint. Use + * `nghttp2_session_get_local_window_size()` to know the amount of + * data the remote endpoint can send without receiving + * connection-level WINDOW_UPDATE frame. Note that each stream is + * still subject to the stream level flow control. + * + * This function returns -1 if it fails. + */ +NGHTTP2_EXTERN int32_t +nghttp2_session_get_effective_local_window_size(nghttp2_session *session); + +/** + * @function + * + * Returns the amount of flow-controlled payload (e.g., DATA) that the + * remote endpoint can send without receiving connection level + * WINDOW_UPDATE frame. Note that each stream is still subject to the + * stream level flow control (see + * `nghttp2_session_get_stream_local_window_size()`). + * + * This function returns -1 if it fails. + */ +NGHTTP2_EXTERN int32_t +nghttp2_session_get_local_window_size(nghttp2_session *session); + +/** + * @function + * + * Returns the remote window size for a given stream |stream_id|. + * + * This is the amount of flow-controlled payload (e.g., DATA) that the + * local endpoint can send without stream level WINDOW_UPDATE. There + * is also connection level flow control, so the effective size of + * payload that the local endpoint can actually send is + * min(`nghttp2_session_get_stream_remote_window_size()`, + * `nghttp2_session_get_remote_window_size()`). + * + * This function returns -1 if it fails. + */ +NGHTTP2_EXTERN int32_t nghttp2_session_get_stream_remote_window_size( + nghttp2_session *session, int32_t stream_id); + +/** + * @function + * + * Returns the remote window size for a connection. + * + * This function always succeeds. + */ +NGHTTP2_EXTERN int32_t +nghttp2_session_get_remote_window_size(nghttp2_session *session); + +/** + * @function + * + * Returns 1 if local peer half closed the given stream |stream_id|. + * Returns 0 if it did not. Returns -1 if no such stream exists. + */ +NGHTTP2_EXTERN int +nghttp2_session_get_stream_local_close(nghttp2_session *session, + int32_t stream_id); + +/** + * @function + * + * Returns 1 if remote peer half closed the given stream |stream_id|. + * Returns 0 if it did not. Returns -1 if no such stream exists. + */ +NGHTTP2_EXTERN int +nghttp2_session_get_stream_remote_close(nghttp2_session *session, + int32_t stream_id); + +/** + * @function + * + * Returns the current dynamic table size of HPACK inflater, including + * the overhead 32 bytes per entry described in RFC 7541. + */ +NGHTTP2_EXTERN size_t +nghttp2_session_get_hd_inflate_dynamic_table_size(nghttp2_session *session); + +/** + * @function + * + * Returns the current dynamic table size of HPACK deflater including + * the overhead 32 bytes per entry described in RFC 7541. + */ +NGHTTP2_EXTERN size_t +nghttp2_session_get_hd_deflate_dynamic_table_size(nghttp2_session *session); + +/** + * @function + * + * Signals the session so that the connection should be terminated. + * + * The last stream ID is the minimum value between the stream ID of a + * stream for which :type:`nghttp2_on_frame_recv_callback` was called + * most recently and the last stream ID we have sent to the peer + * previously. + * + * The |error_code| is the error code of this GOAWAY frame. The + * pre-defined error code is one of :enum:`nghttp2_error_code`. + * + * After the transmission, both `nghttp2_session_want_read()` and + * `nghttp2_session_want_write()` return 0. + * + * This function should be called when the connection should be + * terminated after sending GOAWAY. If the remaining streams should + * be processed after GOAWAY, use `nghttp2_submit_goaway()` instead. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` - * Out of memory. - */ -NGHTTP2_EXTERN int nghttp2_session_terminate_session(nghttp2_session *session, - uint32_t error_code); - -/** - * @function - * - * Signals the session so that the connection should be terminated. - * - * This function behaves like `nghttp2_session_terminate_session()`, - * but the last stream ID can be specified by the application for fine - * grained control of stream. The HTTP/2 specification does not allow - * last_stream_id to be increased. So the actual value sent as - * last_stream_id is the minimum value between the given - * |last_stream_id| and the last_stream_id we have previously sent to - * the peer. - * - * The |last_stream_id| is peer's stream ID or 0. So if |session| is - * initialized as client, |last_stream_id| must be even or 0. If - * |session| is initialized as server, |last_stream_id| must be odd or - * 0. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * + * Out of memory. + */ +NGHTTP2_EXTERN int nghttp2_session_terminate_session(nghttp2_session *session, + uint32_t error_code); + +/** + * @function + * + * Signals the session so that the connection should be terminated. + * + * This function behaves like `nghttp2_session_terminate_session()`, + * but the last stream ID can be specified by the application for fine + * grained control of stream. The HTTP/2 specification does not allow + * last_stream_id to be increased. So the actual value sent as + * last_stream_id is the minimum value between the given + * |last_stream_id| and the last_stream_id we have previously sent to + * the peer. + * + * The |last_stream_id| is peer's stream ID or 0. So if |session| is + * initialized as client, |last_stream_id| must be even or 0. If + * |session| is initialized as server, |last_stream_id| must be odd or + * 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. + * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` - * The |last_stream_id| is invalid. - */ -NGHTTP2_EXTERN int nghttp2_session_terminate_session2(nghttp2_session *session, - int32_t last_stream_id, - uint32_t error_code); - -/** - * @function - * - * Signals to the client that the server started graceful shutdown - * procedure. - * - * This function is only usable for server. If this function is - * called with client side session, this function returns + * The |last_stream_id| is invalid. + */ +NGHTTP2_EXTERN int nghttp2_session_terminate_session2(nghttp2_session *session, + int32_t last_stream_id, + uint32_t error_code); + +/** + * @function + * + * Signals to the client that the server started graceful shutdown + * procedure. + * + * This function is only usable for server. If this function is + * called with client side session, this function returns * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE`. - * - * To gracefully shutdown HTTP/2 session, server should call this - * function to send GOAWAY with last_stream_id (1u << 31) - 1. And - * after some delay (e.g., 1 RTT), send another GOAWAY with the stream - * ID that the server has some processing using - * `nghttp2_submit_goaway()`. See also - * `nghttp2_session_get_last_proc_stream_id()`. - * - * Unlike `nghttp2_submit_goaway()`, this function just sends GOAWAY - * and does nothing more. This is a mere indication to the client - * that session shutdown is imminent. The application should call - * `nghttp2_submit_goaway()` with appropriate last_stream_id after - * this call. - * - * If one or more GOAWAY frame have been already sent by either - * `nghttp2_submit_goaway()` or `nghttp2_session_terminate_session()`, - * this function has no effect. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * + * + * To gracefully shutdown HTTP/2 session, server should call this + * function to send GOAWAY with last_stream_id (1u << 31) - 1. And + * after some delay (e.g., 1 RTT), send another GOAWAY with the stream + * ID that the server has some processing using + * `nghttp2_submit_goaway()`. See also + * `nghttp2_session_get_last_proc_stream_id()`. + * + * Unlike `nghttp2_submit_goaway()`, this function just sends GOAWAY + * and does nothing more. This is a mere indication to the client + * that session shutdown is imminent. The application should call + * `nghttp2_submit_goaway()` with appropriate last_stream_id after + * this call. + * + * If one or more GOAWAY frame have been already sent by either + * `nghttp2_submit_goaway()` or `nghttp2_session_terminate_session()`, + * this function has no effect. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` - * Out of memory. + * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` - * The |session| is initialized as client. - */ -NGHTTP2_EXTERN int nghttp2_submit_shutdown_notice(nghttp2_session *session); - -/** - * @function - * - * Returns the value of SETTINGS |id| notified by a remote endpoint. - * The |id| must be one of values defined in - * :enum:`nghttp2_settings_id`. - */ -NGHTTP2_EXTERN uint32_t nghttp2_session_get_remote_settings( - nghttp2_session *session, nghttp2_settings_id id); - -/** - * @function - * - * Returns the value of SETTINGS |id| of local endpoint acknowledged - * by the remote endpoint. The |id| must be one of the values defined - * in :enum:`nghttp2_settings_id`. - */ -NGHTTP2_EXTERN uint32_t nghttp2_session_get_local_settings( - nghttp2_session *session, nghttp2_settings_id id); - -/** - * @function - * - * Tells the |session| that next stream ID is |next_stream_id|. The - * |next_stream_id| must be equal or greater than the value returned - * by `nghttp2_session_get_next_stream_id()`. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * + * The |session| is initialized as client. + */ +NGHTTP2_EXTERN int nghttp2_submit_shutdown_notice(nghttp2_session *session); + +/** + * @function + * + * Returns the value of SETTINGS |id| notified by a remote endpoint. + * The |id| must be one of values defined in + * :enum:`nghttp2_settings_id`. + */ +NGHTTP2_EXTERN uint32_t nghttp2_session_get_remote_settings( + nghttp2_session *session, nghttp2_settings_id id); + +/** + * @function + * + * Returns the value of SETTINGS |id| of local endpoint acknowledged + * by the remote endpoint. The |id| must be one of the values defined + * in :enum:`nghttp2_settings_id`. + */ +NGHTTP2_EXTERN uint32_t nghttp2_session_get_local_settings( + nghttp2_session *session, nghttp2_settings_id id); + +/** + * @function + * + * Tells the |session| that next stream ID is |next_stream_id|. The + * |next_stream_id| must be equal or greater than the value returned + * by `nghttp2_session_get_next_stream_id()`. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` - * The |next_stream_id| is strictly less than the value - * `nghttp2_session_get_next_stream_id()` returns; or - * |next_stream_id| is invalid (e.g., even integer for client, or - * odd integer for server). - */ -NGHTTP2_EXTERN int nghttp2_session_set_next_stream_id(nghttp2_session *session, - int32_t next_stream_id); - -/** - * @function - * - * Returns the next outgoing stream ID. Notice that return type is - * uint32_t. If we run out of stream ID for this session, this - * function returns 1 << 31. - */ -NGHTTP2_EXTERN uint32_t -nghttp2_session_get_next_stream_id(nghttp2_session *session); - -/** - * @function - * - * Tells the |session| that |size| bytes for a stream denoted by - * |stream_id| were consumed by application and are ready to - * WINDOW_UPDATE. The consumed bytes are counted towards both - * connection and stream level WINDOW_UPDATE (see - * `nghttp2_session_consume_connection()` and - * `nghttp2_session_consume_stream()` to update consumption - * independently). This function is intended to be used without - * automatic window update (see - * `nghttp2_option_set_no_auto_window_update()`). - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * + * The |next_stream_id| is strictly less than the value + * `nghttp2_session_get_next_stream_id()` returns; or + * |next_stream_id| is invalid (e.g., even integer for client, or + * odd integer for server). + */ +NGHTTP2_EXTERN int nghttp2_session_set_next_stream_id(nghttp2_session *session, + int32_t next_stream_id); + +/** + * @function + * + * Returns the next outgoing stream ID. Notice that return type is + * uint32_t. If we run out of stream ID for this session, this + * function returns 1 << 31. + */ +NGHTTP2_EXTERN uint32_t +nghttp2_session_get_next_stream_id(nghttp2_session *session); + +/** + * @function + * + * Tells the |session| that |size| bytes for a stream denoted by + * |stream_id| were consumed by application and are ready to + * WINDOW_UPDATE. The consumed bytes are counted towards both + * connection and stream level WINDOW_UPDATE (see + * `nghttp2_session_consume_connection()` and + * `nghttp2_session_consume_stream()` to update consumption + * independently). This function is intended to be used without + * automatic window update (see + * `nghttp2_option_set_no_auto_window_update()`). + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` - * Out of memory. + * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` - * The |stream_id| is 0. + * The |stream_id| is 0. * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` - * Automatic WINDOW_UPDATE is not disabled. - */ -NGHTTP2_EXTERN int nghttp2_session_consume(nghttp2_session *session, - int32_t stream_id, size_t size); - -/** - * @function - * - * Like `nghttp2_session_consume()`, but this only tells library that - * |size| bytes were consumed only for connection level. Note that - * HTTP/2 maintains connection and stream level flow control windows - * independently. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * + * Automatic WINDOW_UPDATE is not disabled. + */ +NGHTTP2_EXTERN int nghttp2_session_consume(nghttp2_session *session, + int32_t stream_id, size_t size); + +/** + * @function + * + * Like `nghttp2_session_consume()`, but this only tells library that + * |size| bytes were consumed only for connection level. Note that + * HTTP/2 maintains connection and stream level flow control windows + * independently. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` - * Out of memory. + * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` - * Automatic WINDOW_UPDATE is not disabled. - */ -NGHTTP2_EXTERN int nghttp2_session_consume_connection(nghttp2_session *session, - size_t size); - -/** - * @function - * - * Like `nghttp2_session_consume()`, but this only tells library that - * |size| bytes were consumed only for stream denoted by |stream_id|. - * Note that HTTP/2 maintains connection and stream level flow control - * windows independently. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * + * Automatic WINDOW_UPDATE is not disabled. + */ +NGHTTP2_EXTERN int nghttp2_session_consume_connection(nghttp2_session *session, + size_t size); + +/** + * @function + * + * Like `nghttp2_session_consume()`, but this only tells library that + * |size| bytes were consumed only for stream denoted by |stream_id|. + * Note that HTTP/2 maintains connection and stream level flow control + * windows independently. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` - * Out of memory. + * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` - * The |stream_id| is 0. + * The |stream_id| is 0. * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` - * Automatic WINDOW_UPDATE is not disabled. - */ -NGHTTP2_EXTERN int nghttp2_session_consume_stream(nghttp2_session *session, - int32_t stream_id, - size_t size); - -/** - * @function - * - * Changes priority of existing stream denoted by |stream_id|. The - * new priority specification is |pri_spec|. - * - * The priority is changed silently and instantly, and no PRIORITY - * frame will be sent to notify the peer of this change. This - * function may be useful for server to change the priority of pushed - * stream. - * - * If |session| is initialized as server, and ``pri_spec->stream_id`` - * points to the idle stream, the idle stream is created if it does - * not exist. The created idle stream will depend on root stream - * (stream 0) with weight 16. - * - * Otherwise, if stream denoted by ``pri_spec->stream_id`` is not - * found, we use default priority instead of given |pri_spec|. That - * is make stream depend on root stream with weight 16. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * + * Automatic WINDOW_UPDATE is not disabled. + */ +NGHTTP2_EXTERN int nghttp2_session_consume_stream(nghttp2_session *session, + int32_t stream_id, + size_t size); + +/** + * @function + * + * Changes priority of existing stream denoted by |stream_id|. The + * new priority specification is |pri_spec|. + * + * The priority is changed silently and instantly, and no PRIORITY + * frame will be sent to notify the peer of this change. This + * function may be useful for server to change the priority of pushed + * stream. + * + * If |session| is initialized as server, and ``pri_spec->stream_id`` + * points to the idle stream, the idle stream is created if it does + * not exist. The created idle stream will depend on root stream + * (stream 0) with weight 16. + * + * Otherwise, if stream denoted by ``pri_spec->stream_id`` is not + * found, we use default priority instead of given |pri_spec|. That + * is make stream depend on root stream with weight 16. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` - * Out of memory. + * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` - * Attempted to depend on itself; or no stream exist for the given - * |stream_id|; or |stream_id| is 0 - */ -NGHTTP2_EXTERN int -nghttp2_session_change_stream_priority(nghttp2_session *session, - int32_t stream_id, - const nghttp2_priority_spec *pri_spec); - -/** - * @function - * - * Creates idle stream with the given |stream_id|, and priority - * |pri_spec|. - * - * The stream creation is done without sending PRIORITY frame, which - * means that peer does not know about the existence of this idle - * stream in the local endpoint. - * - * RFC 7540 does not disallow the use of creation of idle stream with - * odd or even stream ID regardless of client or server. So this - * function can create odd or even stream ID regardless of client or - * server. But probably it is a bit safer to use the stream ID the - * local endpoint can initiate (in other words, use odd stream ID for - * client, and even stream ID for server), to avoid potential - * collision from peer's instruction. Also we can use - * `nghttp2_session_set_next_stream_id()` to avoid to open created - * idle streams accidentally if we follow this recommendation. - * - * If |session| is initialized as server, and ``pri_spec->stream_id`` - * points to the idle stream, the idle stream is created if it does - * not exist. The created idle stream will depend on root stream - * (stream 0) with weight 16. - * - * Otherwise, if stream denoted by ``pri_spec->stream_id`` is not - * found, we use default priority instead of given |pri_spec|. That - * is make stream depend on root stream with weight 16. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * + * Attempted to depend on itself; or no stream exist for the given + * |stream_id|; or |stream_id| is 0 + */ +NGHTTP2_EXTERN int +nghttp2_session_change_stream_priority(nghttp2_session *session, + int32_t stream_id, + const nghttp2_priority_spec *pri_spec); + +/** + * @function + * + * Creates idle stream with the given |stream_id|, and priority + * |pri_spec|. + * + * The stream creation is done without sending PRIORITY frame, which + * means that peer does not know about the existence of this idle + * stream in the local endpoint. + * + * RFC 7540 does not disallow the use of creation of idle stream with + * odd or even stream ID regardless of client or server. So this + * function can create odd or even stream ID regardless of client or + * server. But probably it is a bit safer to use the stream ID the + * local endpoint can initiate (in other words, use odd stream ID for + * client, and even stream ID for server), to avoid potential + * collision from peer's instruction. Also we can use + * `nghttp2_session_set_next_stream_id()` to avoid to open created + * idle streams accidentally if we follow this recommendation. + * + * If |session| is initialized as server, and ``pri_spec->stream_id`` + * points to the idle stream, the idle stream is created if it does + * not exist. The created idle stream will depend on root stream + * (stream 0) with weight 16. + * + * Otherwise, if stream denoted by ``pri_spec->stream_id`` is not + * found, we use default priority instead of given |pri_spec|. That + * is make stream depend on root stream with weight 16. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` - * Out of memory. + * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` - * Attempted to depend on itself; or stream denoted by |stream_id| - * already exists; or |stream_id| cannot be used to create idle - * stream (in other words, local endpoint has already opened - * stream ID greater than or equal to the given stream ID; or - * |stream_id| is 0 - */ -NGHTTP2_EXTERN int -nghttp2_session_create_idle_stream(nghttp2_session *session, int32_t stream_id, - const nghttp2_priority_spec *pri_spec); - -/** - * @function - * - * Performs post-process of HTTP Upgrade request. This function can - * be called from both client and server, but the behavior is very - * different in each other. - * - * .. warning:: - * - * This function is deprecated in favor of - * `nghttp2_session_upgrade2()`, because this function lacks the - * parameter to tell the library the request method used in the - * original HTTP request. This information is required for client - * to validate actual response body length against content-length - * header field (see `nghttp2_option_set_no_http_messaging()`). If - * HEAD is used in request, the length of response body must be 0 - * regardless of value included in content-length header field. - * - * If called from client side, the |settings_payload| must be the - * value sent in ``HTTP2-Settings`` header field and must be decoded - * by base64url decoder. The |settings_payloadlen| is the length of - * |settings_payload|. The |settings_payload| is unpacked and its - * setting values will be submitted using `nghttp2_submit_settings()`. - * This means that the client application code does not need to submit - * SETTINGS by itself. The stream with stream ID=1 is opened and the - * |stream_user_data| is used for its stream_user_data. The opened - * stream becomes half-closed (local) state. - * - * If called from server side, the |settings_payload| must be the - * value received in ``HTTP2-Settings`` header field and must be - * decoded by base64url decoder. The |settings_payloadlen| is the - * length of |settings_payload|. It is treated as if the SETTINGS - * frame with that payload is received. Thus, callback functions for - * the reception of SETTINGS frame will be invoked. The stream with - * stream ID=1 is opened. The |stream_user_data| is ignored. The - * opened stream becomes half-closed (remote). - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * + * Attempted to depend on itself; or stream denoted by |stream_id| + * already exists; or |stream_id| cannot be used to create idle + * stream (in other words, local endpoint has already opened + * stream ID greater than or equal to the given stream ID; or + * |stream_id| is 0 + */ +NGHTTP2_EXTERN int +nghttp2_session_create_idle_stream(nghttp2_session *session, int32_t stream_id, + const nghttp2_priority_spec *pri_spec); + +/** + * @function + * + * Performs post-process of HTTP Upgrade request. This function can + * be called from both client and server, but the behavior is very + * different in each other. + * + * .. warning:: + * + * This function is deprecated in favor of + * `nghttp2_session_upgrade2()`, because this function lacks the + * parameter to tell the library the request method used in the + * original HTTP request. This information is required for client + * to validate actual response body length against content-length + * header field (see `nghttp2_option_set_no_http_messaging()`). If + * HEAD is used in request, the length of response body must be 0 + * regardless of value included in content-length header field. + * + * If called from client side, the |settings_payload| must be the + * value sent in ``HTTP2-Settings`` header field and must be decoded + * by base64url decoder. The |settings_payloadlen| is the length of + * |settings_payload|. The |settings_payload| is unpacked and its + * setting values will be submitted using `nghttp2_submit_settings()`. + * This means that the client application code does not need to submit + * SETTINGS by itself. The stream with stream ID=1 is opened and the + * |stream_user_data| is used for its stream_user_data. The opened + * stream becomes half-closed (local) state. + * + * If called from server side, the |settings_payload| must be the + * value received in ``HTTP2-Settings`` header field and must be + * decoded by base64url decoder. The |settings_payloadlen| is the + * length of |settings_payload|. It is treated as if the SETTINGS + * frame with that payload is received. Thus, callback functions for + * the reception of SETTINGS frame will be invoked. The stream with + * stream ID=1 is opened. The |stream_user_data| is ignored. The + * opened stream becomes half-closed (remote). + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` - * Out of memory. + * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` - * The |settings_payload| is badly formed. + * The |settings_payload| is badly formed. * :enum:`nghttp2_error.NGHTTP2_ERR_PROTO` - * The stream ID 1 is already used or closed; or is not available. - */ -NGHTTP2_EXTERN int nghttp2_session_upgrade(nghttp2_session *session, - const uint8_t *settings_payload, - size_t settings_payloadlen, - void *stream_user_data); - -/** - * @function - * - * Performs post-process of HTTP Upgrade request. This function can - * be called from both client and server, but the behavior is very - * different in each other. - * - * If called from client side, the |settings_payload| must be the - * value sent in ``HTTP2-Settings`` header field and must be decoded - * by base64url decoder. The |settings_payloadlen| is the length of - * |settings_payload|. The |settings_payload| is unpacked and its - * setting values will be submitted using `nghttp2_submit_settings()`. - * This means that the client application code does not need to submit - * SETTINGS by itself. The stream with stream ID=1 is opened and the - * |stream_user_data| is used for its stream_user_data. The opened - * stream becomes half-closed (local) state. - * - * If called from server side, the |settings_payload| must be the - * value received in ``HTTP2-Settings`` header field and must be - * decoded by base64url decoder. The |settings_payloadlen| is the - * length of |settings_payload|. It is treated as if the SETTINGS - * frame with that payload is received. Thus, callback functions for - * the reception of SETTINGS frame will be invoked. The stream with - * stream ID=1 is opened. The |stream_user_data| is ignored. The - * opened stream becomes half-closed (remote). - * - * If the request method is HEAD, pass nonzero value to - * |head_request|. Otherwise, pass 0. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * + * The stream ID 1 is already used or closed; or is not available. + */ +NGHTTP2_EXTERN int nghttp2_session_upgrade(nghttp2_session *session, + const uint8_t *settings_payload, + size_t settings_payloadlen, + void *stream_user_data); + +/** + * @function + * + * Performs post-process of HTTP Upgrade request. This function can + * be called from both client and server, but the behavior is very + * different in each other. + * + * If called from client side, the |settings_payload| must be the + * value sent in ``HTTP2-Settings`` header field and must be decoded + * by base64url decoder. The |settings_payloadlen| is the length of + * |settings_payload|. The |settings_payload| is unpacked and its + * setting values will be submitted using `nghttp2_submit_settings()`. + * This means that the client application code does not need to submit + * SETTINGS by itself. The stream with stream ID=1 is opened and the + * |stream_user_data| is used for its stream_user_data. The opened + * stream becomes half-closed (local) state. + * + * If called from server side, the |settings_payload| must be the + * value received in ``HTTP2-Settings`` header field and must be + * decoded by base64url decoder. The |settings_payloadlen| is the + * length of |settings_payload|. It is treated as if the SETTINGS + * frame with that payload is received. Thus, callback functions for + * the reception of SETTINGS frame will be invoked. The stream with + * stream ID=1 is opened. The |stream_user_data| is ignored. The + * opened stream becomes half-closed (remote). + * + * If the request method is HEAD, pass nonzero value to + * |head_request|. Otherwise, pass 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. + * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` - * The |settings_payload| is badly formed. + * The |settings_payload| is badly formed. * :enum:`nghttp2_error.NGHTTP2_ERR_PROTO` - * The stream ID 1 is already used or closed; or is not available. - */ -NGHTTP2_EXTERN int nghttp2_session_upgrade2(nghttp2_session *session, - const uint8_t *settings_payload, - size_t settings_payloadlen, - int head_request, - void *stream_user_data); - -/** - * @function - * - * Serializes the SETTINGS values |iv| in the |buf|. The size of the - * |buf| is specified by |buflen|. The number of entries in the |iv| - * array is given by |niv|. The required space in |buf| for the |niv| - * entries is ``6*niv`` bytes and if the given buffer is too small, an - * error is returned. This function is used mainly for creating a - * SETTINGS payload to be sent with the ``HTTP2-Settings`` header - * field in an HTTP Upgrade request. The data written in |buf| is NOT - * base64url encoded and the application is responsible for encoding. - * - * This function returns the number of bytes written in |buf|, or one - * of the following negative error codes: - * + * The stream ID 1 is already used or closed; or is not available. + */ +NGHTTP2_EXTERN int nghttp2_session_upgrade2(nghttp2_session *session, + const uint8_t *settings_payload, + size_t settings_payloadlen, + int head_request, + void *stream_user_data); + +/** + * @function + * + * Serializes the SETTINGS values |iv| in the |buf|. The size of the + * |buf| is specified by |buflen|. The number of entries in the |iv| + * array is given by |niv|. The required space in |buf| for the |niv| + * entries is ``6*niv`` bytes and if the given buffer is too small, an + * error is returned. This function is used mainly for creating a + * SETTINGS payload to be sent with the ``HTTP2-Settings`` header + * field in an HTTP Upgrade request. The data written in |buf| is NOT + * base64url encoded and the application is responsible for encoding. + * + * This function returns the number of bytes written in |buf|, or one + * of the following negative error codes: + * * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` - * The |iv| contains duplicate settings ID or invalid value. - * + * The |iv| contains duplicate settings ID or invalid value. + * * :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE` - * The provided |buflen| size is too small to hold the output. - */ -NGHTTP2_EXTERN ssize_t nghttp2_pack_settings_payload( - uint8_t *buf, size_t buflen, const nghttp2_settings_entry *iv, size_t niv); - -/** - * @function - * - * Returns string describing the |lib_error_code|. The - * |lib_error_code| must be one of the :enum:`nghttp2_error`. - */ -NGHTTP2_EXTERN const char *nghttp2_strerror(int lib_error_code); - -/** - * @function - * - * Returns string representation of HTTP/2 error code |error_code| - * (e.g., ``PROTOCOL_ERROR`` is returned if ``error_code == - * NGHTTP2_PROTOCOL_ERROR``). If string representation is unknown for - * given |error_code|, this function returns string ``unknown``. - */ -NGHTTP2_EXTERN const char *nghttp2_http2_strerror(uint32_t error_code); - -/** - * @function - * - * Initializes |pri_spec| with the |stream_id| of the stream to depend - * on with |weight| and its exclusive flag. If |exclusive| is - * nonzero, exclusive flag is set. - * + * The provided |buflen| size is too small to hold the output. + */ +NGHTTP2_EXTERN ssize_t nghttp2_pack_settings_payload( + uint8_t *buf, size_t buflen, const nghttp2_settings_entry *iv, size_t niv); + +/** + * @function + * + * Returns string describing the |lib_error_code|. The + * |lib_error_code| must be one of the :enum:`nghttp2_error`. + */ +NGHTTP2_EXTERN const char *nghttp2_strerror(int lib_error_code); + +/** + * @function + * + * Returns string representation of HTTP/2 error code |error_code| + * (e.g., ``PROTOCOL_ERROR`` is returned if ``error_code == + * NGHTTP2_PROTOCOL_ERROR``). If string representation is unknown for + * given |error_code|, this function returns string ``unknown``. + */ +NGHTTP2_EXTERN const char *nghttp2_http2_strerror(uint32_t error_code); + +/** + * @function + * + * Initializes |pri_spec| with the |stream_id| of the stream to depend + * on with |weight| and its exclusive flag. If |exclusive| is + * nonzero, exclusive flag is set. + * * The |weight| must be in [:macro:`NGHTTP2_MIN_WEIGHT`, * :macro:`NGHTTP2_MAX_WEIGHT`], inclusive. - */ -NGHTTP2_EXTERN void nghttp2_priority_spec_init(nghttp2_priority_spec *pri_spec, - int32_t stream_id, - int32_t weight, int exclusive); - -/** - * @function - * - * Initializes |pri_spec| with the default values. The default values - * are: stream_id = 0, weight = :macro:`NGHTTP2_DEFAULT_WEIGHT` and - * exclusive = 0. - */ -NGHTTP2_EXTERN void -nghttp2_priority_spec_default_init(nghttp2_priority_spec *pri_spec); - -/** - * @function - * - * Returns nonzero if the |pri_spec| is filled with default values. - */ -NGHTTP2_EXTERN int -nghttp2_priority_spec_check_default(const nghttp2_priority_spec *pri_spec); - -/** - * @function - * - * Submits HEADERS frame and optionally one or more DATA frames. - * - * The |pri_spec| is priority specification of this request. ``NULL`` - * means the default priority (see - * `nghttp2_priority_spec_default_init()`). To specify the priority, - * use `nghttp2_priority_spec_init()`. If |pri_spec| is not ``NULL``, - * this function will copy its data members. - * + */ +NGHTTP2_EXTERN void nghttp2_priority_spec_init(nghttp2_priority_spec *pri_spec, + int32_t stream_id, + int32_t weight, int exclusive); + +/** + * @function + * + * Initializes |pri_spec| with the default values. The default values + * are: stream_id = 0, weight = :macro:`NGHTTP2_DEFAULT_WEIGHT` and + * exclusive = 0. + */ +NGHTTP2_EXTERN void +nghttp2_priority_spec_default_init(nghttp2_priority_spec *pri_spec); + +/** + * @function + * + * Returns nonzero if the |pri_spec| is filled with default values. + */ +NGHTTP2_EXTERN int +nghttp2_priority_spec_check_default(const nghttp2_priority_spec *pri_spec); + +/** + * @function + * + * Submits HEADERS frame and optionally one or more DATA frames. + * + * The |pri_spec| is priority specification of this request. ``NULL`` + * means the default priority (see + * `nghttp2_priority_spec_default_init()`). To specify the priority, + * use `nghttp2_priority_spec_init()`. If |pri_spec| is not ``NULL``, + * this function will copy its data members. + * * The ``pri_spec->weight`` must be in [:macro:`NGHTTP2_MIN_WEIGHT`, * :macro:`NGHTTP2_MAX_WEIGHT`], inclusive. If ``pri_spec->weight`` * is strictly less than :macro:`NGHTTP2_MIN_WEIGHT`, it becomes * :macro:`NGHTTP2_MIN_WEIGHT`. If it is strictly greater than * :macro:`NGHTTP2_MAX_WEIGHT`, it becomes * :macro:`NGHTTP2_MAX_WEIGHT`. - * - * 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 - * ":") in |nva| and must place pseudo-headers before regular header - * fields. - * - * This function creates copies of all name/value pairs in |nva|. It - * also lower-cases all names in |nva|. The order of elements in - * |nva| is preserved. For header fields with + * + * 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 + * ":") in |nva| and must place pseudo-headers before regular header + * fields. + * + * This function creates copies of all name/value pairs in |nva|. It + * also lower-cases all names in |nva|. The order of elements in + * |nva| is preserved. For header fields with * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME` and * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, * header field name and value are not copied respectively. With * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME`, application * is responsible to pass header field name in lowercase. The * application should maintain the references to them until - * :type:`nghttp2_on_frame_send_callback` or - * :type:`nghttp2_on_frame_not_send_callback` is called. - * - * HTTP/2 specification has requirement about header fields in the - * request HEADERS. See the specification for more details. - * - * If |data_prd| is not ``NULL``, it provides data which will be sent - * in subsequent DATA frames. In this case, a method that allows - * request message bodies - * (https://tools.ietf.org/html/rfc7231#section-4) must be specified - * with ``:method`` key in |nva| (e.g. ``POST``). This function does - * not take ownership of the |data_prd|. The function copies the - * members of the |data_prd|. If |data_prd| is ``NULL``, HEADERS have - * END_STREAM set. The |stream_user_data| is data associated to the - * stream opened by this request and can be an arbitrary pointer, - * which can be retrieved later by - * `nghttp2_session_get_stream_user_data()`. - * - * This function returns assigned stream ID if it succeeds, or one of - * the following negative error codes: - * + * :type:`nghttp2_on_frame_send_callback` or + * :type:`nghttp2_on_frame_not_send_callback` is called. + * + * HTTP/2 specification has requirement about header fields in the + * request HEADERS. See the specification for more details. + * + * If |data_prd| is not ``NULL``, it provides data which will be sent + * in subsequent DATA frames. In this case, a method that allows + * request message bodies + * (https://tools.ietf.org/html/rfc7231#section-4) must be specified + * with ``:method`` key in |nva| (e.g. ``POST``). This function does + * not take ownership of the |data_prd|. The function copies the + * members of the |data_prd|. If |data_prd| is ``NULL``, HEADERS have + * END_STREAM set. The |stream_user_data| is data associated to the + * stream opened by this request and can be an arbitrary pointer, + * which can be retrieved later by + * `nghttp2_session_get_stream_user_data()`. + * + * This function returns assigned stream ID if it succeeds, or one of + * the following negative error codes: + * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` - * Out of memory. + * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE` - * No stream ID is available because maximum stream ID was - * reached. + * No stream ID is available because maximum stream ID was + * reached. * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` - * Trying to depend on itself (new stream ID equals - * ``pri_spec->stream_id``). + * Trying to depend on itself (new stream ID equals + * ``pri_spec->stream_id``). * :enum:`nghttp2_error.NGHTTP2_ERR_PROTO` - * The |session| is server session. - * - * .. warning:: - * - * This function returns assigned stream ID if it succeeds. But + * The |session| is server session. + * + * .. warning:: + * + * This function returns assigned stream ID if it succeeds. But * that stream is not created yet. The application must not submit - * frame to that stream ID before - * :type:`nghttp2_before_frame_send_callback` is called for this + * frame to that stream ID before + * :type:`nghttp2_before_frame_send_callback` is called for this * frame. This means `nghttp2_session_get_stream_user_data()` does * not work before the callback. But * `nghttp2_session_set_stream_user_data()` handles this situation * specially, and it can set data to a stream during this period. - * - */ -NGHTTP2_EXTERN int32_t nghttp2_submit_request( - nghttp2_session *session, const nghttp2_priority_spec *pri_spec, - const nghttp2_nv *nva, size_t nvlen, const nghttp2_data_provider *data_prd, - void *stream_user_data); - -/** - * @function - * - * Submits response HEADERS frame and optionally one or more DATA - * frames against the stream |stream_id|. - * - * 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 - * ":") in |nva| and must place pseudo-headers before regular header - * fields. - * - * This function creates copies of all name/value pairs in |nva|. It - * also lower-cases all names in |nva|. The order of elements in - * |nva| is preserved. For header fields with + * + */ +NGHTTP2_EXTERN int32_t nghttp2_submit_request( + nghttp2_session *session, const nghttp2_priority_spec *pri_spec, + const nghttp2_nv *nva, size_t nvlen, const nghttp2_data_provider *data_prd, + void *stream_user_data); + +/** + * @function + * + * Submits response HEADERS frame and optionally one or more DATA + * frames against the stream |stream_id|. + * + * 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 + * ":") in |nva| and must place pseudo-headers before regular header + * fields. + * + * This function creates copies of all name/value pairs in |nva|. It + * also lower-cases all names in |nva|. The order of elements in + * |nva| is preserved. For header fields with * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME` and * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, * header field name and value are not copied respectively. With * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME`, application * is responsible to pass header field name in lowercase. The * application should maintain the references to them until - * :type:`nghttp2_on_frame_send_callback` or - * :type:`nghttp2_on_frame_not_send_callback` is called. - * - * HTTP/2 specification has requirement about header fields in the - * response HEADERS. See the specification for more details. - * - * If |data_prd| is not ``NULL``, it provides data which will be sent - * in subsequent DATA frames. This function does not take ownership - * of the |data_prd|. The function copies the members of the - * |data_prd|. If |data_prd| is ``NULL``, HEADERS will have - * END_STREAM flag set. - * - * This method can be used as normal HTTP response and push response. - * When pushing a resource using this function, the |session| must be - * configured using `nghttp2_session_server_new()` or its variants and - * the target stream denoted by the |stream_id| must be reserved using - * `nghttp2_submit_push_promise()`. - * - * To send non-final response headers (e.g., HTTP status 101), don't - * use this function because this function half-closes the outbound - * stream. Instead, use `nghttp2_submit_headers()` for this purpose. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * + * :type:`nghttp2_on_frame_send_callback` or + * :type:`nghttp2_on_frame_not_send_callback` is called. + * + * HTTP/2 specification has requirement about header fields in the + * response HEADERS. See the specification for more details. + * + * If |data_prd| is not ``NULL``, it provides data which will be sent + * in subsequent DATA frames. This function does not take ownership + * of the |data_prd|. The function copies the members of the + * |data_prd|. If |data_prd| is ``NULL``, HEADERS will have + * END_STREAM flag set. + * + * This method can be used as normal HTTP response and push response. + * When pushing a resource using this function, the |session| must be + * configured using `nghttp2_session_server_new()` or its variants and + * the target stream denoted by the |stream_id| must be reserved using + * `nghttp2_submit_push_promise()`. + * + * To send non-final response headers (e.g., HTTP status 101), don't + * use this function because this function half-closes the outbound + * stream. Instead, use `nghttp2_submit_headers()` for this purpose. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` - * Out of memory. + * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` - * The |stream_id| is 0. + * The |stream_id| is 0. * :enum:`nghttp2_error.NGHTTP2_ERR_DATA_EXIST` - * DATA or HEADERS has been already submitted and not fully - * processed yet. Normally, this does not happen, but when - * application wrongly calls `nghttp2_submit_response()` twice, - * this may happen. + * DATA or HEADERS has been already submitted and not fully + * processed yet. Normally, this does not happen, but when + * application wrongly calls `nghttp2_submit_response()` twice, + * this may happen. * :enum:`nghttp2_error.NGHTTP2_ERR_PROTO` - * The |session| is client session. - * - * .. warning:: - * - * Calling this function twice for the same stream ID may lead to - * program crash. It is generally considered to a programming error - * to commit response twice. - */ -NGHTTP2_EXTERN int -nghttp2_submit_response(nghttp2_session *session, int32_t stream_id, - const nghttp2_nv *nva, size_t nvlen, - const nghttp2_data_provider *data_prd); - -/** - * @function - * - * Submits trailer fields HEADERS against the stream |stream_id|. - * - * The |nva| is an array of name/value pair :type:`nghttp2_nv` with - * |nvlen| elements. The application must not include pseudo-header - * fields (headers whose names starts with ":") in |nva|. - * - * This function creates copies of all name/value pairs in |nva|. It - * also lower-cases all names in |nva|. The order of elements in - * |nva| is preserved. For header fields with + * The |session| is client session. + * + * .. warning:: + * + * Calling this function twice for the same stream ID may lead to + * program crash. It is generally considered to a programming error + * to commit response twice. + */ +NGHTTP2_EXTERN int +nghttp2_submit_response(nghttp2_session *session, int32_t stream_id, + const nghttp2_nv *nva, size_t nvlen, + const nghttp2_data_provider *data_prd); + +/** + * @function + * + * Submits trailer fields HEADERS against the stream |stream_id|. + * + * The |nva| is an array of name/value pair :type:`nghttp2_nv` with + * |nvlen| elements. The application must not include pseudo-header + * fields (headers whose names starts with ":") in |nva|. + * + * This function creates copies of all name/value pairs in |nva|. It + * also lower-cases all names in |nva|. The order of elements in + * |nva| is preserved. For header fields with * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME` and * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, * header field name and value are not copied respectively. With * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME`, application * is responsible to pass header field name in lowercase. The * application should maintain the references to them until - * :type:`nghttp2_on_frame_send_callback` or - * :type:`nghttp2_on_frame_not_send_callback` is called. - * - * For server, trailer fields must follow response HEADERS or response - * DATA without END_STREAM flat set. The library does not enforce - * this requirement, and applications should do this for themselves. - * If `nghttp2_submit_trailer()` is called before any response HEADERS - * submission (usually by `nghttp2_submit_response()`), the content of - * |nva| will be sent as response headers, which will result in error. - * - * This function has the same effect with `nghttp2_submit_headers()`, + * :type:`nghttp2_on_frame_send_callback` or + * :type:`nghttp2_on_frame_not_send_callback` is called. + * + * For server, trailer fields must follow response HEADERS or response + * DATA without END_STREAM flat set. The library does not enforce + * this requirement, and applications should do this for themselves. + * If `nghttp2_submit_trailer()` is called before any response HEADERS + * submission (usually by `nghttp2_submit_response()`), the content of + * |nva| will be sent as response headers, which will result in error. + * + * This function has the same effect with `nghttp2_submit_headers()`, * with flags = :enum:`nghttp2_flag.NGHTTP2_FLAG_END_STREAM` and both * pri_spec and stream_user_data to NULL. - * - * To submit trailer fields after `nghttp2_submit_response()` is - * called, the application has to specify - * :type:`nghttp2_data_provider` to `nghttp2_submit_response()`. - * Inside of :type:`nghttp2_data_source_read_callback`, when setting + * + * To submit trailer fields after `nghttp2_submit_response()` is + * called, the application has to specify + * :type:`nghttp2_data_provider` to `nghttp2_submit_response()`. + * Inside of :type:`nghttp2_data_source_read_callback`, when setting * :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_EOF`, also set * :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_NO_END_STREAM`. After * that, the application can send trailer fields using - * `nghttp2_submit_trailer()`. `nghttp2_submit_trailer()` can be used - * inside :type:`nghttp2_data_source_read_callback`. - * - * This function returns 0 if it succeeds and |stream_id| is -1. - * Otherwise, this function returns 0 if it succeeds, or one of the - * following negative error codes: - * + * `nghttp2_submit_trailer()`. `nghttp2_submit_trailer()` can be used + * inside :type:`nghttp2_data_source_read_callback`. + * + * This function returns 0 if it succeeds and |stream_id| is -1. + * Otherwise, this function returns 0 if it succeeds, or one of the + * following negative error codes: + * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` - * Out of memory. + * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` - * The |stream_id| is 0. - */ -NGHTTP2_EXTERN int nghttp2_submit_trailer(nghttp2_session *session, - int32_t stream_id, - const nghttp2_nv *nva, size_t nvlen); - -/** - * @function - * - * Submits HEADERS frame. The |flags| is bitwise OR of the - * following values: - * + * The |stream_id| is 0. + */ +NGHTTP2_EXTERN int nghttp2_submit_trailer(nghttp2_session *session, + int32_t stream_id, + const nghttp2_nv *nva, size_t nvlen); + +/** + * @function + * + * Submits HEADERS frame. The |flags| is bitwise OR of the + * following values: + * * * :enum:`nghttp2_flag.NGHTTP2_FLAG_END_STREAM` - * + * * If |flags| includes :enum:`nghttp2_flag.NGHTTP2_FLAG_END_STREAM`, * this frame has END_STREAM flag set. - * - * The library handles the CONTINUATION frame internally and it - * correctly sets END_HEADERS to the last sequence of the PUSH_PROMISE - * or CONTINUATION frame. - * - * If the |stream_id| is -1, this frame is assumed as request (i.e., - * request HEADERS frame which opens new stream). In this case, the - * assigned stream ID will be returned. Otherwise, specify stream ID - * in |stream_id|. - * - * The |pri_spec| is priority specification of this request. ``NULL`` - * means the default priority (see - * `nghttp2_priority_spec_default_init()`). To specify the priority, - * use `nghttp2_priority_spec_init()`. If |pri_spec| is not ``NULL``, - * this function will copy its data members. - * + * + * The library handles the CONTINUATION frame internally and it + * correctly sets END_HEADERS to the last sequence of the PUSH_PROMISE + * or CONTINUATION frame. + * + * If the |stream_id| is -1, this frame is assumed as request (i.e., + * request HEADERS frame which opens new stream). In this case, the + * assigned stream ID will be returned. Otherwise, specify stream ID + * in |stream_id|. + * + * The |pri_spec| is priority specification of this request. ``NULL`` + * means the default priority (see + * `nghttp2_priority_spec_default_init()`). To specify the priority, + * use `nghttp2_priority_spec_init()`. If |pri_spec| is not ``NULL``, + * this function will copy its data members. + * * The ``pri_spec->weight`` must be in [:macro:`NGHTTP2_MIN_WEIGHT`, * :macro:`NGHTTP2_MAX_WEIGHT`], inclusive. If ``pri_spec->weight`` * is strictly less than :macro:`NGHTTP2_MIN_WEIGHT`, it becomes * :macro:`NGHTTP2_MIN_WEIGHT`. If it is strictly greater than * :macro:`NGHTTP2_MAX_WEIGHT`, it becomes :macro:`NGHTTP2_MAX_WEIGHT`. - * - * 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 - * ":") in |nva| and must place pseudo-headers before regular header - * fields. - * - * This function creates copies of all name/value pairs in |nva|. It - * also lower-cases all names in |nva|. The order of elements in - * |nva| is preserved. For header fields with + * + * 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 + * ":") in |nva| and must place pseudo-headers before regular header + * fields. + * + * This function creates copies of all name/value pairs in |nva|. It + * also lower-cases all names in |nva|. The order of elements in + * |nva| is preserved. For header fields with * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME` and * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, * header field name and value are not copied respectively. With * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME`, application * is responsible to pass header field name in lowercase. The * application should maintain the references to them until - * :type:`nghttp2_on_frame_send_callback` or - * :type:`nghttp2_on_frame_not_send_callback` is called. - * - * The |stream_user_data| is a pointer to an arbitrary data which is - * associated to the stream this frame will open. Therefore it is - * only used if this frame opens streams, in other words, it changes - * stream state from idle or reserved to open. - * - * This function is low-level in a sense that the application code can - * specify flags directly. For usual HTTP request, - * `nghttp2_submit_request()` is useful. Likewise, for HTTP response, - * prefer `nghttp2_submit_response()`. - * - * This function returns newly assigned stream ID if it succeeds and - * |stream_id| is -1. Otherwise, this function returns 0 if it - * succeeds, or one of the following negative error codes: - * + * :type:`nghttp2_on_frame_send_callback` or + * :type:`nghttp2_on_frame_not_send_callback` is called. + * + * The |stream_user_data| is a pointer to an arbitrary data which is + * associated to the stream this frame will open. Therefore it is + * only used if this frame opens streams, in other words, it changes + * stream state from idle or reserved to open. + * + * This function is low-level in a sense that the application code can + * specify flags directly. For usual HTTP request, + * `nghttp2_submit_request()` is useful. Likewise, for HTTP response, + * prefer `nghttp2_submit_response()`. + * + * This function returns newly assigned stream ID if it succeeds and + * |stream_id| is -1. Otherwise, this function returns 0 if it + * succeeds, or one of the following negative error codes: + * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` - * Out of memory. + * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE` - * No stream ID is available because maximum stream ID was - * reached. + * No stream ID is available because maximum stream ID was + * reached. * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` - * The |stream_id| is 0; or trying to depend on itself (stream ID - * equals ``pri_spec->stream_id``). + * The |stream_id| is 0; or trying to depend on itself (stream ID + * equals ``pri_spec->stream_id``). * :enum:`nghttp2_error.NGHTTP2_ERR_DATA_EXIST` - * DATA or HEADERS has been already submitted and not fully - * processed yet. This happens if stream denoted by |stream_id| - * is in reserved state. + * DATA or HEADERS has been already submitted and not fully + * processed yet. This happens if stream denoted by |stream_id| + * is in reserved state. * :enum:`nghttp2_error.NGHTTP2_ERR_PROTO` - * The |stream_id| is -1, and |session| is server session. - * - * .. warning:: - * - * This function returns assigned stream ID if it succeeds and - * |stream_id| is -1. But that stream is not opened yet. The - * application must not submit frame to that stream ID before - * :type:`nghttp2_before_frame_send_callback` is called for this - * frame. - * - */ -NGHTTP2_EXTERN int32_t nghttp2_submit_headers( - nghttp2_session *session, uint8_t flags, int32_t stream_id, - const nghttp2_priority_spec *pri_spec, const nghttp2_nv *nva, size_t nvlen, - void *stream_user_data); - -/** - * @function - * - * Submits one or more DATA frames to the stream |stream_id|. The - * data to be sent are provided by |data_prd|. If |flags| contains + * The |stream_id| is -1, and |session| is server session. + * + * .. warning:: + * + * This function returns assigned stream ID if it succeeds and + * |stream_id| is -1. But that stream is not opened yet. The + * application must not submit frame to that stream ID before + * :type:`nghttp2_before_frame_send_callback` is called for this + * frame. + * + */ +NGHTTP2_EXTERN int32_t nghttp2_submit_headers( + nghttp2_session *session, uint8_t flags, int32_t stream_id, + const nghttp2_priority_spec *pri_spec, const nghttp2_nv *nva, size_t nvlen, + void *stream_user_data); + +/** + * @function + * + * Submits one or more DATA frames to the stream |stream_id|. The + * data to be sent are provided by |data_prd|. If |flags| contains * :enum:`nghttp2_flag.NGHTTP2_FLAG_END_STREAM`, the last DATA frame * has END_STREAM flag set. - * - * This function does not take ownership of the |data_prd|. The - * function copies the members of the |data_prd|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * + * + * This function does not take ownership of the |data_prd|. The + * function copies the members of the |data_prd|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` - * Out of memory. + * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_DATA_EXIST` - * DATA or HEADERS has been already submitted and not fully - * processed yet. + * DATA or HEADERS has been already submitted and not fully + * processed yet. * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` - * The |stream_id| is 0. + * The |stream_id| is 0. * :enum:`nghttp2_error.NGHTTP2_ERR_STREAM_CLOSED` - * The stream was already closed; or the |stream_id| is invalid. - * - * .. note:: - * - * Currently, only one DATA or HEADERS is allowed for a stream at a - * time. Submitting these frames more than once before first DATA + * The stream was already closed; or the |stream_id| is invalid. + * + * .. note:: + * + * Currently, only one DATA or HEADERS is allowed for a stream at a + * time. Submitting these frames more than once before first DATA * or HEADERS is finished results in * :enum:`nghttp2_error.NGHTTP2_ERR_DATA_EXIST` error code. The * earliest callback which tells that previous frame is done is @@ -4154,500 +4154,500 @@ NGHTTP2_EXTERN int32_t nghttp2_submit_headers( * course, all data except for last one must not have * :enum:`nghttp2_flag.NGHTTP2_FLAG_END_STREAM` flag set in |flags|. * This sounds a bit complicated, and we recommend to use - * `nghttp2_submit_request()` and `nghttp2_submit_response()` to - * avoid this cascading issue. The experience shows that for HTTP - * use, these two functions are enough to implement both client and - * server. - */ -NGHTTP2_EXTERN int nghttp2_submit_data(nghttp2_session *session, uint8_t flags, - int32_t stream_id, - const nghttp2_data_provider *data_prd); - -/** - * @function - * - * Submits PRIORITY frame to change the priority of stream |stream_id| - * to the priority specification |pri_spec|. - * - * The |flags| is currently ignored and should be + * `nghttp2_submit_request()` and `nghttp2_submit_response()` to + * avoid this cascading issue. The experience shows that for HTTP + * use, these two functions are enough to implement both client and + * server. + */ +NGHTTP2_EXTERN int nghttp2_submit_data(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + const nghttp2_data_provider *data_prd); + +/** + * @function + * + * Submits PRIORITY frame to change the priority of stream |stream_id| + * to the priority specification |pri_spec|. + * + * The |flags| is currently ignored and should be * :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`. - * - * The |pri_spec| is priority specification of this request. ``NULL`` - * is not allowed for this function. To specify the priority, use - * `nghttp2_priority_spec_init()`. This function will copy its data - * members. - * + * + * The |pri_spec| is priority specification of this request. ``NULL`` + * is not allowed for this function. To specify the priority, use + * `nghttp2_priority_spec_init()`. This function will copy its data + * members. + * * The ``pri_spec->weight`` must be in [:macro:`NGHTTP2_MIN_WEIGHT`, * :macro:`NGHTTP2_MAX_WEIGHT`], inclusive. If ``pri_spec->weight`` * is strictly less than :macro:`NGHTTP2_MIN_WEIGHT`, it becomes * :macro:`NGHTTP2_MIN_WEIGHT`. If it is strictly greater than * :macro:`NGHTTP2_MAX_WEIGHT`, it becomes * :macro:`NGHTTP2_MAX_WEIGHT`. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` - * Out of memory. + * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` - * The |stream_id| is 0; or the |pri_spec| is NULL; or trying to - * depend on itself. - */ -NGHTTP2_EXTERN int -nghttp2_submit_priority(nghttp2_session *session, uint8_t flags, - int32_t stream_id, - const nghttp2_priority_spec *pri_spec); - -/** - * @function - * - * Submits RST_STREAM frame to cancel/reject the stream |stream_id| - * with the error code |error_code|. - * - * The pre-defined error code is one of :enum:`nghttp2_error_code`. - * - * The |flags| is currently ignored and should be + * The |stream_id| is 0; or the |pri_spec| is NULL; or trying to + * depend on itself. + */ +NGHTTP2_EXTERN int +nghttp2_submit_priority(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + const nghttp2_priority_spec *pri_spec); + +/** + * @function + * + * Submits RST_STREAM frame to cancel/reject the stream |stream_id| + * with the error code |error_code|. + * + * The pre-defined error code is one of :enum:`nghttp2_error_code`. + * + * The |flags| is currently ignored and should be * :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` - * Out of memory. + * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` - * The |stream_id| is 0. - */ -NGHTTP2_EXTERN int nghttp2_submit_rst_stream(nghttp2_session *session, - uint8_t flags, int32_t stream_id, - uint32_t error_code); - -/** - * @function - * - * Stores local settings and submits SETTINGS frame. The |iv| is the - * pointer to the array of :type:`nghttp2_settings_entry`. The |niv| - * indicates the number of :type:`nghttp2_settings_entry`. - * - * The |flags| is currently ignored and should be + * The |stream_id| is 0. + */ +NGHTTP2_EXTERN int nghttp2_submit_rst_stream(nghttp2_session *session, + uint8_t flags, int32_t stream_id, + uint32_t error_code); + +/** + * @function + * + * Stores local settings and submits SETTINGS frame. The |iv| is the + * pointer to the array of :type:`nghttp2_settings_entry`. The |niv| + * indicates the number of :type:`nghttp2_settings_entry`. + * + * The |flags| is currently ignored and should be * :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`. - * - * This function does not take ownership of the |iv|. This function - * copies all the elements in the |iv|. - * - * While updating individual stream's local window size, if the window - * size becomes strictly larger than NGHTTP2_MAX_WINDOW_SIZE, - * RST_STREAM is issued against such a stream. - * + * + * This function does not take ownership of the |iv|. This function + * copies all the elements in the |iv|. + * + * While updating individual stream's local window size, if the window + * size becomes strictly larger than NGHTTP2_MAX_WINDOW_SIZE, + * RST_STREAM is issued against such a stream. + * * SETTINGS with :enum:`nghttp2_flag.NGHTTP2_FLAG_ACK` is * automatically submitted by the library and application could not * send it at its will. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` - * The |iv| contains invalid value (e.g., initial window size - * strictly greater than (1 << 31) - 1. + * The |iv| contains invalid value (e.g., initial window size + * strictly greater than (1 << 31) - 1. * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` - * Out of memory. - */ -NGHTTP2_EXTERN int nghttp2_submit_settings(nghttp2_session *session, - uint8_t flags, - const nghttp2_settings_entry *iv, - size_t niv); - -/** - * @function - * - * Submits PUSH_PROMISE frame. - * - * The |flags| is currently ignored. The library handles the - * CONTINUATION frame internally and it correctly sets END_HEADERS to - * the last sequence of the PUSH_PROMISE or CONTINUATION frame. - * - * The |stream_id| must be client initiated stream ID. - * - * 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 - * ":") in |nva| and must place pseudo-headers before regular header - * fields. - * - * This function creates copies of all name/value pairs in |nva|. It - * also lower-cases all names in |nva|. The order of elements in - * |nva| is preserved. For header fields with + * Out of memory. + */ +NGHTTP2_EXTERN int nghttp2_submit_settings(nghttp2_session *session, + uint8_t flags, + const nghttp2_settings_entry *iv, + size_t niv); + +/** + * @function + * + * Submits PUSH_PROMISE frame. + * + * The |flags| is currently ignored. The library handles the + * CONTINUATION frame internally and it correctly sets END_HEADERS to + * the last sequence of the PUSH_PROMISE or CONTINUATION frame. + * + * The |stream_id| must be client initiated stream ID. + * + * 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 + * ":") in |nva| and must place pseudo-headers before regular header + * fields. + * + * This function creates copies of all name/value pairs in |nva|. It + * also lower-cases all names in |nva|. The order of elements in + * |nva| is preserved. For header fields with * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME` and * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, * header field name and value are not copied respectively. With * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME`, application * is responsible to pass header field name in lowercase. The * application should maintain the references to them until - * :type:`nghttp2_on_frame_send_callback` or - * :type:`nghttp2_on_frame_not_send_callback` is called. - * - * The |promised_stream_user_data| is a pointer to an arbitrary data - * which is associated to the promised stream this frame will open and - * make it in reserved state. It is available using - * `nghttp2_session_get_stream_user_data()`. The application can - * access it in :type:`nghttp2_before_frame_send_callback` and - * :type:`nghttp2_on_frame_send_callback` of this frame. - * - * The client side is not allowed to use this function. - * - * To submit response headers and data, use - * `nghttp2_submit_response()`. - * - * This function returns assigned promised stream ID if it succeeds, - * or one of the following negative error codes: - * + * :type:`nghttp2_on_frame_send_callback` or + * :type:`nghttp2_on_frame_not_send_callback` is called. + * + * The |promised_stream_user_data| is a pointer to an arbitrary data + * which is associated to the promised stream this frame will open and + * make it in reserved state. It is available using + * `nghttp2_session_get_stream_user_data()`. The application can + * access it in :type:`nghttp2_before_frame_send_callback` and + * :type:`nghttp2_on_frame_send_callback` of this frame. + * + * The client side is not allowed to use this function. + * + * To submit response headers and data, use + * `nghttp2_submit_response()`. + * + * This function returns assigned promised stream ID if it succeeds, + * or one of the following negative error codes: + * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` - * Out of memory. + * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_PROTO` - * This function was invoked when |session| is initialized as - * client. + * This function was invoked when |session| is initialized as + * client. * :enum:`nghttp2_error.NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE` - * No stream ID is available because maximum stream ID was - * reached. + * No stream ID is available because maximum stream ID was + * reached. * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` - * The |stream_id| is 0; The |stream_id| does not designate stream - * that peer initiated. + * The |stream_id| is 0; The |stream_id| does not designate stream + * that peer initiated. * :enum:`nghttp2_error.NGHTTP2_ERR_STREAM_CLOSED` - * The stream was already closed; or the |stream_id| is invalid. - * - * .. warning:: - * - * This function returns assigned promised stream ID if it succeeds. - * As of 1.16.0, stream object for pushed resource is created when - * this function succeeds. In that case, the application can submit - * push response for the promised frame. - * - * In 1.15.0 or prior versions, pushed stream is not opened yet when - * this function succeeds. The application must not submit frame to - * that stream ID before :type:`nghttp2_before_frame_send_callback` - * is called for this frame. - * - */ -NGHTTP2_EXTERN int32_t nghttp2_submit_push_promise( - nghttp2_session *session, uint8_t flags, int32_t stream_id, - const nghttp2_nv *nva, size_t nvlen, void *promised_stream_user_data); - -/** - * @function - * - * Submits PING frame. You don't have to send PING back when you - * received PING frame. The library automatically submits PING frame - * in this case. - * - * The |flags| is bitwise OR of 0 or more of the following value. - * + * The stream was already closed; or the |stream_id| is invalid. + * + * .. warning:: + * + * This function returns assigned promised stream ID if it succeeds. + * As of 1.16.0, stream object for pushed resource is created when + * this function succeeds. In that case, the application can submit + * push response for the promised frame. + * + * In 1.15.0 or prior versions, pushed stream is not opened yet when + * this function succeeds. The application must not submit frame to + * that stream ID before :type:`nghttp2_before_frame_send_callback` + * is called for this frame. + * + */ +NGHTTP2_EXTERN int32_t nghttp2_submit_push_promise( + nghttp2_session *session, uint8_t flags, int32_t stream_id, + const nghttp2_nv *nva, size_t nvlen, void *promised_stream_user_data); + +/** + * @function + * + * Submits PING frame. You don't have to send PING back when you + * received PING frame. The library automatically submits PING frame + * in this case. + * + * The |flags| is bitwise OR of 0 or more of the following value. + * * * :enum:`nghttp2_flag.NGHTTP2_FLAG_ACK` - * - * Unless `nghttp2_option_set_no_auto_ping_ack()` is used, the |flags| + * + * Unless `nghttp2_option_set_no_auto_ping_ack()` is used, the |flags| * should be :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`. - * - * If the |opaque_data| is non ``NULL``, then it should point to the 8 - * bytes array of memory to specify opaque data to send with PING - * frame. If the |opaque_data| is ``NULL``, zero-cleared 8 bytes will - * be sent as opaque data. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * + * + * If the |opaque_data| is non ``NULL``, then it should point to the 8 + * bytes array of memory to specify opaque data to send with PING + * frame. If the |opaque_data| is ``NULL``, zero-cleared 8 bytes will + * be sent as opaque data. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` - * Out of memory. - */ -NGHTTP2_EXTERN int nghttp2_submit_ping(nghttp2_session *session, uint8_t flags, - const uint8_t *opaque_data); - -/** - * @function - * - * Submits GOAWAY frame with the last stream ID |last_stream_id| and - * the error code |error_code|. - * - * The pre-defined error code is one of :enum:`nghttp2_error_code`. - * - * The |flags| is currently ignored and should be + * Out of memory. + */ +NGHTTP2_EXTERN int nghttp2_submit_ping(nghttp2_session *session, uint8_t flags, + const uint8_t *opaque_data); + +/** + * @function + * + * Submits GOAWAY frame with the last stream ID |last_stream_id| and + * the error code |error_code|. + * + * The pre-defined error code is one of :enum:`nghttp2_error_code`. + * + * The |flags| is currently ignored and should be * :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`. - * - * The |last_stream_id| is peer's stream ID or 0. So if |session| is - * initialized as client, |last_stream_id| must be even or 0. If - * |session| is initialized as server, |last_stream_id| must be odd or - * 0. - * - * The HTTP/2 specification says last_stream_id must not be increased - * from the value previously sent. So the actual value sent as - * last_stream_id is the minimum value between the given - * |last_stream_id| and the last_stream_id previously sent to the - * peer. - * - * If the |opaque_data| is not ``NULL`` and |opaque_data_len| is not - * zero, those data will be sent as additional debug data. The - * library makes a copy of the memory region pointed by |opaque_data| - * with the length |opaque_data_len|, so the caller does not need to - * keep this memory after the return of this function. If the - * |opaque_data_len| is 0, the |opaque_data| could be ``NULL``. - * - * After successful transmission of GOAWAY, following things happen. - * All incoming streams having strictly more than |last_stream_id| are - * closed. All incoming HEADERS which starts new stream are simply - * ignored. After all active streams are handled, both - * `nghttp2_session_want_read()` and `nghttp2_session_want_write()` - * return 0 and the application can close session. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * + * + * The |last_stream_id| is peer's stream ID or 0. So if |session| is + * initialized as client, |last_stream_id| must be even or 0. If + * |session| is initialized as server, |last_stream_id| must be odd or + * 0. + * + * The HTTP/2 specification says last_stream_id must not be increased + * from the value previously sent. So the actual value sent as + * last_stream_id is the minimum value between the given + * |last_stream_id| and the last_stream_id previously sent to the + * peer. + * + * If the |opaque_data| is not ``NULL`` and |opaque_data_len| is not + * zero, those data will be sent as additional debug data. The + * library makes a copy of the memory region pointed by |opaque_data| + * with the length |opaque_data_len|, so the caller does not need to + * keep this memory after the return of this function. If the + * |opaque_data_len| is 0, the |opaque_data| could be ``NULL``. + * + * After successful transmission of GOAWAY, following things happen. + * All incoming streams having strictly more than |last_stream_id| are + * closed. All incoming HEADERS which starts new stream are simply + * ignored. After all active streams are handled, both + * `nghttp2_session_want_read()` and `nghttp2_session_want_write()` + * return 0 and the application can close session. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` - * Out of memory. + * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` - * The |opaque_data_len| is too large; the |last_stream_id| is - * invalid. - */ -NGHTTP2_EXTERN int nghttp2_submit_goaway(nghttp2_session *session, - uint8_t flags, int32_t last_stream_id, - uint32_t error_code, - const uint8_t *opaque_data, - size_t opaque_data_len); - -/** - * @function - * - * Returns the last stream ID of a stream for which - * :type:`nghttp2_on_frame_recv_callback` was invoked most recently. - * The returned value can be used as last_stream_id parameter for - * `nghttp2_submit_goaway()` and - * `nghttp2_session_terminate_session2()`. - * - * This function always succeeds. - */ -NGHTTP2_EXTERN int32_t -nghttp2_session_get_last_proc_stream_id(nghttp2_session *session); - -/** - * @function - * - * Returns nonzero if new request can be sent from local endpoint. - * - * This function return 0 if request is not allowed for this session. - * There are several reasons why request is not allowed. Some of the - * reasons are: session is server; stream ID has been spent; GOAWAY - * has been sent or received. - * - * The application can call `nghttp2_submit_request()` without - * consulting this function. In that case, `nghttp2_submit_request()` - * may return error. Or, request is failed to sent, and - * :type:`nghttp2_on_stream_close_callback` is called. - */ -NGHTTP2_EXTERN int -nghttp2_session_check_request_allowed(nghttp2_session *session); - -/** - * @function - * - * Returns nonzero if |session| is initialized as server side session. - */ -NGHTTP2_EXTERN int -nghttp2_session_check_server_session(nghttp2_session *session); - -/** - * @function - * - * Submits WINDOW_UPDATE frame. - * - * The |flags| is currently ignored and should be + * The |opaque_data_len| is too large; the |last_stream_id| is + * invalid. + */ +NGHTTP2_EXTERN int nghttp2_submit_goaway(nghttp2_session *session, + uint8_t flags, int32_t last_stream_id, + uint32_t error_code, + const uint8_t *opaque_data, + size_t opaque_data_len); + +/** + * @function + * + * Returns the last stream ID of a stream for which + * :type:`nghttp2_on_frame_recv_callback` was invoked most recently. + * The returned value can be used as last_stream_id parameter for + * `nghttp2_submit_goaway()` and + * `nghttp2_session_terminate_session2()`. + * + * This function always succeeds. + */ +NGHTTP2_EXTERN int32_t +nghttp2_session_get_last_proc_stream_id(nghttp2_session *session); + +/** + * @function + * + * Returns nonzero if new request can be sent from local endpoint. + * + * This function return 0 if request is not allowed for this session. + * There are several reasons why request is not allowed. Some of the + * reasons are: session is server; stream ID has been spent; GOAWAY + * has been sent or received. + * + * The application can call `nghttp2_submit_request()` without + * consulting this function. In that case, `nghttp2_submit_request()` + * may return error. Or, request is failed to sent, and + * :type:`nghttp2_on_stream_close_callback` is called. + */ +NGHTTP2_EXTERN int +nghttp2_session_check_request_allowed(nghttp2_session *session); + +/** + * @function + * + * Returns nonzero if |session| is initialized as server side session. + */ +NGHTTP2_EXTERN int +nghttp2_session_check_server_session(nghttp2_session *session); + +/** + * @function + * + * Submits WINDOW_UPDATE frame. + * + * The |flags| is currently ignored and should be * :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`. - * - * The |stream_id| is the stream ID to send this WINDOW_UPDATE. To - * send connection level WINDOW_UPDATE, specify 0 to |stream_id|. - * - * If the |window_size_increment| is positive, the WINDOW_UPDATE with - * that value as window_size_increment is queued. If the - * |window_size_increment| is larger than the received bytes from the - * remote endpoint, the local window size is increased by that - * difference. If the sole purpose is to increase the local window - * size, consider to use `nghttp2_session_set_local_window_size()`. - * - * If the |window_size_increment| is negative, the local window size - * is decreased by -|window_size_increment|. If automatic - * WINDOW_UPDATE is enabled - * (`nghttp2_option_set_no_auto_window_update()`), and the library - * decided that the WINDOW_UPDATE should be submitted, then - * WINDOW_UPDATE is queued with the current received bytes count. If - * the sole purpose is to decrease the local window size, consider to - * use `nghttp2_session_set_local_window_size()`. - * - * If the |window_size_increment| is 0, the function does nothing and - * returns 0. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * + * + * The |stream_id| is the stream ID to send this WINDOW_UPDATE. To + * send connection level WINDOW_UPDATE, specify 0 to |stream_id|. + * + * If the |window_size_increment| is positive, the WINDOW_UPDATE with + * that value as window_size_increment is queued. If the + * |window_size_increment| is larger than the received bytes from the + * remote endpoint, the local window size is increased by that + * difference. If the sole purpose is to increase the local window + * size, consider to use `nghttp2_session_set_local_window_size()`. + * + * If the |window_size_increment| is negative, the local window size + * is decreased by -|window_size_increment|. If automatic + * WINDOW_UPDATE is enabled + * (`nghttp2_option_set_no_auto_window_update()`), and the library + * decided that the WINDOW_UPDATE should be submitted, then + * WINDOW_UPDATE is queued with the current received bytes count. If + * the sole purpose is to decrease the local window size, consider to + * use `nghttp2_session_set_local_window_size()`. + * + * If the |window_size_increment| is 0, the 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_FLOW_CONTROL` - * The local window size overflow or gets negative. + * The local window size overflow or gets negative. * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` - * Out of memory. - */ -NGHTTP2_EXTERN int nghttp2_submit_window_update(nghttp2_session *session, - uint8_t flags, - int32_t stream_id, - int32_t window_size_increment); - -/** - * @function - * - * Set local window size (local endpoints's window size) to the given - * |window_size| for the given stream denoted by |stream_id|. To - * change connection level window size, specify 0 to |stream_id|. To - * increase window size, this function may submit WINDOW_UPDATE frame - * to transmission queue. - * - * The |flags| is currently ignored and should be + * Out of memory. + */ +NGHTTP2_EXTERN int nghttp2_submit_window_update(nghttp2_session *session, + uint8_t flags, + int32_t stream_id, + int32_t window_size_increment); + +/** + * @function + * + * Set local window size (local endpoints's window size) to the given + * |window_size| for the given stream denoted by |stream_id|. To + * change connection level window size, specify 0 to |stream_id|. To + * increase window size, this function may submit WINDOW_UPDATE frame + * to transmission queue. + * + * The |flags| is currently ignored and should be * :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`. - * - * This sounds similar to `nghttp2_submit_window_update()`, but there - * are 2 differences. The first difference is that this function - * takes the absolute value of window size to set, rather than the - * delta. To change the window size, this may be easier to use since - * the application just declares the intended window size, rather than - * calculating delta. The second difference is that - * `nghttp2_submit_window_update()` affects the received bytes count - * which has not acked yet. By the specification of - * `nghttp2_submit_window_update()`, to strictly increase the local - * window size, we have to submit delta including all received bytes - * count, which might not be desirable in some cases. On the other - * hand, this function does not affect the received bytes count. It - * just sets the local window size to the given value. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * + * + * This sounds similar to `nghttp2_submit_window_update()`, but there + * are 2 differences. The first difference is that this function + * takes the absolute value of window size to set, rather than the + * delta. To change the window size, this may be easier to use since + * the application just declares the intended window size, rather than + * calculating delta. The second difference is that + * `nghttp2_submit_window_update()` affects the received bytes count + * which has not acked yet. By the specification of + * `nghttp2_submit_window_update()`, to strictly increase the local + * window size, we have to submit delta including all received bytes + * count, which might not be desirable in some cases. On the other + * hand, this function does not affect the received bytes count. It + * just sets the local window size to the given value. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` - * The |stream_id| is negative. + * The |stream_id| is negative. * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` - * Out of memory. - */ -NGHTTP2_EXTERN int -nghttp2_session_set_local_window_size(nghttp2_session *session, uint8_t flags, - int32_t stream_id, int32_t window_size); - -/** - * @function - * - * Submits extension frame. - * - * Application can pass arbitrary frame flags and stream ID in |flags| - * and |stream_id| respectively. The |payload| is opaque pointer, and - * it can be accessible though ``frame->ext.payload`` in - * :type:`nghttp2_pack_extension_callback`. The library will not own - * passed |payload| pointer. - * - * The application must set :type:`nghttp2_pack_extension_callback` - * using `nghttp2_session_callbacks_set_pack_extension_callback()`. - * - * The application should retain the memory pointed by |payload| until - * the transmission of extension frame is done (which is indicated by - * :type:`nghttp2_on_frame_send_callback`), or transmission fails - * (which is indicated by :type:`nghttp2_on_frame_not_send_callback`). - * If application does not touch this memory region after packing it - * into a wire format, application can free it inside - * :type:`nghttp2_pack_extension_callback`. - * - * The standard HTTP/2 frame cannot be sent with this function, so - * |type| must be strictly grater than 0x9. Otherwise, this function + * Out of memory. + */ +NGHTTP2_EXTERN int +nghttp2_session_set_local_window_size(nghttp2_session *session, uint8_t flags, + int32_t stream_id, int32_t window_size); + +/** + * @function + * + * Submits extension frame. + * + * Application can pass arbitrary frame flags and stream ID in |flags| + * and |stream_id| respectively. The |payload| is opaque pointer, and + * it can be accessible though ``frame->ext.payload`` in + * :type:`nghttp2_pack_extension_callback`. The library will not own + * passed |payload| pointer. + * + * The application must set :type:`nghttp2_pack_extension_callback` + * using `nghttp2_session_callbacks_set_pack_extension_callback()`. + * + * The application should retain the memory pointed by |payload| until + * the transmission of extension frame is done (which is indicated by + * :type:`nghttp2_on_frame_send_callback`), or transmission fails + * (which is indicated by :type:`nghttp2_on_frame_not_send_callback`). + * If application does not touch this memory region after packing it + * into a wire format, application can free it inside + * :type:`nghttp2_pack_extension_callback`. + * + * The standard HTTP/2 frame cannot be sent with this function, so + * |type| must be strictly grater than 0x9. Otherwise, this function * will fail with error code * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT`. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` - * If :type:`nghttp2_pack_extension_callback` is not set. + * If :type:`nghttp2_pack_extension_callback` is not set. * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` - * If |type| specifies standard HTTP/2 frame type. The frame - * types in the rage [0x0, 0x9], both inclusive, are standard - * HTTP/2 frame type, and cannot be sent using this function. + * If |type| specifies standard HTTP/2 frame type. The frame + * types in the rage [0x0, 0x9], both inclusive, are standard + * HTTP/2 frame type, and cannot be sent using this function. * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` - * Out of memory - */ -NGHTTP2_EXTERN int nghttp2_submit_extension(nghttp2_session *session, - uint8_t type, uint8_t flags, - int32_t stream_id, void *payload); - -/** - * @struct - * - * The payload of ALTSVC frame. ALTSVC 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 + * Out of memory + */ +NGHTTP2_EXTERN int nghttp2_submit_extension(nghttp2_session *session, + uint8_t type, uint8_t flags, + int32_t stream_id, void *payload); + +/** + * @struct + * + * The payload of ALTSVC frame. ALTSVC 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_ALTSVC`, * ``nghttp2_extension.payload`` will point to this struct. - * - * It has the following members: - */ -typedef struct { - /** - * The pointer to origin which this alternative service is - * associated with. This is not necessarily NULL-terminated. - */ - uint8_t *origin; - /** - * The length of the |origin|. - */ - size_t origin_len; - /** - * The pointer to Alt-Svc field value contained in ALTSVC frame. - * This is not necessarily NULL-terminated. - */ - uint8_t *field_value; - /** - * The length of the |field_value|. - */ - size_t field_value_len; -} nghttp2_ext_altsvc; - -/** - * @function - * - * Submits ALTSVC frame. - * - * ALTSVC frame is a non-critical extension to HTTP/2, and defined in + * + * It has the following members: + */ +typedef struct { + /** + * The pointer to origin which this alternative service is + * associated with. This is not necessarily NULL-terminated. + */ + uint8_t *origin; + /** + * The length of the |origin|. + */ + size_t origin_len; + /** + * The pointer to Alt-Svc field value contained in ALTSVC frame. + * This is not necessarily NULL-terminated. + */ + uint8_t *field_value; + /** + * The length of the |field_value|. + */ + size_t field_value_len; +} nghttp2_ext_altsvc; + +/** + * @function + * + * Submits ALTSVC frame. + * + * ALTSVC frame is a non-critical extension to HTTP/2, and defined in * `RFC 7383 <https://tools.ietf.org/html/rfc7838#section-4>`_. - * - * The |flags| is currently ignored and should be + * + * The |flags| is currently ignored and should be * :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`. - * - * The |origin| points to the origin this alternative service is - * associated with. The |origin_len| is the length of the origin. If - * |stream_id| is 0, the origin must be specified. If |stream_id| is - * not zero, the origin must be empty (in other words, |origin_len| - * must be 0). - * - * The ALTSVC frame is only usable from server side. If this function - * is invoked with client side session, this function returns + * + * The |origin| points to the origin this alternative service is + * associated with. The |origin_len| is the length of the origin. If + * |stream_id| is 0, the origin must be specified. If |stream_id| is + * not zero, the origin must be empty (in other words, |origin_len| + * must be 0). + * + * The ALTSVC frame is only usable from server side. If this function + * is invoked with client side session, this function returns * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE`. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` - * Out of memory + * Out of memory * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` - * The function is called from client side session + * The function is called from client side session * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` - * The sum of |origin_len| and |field_value_len| is larger than - * 16382; or |origin_len| is 0 while |stream_id| is 0; or - * |origin_len| is not 0 while |stream_id| is not 0. - */ -NGHTTP2_EXTERN int nghttp2_submit_altsvc(nghttp2_session *session, - uint8_t flags, int32_t stream_id, - const uint8_t *origin, - size_t origin_len, - const uint8_t *field_value, - size_t field_value_len); - -/** + * The sum of |origin_len| and |field_value_len| is larger than + * 16382; or |origin_len| is 0 while |stream_id| is 0; or + * |origin_len| is not 0 while |stream_id| is not 0. + */ +NGHTTP2_EXTERN int nghttp2_submit_altsvc(nghttp2_session *session, + uint8_t flags, int32_t stream_id, + const uint8_t *origin, + size_t origin_len, + const uint8_t *field_value, + size_t field_value_len); + +/** * @struct * * The single entry of an origin. @@ -4691,8 +4691,8 @@ typedef struct { } nghttp2_ext_origin; /** - * @function - * + * @function + * * Submits ORIGIN frame. * * ORIGIN frame is a non-critical extension to HTTP/2 and defined by @@ -4725,117 +4725,117 @@ NGHTTP2_EXTERN int nghttp2_submit_origin(nghttp2_session *session, /** * @function * - * Compares ``lhs->name`` of length ``lhs->namelen`` bytes and - * ``rhs->name`` of length ``rhs->namelen`` bytes. Returns negative - * integer if ``lhs->name`` is found to be less than ``rhs->name``; or - * returns positive integer if ``lhs->name`` is found to be greater - * than ``rhs->name``; or returns 0 otherwise. - */ -NGHTTP2_EXTERN int nghttp2_nv_compare_name(const nghttp2_nv *lhs, - const nghttp2_nv *rhs); - -/** - * @function - * - * A helper function for dealing with NPN in client side or ALPN in - * server side. The |in| contains peer's protocol list in preferable - * order. The format of |in| is length-prefixed and not - * null-terminated. For example, ``h2`` and - * ``http/1.1`` stored in |in| like this:: - * - * in[0] = 2 - * in[1..2] = "h2" - * in[3] = 8 - * in[4..11] = "http/1.1" - * inlen = 12 - * - * The selection algorithm is as follows: - * - * 1. If peer's list contains HTTP/2 protocol the library supports, - * it is selected and returns 1. The following step is not taken. - * - * 2. If peer's list contains ``http/1.1``, this function selects - * ``http/1.1`` and returns 0. The following step is not taken. - * - * 3. This function selects nothing and returns -1 (So called - * non-overlap case). In this case, |out| and |outlen| are left - * untouched. - * - * Selecting ``h2`` means that ``h2`` is written into |*out| and its - * length (which is 2) is assigned to |*outlen|. - * - * For ALPN, refer to https://tools.ietf.org/html/rfc7301 - * - * See http://technotes.googlecode.com/git/nextprotoneg.html for more - * details about NPN. - * - * For NPN, to use this method you should do something like:: - * - * static int select_next_proto_cb(SSL* ssl, - * unsigned char **out, - * unsigned char *outlen, - * const unsigned char *in, - * unsigned int inlen, - * void *arg) - * { - * int rv; - * rv = nghttp2_select_next_protocol(out, outlen, in, inlen); - * if (rv == -1) { - * return SSL_TLSEXT_ERR_NOACK; - * } - * if (rv == 1) { - * ((MyType*)arg)->http2_selected = 1; - * } - * return SSL_TLSEXT_ERR_OK; - * } - * ... - * SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, my_obj); - * - */ -NGHTTP2_EXTERN int nghttp2_select_next_protocol(unsigned char **out, - unsigned char *outlen, - const unsigned char *in, - unsigned int inlen); - -/** - * @function - * - * Returns a pointer to a nghttp2_info struct with version information - * about the run-time library in use. The |least_version| argument - * can be set to a 24 bit numerical value for the least accepted - * version number and if the condition is not met, this function will - * return a ``NULL``. Pass in 0 to skip the version checking. - */ -NGHTTP2_EXTERN nghttp2_info *nghttp2_version(int least_version); - -/** - * @function - * - * Returns nonzero if the :type:`nghttp2_error` library error code - * |lib_error| is fatal. - */ -NGHTTP2_EXTERN int nghttp2_is_fatal(int lib_error_code); - -/** - * @function - * - * Returns nonzero if HTTP header field name |name| of length |len| is - * valid according to http://tools.ietf.org/html/rfc7230#section-3.2 - * - * Because this is a header field name in HTTP2, the upper cased alphabet - * is treated as error. - */ -NGHTTP2_EXTERN int nghttp2_check_header_name(const uint8_t *name, size_t len); - -/** - * @function - * - * Returns nonzero if HTTP header field value |value| of length |len| - * is valid according to - * http://tools.ietf.org/html/rfc7230#section-3.2 - */ -NGHTTP2_EXTERN int nghttp2_check_header_value(const uint8_t *value, size_t len); - + * Compares ``lhs->name`` of length ``lhs->namelen`` bytes and + * ``rhs->name`` of length ``rhs->namelen`` bytes. Returns negative + * integer if ``lhs->name`` is found to be less than ``rhs->name``; or + * returns positive integer if ``lhs->name`` is found to be greater + * than ``rhs->name``; or returns 0 otherwise. + */ +NGHTTP2_EXTERN int nghttp2_nv_compare_name(const nghttp2_nv *lhs, + const nghttp2_nv *rhs); + +/** + * @function + * + * A helper function for dealing with NPN in client side or ALPN in + * server side. The |in| contains peer's protocol list in preferable + * order. The format of |in| is length-prefixed and not + * null-terminated. For example, ``h2`` and + * ``http/1.1`` stored in |in| like this:: + * + * in[0] = 2 + * in[1..2] = "h2" + * in[3] = 8 + * in[4..11] = "http/1.1" + * inlen = 12 + * + * The selection algorithm is as follows: + * + * 1. If peer's list contains HTTP/2 protocol the library supports, + * it is selected and returns 1. The following step is not taken. + * + * 2. If peer's list contains ``http/1.1``, this function selects + * ``http/1.1`` and returns 0. The following step is not taken. + * + * 3. This function selects nothing and returns -1 (So called + * non-overlap case). In this case, |out| and |outlen| are left + * untouched. + * + * Selecting ``h2`` means that ``h2`` is written into |*out| and its + * length (which is 2) is assigned to |*outlen|. + * + * For ALPN, refer to https://tools.ietf.org/html/rfc7301 + * + * See http://technotes.googlecode.com/git/nextprotoneg.html for more + * details about NPN. + * + * For NPN, to use this method you should do something like:: + * + * static int select_next_proto_cb(SSL* ssl, + * unsigned char **out, + * unsigned char *outlen, + * const unsigned char *in, + * unsigned int inlen, + * void *arg) + * { + * int rv; + * rv = nghttp2_select_next_protocol(out, outlen, in, inlen); + * if (rv == -1) { + * return SSL_TLSEXT_ERR_NOACK; + * } + * if (rv == 1) { + * ((MyType*)arg)->http2_selected = 1; + * } + * return SSL_TLSEXT_ERR_OK; + * } + * ... + * SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, my_obj); + * + */ +NGHTTP2_EXTERN int nghttp2_select_next_protocol(unsigned char **out, + unsigned char *outlen, + const unsigned char *in, + unsigned int inlen); + +/** + * @function + * + * Returns a pointer to a nghttp2_info struct with version information + * about the run-time library in use. The |least_version| argument + * can be set to a 24 bit numerical value for the least accepted + * version number and if the condition is not met, this function will + * return a ``NULL``. Pass in 0 to skip the version checking. + */ +NGHTTP2_EXTERN nghttp2_info *nghttp2_version(int least_version); + +/** + * @function + * + * Returns nonzero if the :type:`nghttp2_error` library error code + * |lib_error| is fatal. + */ +NGHTTP2_EXTERN int nghttp2_is_fatal(int lib_error_code); + +/** + * @function + * + * Returns nonzero if HTTP header field name |name| of length |len| is + * valid according to http://tools.ietf.org/html/rfc7230#section-3.2 + * + * Because this is a header field name in HTTP2, the upper cased alphabet + * is treated as error. + */ +NGHTTP2_EXTERN int nghttp2_check_header_name(const uint8_t *name, size_t len); + +/** + * @function + * + * Returns nonzero if HTTP header field value |value| of length |len| + * is valid according to + * http://tools.ietf.org/html/rfc7230#section-3.2 + */ +NGHTTP2_EXTERN int nghttp2_check_header_value(const uint8_t *value, size_t len); + /** * @function * @@ -4873,710 +4873,710 @@ NGHTTP2_EXTERN int nghttp2_check_path(const uint8_t *value, size_t len); */ NGHTTP2_EXTERN int nghttp2_check_authority(const uint8_t *value, size_t len); -/* HPACK API */ - -struct nghttp2_hd_deflater; - -/** - * @struct - * - * HPACK deflater object. - */ -typedef struct nghttp2_hd_deflater nghttp2_hd_deflater; - -/** - * @function - * - * Initializes |*deflater_ptr| for deflating name/values pairs. - * - * The |max_deflate_dynamic_table_size| is the upper bound of header - * table size the deflater will use. - * - * If this function fails, |*deflater_ptr| is left untouched. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * +/* HPACK API */ + +struct nghttp2_hd_deflater; + +/** + * @struct + * + * HPACK deflater object. + */ +typedef struct nghttp2_hd_deflater nghttp2_hd_deflater; + +/** + * @function + * + * Initializes |*deflater_ptr| for deflating name/values pairs. + * + * The |max_deflate_dynamic_table_size| is the upper bound of header + * table size the deflater will use. + * + * If this function fails, |*deflater_ptr| is left untouched. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` - * Out of memory. - */ -NGHTTP2_EXTERN int -nghttp2_hd_deflate_new(nghttp2_hd_deflater **deflater_ptr, - size_t max_deflate_dynamic_table_size); - -/** - * @function - * - * Like `nghttp2_hd_deflate_new()`, but with additional custom memory - * allocator specified in the |mem|. - * - * The |mem| can be ``NULL`` and the call is equivalent to - * `nghttp2_hd_deflate_new()`. - * - * This function does not take ownership |mem|. The application is - * responsible for freeing |mem|. - * - * The library code does not refer to |mem| pointer after this - * function returns, so the application can safely free it. - */ -NGHTTP2_EXTERN int -nghttp2_hd_deflate_new2(nghttp2_hd_deflater **deflater_ptr, - size_t max_deflate_dynamic_table_size, - nghttp2_mem *mem); - -/** - * @function - * - * Deallocates any resources allocated for |deflater|. - */ -NGHTTP2_EXTERN void nghttp2_hd_deflate_del(nghttp2_hd_deflater *deflater); - -/** - * @function - * - * Changes header table size of the |deflater| to - * |settings_max_dynamic_table_size| bytes. This may trigger eviction - * in the dynamic table. - * - * The |settings_max_dynamic_table_size| should be the value received - * in SETTINGS_HEADER_TABLE_SIZE. - * - * The deflater never uses more memory than - * ``max_deflate_dynamic_table_size`` bytes specified in - * `nghttp2_hd_deflate_new()`. Therefore, if - * |settings_max_dynamic_table_size| > - * ``max_deflate_dynamic_table_size``, resulting maximum table size - * becomes ``max_deflate_dynamic_table_size``. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * + * Out of memory. + */ +NGHTTP2_EXTERN int +nghttp2_hd_deflate_new(nghttp2_hd_deflater **deflater_ptr, + size_t max_deflate_dynamic_table_size); + +/** + * @function + * + * Like `nghttp2_hd_deflate_new()`, but with additional custom memory + * allocator specified in the |mem|. + * + * The |mem| can be ``NULL`` and the call is equivalent to + * `nghttp2_hd_deflate_new()`. + * + * This function does not take ownership |mem|. The application is + * responsible for freeing |mem|. + * + * The library code does not refer to |mem| pointer after this + * function returns, so the application can safely free it. + */ +NGHTTP2_EXTERN int +nghttp2_hd_deflate_new2(nghttp2_hd_deflater **deflater_ptr, + size_t max_deflate_dynamic_table_size, + nghttp2_mem *mem); + +/** + * @function + * + * Deallocates any resources allocated for |deflater|. + */ +NGHTTP2_EXTERN void nghttp2_hd_deflate_del(nghttp2_hd_deflater *deflater); + +/** + * @function + * + * Changes header table size of the |deflater| to + * |settings_max_dynamic_table_size| bytes. This may trigger eviction + * in the dynamic table. + * + * The |settings_max_dynamic_table_size| should be the value received + * in SETTINGS_HEADER_TABLE_SIZE. + * + * The deflater never uses more memory than + * ``max_deflate_dynamic_table_size`` bytes specified in + * `nghttp2_hd_deflate_new()`. Therefore, if + * |settings_max_dynamic_table_size| > + * ``max_deflate_dynamic_table_size``, resulting maximum table size + * becomes ``max_deflate_dynamic_table_size``. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` - * Out of memory. - */ -NGHTTP2_EXTERN int -nghttp2_hd_deflate_change_table_size(nghttp2_hd_deflater *deflater, - size_t settings_max_dynamic_table_size); - -/** - * @function - * - * Deflates the |nva|, which has the |nvlen| name/value pairs, into - * the |buf| of length |buflen|. - * - * If |buf| is not large enough to store the deflated header block, + * Out of memory. + */ +NGHTTP2_EXTERN int +nghttp2_hd_deflate_change_table_size(nghttp2_hd_deflater *deflater, + size_t settings_max_dynamic_table_size); + +/** + * @function + * + * Deflates the |nva|, which has the |nvlen| name/value pairs, into + * the |buf| of length |buflen|. + * + * If |buf| is not large enough to store the deflated header block, * this function fails with * :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE`. The caller * should use `nghttp2_hd_deflate_bound()` to know the upper bound of * buffer size required to deflate given header name/value pairs. - * - * Once this function fails, subsequent call of this function always + * + * Once this function fails, subsequent call of this function always * returns :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP`. - * - * After this function returns, it is safe to delete the |nva|. - * - * This function returns the number of bytes written to |buf| if it - * succeeds, or one of the following negative error codes: - * + * + * After this function returns, it is safe to delete the |nva|. + * + * This function returns the number of bytes written to |buf| if it + * succeeds, or one of the following negative error codes: + * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` - * Out of memory. + * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP` - * Deflation process has failed. + * Deflation process has failed. * :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE` - * The provided |buflen| size is too small to hold the output. - */ -NGHTTP2_EXTERN ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, - uint8_t *buf, size_t buflen, - const nghttp2_nv *nva, - size_t nvlen); - -/** - * @function - * - * Deflates the |nva|, which has the |nvlen| name/value pairs, into - * the |veclen| size of buf vector |vec|. The each size of buffer - * must be set in len field of :type:`nghttp2_vec`. If and only if - * one chunk is filled up completely, next chunk will be used. If - * |vec| is not large enough to store the deflated header block, this + * The provided |buflen| size is too small to hold the output. + */ +NGHTTP2_EXTERN ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, + uint8_t *buf, size_t buflen, + const nghttp2_nv *nva, + size_t nvlen); + +/** + * @function + * + * Deflates the |nva|, which has the |nvlen| name/value pairs, into + * the |veclen| size of buf vector |vec|. The each size of buffer + * must be set in len field of :type:`nghttp2_vec`. If and only if + * one chunk is filled up completely, next chunk will be used. If + * |vec| is not large enough to store the deflated header block, this * function fails with * :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE`. The caller - * should use `nghttp2_hd_deflate_bound()` to know the upper bound of - * buffer size required to deflate given header name/value pairs. - * - * Once this function fails, subsequent call of this function always + * should use `nghttp2_hd_deflate_bound()` to know the upper bound of + * buffer size required to deflate given header name/value pairs. + * + * Once this function fails, subsequent call of this function always * returns :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP`. - * - * After this function returns, it is safe to delete the |nva|. - * - * This function returns the number of bytes written to |vec| if it - * succeeds, or one of the following negative error codes: - * + * + * After this function returns, it is safe to delete the |nva|. + * + * This function returns the number of bytes written to |vec| if it + * succeeds, or one of the following negative error codes: + * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` - * Out of memory. + * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP` - * Deflation process has failed. + * Deflation process has failed. * :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE` - * The provided |buflen| size is too small to hold the output. - */ -NGHTTP2_EXTERN ssize_t nghttp2_hd_deflate_hd_vec(nghttp2_hd_deflater *deflater, - const nghttp2_vec *vec, - size_t veclen, - const nghttp2_nv *nva, - size_t nvlen); - -/** - * @function - * - * Returns an upper bound on the compressed size after deflation of - * |nva| of length |nvlen|. - */ -NGHTTP2_EXTERN size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater, - const nghttp2_nv *nva, - size_t nvlen); - -/** - * @function - * - * Returns the number of entries that header table of |deflater| - * contains. This is the sum of the number of static table and - * dynamic table, so the return value is at least 61. - */ -NGHTTP2_EXTERN -size_t nghttp2_hd_deflate_get_num_table_entries(nghttp2_hd_deflater *deflater); - -/** - * @function - * - * Returns the table entry denoted by |idx| from header table of - * |deflater|. The |idx| is 1-based, and idx=1 returns first entry of - * static table. idx=62 returns first entry of dynamic table if it - * exists. Specifying idx=0 is error, and this function returns NULL. - * If |idx| is strictly greater than the number of entries the tables - * contain, this function returns NULL. - */ -NGHTTP2_EXTERN -const nghttp2_nv * -nghttp2_hd_deflate_get_table_entry(nghttp2_hd_deflater *deflater, size_t idx); - -/** - * @function - * - * Returns the used dynamic table size, including the overhead 32 - * bytes per entry described in RFC 7541. - */ -NGHTTP2_EXTERN -size_t nghttp2_hd_deflate_get_dynamic_table_size(nghttp2_hd_deflater *deflater); - -/** - * @function - * - * Returns the maximum dynamic table size. - */ -NGHTTP2_EXTERN -size_t -nghttp2_hd_deflate_get_max_dynamic_table_size(nghttp2_hd_deflater *deflater); - -struct nghttp2_hd_inflater; - -/** - * @struct - * - * HPACK inflater object. - */ -typedef struct nghttp2_hd_inflater nghttp2_hd_inflater; - -/** - * @function - * - * Initializes |*inflater_ptr| for inflating name/values pairs. - * - * If this function fails, |*inflater_ptr| is left untouched. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * + * The provided |buflen| size is too small to hold the output. + */ +NGHTTP2_EXTERN ssize_t nghttp2_hd_deflate_hd_vec(nghttp2_hd_deflater *deflater, + const nghttp2_vec *vec, + size_t veclen, + const nghttp2_nv *nva, + size_t nvlen); + +/** + * @function + * + * Returns an upper bound on the compressed size after deflation of + * |nva| of length |nvlen|. + */ +NGHTTP2_EXTERN size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater, + const nghttp2_nv *nva, + size_t nvlen); + +/** + * @function + * + * Returns the number of entries that header table of |deflater| + * contains. This is the sum of the number of static table and + * dynamic table, so the return value is at least 61. + */ +NGHTTP2_EXTERN +size_t nghttp2_hd_deflate_get_num_table_entries(nghttp2_hd_deflater *deflater); + +/** + * @function + * + * Returns the table entry denoted by |idx| from header table of + * |deflater|. The |idx| is 1-based, and idx=1 returns first entry of + * static table. idx=62 returns first entry of dynamic table if it + * exists. Specifying idx=0 is error, and this function returns NULL. + * If |idx| is strictly greater than the number of entries the tables + * contain, this function returns NULL. + */ +NGHTTP2_EXTERN +const nghttp2_nv * +nghttp2_hd_deflate_get_table_entry(nghttp2_hd_deflater *deflater, size_t idx); + +/** + * @function + * + * Returns the used dynamic table size, including the overhead 32 + * bytes per entry described in RFC 7541. + */ +NGHTTP2_EXTERN +size_t nghttp2_hd_deflate_get_dynamic_table_size(nghttp2_hd_deflater *deflater); + +/** + * @function + * + * Returns the maximum dynamic table size. + */ +NGHTTP2_EXTERN +size_t +nghttp2_hd_deflate_get_max_dynamic_table_size(nghttp2_hd_deflater *deflater); + +struct nghttp2_hd_inflater; + +/** + * @struct + * + * HPACK inflater object. + */ +typedef struct nghttp2_hd_inflater nghttp2_hd_inflater; + +/** + * @function + * + * Initializes |*inflater_ptr| for inflating name/values pairs. + * + * If this function fails, |*inflater_ptr| is left untouched. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` - * Out of memory. - */ -NGHTTP2_EXTERN int nghttp2_hd_inflate_new(nghttp2_hd_inflater **inflater_ptr); - -/** - * @function - * - * Like `nghttp2_hd_inflate_new()`, but with additional custom memory - * allocator specified in the |mem|. - * - * The |mem| can be ``NULL`` and the call is equivalent to - * `nghttp2_hd_inflate_new()`. - * - * This function does not take ownership |mem|. The application is - * responsible for freeing |mem|. - * - * The library code does not refer to |mem| pointer after this - * function returns, so the application can safely free it. - */ -NGHTTP2_EXTERN int nghttp2_hd_inflate_new2(nghttp2_hd_inflater **inflater_ptr, - nghttp2_mem *mem); - -/** - * @function - * - * Deallocates any resources allocated for |inflater|. - */ -NGHTTP2_EXTERN void nghttp2_hd_inflate_del(nghttp2_hd_inflater *inflater); - -/** - * @function - * - * Changes header table size in the |inflater|. This may trigger - * eviction in the dynamic table. - * - * The |settings_max_dynamic_table_size| should be the value - * transmitted in SETTINGS_HEADER_TABLE_SIZE. - * - * This function must not be called while header block is being - * inflated. In other words, this function must be called after - * initialization of |inflater|, but before calling - * `nghttp2_hd_inflate_hd2()`, or after - * `nghttp2_hd_inflate_end_headers()`. Otherwise, - * `NGHTTP2_ERR_INVALID_STATE` was returned. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * + * Out of memory. + */ +NGHTTP2_EXTERN int nghttp2_hd_inflate_new(nghttp2_hd_inflater **inflater_ptr); + +/** + * @function + * + * Like `nghttp2_hd_inflate_new()`, but with additional custom memory + * allocator specified in the |mem|. + * + * The |mem| can be ``NULL`` and the call is equivalent to + * `nghttp2_hd_inflate_new()`. + * + * This function does not take ownership |mem|. The application is + * responsible for freeing |mem|. + * + * The library code does not refer to |mem| pointer after this + * function returns, so the application can safely free it. + */ +NGHTTP2_EXTERN int nghttp2_hd_inflate_new2(nghttp2_hd_inflater **inflater_ptr, + nghttp2_mem *mem); + +/** + * @function + * + * Deallocates any resources allocated for |inflater|. + */ +NGHTTP2_EXTERN void nghttp2_hd_inflate_del(nghttp2_hd_inflater *inflater); + +/** + * @function + * + * Changes header table size in the |inflater|. This may trigger + * eviction in the dynamic table. + * + * The |settings_max_dynamic_table_size| should be the value + * transmitted in SETTINGS_HEADER_TABLE_SIZE. + * + * This function must not be called while header block is being + * inflated. In other words, this function must be called after + * initialization of |inflater|, but before calling + * `nghttp2_hd_inflate_hd2()`, or after + * `nghttp2_hd_inflate_end_headers()`. Otherwise, + * `NGHTTP2_ERR_INVALID_STATE` was returned. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` - * Out of memory. + * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` - * The function is called while header block is being inflated. - * Probably, application missed to call - * `nghttp2_hd_inflate_end_headers()`. - */ -NGHTTP2_EXTERN int -nghttp2_hd_inflate_change_table_size(nghttp2_hd_inflater *inflater, - size_t settings_max_dynamic_table_size); - -/** - * @enum - * - * The flags for header inflation. - */ -typedef enum { - /** - * No flag set. - */ - NGHTTP2_HD_INFLATE_NONE = 0, - /** - * Indicates all headers were inflated. - */ - NGHTTP2_HD_INFLATE_FINAL = 0x01, - /** - * Indicates a header was emitted. - */ - NGHTTP2_HD_INFLATE_EMIT = 0x02 -} nghttp2_hd_inflate_flag; - -/** - * @function - * - * .. warning:: - * - * Deprecated. Use `nghttp2_hd_inflate_hd2()` instead. - * - * Inflates name/value block stored in |in| with length |inlen|. This - * function performs decompression. For each successful emission of + * The function is called while header block is being inflated. + * Probably, application missed to call + * `nghttp2_hd_inflate_end_headers()`. + */ +NGHTTP2_EXTERN int +nghttp2_hd_inflate_change_table_size(nghttp2_hd_inflater *inflater, + size_t settings_max_dynamic_table_size); + +/** + * @enum + * + * The flags for header inflation. + */ +typedef enum { + /** + * No flag set. + */ + NGHTTP2_HD_INFLATE_NONE = 0, + /** + * Indicates all headers were inflated. + */ + NGHTTP2_HD_INFLATE_FINAL = 0x01, + /** + * Indicates a header was emitted. + */ + NGHTTP2_HD_INFLATE_EMIT = 0x02 +} nghttp2_hd_inflate_flag; + +/** + * @function + * + * .. warning:: + * + * Deprecated. Use `nghttp2_hd_inflate_hd2()` instead. + * + * Inflates name/value block stored in |in| with length |inlen|. This + * function performs decompression. For each successful emission of * header name/value pair, * :enum:`nghttp2_hd_inflate_flag.NGHTTP2_HD_INFLATE_EMIT` is set in - * |*inflate_flags| and name/value pair is assigned to the |nv_out| - * and the function returns. The caller must not free the members of - * |nv_out|. - * - * The |nv_out| may include pointers to the memory region in the |in|. - * The caller must retain the |in| while the |nv_out| is used. - * - * The application should call this function repeatedly until the - * ``(*inflate_flags) & NGHTTP2_HD_INFLATE_FINAL`` is nonzero and - * return value is non-negative. This means the all input values are - * processed successfully. Then the application must call - * `nghttp2_hd_inflate_end_headers()` to prepare for the next header - * block input. - * - * The caller can feed complete compressed header block. It also can - * feed it in several chunks. The caller must set |in_final| to - * nonzero if the given input is the last block of the compressed - * header. - * - * This function returns the number of bytes processed if it succeeds, - * or one of the following negative error codes: - * + * |*inflate_flags| and name/value pair is assigned to the |nv_out| + * and the function returns. The caller must not free the members of + * |nv_out|. + * + * The |nv_out| may include pointers to the memory region in the |in|. + * The caller must retain the |in| while the |nv_out| is used. + * + * The application should call this function repeatedly until the + * ``(*inflate_flags) & NGHTTP2_HD_INFLATE_FINAL`` is nonzero and + * return value is non-negative. This means the all input values are + * processed successfully. Then the application must call + * `nghttp2_hd_inflate_end_headers()` to prepare for the next header + * block input. + * + * The caller can feed complete compressed header block. It also can + * feed it in several chunks. The caller must set |in_final| to + * nonzero if the given input is the last block of the compressed + * header. + * + * This function returns the number of bytes processed if it succeeds, + * or one of the following negative error codes: + * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` - * Out of memory. + * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP` - * Inflation process has failed. + * Inflation process has failed. * :enum:`nghttp2_error.NGHTTP2_ERR_BUFFER_ERROR` - * The header field name or value is too large. - * - * Example follows:: - * - * int inflate_header_block(nghttp2_hd_inflater *hd_inflater, - * uint8_t *in, size_t inlen, int final) - * { - * ssize_t rv; - * - * for(;;) { - * nghttp2_nv nv; - * int inflate_flags = 0; - * - * rv = nghttp2_hd_inflate_hd(hd_inflater, &nv, &inflate_flags, - * in, inlen, final); - * - * if(rv < 0) { - * fprintf(stderr, "inflate failed with error code %zd", rv); - * return -1; - * } - * - * in += rv; - * inlen -= rv; - * - * if(inflate_flags & NGHTTP2_HD_INFLATE_EMIT) { - * fwrite(nv.name, nv.namelen, 1, stderr); - * fprintf(stderr, ": "); - * fwrite(nv.value, nv.valuelen, 1, stderr); - * fprintf(stderr, "\n"); - * } - * if(inflate_flags & NGHTTP2_HD_INFLATE_FINAL) { - * nghttp2_hd_inflate_end_headers(hd_inflater); - * break; - * } - * if((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 && - * inlen == 0) { - * break; - * } - * } - * - * return 0; - * } - * - */ -NGHTTP2_EXTERN ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, - nghttp2_nv *nv_out, - int *inflate_flags, uint8_t *in, - size_t inlen, int in_final); - -/** - * @function - * - * Inflates name/value block stored in |in| with length |inlen|. This - * function performs decompression. For each successful emission of + * The header field name or value is too large. + * + * Example follows:: + * + * int inflate_header_block(nghttp2_hd_inflater *hd_inflater, + * uint8_t *in, size_t inlen, int final) + * { + * ssize_t rv; + * + * for(;;) { + * nghttp2_nv nv; + * int inflate_flags = 0; + * + * rv = nghttp2_hd_inflate_hd(hd_inflater, &nv, &inflate_flags, + * in, inlen, final); + * + * if(rv < 0) { + * fprintf(stderr, "inflate failed with error code %zd", rv); + * return -1; + * } + * + * in += rv; + * inlen -= rv; + * + * if(inflate_flags & NGHTTP2_HD_INFLATE_EMIT) { + * fwrite(nv.name, nv.namelen, 1, stderr); + * fprintf(stderr, ": "); + * fwrite(nv.value, nv.valuelen, 1, stderr); + * fprintf(stderr, "\n"); + * } + * if(inflate_flags & NGHTTP2_HD_INFLATE_FINAL) { + * nghttp2_hd_inflate_end_headers(hd_inflater); + * break; + * } + * if((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 && + * inlen == 0) { + * break; + * } + * } + * + * return 0; + * } + * + */ +NGHTTP2_EXTERN ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, + nghttp2_nv *nv_out, + int *inflate_flags, uint8_t *in, + size_t inlen, int in_final); + +/** + * @function + * + * Inflates name/value block stored in |in| with length |inlen|. This + * function performs decompression. For each successful emission of * header name/value pair, * :enum:`nghttp2_hd_inflate_flag.NGHTTP2_HD_INFLATE_EMIT` is set in - * |*inflate_flags| and name/value pair is assigned to the |nv_out| - * and the function returns. The caller must not free the members of - * |nv_out|. - * - * The |nv_out| may include pointers to the memory region in the |in|. - * The caller must retain the |in| while the |nv_out| is used. - * - * The application should call this function repeatedly until the - * ``(*inflate_flags) & NGHTTP2_HD_INFLATE_FINAL`` is nonzero and - * return value is non-negative. If that happens, all given input - * data (|inlen| bytes) are processed successfully. Then the - * application must call `nghttp2_hd_inflate_end_headers()` to prepare - * for the next header block input. - * - * In other words, if |in_final| is nonzero, and this function returns + * |*inflate_flags| and name/value pair is assigned to the |nv_out| + * and the function returns. The caller must not free the members of + * |nv_out|. + * + * The |nv_out| may include pointers to the memory region in the |in|. + * The caller must retain the |in| while the |nv_out| is used. + * + * The application should call this function repeatedly until the + * ``(*inflate_flags) & NGHTTP2_HD_INFLATE_FINAL`` is nonzero and + * return value is non-negative. If that happens, all given input + * data (|inlen| bytes) are processed successfully. Then the + * application must call `nghttp2_hd_inflate_end_headers()` to prepare + * for the next header block input. + * + * In other words, if |in_final| is nonzero, and this function returns * |inlen|, you can assert that * :enum:`nghttp2_hd_inflate_final.NGHTTP2_HD_INFLATE_FINAL` is set in * |*inflate_flags|. - * - * The caller can feed complete compressed header block. It also can - * feed it in several chunks. The caller must set |in_final| to - * nonzero if the given input is the last block of the compressed - * header. - * - * This function returns the number of bytes processed if it succeeds, - * or one of the following negative error codes: - * + * + * The caller can feed complete compressed header block. It also can + * feed it in several chunks. The caller must set |in_final| to + * nonzero if the given input is the last block of the compressed + * header. + * + * This function returns the number of bytes processed if it succeeds, + * or one of the following negative error codes: + * * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` - * Out of memory. + * Out of memory. * :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP` - * Inflation process has failed. + * Inflation process has failed. * :enum:`nghttp2_error.NGHTTP2_ERR_BUFFER_ERROR` - * The header field name or value is too large. - * - * Example follows:: - * - * int inflate_header_block(nghttp2_hd_inflater *hd_inflater, - * uint8_t *in, size_t inlen, int final) - * { - * ssize_t rv; - * - * for(;;) { - * nghttp2_nv nv; - * int inflate_flags = 0; - * - * rv = nghttp2_hd_inflate_hd2(hd_inflater, &nv, &inflate_flags, - * in, inlen, final); - * - * if(rv < 0) { - * fprintf(stderr, "inflate failed with error code %zd", rv); - * return -1; - * } - * - * in += rv; - * inlen -= rv; - * - * if(inflate_flags & NGHTTP2_HD_INFLATE_EMIT) { - * fwrite(nv.name, nv.namelen, 1, stderr); - * fprintf(stderr, ": "); - * fwrite(nv.value, nv.valuelen, 1, stderr); - * fprintf(stderr, "\n"); - * } - * if(inflate_flags & NGHTTP2_HD_INFLATE_FINAL) { - * nghttp2_hd_inflate_end_headers(hd_inflater); - * break; - * } - * if((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 && - * inlen == 0) { - * break; - * } - * } - * - * return 0; - * } - * - */ -NGHTTP2_EXTERN ssize_t nghttp2_hd_inflate_hd2(nghttp2_hd_inflater *inflater, - nghttp2_nv *nv_out, - int *inflate_flags, - const uint8_t *in, size_t inlen, - int in_final); - -/** - * @function - * - * Signals the end of decompression for one header block. - * - * This function returns 0 if it succeeds. Currently this function - * always succeeds. - */ -NGHTTP2_EXTERN int -nghttp2_hd_inflate_end_headers(nghttp2_hd_inflater *inflater); - -/** - * @function - * - * Returns the number of entries that header table of |inflater| - * contains. This is the sum of the number of static table and - * dynamic table, so the return value is at least 61. - */ -NGHTTP2_EXTERN -size_t nghttp2_hd_inflate_get_num_table_entries(nghttp2_hd_inflater *inflater); - -/** - * @function - * - * Returns the table entry denoted by |idx| from header table of - * |inflater|. The |idx| is 1-based, and idx=1 returns first entry of - * static table. idx=62 returns first entry of dynamic table if it - * exists. Specifying idx=0 is error, and this function returns NULL. - * If |idx| is strictly greater than the number of entries the tables - * contain, this function returns NULL. - */ -NGHTTP2_EXTERN -const nghttp2_nv * -nghttp2_hd_inflate_get_table_entry(nghttp2_hd_inflater *inflater, size_t idx); - -/** - * @function - * - * Returns the used dynamic table size, including the overhead 32 - * bytes per entry described in RFC 7541. - */ -NGHTTP2_EXTERN -size_t nghttp2_hd_inflate_get_dynamic_table_size(nghttp2_hd_inflater *inflater); - -/** - * @function - * - * Returns the maximum dynamic table size. - */ -NGHTTP2_EXTERN -size_t -nghttp2_hd_inflate_get_max_dynamic_table_size(nghttp2_hd_inflater *inflater); - -struct nghttp2_stream; - -/** - * @struct - * - * The structure to represent HTTP/2 stream. The details of this - * structure are intentionally hidden from the public API. - */ -typedef struct nghttp2_stream nghttp2_stream; - -/** - * @function - * - * Returns pointer to :type:`nghttp2_stream` object denoted by - * |stream_id|. If stream was not found, returns NULL. - * - * Returns imaginary root stream (see - * `nghttp2_session_get_root_stream()`) if 0 is given in |stream_id|. - * - * Unless |stream_id| == 0, the returned pointer is valid until next - * call of `nghttp2_session_send()`, `nghttp2_session_mem_send()`, - * `nghttp2_session_recv()`, and `nghttp2_session_mem_recv()`. - */ -NGHTTP2_EXTERN nghttp2_stream * -nghttp2_session_find_stream(nghttp2_session *session, int32_t stream_id); - -/** - * @enum - * - * State of stream as described in RFC 7540. - */ -typedef enum { - /** - * idle state. - */ - NGHTTP2_STREAM_STATE_IDLE = 1, - /** - * open state. - */ - NGHTTP2_STREAM_STATE_OPEN, - /** - * reserved (local) state. - */ - NGHTTP2_STREAM_STATE_RESERVED_LOCAL, - /** - * reserved (remote) state. - */ - NGHTTP2_STREAM_STATE_RESERVED_REMOTE, - /** - * half closed (local) state. - */ - NGHTTP2_STREAM_STATE_HALF_CLOSED_LOCAL, - /** - * half closed (remote) state. - */ - NGHTTP2_STREAM_STATE_HALF_CLOSED_REMOTE, - /** - * closed state. - */ - NGHTTP2_STREAM_STATE_CLOSED -} nghttp2_stream_proto_state; - -/** - * @function - * - * Returns state of |stream|. The root stream retrieved by - * `nghttp2_session_get_root_stream()` will have stream state + * The header field name or value is too large. + * + * Example follows:: + * + * int inflate_header_block(nghttp2_hd_inflater *hd_inflater, + * uint8_t *in, size_t inlen, int final) + * { + * ssize_t rv; + * + * for(;;) { + * nghttp2_nv nv; + * int inflate_flags = 0; + * + * rv = nghttp2_hd_inflate_hd2(hd_inflater, &nv, &inflate_flags, + * in, inlen, final); + * + * if(rv < 0) { + * fprintf(stderr, "inflate failed with error code %zd", rv); + * return -1; + * } + * + * in += rv; + * inlen -= rv; + * + * if(inflate_flags & NGHTTP2_HD_INFLATE_EMIT) { + * fwrite(nv.name, nv.namelen, 1, stderr); + * fprintf(stderr, ": "); + * fwrite(nv.value, nv.valuelen, 1, stderr); + * fprintf(stderr, "\n"); + * } + * if(inflate_flags & NGHTTP2_HD_INFLATE_FINAL) { + * nghttp2_hd_inflate_end_headers(hd_inflater); + * break; + * } + * if((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 && + * inlen == 0) { + * break; + * } + * } + * + * return 0; + * } + * + */ +NGHTTP2_EXTERN ssize_t nghttp2_hd_inflate_hd2(nghttp2_hd_inflater *inflater, + nghttp2_nv *nv_out, + int *inflate_flags, + const uint8_t *in, size_t inlen, + int in_final); + +/** + * @function + * + * Signals the end of decompression for one header block. + * + * This function returns 0 if it succeeds. Currently this function + * always succeeds. + */ +NGHTTP2_EXTERN int +nghttp2_hd_inflate_end_headers(nghttp2_hd_inflater *inflater); + +/** + * @function + * + * Returns the number of entries that header table of |inflater| + * contains. This is the sum of the number of static table and + * dynamic table, so the return value is at least 61. + */ +NGHTTP2_EXTERN +size_t nghttp2_hd_inflate_get_num_table_entries(nghttp2_hd_inflater *inflater); + +/** + * @function + * + * Returns the table entry denoted by |idx| from header table of + * |inflater|. The |idx| is 1-based, and idx=1 returns first entry of + * static table. idx=62 returns first entry of dynamic table if it + * exists. Specifying idx=0 is error, and this function returns NULL. + * If |idx| is strictly greater than the number of entries the tables + * contain, this function returns NULL. + */ +NGHTTP2_EXTERN +const nghttp2_nv * +nghttp2_hd_inflate_get_table_entry(nghttp2_hd_inflater *inflater, size_t idx); + +/** + * @function + * + * Returns the used dynamic table size, including the overhead 32 + * bytes per entry described in RFC 7541. + */ +NGHTTP2_EXTERN +size_t nghttp2_hd_inflate_get_dynamic_table_size(nghttp2_hd_inflater *inflater); + +/** + * @function + * + * Returns the maximum dynamic table size. + */ +NGHTTP2_EXTERN +size_t +nghttp2_hd_inflate_get_max_dynamic_table_size(nghttp2_hd_inflater *inflater); + +struct nghttp2_stream; + +/** + * @struct + * + * The structure to represent HTTP/2 stream. The details of this + * structure are intentionally hidden from the public API. + */ +typedef struct nghttp2_stream nghttp2_stream; + +/** + * @function + * + * Returns pointer to :type:`nghttp2_stream` object denoted by + * |stream_id|. If stream was not found, returns NULL. + * + * Returns imaginary root stream (see + * `nghttp2_session_get_root_stream()`) if 0 is given in |stream_id|. + * + * Unless |stream_id| == 0, the returned pointer is valid until next + * call of `nghttp2_session_send()`, `nghttp2_session_mem_send()`, + * `nghttp2_session_recv()`, and `nghttp2_session_mem_recv()`. + */ +NGHTTP2_EXTERN nghttp2_stream * +nghttp2_session_find_stream(nghttp2_session *session, int32_t stream_id); + +/** + * @enum + * + * State of stream as described in RFC 7540. + */ +typedef enum { + /** + * idle state. + */ + NGHTTP2_STREAM_STATE_IDLE = 1, + /** + * open state. + */ + NGHTTP2_STREAM_STATE_OPEN, + /** + * reserved (local) state. + */ + NGHTTP2_STREAM_STATE_RESERVED_LOCAL, + /** + * reserved (remote) state. + */ + NGHTTP2_STREAM_STATE_RESERVED_REMOTE, + /** + * half closed (local) state. + */ + NGHTTP2_STREAM_STATE_HALF_CLOSED_LOCAL, + /** + * half closed (remote) state. + */ + NGHTTP2_STREAM_STATE_HALF_CLOSED_REMOTE, + /** + * closed state. + */ + NGHTTP2_STREAM_STATE_CLOSED +} nghttp2_stream_proto_state; + +/** + * @function + * + * Returns state of |stream|. The root stream retrieved by + * `nghttp2_session_get_root_stream()` will have stream state * :enum:`nghttp2_stream_proto_state.NGHTTP2_STREAM_STATE_IDLE`. - */ -NGHTTP2_EXTERN nghttp2_stream_proto_state -nghttp2_stream_get_state(nghttp2_stream *stream); - -/** - * @function - * - * Returns root of dependency tree, which is imaginary stream with - * stream ID 0. The returned pointer is valid until |session| is - * freed by `nghttp2_session_del()`. - */ -NGHTTP2_EXTERN nghttp2_stream * -nghttp2_session_get_root_stream(nghttp2_session *session); - -/** - * @function - * - * Returns the parent stream of |stream| in dependency tree. Returns - * NULL if there is no such stream. - */ -NGHTTP2_EXTERN nghttp2_stream * -nghttp2_stream_get_parent(nghttp2_stream *stream); - -NGHTTP2_EXTERN int32_t nghttp2_stream_get_stream_id(nghttp2_stream *stream); - -/** - * @function - * - * Returns the next sibling stream of |stream| in dependency tree. - * Returns NULL if there is no such stream. - */ -NGHTTP2_EXTERN nghttp2_stream * -nghttp2_stream_get_next_sibling(nghttp2_stream *stream); - -/** - * @function - * - * Returns the previous sibling stream of |stream| in dependency tree. - * Returns NULL if there is no such stream. - */ -NGHTTP2_EXTERN nghttp2_stream * -nghttp2_stream_get_previous_sibling(nghttp2_stream *stream); - -/** - * @function - * - * Returns the first child stream of |stream| in dependency tree. - * Returns NULL if there is no such stream. - */ -NGHTTP2_EXTERN nghttp2_stream * -nghttp2_stream_get_first_child(nghttp2_stream *stream); - -/** - * @function - * - * Returns dependency weight to the parent stream of |stream|. - */ -NGHTTP2_EXTERN int32_t nghttp2_stream_get_weight(nghttp2_stream *stream); - -/** - * @function - * - * Returns the sum of the weight for |stream|'s children. - */ -NGHTTP2_EXTERN int32_t -nghttp2_stream_get_sum_dependency_weight(nghttp2_stream *stream); - -/** - * @functypedef - * - * Callback function invoked when the library outputs debug logging. - * The function is called with arguments suitable for ``vfprintf(3)`` - * - * The debug output is only enabled if the library is built with - * ``DEBUGBUILD`` macro defined. - */ -typedef void (*nghttp2_debug_vprintf_callback)(const char *format, - va_list args); - -/** - * @function - * - * Sets a debug output callback called by the library when built with - * ``DEBUGBUILD`` macro defined. If this option is not used, debug - * log is written into standard error output. - * - * For builds without ``DEBUGBUILD`` macro defined, this function is - * noop. - * - * Note that building with ``DEBUGBUILD`` may cause significant - * performance penalty to libnghttp2 because of extra processing. It - * should be used for debugging purpose only. - * - * .. Warning:: - * - * Building with ``DEBUGBUILD`` may cause significant performance - * penalty to libnghttp2 because of extra processing. It should be - * used for debugging purpose only. We write this two times because - * this is important. - */ -NGHTTP2_EXTERN void nghttp2_set_debug_vprintf_callback( - nghttp2_debug_vprintf_callback debug_vprintf_callback); - -#ifdef __cplusplus -} -#endif - -#endif /* NGHTTP2_H */ + */ +NGHTTP2_EXTERN nghttp2_stream_proto_state +nghttp2_stream_get_state(nghttp2_stream *stream); + +/** + * @function + * + * Returns root of dependency tree, which is imaginary stream with + * stream ID 0. The returned pointer is valid until |session| is + * freed by `nghttp2_session_del()`. + */ +NGHTTP2_EXTERN nghttp2_stream * +nghttp2_session_get_root_stream(nghttp2_session *session); + +/** + * @function + * + * Returns the parent stream of |stream| in dependency tree. Returns + * NULL if there is no such stream. + */ +NGHTTP2_EXTERN nghttp2_stream * +nghttp2_stream_get_parent(nghttp2_stream *stream); + +NGHTTP2_EXTERN int32_t nghttp2_stream_get_stream_id(nghttp2_stream *stream); + +/** + * @function + * + * Returns the next sibling stream of |stream| in dependency tree. + * Returns NULL if there is no such stream. + */ +NGHTTP2_EXTERN nghttp2_stream * +nghttp2_stream_get_next_sibling(nghttp2_stream *stream); + +/** + * @function + * + * Returns the previous sibling stream of |stream| in dependency tree. + * Returns NULL if there is no such stream. + */ +NGHTTP2_EXTERN nghttp2_stream * +nghttp2_stream_get_previous_sibling(nghttp2_stream *stream); + +/** + * @function + * + * Returns the first child stream of |stream| in dependency tree. + * Returns NULL if there is no such stream. + */ +NGHTTP2_EXTERN nghttp2_stream * +nghttp2_stream_get_first_child(nghttp2_stream *stream); + +/** + * @function + * + * Returns dependency weight to the parent stream of |stream|. + */ +NGHTTP2_EXTERN int32_t nghttp2_stream_get_weight(nghttp2_stream *stream); + +/** + * @function + * + * Returns the sum of the weight for |stream|'s children. + */ +NGHTTP2_EXTERN int32_t +nghttp2_stream_get_sum_dependency_weight(nghttp2_stream *stream); + +/** + * @functypedef + * + * Callback function invoked when the library outputs debug logging. + * The function is called with arguments suitable for ``vfprintf(3)`` + * + * The debug output is only enabled if the library is built with + * ``DEBUGBUILD`` macro defined. + */ +typedef void (*nghttp2_debug_vprintf_callback)(const char *format, + va_list args); + +/** + * @function + * + * Sets a debug output callback called by the library when built with + * ``DEBUGBUILD`` macro defined. If this option is not used, debug + * log is written into standard error output. + * + * For builds without ``DEBUGBUILD`` macro defined, this function is + * noop. + * + * Note that building with ``DEBUGBUILD`` may cause significant + * performance penalty to libnghttp2 because of extra processing. It + * should be used for debugging purpose only. + * + * .. Warning:: + * + * Building with ``DEBUGBUILD`` may cause significant performance + * penalty to libnghttp2 because of extra processing. It should be + * used for debugging purpose only. We write this two times because + * this is important. + */ +NGHTTP2_EXTERN void nghttp2_set_debug_vprintf_callback( + nghttp2_debug_vprintf_callback debug_vprintf_callback); + +#ifdef __cplusplus +} +#endif + +#endif /* NGHTTP2_H */ diff --git a/contrib/libs/nghttp2/lib/includes/nghttp2/nghttp2ver.h b/contrib/libs/nghttp2/lib/includes/nghttp2/nghttp2ver.h index bb83e3e2b3..0b909b4451 100644 --- a/contrib/libs/nghttp2/lib/includes/nghttp2/nghttp2ver.h +++ b/contrib/libs/nghttp2/lib/includes/nghttp2/nghttp2ver.h @@ -1,42 +1,42 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa - * - * 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 NGHTTP2VER_H -#define NGHTTP2VER_H - -/** - * @macro - * Version number of the nghttp2 library release - */ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa + * + * 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 NGHTTP2VER_H +#define NGHTTP2VER_H + +/** + * @macro + * Version number of the nghttp2 library release + */ #define NGHTTP2_VERSION "1.46.0" - -/** - * @macro - * Numerical representation of the version number of the nghttp2 library - * 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. - */ + +/** + * @macro + * Numerical representation of the version number of the nghttp2 library + * 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 0x012e00 - -#endif /* NGHTTP2VER_H */ + +#endif /* NGHTTP2VER_H */ diff --git a/contrib/libs/nghttp2/lib/nghttp2_buf.c b/contrib/libs/nghttp2/lib/nghttp2_buf.c index 8ee5063d83..a32844712e 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_buf.c +++ b/contrib/libs/nghttp2/lib/nghttp2_buf.c @@ -1,527 +1,527 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * 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_buf.h" - -#include <stdio.h> - -#include "nghttp2_helper.h" -#include "nghttp2_debug.h" - -void nghttp2_buf_init(nghttp2_buf *buf) { - buf->begin = NULL; - buf->end = NULL; - buf->pos = NULL; - buf->last = NULL; - buf->mark = NULL; -} - -int nghttp2_buf_init2(nghttp2_buf *buf, size_t initial, nghttp2_mem *mem) { - nghttp2_buf_init(buf); - return nghttp2_buf_reserve(buf, initial, mem); -} - -void nghttp2_buf_free(nghttp2_buf *buf, nghttp2_mem *mem) { - if (buf == NULL) { - return; - } - - nghttp2_mem_free(mem, buf->begin); - buf->begin = NULL; -} - -int nghttp2_buf_reserve(nghttp2_buf *buf, size_t new_cap, nghttp2_mem *mem) { - uint8_t *ptr; - size_t cap; - - cap = nghttp2_buf_cap(buf); - - if (cap >= new_cap) { - return 0; - } - - new_cap = nghttp2_max(new_cap, cap * 2); - - ptr = nghttp2_mem_realloc(mem, buf->begin, new_cap); - if (ptr == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - buf->pos = ptr + (buf->pos - buf->begin); - buf->last = ptr + (buf->last - buf->begin); - buf->mark = ptr + (buf->mark - buf->begin); - buf->begin = ptr; - buf->end = ptr + new_cap; - - return 0; -} - -void nghttp2_buf_reset(nghttp2_buf *buf) { - buf->pos = buf->last = buf->mark = buf->begin; -} - -void nghttp2_buf_wrap_init(nghttp2_buf *buf, uint8_t *begin, size_t len) { +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * 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_buf.h" + +#include <stdio.h> + +#include "nghttp2_helper.h" +#include "nghttp2_debug.h" + +void nghttp2_buf_init(nghttp2_buf *buf) { + buf->begin = NULL; + buf->end = NULL; + buf->pos = NULL; + buf->last = NULL; + buf->mark = NULL; +} + +int nghttp2_buf_init2(nghttp2_buf *buf, size_t initial, nghttp2_mem *mem) { + nghttp2_buf_init(buf); + return nghttp2_buf_reserve(buf, initial, mem); +} + +void nghttp2_buf_free(nghttp2_buf *buf, nghttp2_mem *mem) { + if (buf == NULL) { + return; + } + + nghttp2_mem_free(mem, buf->begin); + buf->begin = NULL; +} + +int nghttp2_buf_reserve(nghttp2_buf *buf, size_t new_cap, nghttp2_mem *mem) { + uint8_t *ptr; + size_t cap; + + cap = nghttp2_buf_cap(buf); + + if (cap >= new_cap) { + return 0; + } + + new_cap = nghttp2_max(new_cap, cap * 2); + + ptr = nghttp2_mem_realloc(mem, buf->begin, new_cap); + if (ptr == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + buf->pos = ptr + (buf->pos - buf->begin); + buf->last = ptr + (buf->last - buf->begin); + buf->mark = ptr + (buf->mark - buf->begin); + buf->begin = ptr; + buf->end = ptr + new_cap; + + return 0; +} + +void nghttp2_buf_reset(nghttp2_buf *buf) { + buf->pos = buf->last = buf->mark = buf->begin; +} + +void nghttp2_buf_wrap_init(nghttp2_buf *buf, uint8_t *begin, size_t len) { buf->begin = buf->pos = buf->last = buf->mark = buf->end = begin; if (len) { buf->end += len; } -} - -static int buf_chain_new(nghttp2_buf_chain **chain, size_t chunk_length, - nghttp2_mem *mem) { - int rv; - - *chain = nghttp2_mem_malloc(mem, sizeof(nghttp2_buf_chain)); - if (*chain == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - (*chain)->next = NULL; - - rv = nghttp2_buf_init2(&(*chain)->buf, chunk_length, mem); - if (rv != 0) { - nghttp2_mem_free(mem, *chain); - return NGHTTP2_ERR_NOMEM; - } - - return 0; -} - -static void buf_chain_del(nghttp2_buf_chain *chain, nghttp2_mem *mem) { - nghttp2_buf_free(&chain->buf, mem); - nghttp2_mem_free(mem, chain); -} - -int nghttp2_bufs_init(nghttp2_bufs *bufs, size_t chunk_length, size_t max_chunk, - nghttp2_mem *mem) { - return nghttp2_bufs_init2(bufs, chunk_length, max_chunk, 0, mem); -} - -int nghttp2_bufs_init2(nghttp2_bufs *bufs, size_t chunk_length, - size_t max_chunk, size_t offset, nghttp2_mem *mem) { - return nghttp2_bufs_init3(bufs, chunk_length, max_chunk, max_chunk, offset, - mem); -} - -int nghttp2_bufs_init3(nghttp2_bufs *bufs, size_t chunk_length, - size_t max_chunk, size_t chunk_keep, size_t offset, - nghttp2_mem *mem) { - int rv; - nghttp2_buf_chain *chain; - - if (chunk_keep == 0 || max_chunk < chunk_keep || chunk_length < offset) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - - rv = buf_chain_new(&chain, chunk_length, mem); - if (rv != 0) { - return rv; - } - - bufs->mem = mem; - bufs->offset = offset; - - bufs->head = chain; - bufs->cur = bufs->head; - - nghttp2_buf_shift_right(&bufs->cur->buf, offset); - - bufs->chunk_length = chunk_length; - bufs->chunk_used = 1; - bufs->max_chunk = max_chunk; - bufs->chunk_keep = chunk_keep; - - return 0; -} - -int nghttp2_bufs_realloc(nghttp2_bufs *bufs, size_t chunk_length) { - int rv; - nghttp2_buf_chain *chain; - - if (chunk_length < bufs->offset) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - - rv = buf_chain_new(&chain, chunk_length, bufs->mem); - if (rv != 0) { - return rv; - } - - nghttp2_bufs_free(bufs); - - bufs->head = chain; - bufs->cur = bufs->head; - - nghttp2_buf_shift_right(&bufs->cur->buf, bufs->offset); - - bufs->chunk_length = chunk_length; - bufs->chunk_used = 1; - - return 0; -} - -void nghttp2_bufs_free(nghttp2_bufs *bufs) { - nghttp2_buf_chain *chain, *next_chain; - - if (bufs == NULL) { - return; - } - - for (chain = bufs->head; chain;) { - next_chain = chain->next; - - buf_chain_del(chain, bufs->mem); - - chain = next_chain; - } - - bufs->head = NULL; -} - -int nghttp2_bufs_wrap_init(nghttp2_bufs *bufs, uint8_t *begin, size_t len, - nghttp2_mem *mem) { - nghttp2_buf_chain *chain; - - chain = nghttp2_mem_malloc(mem, sizeof(nghttp2_buf_chain)); - if (chain == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - chain->next = NULL; - - nghttp2_buf_wrap_init(&chain->buf, begin, len); - - bufs->mem = mem; - bufs->offset = 0; - - bufs->head = chain; - bufs->cur = bufs->head; - - bufs->chunk_length = len; - bufs->chunk_used = 1; - bufs->max_chunk = 1; - bufs->chunk_keep = 1; - - return 0; -} - -int nghttp2_bufs_wrap_init2(nghttp2_bufs *bufs, const nghttp2_vec *vec, - size_t veclen, nghttp2_mem *mem) { - size_t i = 0; - nghttp2_buf_chain *cur_chain; - nghttp2_buf_chain *head_chain; - nghttp2_buf_chain **dst_chain = &head_chain; - - if (veclen == 0) { - return nghttp2_bufs_wrap_init(bufs, NULL, 0, mem); - } - - head_chain = nghttp2_mem_malloc(mem, sizeof(nghttp2_buf_chain) * veclen); - if (head_chain == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - for (i = 0; i < veclen; ++i) { - cur_chain = &head_chain[i]; - cur_chain->next = NULL; - nghttp2_buf_wrap_init(&cur_chain->buf, vec[i].base, vec[i].len); - - *dst_chain = cur_chain; - dst_chain = &cur_chain->next; - } - - bufs->mem = mem; - bufs->offset = 0; - - bufs->head = head_chain; - bufs->cur = bufs->head; - - /* We don't use chunk_length since no allocation is expected. */ - bufs->chunk_length = 0; - bufs->chunk_used = veclen; - bufs->max_chunk = veclen; - bufs->chunk_keep = veclen; - - return 0; -} - -void nghttp2_bufs_wrap_free(nghttp2_bufs *bufs) { - if (bufs == NULL) { - return; - } - - if (bufs->head) { - nghttp2_mem_free(bufs->mem, bufs->head); - } -} - -void nghttp2_bufs_seek_last_present(nghttp2_bufs *bufs) { - nghttp2_buf_chain *ci; - - for (ci = bufs->cur; ci; ci = ci->next) { - if (nghttp2_buf_len(&ci->buf) == 0) { - return; - } else { - bufs->cur = ci; - } - } -} - -size_t nghttp2_bufs_len(nghttp2_bufs *bufs) { - nghttp2_buf_chain *ci; - size_t len; - - len = 0; - for (ci = bufs->head; ci; ci = ci->next) { - len += nghttp2_buf_len(&ci->buf); - } - - return len; -} - -static int bufs_alloc_chain(nghttp2_bufs *bufs) { - int rv; - nghttp2_buf_chain *chain; - - if (bufs->cur->next) { - bufs->cur = bufs->cur->next; - - return 0; - } - - if (bufs->max_chunk == bufs->chunk_used) { - return NGHTTP2_ERR_BUFFER_ERROR; - } - - rv = buf_chain_new(&chain, bufs->chunk_length, bufs->mem); - if (rv != 0) { - return rv; - } - - DEBUGF("new buffer %zu bytes allocated for bufs %p, used %zu\n", - bufs->chunk_length, bufs, bufs->chunk_used); - - ++bufs->chunk_used; - - bufs->cur->next = chain; - bufs->cur = chain; - - nghttp2_buf_shift_right(&bufs->cur->buf, bufs->offset); - - return 0; -} - -int nghttp2_bufs_add(nghttp2_bufs *bufs, const void *data, size_t len) { - int rv; - size_t nwrite; - nghttp2_buf *buf; - const uint8_t *p; - - p = data; - - while (len) { - buf = &bufs->cur->buf; - - nwrite = nghttp2_min(nghttp2_buf_avail(buf), len); - if (nwrite == 0) { - rv = bufs_alloc_chain(bufs); - if (rv != 0) { - return rv; - } - continue; - } - - buf->last = nghttp2_cpymem(buf->last, p, nwrite); - p += nwrite; - len -= nwrite; - } - - return 0; -} - -static int bufs_ensure_addb(nghttp2_bufs *bufs) { - int rv; - nghttp2_buf *buf; - - buf = &bufs->cur->buf; - - if (nghttp2_buf_avail(buf) > 0) { - return 0; - } - - rv = bufs_alloc_chain(bufs); - if (rv != 0) { - return rv; - } - - return 0; -} - -int nghttp2_bufs_addb(nghttp2_bufs *bufs, uint8_t b) { - int rv; - - rv = bufs_ensure_addb(bufs); - if (rv != 0) { - return rv; - } - - *bufs->cur->buf.last++ = b; - - return 0; -} - -int nghttp2_bufs_addb_hold(nghttp2_bufs *bufs, uint8_t b) { - int rv; - - rv = bufs_ensure_addb(bufs); - if (rv != 0) { - return rv; - } - - *bufs->cur->buf.last = b; - - return 0; -} - -int nghttp2_bufs_orb(nghttp2_bufs *bufs, uint8_t b) { - int rv; - - rv = bufs_ensure_addb(bufs); - if (rv != 0) { - return rv; - } - - *bufs->cur->buf.last++ |= b; - - return 0; -} - -int nghttp2_bufs_orb_hold(nghttp2_bufs *bufs, uint8_t b) { - int rv; - - rv = bufs_ensure_addb(bufs); - if (rv != 0) { - return rv; - } - - *bufs->cur->buf.last |= b; - - return 0; -} - -ssize_t nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out) { - size_t len; - nghttp2_buf_chain *chain; - nghttp2_buf *buf; - uint8_t *res; - nghttp2_buf resbuf; - - len = 0; - - for (chain = bufs->head; chain; chain = chain->next) { - len += nghttp2_buf_len(&chain->buf); - } - - if (len == 0) { - res = NULL; - return 0; - } - - res = nghttp2_mem_malloc(bufs->mem, len); - if (res == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - nghttp2_buf_wrap_init(&resbuf, res, len); - - for (chain = bufs->head; chain; chain = chain->next) { - buf = &chain->buf; - resbuf.last = nghttp2_cpymem(resbuf.last, buf->pos, nghttp2_buf_len(buf)); - } - - *out = res; - - return (ssize_t)len; -} - -size_t nghttp2_bufs_remove_copy(nghttp2_bufs *bufs, uint8_t *out) { - size_t len; - nghttp2_buf_chain *chain; - nghttp2_buf *buf; - nghttp2_buf resbuf; - - len = nghttp2_bufs_len(bufs); - - nghttp2_buf_wrap_init(&resbuf, out, len); - - for (chain = bufs->head; chain; chain = chain->next) { - buf = &chain->buf; - resbuf.last = nghttp2_cpymem(resbuf.last, buf->pos, nghttp2_buf_len(buf)); - } - - return len; -} - -void nghttp2_bufs_reset(nghttp2_bufs *bufs) { - nghttp2_buf_chain *chain, *ci; - size_t k; - - k = bufs->chunk_keep; - - for (ci = bufs->head; ci; ci = ci->next) { - nghttp2_buf_reset(&ci->buf); - nghttp2_buf_shift_right(&ci->buf, bufs->offset); - - if (--k == 0) { - break; - } - } - - if (ci) { - chain = ci->next; - ci->next = NULL; - - for (ci = chain; ci;) { - chain = ci->next; - - buf_chain_del(ci, bufs->mem); - - ci = chain; - } - - bufs->chunk_used = bufs->chunk_keep; - } - - bufs->cur = bufs->head; -} - -int nghttp2_bufs_advance(nghttp2_bufs *bufs) { return bufs_alloc_chain(bufs); } - -int nghttp2_bufs_next_present(nghttp2_bufs *bufs) { - nghttp2_buf_chain *chain; - - chain = bufs->cur->next; - - return chain && nghttp2_buf_len(&chain->buf); -} +} + +static int buf_chain_new(nghttp2_buf_chain **chain, size_t chunk_length, + nghttp2_mem *mem) { + int rv; + + *chain = nghttp2_mem_malloc(mem, sizeof(nghttp2_buf_chain)); + if (*chain == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + (*chain)->next = NULL; + + rv = nghttp2_buf_init2(&(*chain)->buf, chunk_length, mem); + if (rv != 0) { + nghttp2_mem_free(mem, *chain); + return NGHTTP2_ERR_NOMEM; + } + + return 0; +} + +static void buf_chain_del(nghttp2_buf_chain *chain, nghttp2_mem *mem) { + nghttp2_buf_free(&chain->buf, mem); + nghttp2_mem_free(mem, chain); +} + +int nghttp2_bufs_init(nghttp2_bufs *bufs, size_t chunk_length, size_t max_chunk, + nghttp2_mem *mem) { + return nghttp2_bufs_init2(bufs, chunk_length, max_chunk, 0, mem); +} + +int nghttp2_bufs_init2(nghttp2_bufs *bufs, size_t chunk_length, + size_t max_chunk, size_t offset, nghttp2_mem *mem) { + return nghttp2_bufs_init3(bufs, chunk_length, max_chunk, max_chunk, offset, + mem); +} + +int nghttp2_bufs_init3(nghttp2_bufs *bufs, size_t chunk_length, + size_t max_chunk, size_t chunk_keep, size_t offset, + nghttp2_mem *mem) { + int rv; + nghttp2_buf_chain *chain; + + if (chunk_keep == 0 || max_chunk < chunk_keep || chunk_length < offset) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + rv = buf_chain_new(&chain, chunk_length, mem); + if (rv != 0) { + return rv; + } + + bufs->mem = mem; + bufs->offset = offset; + + bufs->head = chain; + bufs->cur = bufs->head; + + nghttp2_buf_shift_right(&bufs->cur->buf, offset); + + bufs->chunk_length = chunk_length; + bufs->chunk_used = 1; + bufs->max_chunk = max_chunk; + bufs->chunk_keep = chunk_keep; + + return 0; +} + +int nghttp2_bufs_realloc(nghttp2_bufs *bufs, size_t chunk_length) { + int rv; + nghttp2_buf_chain *chain; + + if (chunk_length < bufs->offset) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + rv = buf_chain_new(&chain, chunk_length, bufs->mem); + if (rv != 0) { + return rv; + } + + nghttp2_bufs_free(bufs); + + bufs->head = chain; + bufs->cur = bufs->head; + + nghttp2_buf_shift_right(&bufs->cur->buf, bufs->offset); + + bufs->chunk_length = chunk_length; + bufs->chunk_used = 1; + + return 0; +} + +void nghttp2_bufs_free(nghttp2_bufs *bufs) { + nghttp2_buf_chain *chain, *next_chain; + + if (bufs == NULL) { + return; + } + + for (chain = bufs->head; chain;) { + next_chain = chain->next; + + buf_chain_del(chain, bufs->mem); + + chain = next_chain; + } + + bufs->head = NULL; +} + +int nghttp2_bufs_wrap_init(nghttp2_bufs *bufs, uint8_t *begin, size_t len, + nghttp2_mem *mem) { + nghttp2_buf_chain *chain; + + chain = nghttp2_mem_malloc(mem, sizeof(nghttp2_buf_chain)); + if (chain == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + chain->next = NULL; + + nghttp2_buf_wrap_init(&chain->buf, begin, len); + + bufs->mem = mem; + bufs->offset = 0; + + bufs->head = chain; + bufs->cur = bufs->head; + + bufs->chunk_length = len; + bufs->chunk_used = 1; + bufs->max_chunk = 1; + bufs->chunk_keep = 1; + + return 0; +} + +int nghttp2_bufs_wrap_init2(nghttp2_bufs *bufs, const nghttp2_vec *vec, + size_t veclen, nghttp2_mem *mem) { + size_t i = 0; + nghttp2_buf_chain *cur_chain; + nghttp2_buf_chain *head_chain; + nghttp2_buf_chain **dst_chain = &head_chain; + + if (veclen == 0) { + return nghttp2_bufs_wrap_init(bufs, NULL, 0, mem); + } + + head_chain = nghttp2_mem_malloc(mem, sizeof(nghttp2_buf_chain) * veclen); + if (head_chain == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + for (i = 0; i < veclen; ++i) { + cur_chain = &head_chain[i]; + cur_chain->next = NULL; + nghttp2_buf_wrap_init(&cur_chain->buf, vec[i].base, vec[i].len); + + *dst_chain = cur_chain; + dst_chain = &cur_chain->next; + } + + bufs->mem = mem; + bufs->offset = 0; + + bufs->head = head_chain; + bufs->cur = bufs->head; + + /* We don't use chunk_length since no allocation is expected. */ + bufs->chunk_length = 0; + bufs->chunk_used = veclen; + bufs->max_chunk = veclen; + bufs->chunk_keep = veclen; + + return 0; +} + +void nghttp2_bufs_wrap_free(nghttp2_bufs *bufs) { + if (bufs == NULL) { + return; + } + + if (bufs->head) { + nghttp2_mem_free(bufs->mem, bufs->head); + } +} + +void nghttp2_bufs_seek_last_present(nghttp2_bufs *bufs) { + nghttp2_buf_chain *ci; + + for (ci = bufs->cur; ci; ci = ci->next) { + if (nghttp2_buf_len(&ci->buf) == 0) { + return; + } else { + bufs->cur = ci; + } + } +} + +size_t nghttp2_bufs_len(nghttp2_bufs *bufs) { + nghttp2_buf_chain *ci; + size_t len; + + len = 0; + for (ci = bufs->head; ci; ci = ci->next) { + len += nghttp2_buf_len(&ci->buf); + } + + return len; +} + +static int bufs_alloc_chain(nghttp2_bufs *bufs) { + int rv; + nghttp2_buf_chain *chain; + + if (bufs->cur->next) { + bufs->cur = bufs->cur->next; + + return 0; + } + + if (bufs->max_chunk == bufs->chunk_used) { + return NGHTTP2_ERR_BUFFER_ERROR; + } + + rv = buf_chain_new(&chain, bufs->chunk_length, bufs->mem); + if (rv != 0) { + return rv; + } + + DEBUGF("new buffer %zu bytes allocated for bufs %p, used %zu\n", + bufs->chunk_length, bufs, bufs->chunk_used); + + ++bufs->chunk_used; + + bufs->cur->next = chain; + bufs->cur = chain; + + nghttp2_buf_shift_right(&bufs->cur->buf, bufs->offset); + + return 0; +} + +int nghttp2_bufs_add(nghttp2_bufs *bufs, const void *data, size_t len) { + int rv; + size_t nwrite; + nghttp2_buf *buf; + const uint8_t *p; + + p = data; + + while (len) { + buf = &bufs->cur->buf; + + nwrite = nghttp2_min(nghttp2_buf_avail(buf), len); + if (nwrite == 0) { + rv = bufs_alloc_chain(bufs); + if (rv != 0) { + return rv; + } + continue; + } + + buf->last = nghttp2_cpymem(buf->last, p, nwrite); + p += nwrite; + len -= nwrite; + } + + return 0; +} + +static int bufs_ensure_addb(nghttp2_bufs *bufs) { + int rv; + nghttp2_buf *buf; + + buf = &bufs->cur->buf; + + if (nghttp2_buf_avail(buf) > 0) { + return 0; + } + + rv = bufs_alloc_chain(bufs); + if (rv != 0) { + return rv; + } + + return 0; +} + +int nghttp2_bufs_addb(nghttp2_bufs *bufs, uint8_t b) { + int rv; + + rv = bufs_ensure_addb(bufs); + if (rv != 0) { + return rv; + } + + *bufs->cur->buf.last++ = b; + + return 0; +} + +int nghttp2_bufs_addb_hold(nghttp2_bufs *bufs, uint8_t b) { + int rv; + + rv = bufs_ensure_addb(bufs); + if (rv != 0) { + return rv; + } + + *bufs->cur->buf.last = b; + + return 0; +} + +int nghttp2_bufs_orb(nghttp2_bufs *bufs, uint8_t b) { + int rv; + + rv = bufs_ensure_addb(bufs); + if (rv != 0) { + return rv; + } + + *bufs->cur->buf.last++ |= b; + + return 0; +} + +int nghttp2_bufs_orb_hold(nghttp2_bufs *bufs, uint8_t b) { + int rv; + + rv = bufs_ensure_addb(bufs); + if (rv != 0) { + return rv; + } + + *bufs->cur->buf.last |= b; + + return 0; +} + +ssize_t nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out) { + size_t len; + nghttp2_buf_chain *chain; + nghttp2_buf *buf; + uint8_t *res; + nghttp2_buf resbuf; + + len = 0; + + for (chain = bufs->head; chain; chain = chain->next) { + len += nghttp2_buf_len(&chain->buf); + } + + if (len == 0) { + res = NULL; + return 0; + } + + res = nghttp2_mem_malloc(bufs->mem, len); + if (res == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + nghttp2_buf_wrap_init(&resbuf, res, len); + + for (chain = bufs->head; chain; chain = chain->next) { + buf = &chain->buf; + resbuf.last = nghttp2_cpymem(resbuf.last, buf->pos, nghttp2_buf_len(buf)); + } + + *out = res; + + return (ssize_t)len; +} + +size_t nghttp2_bufs_remove_copy(nghttp2_bufs *bufs, uint8_t *out) { + size_t len; + nghttp2_buf_chain *chain; + nghttp2_buf *buf; + nghttp2_buf resbuf; + + len = nghttp2_bufs_len(bufs); + + nghttp2_buf_wrap_init(&resbuf, out, len); + + for (chain = bufs->head; chain; chain = chain->next) { + buf = &chain->buf; + resbuf.last = nghttp2_cpymem(resbuf.last, buf->pos, nghttp2_buf_len(buf)); + } + + return len; +} + +void nghttp2_bufs_reset(nghttp2_bufs *bufs) { + nghttp2_buf_chain *chain, *ci; + size_t k; + + k = bufs->chunk_keep; + + for (ci = bufs->head; ci; ci = ci->next) { + nghttp2_buf_reset(&ci->buf); + nghttp2_buf_shift_right(&ci->buf, bufs->offset); + + if (--k == 0) { + break; + } + } + + if (ci) { + chain = ci->next; + ci->next = NULL; + + for (ci = chain; ci;) { + chain = ci->next; + + buf_chain_del(ci, bufs->mem); + + ci = chain; + } + + bufs->chunk_used = bufs->chunk_keep; + } + + bufs->cur = bufs->head; +} + +int nghttp2_bufs_advance(nghttp2_bufs *bufs) { return bufs_alloc_chain(bufs); } + +int nghttp2_bufs_next_present(nghttp2_bufs *bufs) { + nghttp2_buf_chain *chain; + + chain = bufs->cur->next; + + return chain && nghttp2_buf_len(&chain->buf); +} diff --git a/contrib/libs/nghttp2/lib/nghttp2_buf.h b/contrib/libs/nghttp2/lib/nghttp2_buf.h index 6233732da3..06cce67a11 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_buf.h +++ b/contrib/libs/nghttp2/lib/nghttp2_buf.h @@ -1,412 +1,412 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * 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_BUF_H -#define NGHTTP2_BUF_H - -#ifdef HAVE_CONFIG_H +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * 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_BUF_H +#define NGHTTP2_BUF_H + +#ifdef HAVE_CONFIG_H # include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <nghttp2/nghttp2.h> - -#include "nghttp2_int.h" -#include "nghttp2_mem.h" - -typedef struct { - /* This points to the beginning of the buffer. The effective range - of buffer is [begin, end). */ - uint8_t *begin; - /* This points to the memory one byte beyond the end of the - buffer. */ - uint8_t *end; - /* The position indicator for effective start of the buffer. pos <= - last must be hold. */ - uint8_t *pos; - /* The position indicator for effective one beyond of the end of the - buffer. last <= end must be hold. */ - uint8_t *last; - /* Mark arbitrary position in buffer [begin, end) */ - uint8_t *mark; -} nghttp2_buf; - -#define nghttp2_buf_len(BUF) ((size_t)((BUF)->last - (BUF)->pos)) -#define nghttp2_buf_avail(BUF) ((size_t)((BUF)->end - (BUF)->last)) -#define nghttp2_buf_mark_avail(BUF) ((size_t)((BUF)->mark - (BUF)->last)) -#define nghttp2_buf_cap(BUF) ((size_t)((BUF)->end - (BUF)->begin)) - -#define nghttp2_buf_pos_offset(BUF) ((size_t)((BUF)->pos - (BUF)->begin)) -#define nghttp2_buf_last_offset(BUF) ((size_t)((BUF)->last - (BUF)->begin)) - -#define nghttp2_buf_shift_right(BUF, AMT) \ - do { \ - (BUF)->pos += AMT; \ - (BUF)->last += AMT; \ - } while (0) - -#define nghttp2_buf_shift_left(BUF, AMT) \ - do { \ - (BUF)->pos -= AMT; \ - (BUF)->last -= AMT; \ - } while (0) - -/* - * Initializes the |buf|. No memory is allocated in this function. Use - * nghttp2_buf_reserve() to allocate memory. - */ -void nghttp2_buf_init(nghttp2_buf *buf); - -/* - * Initializes the |buf| and allocates at least |initial| bytes of - * memory. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory - */ -int nghttp2_buf_init2(nghttp2_buf *buf, size_t initial, nghttp2_mem *mem); - -/* - * Frees buffer in |buf|. - */ -void nghttp2_buf_free(nghttp2_buf *buf, nghttp2_mem *mem); - -/* - * Extends buffer so that nghttp2_buf_cap() returns at least - * |new_cap|. If extensions took place, buffer pointers in |buf| will - * change. - * - * This function returns 0 if it succeeds, or one of the followings - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory - */ -int nghttp2_buf_reserve(nghttp2_buf *buf, size_t new_cap, nghttp2_mem *mem); - -/* - * Resets pos, last, mark member of |buf| to buf->begin. - */ -void nghttp2_buf_reset(nghttp2_buf *buf); - -/* - * Initializes |buf| using supplied buffer |begin| of length - * |len|. Semantically, the application should not call *_reserve() or - * nghttp2_free() functions for |buf|. - */ -void nghttp2_buf_wrap_init(nghttp2_buf *buf, uint8_t *begin, size_t len); - -struct nghttp2_buf_chain; - -typedef struct nghttp2_buf_chain nghttp2_buf_chain; - -/* Chains 2 buffers */ -struct nghttp2_buf_chain { - /* Points to the subsequent buffer. NULL if there is no such - buffer. */ - nghttp2_buf_chain *next; - nghttp2_buf buf; -}; - -typedef struct { - /* Points to the first buffer */ - nghttp2_buf_chain *head; - /* Buffer pointer where write occurs. */ - nghttp2_buf_chain *cur; - /* Memory allocator */ - nghttp2_mem *mem; - /* The buffer capacity of each buf. This field may be 0 if - nghttp2_bufs is initialized by nghttp2_bufs_wrap_init* family - functions. */ - size_t chunk_length; - /* The maximum number of nghttp2_buf_chain */ - size_t max_chunk; - /* The number of nghttp2_buf_chain allocated */ - size_t chunk_used; - /* The number of nghttp2_buf_chain to keep on reset */ - size_t chunk_keep; - /* pos offset from begin in each buffers. On initialization and - reset, buf->pos and buf->last are positioned at buf->begin + - offset. */ - size_t offset; -} nghttp2_bufs; - -/* - * This is the same as calling nghttp2_bufs_init2 with the given - * arguments and offset = 0. - */ -int nghttp2_bufs_init(nghttp2_bufs *bufs, size_t chunk_length, size_t max_chunk, - nghttp2_mem *mem); - -/* - * This is the same as calling nghttp2_bufs_init3 with the given - * arguments and chunk_keep = max_chunk. - */ -int nghttp2_bufs_init2(nghttp2_bufs *bufs, size_t chunk_length, - size_t max_chunk, size_t offset, nghttp2_mem *mem); - -/* - * Initializes |bufs|. Each buffer size is given in the - * |chunk_length|. The maximum number of buffers is given in the - * |max_chunk|. On reset, first |chunk_keep| buffers are kept and - * remaining buffers are deleted. Each buffer will have bufs->pos and - * bufs->last shifted to left by |offset| bytes on creation and reset. - * - * This function allocates first buffer. bufs->head and bufs->cur - * will point to the first buffer after this call. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - * NGHTTP2_ERR_INVALID_ARGUMENT - * chunk_keep is 0; or max_chunk < chunk_keep; or offset is too - * long. - */ -int nghttp2_bufs_init3(nghttp2_bufs *bufs, size_t chunk_length, - size_t max_chunk, size_t chunk_keep, size_t offset, - nghttp2_mem *mem); - -/* - * Frees any related resources to the |bufs|. - */ -void nghttp2_bufs_free(nghttp2_bufs *bufs); - -/* - * Initializes |bufs| using supplied buffer |begin| of length |len|. - * The first buffer bufs->head uses buffer |begin|. The buffer size - * is fixed and no extra chunk buffer is allocated. In other - * words, max_chunk = chunk_keep = 1. To free the resource allocated - * for |bufs|, use nghttp2_bufs_wrap_free(). - * - * Don't use the function which performs allocation, such as - * nghttp2_bufs_realloc(). - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -int nghttp2_bufs_wrap_init(nghttp2_bufs *bufs, uint8_t *begin, size_t len, - nghttp2_mem *mem); - -/* - * Initializes |bufs| using supplied |veclen| size of buf vector - * |vec|. The number of buffers is fixed and no extra chunk buffer is - * allocated. In other words, max_chunk = chunk_keep = |in_len|. To - * free the resource allocated for |bufs|, use - * nghttp2_bufs_wrap_free(). - * - * Don't use the function which performs allocation, such as - * nghttp2_bufs_realloc(). - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -int nghttp2_bufs_wrap_init2(nghttp2_bufs *bufs, const nghttp2_vec *vec, - size_t veclen, nghttp2_mem *mem); - -/* - * Frees any related resource to the |bufs|. This function does not - * free supplied buffer provided in nghttp2_bufs_wrap_init(). - */ -void nghttp2_bufs_wrap_free(nghttp2_bufs *bufs); - -/* - * Reallocates internal buffer using |chunk_length|. The max_chunk, - * chunk_keep and offset do not change. After successful allocation - * of new buffer, previous buffers are deallocated without copying - * anything into new buffers. chunk_used is reset to 1. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - * NGHTTP2_ERR_INVALID_ARGUMENT - * chunk_length < offset - */ -int nghttp2_bufs_realloc(nghttp2_bufs *bufs, size_t chunk_length); - -/* - * Appends the |data| of length |len| to the |bufs|. The write starts - * at bufs->cur->buf.last. A new buffers will be allocated to store - * all data. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - * NGHTTP2_ERR_BUFFER_ERROR - * Out of buffer space. - */ -int nghttp2_bufs_add(nghttp2_bufs *bufs, const void *data, size_t len); - -/* - * Appends a single byte |b| to the |bufs|. The write starts at - * bufs->cur->buf.last. A new buffers will be allocated to store all - * data. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - * NGHTTP2_ERR_BUFFER_ERROR - * Out of buffer space. - */ -int nghttp2_bufs_addb(nghttp2_bufs *bufs, uint8_t b); - -/* - * Behaves like nghttp2_bufs_addb(), but this does not update - * buf->last pointer. - */ -int nghttp2_bufs_addb_hold(nghttp2_bufs *bufs, uint8_t b); - -#define nghttp2_bufs_fast_addb(BUFS, B) \ - do { \ - *(BUFS)->cur->buf.last++ = B; \ - } while (0) - -#define nghttp2_bufs_fast_addb_hold(BUFS, B) \ - do { \ - *(BUFS)->cur->buf.last = B; \ - } while (0) - -/* - * Performs bitwise-OR of |b| at bufs->cur->buf.last. A new buffers - * will be allocated if necessary. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - * NGHTTP2_ERR_BUFFER_ERROR - * Out of buffer space. - */ -int nghttp2_bufs_orb(nghttp2_bufs *bufs, uint8_t b); - -/* - * Behaves like nghttp2_bufs_orb(), but does not update buf->last - * pointer. - */ -int nghttp2_bufs_orb_hold(nghttp2_bufs *bufs, uint8_t b); - -#define nghttp2_bufs_fast_orb(BUFS, B) \ - do { \ - uint8_t **p = &(BUFS)->cur->buf.last; \ - **p = (uint8_t)(**p | (B)); \ - ++(*p); \ - } while (0) - -#define nghttp2_bufs_fast_orb_hold(BUFS, B) \ - do { \ - uint8_t *p = (BUFS)->cur->buf.last; \ - *p = (uint8_t)(*p | (B)); \ - } while (0) - -/* - * Copies all data stored in |bufs| to the contiguous buffer. This - * function allocates the contiguous memory to store all data in - * |bufs| and assigns it to |*out|. - * - * The contents of |bufs| is left unchanged. - * - * This function returns the length of copied data and assigns the - * pointer to copied data to |*out| if it succeeds, or one of the - * following negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory - */ -ssize_t nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out); - -/* - * Copies all data stored in |bufs| to |out|. This function assumes - * that the buffer space pointed by |out| has at least - * nghttp2_bufs(bufs) bytes. - * - * The contents of |bufs| is left unchanged. - * - * This function returns the length of copied data. - */ -size_t nghttp2_bufs_remove_copy(nghttp2_bufs *bufs, uint8_t *out); - -/* - * Resets |bufs| and makes the buffers empty. - */ -void nghttp2_bufs_reset(nghttp2_bufs *bufs); - -/* - * Moves bufs->cur to bufs->cur->next. If resulting bufs->cur is - * NULL, this function allocates new buffers and bufs->cur points to - * it. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory - * NGHTTP2_ERR_BUFFER_ERROR - * Out of buffer space. - */ -int nghttp2_bufs_advance(nghttp2_bufs *bufs); - -/* Sets bufs->cur to bufs->head */ -#define nghttp2_bufs_rewind(BUFS) \ - do { \ - (BUFS)->cur = (BUFS)->head; \ - } while (0) - -/* - * Move bufs->cur, from the current position, using next member, to - * the last buf which has nghttp2_buf_len(buf) > 0 without seeing buf - * which satisfies nghttp2_buf_len(buf) == 0. If - * nghttp2_buf_len(&bufs->cur->buf) == 0 or bufs->cur->next is NULL, - * bufs->cur is unchanged. - */ -void nghttp2_bufs_seek_last_present(nghttp2_bufs *bufs); - -/* - * Returns nonzero if bufs->cur->next is not empty. - */ -int nghttp2_bufs_next_present(nghttp2_bufs *bufs); - -#define nghttp2_bufs_cur_avail(BUFS) nghttp2_buf_avail(&(BUFS)->cur->buf) - -/* - * Returns the total buffer length of |bufs|. - */ -size_t nghttp2_bufs_len(nghttp2_bufs *bufs); - -#endif /* NGHTTP2_BUF_H */ +#endif /* HAVE_CONFIG_H */ + +#include <nghttp2/nghttp2.h> + +#include "nghttp2_int.h" +#include "nghttp2_mem.h" + +typedef struct { + /* This points to the beginning of the buffer. The effective range + of buffer is [begin, end). */ + uint8_t *begin; + /* This points to the memory one byte beyond the end of the + buffer. */ + uint8_t *end; + /* The position indicator for effective start of the buffer. pos <= + last must be hold. */ + uint8_t *pos; + /* The position indicator for effective one beyond of the end of the + buffer. last <= end must be hold. */ + uint8_t *last; + /* Mark arbitrary position in buffer [begin, end) */ + uint8_t *mark; +} nghttp2_buf; + +#define nghttp2_buf_len(BUF) ((size_t)((BUF)->last - (BUF)->pos)) +#define nghttp2_buf_avail(BUF) ((size_t)((BUF)->end - (BUF)->last)) +#define nghttp2_buf_mark_avail(BUF) ((size_t)((BUF)->mark - (BUF)->last)) +#define nghttp2_buf_cap(BUF) ((size_t)((BUF)->end - (BUF)->begin)) + +#define nghttp2_buf_pos_offset(BUF) ((size_t)((BUF)->pos - (BUF)->begin)) +#define nghttp2_buf_last_offset(BUF) ((size_t)((BUF)->last - (BUF)->begin)) + +#define nghttp2_buf_shift_right(BUF, AMT) \ + do { \ + (BUF)->pos += AMT; \ + (BUF)->last += AMT; \ + } while (0) + +#define nghttp2_buf_shift_left(BUF, AMT) \ + do { \ + (BUF)->pos -= AMT; \ + (BUF)->last -= AMT; \ + } while (0) + +/* + * Initializes the |buf|. No memory is allocated in this function. Use + * nghttp2_buf_reserve() to allocate memory. + */ +void nghttp2_buf_init(nghttp2_buf *buf); + +/* + * Initializes the |buf| and allocates at least |initial| bytes of + * memory. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_buf_init2(nghttp2_buf *buf, size_t initial, nghttp2_mem *mem); + +/* + * Frees buffer in |buf|. + */ +void nghttp2_buf_free(nghttp2_buf *buf, nghttp2_mem *mem); + +/* + * Extends buffer so that nghttp2_buf_cap() returns at least + * |new_cap|. If extensions took place, buffer pointers in |buf| will + * change. + * + * This function returns 0 if it succeeds, or one of the followings + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_buf_reserve(nghttp2_buf *buf, size_t new_cap, nghttp2_mem *mem); + +/* + * Resets pos, last, mark member of |buf| to buf->begin. + */ +void nghttp2_buf_reset(nghttp2_buf *buf); + +/* + * Initializes |buf| using supplied buffer |begin| of length + * |len|. Semantically, the application should not call *_reserve() or + * nghttp2_free() functions for |buf|. + */ +void nghttp2_buf_wrap_init(nghttp2_buf *buf, uint8_t *begin, size_t len); + +struct nghttp2_buf_chain; + +typedef struct nghttp2_buf_chain nghttp2_buf_chain; + +/* Chains 2 buffers */ +struct nghttp2_buf_chain { + /* Points to the subsequent buffer. NULL if there is no such + buffer. */ + nghttp2_buf_chain *next; + nghttp2_buf buf; +}; + +typedef struct { + /* Points to the first buffer */ + nghttp2_buf_chain *head; + /* Buffer pointer where write occurs. */ + nghttp2_buf_chain *cur; + /* Memory allocator */ + nghttp2_mem *mem; + /* The buffer capacity of each buf. This field may be 0 if + nghttp2_bufs is initialized by nghttp2_bufs_wrap_init* family + functions. */ + size_t chunk_length; + /* The maximum number of nghttp2_buf_chain */ + size_t max_chunk; + /* The number of nghttp2_buf_chain allocated */ + size_t chunk_used; + /* The number of nghttp2_buf_chain to keep on reset */ + size_t chunk_keep; + /* pos offset from begin in each buffers. On initialization and + reset, buf->pos and buf->last are positioned at buf->begin + + offset. */ + size_t offset; +} nghttp2_bufs; + +/* + * This is the same as calling nghttp2_bufs_init2 with the given + * arguments and offset = 0. + */ +int nghttp2_bufs_init(nghttp2_bufs *bufs, size_t chunk_length, size_t max_chunk, + nghttp2_mem *mem); + +/* + * This is the same as calling nghttp2_bufs_init3 with the given + * arguments and chunk_keep = max_chunk. + */ +int nghttp2_bufs_init2(nghttp2_bufs *bufs, size_t chunk_length, + size_t max_chunk, size_t offset, nghttp2_mem *mem); + +/* + * Initializes |bufs|. Each buffer size is given in the + * |chunk_length|. The maximum number of buffers is given in the + * |max_chunk|. On reset, first |chunk_keep| buffers are kept and + * remaining buffers are deleted. Each buffer will have bufs->pos and + * bufs->last shifted to left by |offset| bytes on creation and reset. + * + * This function allocates first buffer. bufs->head and bufs->cur + * will point to the first buffer after this call. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_INVALID_ARGUMENT + * chunk_keep is 0; or max_chunk < chunk_keep; or offset is too + * long. + */ +int nghttp2_bufs_init3(nghttp2_bufs *bufs, size_t chunk_length, + size_t max_chunk, size_t chunk_keep, size_t offset, + nghttp2_mem *mem); + +/* + * Frees any related resources to the |bufs|. + */ +void nghttp2_bufs_free(nghttp2_bufs *bufs); + +/* + * Initializes |bufs| using supplied buffer |begin| of length |len|. + * The first buffer bufs->head uses buffer |begin|. The buffer size + * is fixed and no extra chunk buffer is allocated. In other + * words, max_chunk = chunk_keep = 1. To free the resource allocated + * for |bufs|, use nghttp2_bufs_wrap_free(). + * + * Don't use the function which performs allocation, such as + * nghttp2_bufs_realloc(). + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_bufs_wrap_init(nghttp2_bufs *bufs, uint8_t *begin, size_t len, + nghttp2_mem *mem); + +/* + * Initializes |bufs| using supplied |veclen| size of buf vector + * |vec|. The number of buffers is fixed and no extra chunk buffer is + * allocated. In other words, max_chunk = chunk_keep = |in_len|. To + * free the resource allocated for |bufs|, use + * nghttp2_bufs_wrap_free(). + * + * Don't use the function which performs allocation, such as + * nghttp2_bufs_realloc(). + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_bufs_wrap_init2(nghttp2_bufs *bufs, const nghttp2_vec *vec, + size_t veclen, nghttp2_mem *mem); + +/* + * Frees any related resource to the |bufs|. This function does not + * free supplied buffer provided in nghttp2_bufs_wrap_init(). + */ +void nghttp2_bufs_wrap_free(nghttp2_bufs *bufs); + +/* + * Reallocates internal buffer using |chunk_length|. The max_chunk, + * chunk_keep and offset do not change. After successful allocation + * of new buffer, previous buffers are deallocated without copying + * anything into new buffers. chunk_used is reset to 1. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_INVALID_ARGUMENT + * chunk_length < offset + */ +int nghttp2_bufs_realloc(nghttp2_bufs *bufs, size_t chunk_length); + +/* + * Appends the |data| of length |len| to the |bufs|. The write starts + * at bufs->cur->buf.last. A new buffers will be allocated to store + * all data. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_BUFFER_ERROR + * Out of buffer space. + */ +int nghttp2_bufs_add(nghttp2_bufs *bufs, const void *data, size_t len); + +/* + * Appends a single byte |b| to the |bufs|. The write starts at + * bufs->cur->buf.last. A new buffers will be allocated to store all + * data. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_BUFFER_ERROR + * Out of buffer space. + */ +int nghttp2_bufs_addb(nghttp2_bufs *bufs, uint8_t b); + +/* + * Behaves like nghttp2_bufs_addb(), but this does not update + * buf->last pointer. + */ +int nghttp2_bufs_addb_hold(nghttp2_bufs *bufs, uint8_t b); + +#define nghttp2_bufs_fast_addb(BUFS, B) \ + do { \ + *(BUFS)->cur->buf.last++ = B; \ + } while (0) + +#define nghttp2_bufs_fast_addb_hold(BUFS, B) \ + do { \ + *(BUFS)->cur->buf.last = B; \ + } while (0) + +/* + * Performs bitwise-OR of |b| at bufs->cur->buf.last. A new buffers + * will be allocated if necessary. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_BUFFER_ERROR + * Out of buffer space. + */ +int nghttp2_bufs_orb(nghttp2_bufs *bufs, uint8_t b); + +/* + * Behaves like nghttp2_bufs_orb(), but does not update buf->last + * pointer. + */ +int nghttp2_bufs_orb_hold(nghttp2_bufs *bufs, uint8_t b); + +#define nghttp2_bufs_fast_orb(BUFS, B) \ + do { \ + uint8_t **p = &(BUFS)->cur->buf.last; \ + **p = (uint8_t)(**p | (B)); \ + ++(*p); \ + } while (0) + +#define nghttp2_bufs_fast_orb_hold(BUFS, B) \ + do { \ + uint8_t *p = (BUFS)->cur->buf.last; \ + *p = (uint8_t)(*p | (B)); \ + } while (0) + +/* + * Copies all data stored in |bufs| to the contiguous buffer. This + * function allocates the contiguous memory to store all data in + * |bufs| and assigns it to |*out|. + * + * The contents of |bufs| is left unchanged. + * + * This function returns the length of copied data and assigns the + * pointer to copied data to |*out| if it succeeds, or one of the + * following negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +ssize_t nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out); + +/* + * Copies all data stored in |bufs| to |out|. This function assumes + * that the buffer space pointed by |out| has at least + * nghttp2_bufs(bufs) bytes. + * + * The contents of |bufs| is left unchanged. + * + * This function returns the length of copied data. + */ +size_t nghttp2_bufs_remove_copy(nghttp2_bufs *bufs, uint8_t *out); + +/* + * Resets |bufs| and makes the buffers empty. + */ +void nghttp2_bufs_reset(nghttp2_bufs *bufs); + +/* + * Moves bufs->cur to bufs->cur->next. If resulting bufs->cur is + * NULL, this function allocates new buffers and bufs->cur points to + * it. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + * NGHTTP2_ERR_BUFFER_ERROR + * Out of buffer space. + */ +int nghttp2_bufs_advance(nghttp2_bufs *bufs); + +/* Sets bufs->cur to bufs->head */ +#define nghttp2_bufs_rewind(BUFS) \ + do { \ + (BUFS)->cur = (BUFS)->head; \ + } while (0) + +/* + * Move bufs->cur, from the current position, using next member, to + * the last buf which has nghttp2_buf_len(buf) > 0 without seeing buf + * which satisfies nghttp2_buf_len(buf) == 0. If + * nghttp2_buf_len(&bufs->cur->buf) == 0 or bufs->cur->next is NULL, + * bufs->cur is unchanged. + */ +void nghttp2_bufs_seek_last_present(nghttp2_bufs *bufs); + +/* + * Returns nonzero if bufs->cur->next is not empty. + */ +int nghttp2_bufs_next_present(nghttp2_bufs *bufs); + +#define nghttp2_bufs_cur_avail(BUFS) nghttp2_buf_avail(&(BUFS)->cur->buf) + +/* + * Returns the total buffer length of |bufs|. + */ +size_t nghttp2_bufs_len(nghttp2_bufs *bufs); + +#endif /* NGHTTP2_BUF_H */ diff --git a/contrib/libs/nghttp2/lib/nghttp2_callbacks.c b/contrib/libs/nghttp2/lib/nghttp2_callbacks.c index 99e22ede7d..3c38214859 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_callbacks.c +++ b/contrib/libs/nghttp2/lib/nghttp2_callbacks.c @@ -1,175 +1,175 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * 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_callbacks.h" - -#include <stdlib.h> - -int nghttp2_session_callbacks_new(nghttp2_session_callbacks **callbacks_ptr) { - *callbacks_ptr = calloc(1, sizeof(nghttp2_session_callbacks)); - - if (*callbacks_ptr == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - return 0; -} - -void nghttp2_session_callbacks_del(nghttp2_session_callbacks *callbacks) { - free(callbacks); -} - -void nghttp2_session_callbacks_set_send_callback( - nghttp2_session_callbacks *cbs, nghttp2_send_callback send_callback) { - cbs->send_callback = send_callback; -} - -void nghttp2_session_callbacks_set_recv_callback( - nghttp2_session_callbacks *cbs, nghttp2_recv_callback recv_callback) { - cbs->recv_callback = recv_callback; -} - -void nghttp2_session_callbacks_set_on_frame_recv_callback( - nghttp2_session_callbacks *cbs, - nghttp2_on_frame_recv_callback on_frame_recv_callback) { - cbs->on_frame_recv_callback = on_frame_recv_callback; -} - -void nghttp2_session_callbacks_set_on_invalid_frame_recv_callback( - nghttp2_session_callbacks *cbs, - nghttp2_on_invalid_frame_recv_callback on_invalid_frame_recv_callback) { - cbs->on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; -} - -void nghttp2_session_callbacks_set_on_data_chunk_recv_callback( - nghttp2_session_callbacks *cbs, - nghttp2_on_data_chunk_recv_callback on_data_chunk_recv_callback) { - cbs->on_data_chunk_recv_callback = on_data_chunk_recv_callback; -} - -void nghttp2_session_callbacks_set_before_frame_send_callback( - nghttp2_session_callbacks *cbs, - nghttp2_before_frame_send_callback before_frame_send_callback) { - cbs->before_frame_send_callback = before_frame_send_callback; -} - -void nghttp2_session_callbacks_set_on_frame_send_callback( - nghttp2_session_callbacks *cbs, - nghttp2_on_frame_send_callback on_frame_send_callback) { - cbs->on_frame_send_callback = on_frame_send_callback; -} - -void nghttp2_session_callbacks_set_on_frame_not_send_callback( - nghttp2_session_callbacks *cbs, - nghttp2_on_frame_not_send_callback on_frame_not_send_callback) { - cbs->on_frame_not_send_callback = on_frame_not_send_callback; -} - -void nghttp2_session_callbacks_set_on_stream_close_callback( - nghttp2_session_callbacks *cbs, - nghttp2_on_stream_close_callback on_stream_close_callback) { - cbs->on_stream_close_callback = on_stream_close_callback; -} - -void nghttp2_session_callbacks_set_on_begin_headers_callback( - nghttp2_session_callbacks *cbs, - nghttp2_on_begin_headers_callback on_begin_headers_callback) { - cbs->on_begin_headers_callback = on_begin_headers_callback; -} - -void nghttp2_session_callbacks_set_on_header_callback( - nghttp2_session_callbacks *cbs, - nghttp2_on_header_callback on_header_callback) { - cbs->on_header_callback = on_header_callback; -} - -void nghttp2_session_callbacks_set_on_header_callback2( - nghttp2_session_callbacks *cbs, - nghttp2_on_header_callback2 on_header_callback2) { - cbs->on_header_callback2 = on_header_callback2; -} - -void nghttp2_session_callbacks_set_on_invalid_header_callback( - nghttp2_session_callbacks *cbs, - nghttp2_on_invalid_header_callback on_invalid_header_callback) { - cbs->on_invalid_header_callback = on_invalid_header_callback; -} - -void nghttp2_session_callbacks_set_on_invalid_header_callback2( - nghttp2_session_callbacks *cbs, - nghttp2_on_invalid_header_callback2 on_invalid_header_callback2) { - cbs->on_invalid_header_callback2 = on_invalid_header_callback2; -} - -void nghttp2_session_callbacks_set_select_padding_callback( - nghttp2_session_callbacks *cbs, - nghttp2_select_padding_callback select_padding_callback) { - cbs->select_padding_callback = select_padding_callback; -} - -void nghttp2_session_callbacks_set_data_source_read_length_callback( - nghttp2_session_callbacks *cbs, - nghttp2_data_source_read_length_callback data_source_read_length_callback) { - cbs->read_length_callback = data_source_read_length_callback; -} - -void nghttp2_session_callbacks_set_on_begin_frame_callback( - nghttp2_session_callbacks *cbs, - nghttp2_on_begin_frame_callback on_begin_frame_callback) { - cbs->on_begin_frame_callback = on_begin_frame_callback; -} - -void nghttp2_session_callbacks_set_send_data_callback( - nghttp2_session_callbacks *cbs, - nghttp2_send_data_callback send_data_callback) { - cbs->send_data_callback = send_data_callback; -} - -void nghttp2_session_callbacks_set_pack_extension_callback( - nghttp2_session_callbacks *cbs, - nghttp2_pack_extension_callback pack_extension_callback) { - cbs->pack_extension_callback = pack_extension_callback; -} - -void nghttp2_session_callbacks_set_unpack_extension_callback( - nghttp2_session_callbacks *cbs, - nghttp2_unpack_extension_callback unpack_extension_callback) { - cbs->unpack_extension_callback = unpack_extension_callback; -} - -void nghttp2_session_callbacks_set_on_extension_chunk_recv_callback( - nghttp2_session_callbacks *cbs, - nghttp2_on_extension_chunk_recv_callback on_extension_chunk_recv_callback) { - cbs->on_extension_chunk_recv_callback = on_extension_chunk_recv_callback; -} - -void nghttp2_session_callbacks_set_error_callback( - nghttp2_session_callbacks *cbs, nghttp2_error_callback error_callback) { - cbs->error_callback = error_callback; -} - -void nghttp2_session_callbacks_set_error_callback2( - nghttp2_session_callbacks *cbs, nghttp2_error_callback2 error_callback2) { - cbs->error_callback2 = error_callback2; -} +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * 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_callbacks.h" + +#include <stdlib.h> + +int nghttp2_session_callbacks_new(nghttp2_session_callbacks **callbacks_ptr) { + *callbacks_ptr = calloc(1, sizeof(nghttp2_session_callbacks)); + + if (*callbacks_ptr == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + return 0; +} + +void nghttp2_session_callbacks_del(nghttp2_session_callbacks *callbacks) { + free(callbacks); +} + +void nghttp2_session_callbacks_set_send_callback( + nghttp2_session_callbacks *cbs, nghttp2_send_callback send_callback) { + cbs->send_callback = send_callback; +} + +void nghttp2_session_callbacks_set_recv_callback( + nghttp2_session_callbacks *cbs, nghttp2_recv_callback recv_callback) { + cbs->recv_callback = recv_callback; +} + +void nghttp2_session_callbacks_set_on_frame_recv_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_frame_recv_callback on_frame_recv_callback) { + cbs->on_frame_recv_callback = on_frame_recv_callback; +} + +void nghttp2_session_callbacks_set_on_invalid_frame_recv_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_invalid_frame_recv_callback on_invalid_frame_recv_callback) { + cbs->on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; +} + +void nghttp2_session_callbacks_set_on_data_chunk_recv_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_data_chunk_recv_callback on_data_chunk_recv_callback) { + cbs->on_data_chunk_recv_callback = on_data_chunk_recv_callback; +} + +void nghttp2_session_callbacks_set_before_frame_send_callback( + nghttp2_session_callbacks *cbs, + nghttp2_before_frame_send_callback before_frame_send_callback) { + cbs->before_frame_send_callback = before_frame_send_callback; +} + +void nghttp2_session_callbacks_set_on_frame_send_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_frame_send_callback on_frame_send_callback) { + cbs->on_frame_send_callback = on_frame_send_callback; +} + +void nghttp2_session_callbacks_set_on_frame_not_send_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_frame_not_send_callback on_frame_not_send_callback) { + cbs->on_frame_not_send_callback = on_frame_not_send_callback; +} + +void nghttp2_session_callbacks_set_on_stream_close_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_stream_close_callback on_stream_close_callback) { + cbs->on_stream_close_callback = on_stream_close_callback; +} + +void nghttp2_session_callbacks_set_on_begin_headers_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_begin_headers_callback on_begin_headers_callback) { + cbs->on_begin_headers_callback = on_begin_headers_callback; +} + +void nghttp2_session_callbacks_set_on_header_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_header_callback on_header_callback) { + cbs->on_header_callback = on_header_callback; +} + +void nghttp2_session_callbacks_set_on_header_callback2( + nghttp2_session_callbacks *cbs, + nghttp2_on_header_callback2 on_header_callback2) { + cbs->on_header_callback2 = on_header_callback2; +} + +void nghttp2_session_callbacks_set_on_invalid_header_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_invalid_header_callback on_invalid_header_callback) { + cbs->on_invalid_header_callback = on_invalid_header_callback; +} + +void nghttp2_session_callbacks_set_on_invalid_header_callback2( + nghttp2_session_callbacks *cbs, + nghttp2_on_invalid_header_callback2 on_invalid_header_callback2) { + cbs->on_invalid_header_callback2 = on_invalid_header_callback2; +} + +void nghttp2_session_callbacks_set_select_padding_callback( + nghttp2_session_callbacks *cbs, + nghttp2_select_padding_callback select_padding_callback) { + cbs->select_padding_callback = select_padding_callback; +} + +void nghttp2_session_callbacks_set_data_source_read_length_callback( + nghttp2_session_callbacks *cbs, + nghttp2_data_source_read_length_callback data_source_read_length_callback) { + cbs->read_length_callback = data_source_read_length_callback; +} + +void nghttp2_session_callbacks_set_on_begin_frame_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_begin_frame_callback on_begin_frame_callback) { + cbs->on_begin_frame_callback = on_begin_frame_callback; +} + +void nghttp2_session_callbacks_set_send_data_callback( + nghttp2_session_callbacks *cbs, + nghttp2_send_data_callback send_data_callback) { + cbs->send_data_callback = send_data_callback; +} + +void nghttp2_session_callbacks_set_pack_extension_callback( + nghttp2_session_callbacks *cbs, + nghttp2_pack_extension_callback pack_extension_callback) { + cbs->pack_extension_callback = pack_extension_callback; +} + +void nghttp2_session_callbacks_set_unpack_extension_callback( + nghttp2_session_callbacks *cbs, + nghttp2_unpack_extension_callback unpack_extension_callback) { + cbs->unpack_extension_callback = unpack_extension_callback; +} + +void nghttp2_session_callbacks_set_on_extension_chunk_recv_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_extension_chunk_recv_callback on_extension_chunk_recv_callback) { + cbs->on_extension_chunk_recv_callback = on_extension_chunk_recv_callback; +} + +void nghttp2_session_callbacks_set_error_callback( + nghttp2_session_callbacks *cbs, nghttp2_error_callback error_callback) { + cbs->error_callback = error_callback; +} + +void nghttp2_session_callbacks_set_error_callback2( + nghttp2_session_callbacks *cbs, nghttp2_error_callback2 error_callback2) { + cbs->error_callback2 = error_callback2; +} diff --git a/contrib/libs/nghttp2/lib/nghttp2_callbacks.h b/contrib/libs/nghttp2/lib/nghttp2_callbacks.h index b78557c8ca..61e51fa536 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_callbacks.h +++ b/contrib/libs/nghttp2/lib/nghttp2_callbacks.h @@ -1,125 +1,125 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * 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_CALLBACKS_H -#define NGHTTP2_CALLBACKS_H - -#ifdef HAVE_CONFIG_H +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * 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_CALLBACKS_H +#define NGHTTP2_CALLBACKS_H + +#ifdef HAVE_CONFIG_H # include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <nghttp2/nghttp2.h> - -/* - * Callback functions. - */ -struct nghttp2_session_callbacks { - /** - * Callback function invoked when the session wants to send data to - * the remote peer. This callback is not necessary if the - * application uses solely `nghttp2_session_mem_send()` to serialize - * data to transmit. - */ - nghttp2_send_callback send_callback; - /** - * Callback function invoked when the session wants to receive data - * from the remote peer. This callback is not necessary if the - * application uses solely `nghttp2_session_mem_recv()` to process - * received data. - */ - nghttp2_recv_callback recv_callback; - /** - * Callback function invoked by `nghttp2_session_recv()` when a - * frame is received. - */ - nghttp2_on_frame_recv_callback on_frame_recv_callback; - /** - * Callback function invoked by `nghttp2_session_recv()` when an - * invalid non-DATA frame is received. - */ - nghttp2_on_invalid_frame_recv_callback on_invalid_frame_recv_callback; - /** - * Callback function invoked when a chunk of data in DATA frame is - * received. - */ - nghttp2_on_data_chunk_recv_callback on_data_chunk_recv_callback; - /** - * Callback function invoked before a non-DATA frame is sent. - */ - nghttp2_before_frame_send_callback before_frame_send_callback; - /** - * Callback function invoked after a frame is sent. - */ - nghttp2_on_frame_send_callback on_frame_send_callback; - /** - * The callback function invoked when a non-DATA frame is not sent - * because of an error. - */ - nghttp2_on_frame_not_send_callback on_frame_not_send_callback; - /** - * Callback function invoked when the stream is closed. - */ - nghttp2_on_stream_close_callback on_stream_close_callback; - /** - * Callback function invoked when the reception of header block in - * HEADERS or PUSH_PROMISE is started. - */ - nghttp2_on_begin_headers_callback on_begin_headers_callback; - /** - * Callback function invoked when a header name/value pair is - * received. - */ - nghttp2_on_header_callback on_header_callback; - nghttp2_on_header_callback2 on_header_callback2; - /** - * Callback function invoked when a invalid header name/value pair - * is received which is silently ignored if these callbacks are not - * set. - */ - nghttp2_on_invalid_header_callback on_invalid_header_callback; - nghttp2_on_invalid_header_callback2 on_invalid_header_callback2; - /** - * Callback function invoked when the library asks application how - * many padding bytes are required for the transmission of the given - * frame. - */ - nghttp2_select_padding_callback select_padding_callback; - /** - * The callback function used to determine the length allowed in - * `nghttp2_data_source_read_callback()` - */ - nghttp2_data_source_read_length_callback read_length_callback; - /** - * Sets callback function invoked when a frame header is received. - */ - nghttp2_on_begin_frame_callback on_begin_frame_callback; - nghttp2_send_data_callback send_data_callback; - nghttp2_pack_extension_callback pack_extension_callback; - nghttp2_unpack_extension_callback unpack_extension_callback; - nghttp2_on_extension_chunk_recv_callback on_extension_chunk_recv_callback; - nghttp2_error_callback error_callback; - nghttp2_error_callback2 error_callback2; -}; - -#endif /* NGHTTP2_CALLBACKS_H */ +#endif /* HAVE_CONFIG_H */ + +#include <nghttp2/nghttp2.h> + +/* + * Callback functions. + */ +struct nghttp2_session_callbacks { + /** + * Callback function invoked when the session wants to send data to + * the remote peer. This callback is not necessary if the + * application uses solely `nghttp2_session_mem_send()` to serialize + * data to transmit. + */ + nghttp2_send_callback send_callback; + /** + * Callback function invoked when the session wants to receive data + * from the remote peer. This callback is not necessary if the + * application uses solely `nghttp2_session_mem_recv()` to process + * received data. + */ + nghttp2_recv_callback recv_callback; + /** + * Callback function invoked by `nghttp2_session_recv()` when a + * frame is received. + */ + nghttp2_on_frame_recv_callback on_frame_recv_callback; + /** + * Callback function invoked by `nghttp2_session_recv()` when an + * invalid non-DATA frame is received. + */ + nghttp2_on_invalid_frame_recv_callback on_invalid_frame_recv_callback; + /** + * Callback function invoked when a chunk of data in DATA frame is + * received. + */ + nghttp2_on_data_chunk_recv_callback on_data_chunk_recv_callback; + /** + * Callback function invoked before a non-DATA frame is sent. + */ + nghttp2_before_frame_send_callback before_frame_send_callback; + /** + * Callback function invoked after a frame is sent. + */ + nghttp2_on_frame_send_callback on_frame_send_callback; + /** + * The callback function invoked when a non-DATA frame is not sent + * because of an error. + */ + nghttp2_on_frame_not_send_callback on_frame_not_send_callback; + /** + * Callback function invoked when the stream is closed. + */ + nghttp2_on_stream_close_callback on_stream_close_callback; + /** + * Callback function invoked when the reception of header block in + * HEADERS or PUSH_PROMISE is started. + */ + nghttp2_on_begin_headers_callback on_begin_headers_callback; + /** + * Callback function invoked when a header name/value pair is + * received. + */ + nghttp2_on_header_callback on_header_callback; + nghttp2_on_header_callback2 on_header_callback2; + /** + * Callback function invoked when a invalid header name/value pair + * is received which is silently ignored if these callbacks are not + * set. + */ + nghttp2_on_invalid_header_callback on_invalid_header_callback; + nghttp2_on_invalid_header_callback2 on_invalid_header_callback2; + /** + * Callback function invoked when the library asks application how + * many padding bytes are required for the transmission of the given + * frame. + */ + nghttp2_select_padding_callback select_padding_callback; + /** + * The callback function used to determine the length allowed in + * `nghttp2_data_source_read_callback()` + */ + nghttp2_data_source_read_length_callback read_length_callback; + /** + * Sets callback function invoked when a frame header is received. + */ + nghttp2_on_begin_frame_callback on_begin_frame_callback; + nghttp2_send_data_callback send_data_callback; + nghttp2_pack_extension_callback pack_extension_callback; + nghttp2_unpack_extension_callback unpack_extension_callback; + nghttp2_on_extension_chunk_recv_callback on_extension_chunk_recv_callback; + nghttp2_error_callback error_callback; + nghttp2_error_callback2 error_callback2; +}; + +#endif /* NGHTTP2_CALLBACKS_H */ diff --git a/contrib/libs/nghttp2/lib/nghttp2_debug.c b/contrib/libs/nghttp2/lib/nghttp2_debug.c index 35310d2823..cb2779700b 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_debug.c +++ b/contrib/libs/nghttp2/lib/nghttp2_debug.c @@ -1,60 +1,60 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2016 Tatsuhiro Tsujikawa - * - * 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_debug.h" - -#include <stdio.h> - -#ifdef DEBUGBUILD - -static void nghttp2_default_debug_vfprintf_callback(const char *fmt, - va_list args) { - vfprintf(stderr, fmt, args); -} - -static nghttp2_debug_vprintf_callback static_debug_vprintf_callback = - nghttp2_default_debug_vfprintf_callback; - -void nghttp2_debug_vprintf(const char *format, ...) { - if (static_debug_vprintf_callback) { - va_list args; - va_start(args, format); - static_debug_vprintf_callback(format, args); - va_end(args); - } -} - -void nghttp2_set_debug_vprintf_callback( - nghttp2_debug_vprintf_callback debug_vprintf_callback) { - static_debug_vprintf_callback = debug_vprintf_callback; -} - -#else /* !DEBUGBUILD */ - -void nghttp2_set_debug_vprintf_callback( - nghttp2_debug_vprintf_callback debug_vprintf_callback) { - (void)debug_vprintf_callback; -} - -#endif /* !DEBUGBUILD */ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2016 Tatsuhiro Tsujikawa + * + * 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_debug.h" + +#include <stdio.h> + +#ifdef DEBUGBUILD + +static void nghttp2_default_debug_vfprintf_callback(const char *fmt, + va_list args) { + vfprintf(stderr, fmt, args); +} + +static nghttp2_debug_vprintf_callback static_debug_vprintf_callback = + nghttp2_default_debug_vfprintf_callback; + +void nghttp2_debug_vprintf(const char *format, ...) { + if (static_debug_vprintf_callback) { + va_list args; + va_start(args, format); + static_debug_vprintf_callback(format, args); + va_end(args); + } +} + +void nghttp2_set_debug_vprintf_callback( + nghttp2_debug_vprintf_callback debug_vprintf_callback) { + static_debug_vprintf_callback = debug_vprintf_callback; +} + +#else /* !DEBUGBUILD */ + +void nghttp2_set_debug_vprintf_callback( + nghttp2_debug_vprintf_callback debug_vprintf_callback) { + (void)debug_vprintf_callback; +} + +#endif /* !DEBUGBUILD */ diff --git a/contrib/libs/nghttp2/lib/nghttp2_debug.h b/contrib/libs/nghttp2/lib/nghttp2_debug.h index 4fc63e82f7..cbb4dd5754 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_debug.h +++ b/contrib/libs/nghttp2/lib/nghttp2_debug.h @@ -1,43 +1,43 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2016 Tatsuhiro Tsujikawa - * - * 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_DEBUG_H -#define NGHTTP2_DEBUG_H - -#ifdef HAVE_CONFIG_H +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2016 Tatsuhiro Tsujikawa + * + * 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_DEBUG_H +#define NGHTTP2_DEBUG_H + +#ifdef HAVE_CONFIG_H # include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <nghttp2/nghttp2.h> - -#ifdef DEBUGBUILD +#endif /* HAVE_CONFIG_H */ + +#include <nghttp2/nghttp2.h> + +#ifdef DEBUGBUILD # define DEBUGF(...) nghttp2_debug_vprintf(__VA_ARGS__) -void nghttp2_debug_vprintf(const char *format, ...); -#else +void nghttp2_debug_vprintf(const char *format, ...); +#else # define DEBUGF(...) \ do { \ } while (0) -#endif - -#endif /* NGHTTP2_DEBUG_H */ +#endif + +#endif /* NGHTTP2_DEBUG_H */ diff --git a/contrib/libs/nghttp2/lib/nghttp2_frame.c b/contrib/libs/nghttp2/lib/nghttp2_frame.c index 38eeae44d6..382a26c818 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_frame.c +++ b/contrib/libs/nghttp2/lib/nghttp2_frame.c @@ -1,228 +1,228 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2013 Tatsuhiro Tsujikawa - * - * 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_frame.h" - -#include <string.h> -#include <assert.h> -#include <stdio.h> -#include <errno.h> - -#include "nghttp2_helper.h" -#include "nghttp2_net.h" -#include "nghttp2_priority_spec.h" -#include "nghttp2_debug.h" - -void nghttp2_frame_pack_frame_hd(uint8_t *buf, const nghttp2_frame_hd *hd) { - nghttp2_put_uint32be(&buf[0], (uint32_t)(hd->length << 8)); - buf[3] = hd->type; - buf[4] = hd->flags; - nghttp2_put_uint32be(&buf[5], (uint32_t)hd->stream_id); - /* ignore hd->reserved for now */ -} - -void nghttp2_frame_unpack_frame_hd(nghttp2_frame_hd *hd, const uint8_t *buf) { - hd->length = nghttp2_get_uint32(&buf[0]) >> 8; - hd->type = buf[3]; - hd->flags = buf[4]; - hd->stream_id = nghttp2_get_uint32(&buf[5]) & NGHTTP2_STREAM_ID_MASK; - hd->reserved = 0; -} - -void nghttp2_frame_hd_init(nghttp2_frame_hd *hd, size_t length, uint8_t type, - uint8_t flags, int32_t stream_id) { - hd->length = length; - hd->type = type; - hd->flags = flags; - hd->stream_id = stream_id; - hd->reserved = 0; -} - -void nghttp2_frame_headers_init(nghttp2_headers *frame, uint8_t flags, - int32_t stream_id, nghttp2_headers_category cat, - const nghttp2_priority_spec *pri_spec, - nghttp2_nv *nva, size_t nvlen) { - nghttp2_frame_hd_init(&frame->hd, 0, NGHTTP2_HEADERS, flags, stream_id); - frame->padlen = 0; - frame->nva = nva; - frame->nvlen = nvlen; - frame->cat = cat; - - if (pri_spec) { - frame->pri_spec = *pri_spec; - } else { - nghttp2_priority_spec_default_init(&frame->pri_spec); - } -} - -void nghttp2_frame_headers_free(nghttp2_headers *frame, nghttp2_mem *mem) { - nghttp2_nv_array_del(frame->nva, mem); -} - -void nghttp2_frame_priority_init(nghttp2_priority *frame, int32_t stream_id, - const nghttp2_priority_spec *pri_spec) { - nghttp2_frame_hd_init(&frame->hd, NGHTTP2_PRIORITY_SPECLEN, NGHTTP2_PRIORITY, - NGHTTP2_FLAG_NONE, stream_id); - frame->pri_spec = *pri_spec; -} - -void nghttp2_frame_priority_free(nghttp2_priority *frame) { (void)frame; } - -void nghttp2_frame_rst_stream_init(nghttp2_rst_stream *frame, int32_t stream_id, - uint32_t error_code) { - nghttp2_frame_hd_init(&frame->hd, 4, NGHTTP2_RST_STREAM, NGHTTP2_FLAG_NONE, - stream_id); - frame->error_code = error_code; -} - -void nghttp2_frame_rst_stream_free(nghttp2_rst_stream *frame) { (void)frame; } - -void nghttp2_frame_settings_init(nghttp2_settings *frame, uint8_t flags, - nghttp2_settings_entry *iv, size_t niv) { - nghttp2_frame_hd_init(&frame->hd, niv * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH, - NGHTTP2_SETTINGS, flags, 0); - frame->niv = niv; - frame->iv = iv; -} - -void nghttp2_frame_settings_free(nghttp2_settings *frame, nghttp2_mem *mem) { - nghttp2_mem_free(mem, frame->iv); -} - -void nghttp2_frame_push_promise_init(nghttp2_push_promise *frame, uint8_t flags, - int32_t stream_id, - int32_t promised_stream_id, - nghttp2_nv *nva, size_t nvlen) { - nghttp2_frame_hd_init(&frame->hd, 0, NGHTTP2_PUSH_PROMISE, flags, stream_id); - frame->padlen = 0; - frame->nva = nva; - frame->nvlen = nvlen; - frame->promised_stream_id = promised_stream_id; - frame->reserved = 0; -} - -void nghttp2_frame_push_promise_free(nghttp2_push_promise *frame, - nghttp2_mem *mem) { - nghttp2_nv_array_del(frame->nva, mem); -} - -void nghttp2_frame_ping_init(nghttp2_ping *frame, uint8_t flags, - const uint8_t *opaque_data) { - nghttp2_frame_hd_init(&frame->hd, 8, NGHTTP2_PING, flags, 0); - if (opaque_data) { - memcpy(frame->opaque_data, opaque_data, sizeof(frame->opaque_data)); - } else { - memset(frame->opaque_data, 0, sizeof(frame->opaque_data)); - } -} - -void nghttp2_frame_ping_free(nghttp2_ping *frame) { (void)frame; } - -void nghttp2_frame_goaway_init(nghttp2_goaway *frame, int32_t last_stream_id, - uint32_t error_code, uint8_t *opaque_data, - size_t opaque_data_len) { - nghttp2_frame_hd_init(&frame->hd, 8 + opaque_data_len, NGHTTP2_GOAWAY, - NGHTTP2_FLAG_NONE, 0); - frame->last_stream_id = last_stream_id; - frame->error_code = error_code; - frame->opaque_data = opaque_data; - frame->opaque_data_len = opaque_data_len; - frame->reserved = 0; -} - -void nghttp2_frame_goaway_free(nghttp2_goaway *frame, nghttp2_mem *mem) { - nghttp2_mem_free(mem, frame->opaque_data); -} - -void nghttp2_frame_window_update_init(nghttp2_window_update *frame, - uint8_t flags, int32_t stream_id, - int32_t window_size_increment) { - nghttp2_frame_hd_init(&frame->hd, 4, NGHTTP2_WINDOW_UPDATE, flags, stream_id); - frame->window_size_increment = window_size_increment; - frame->reserved = 0; -} - -void nghttp2_frame_window_update_free(nghttp2_window_update *frame) { - (void)frame; -} - -size_t nghttp2_frame_trail_padlen(nghttp2_frame *frame, size_t padlen) { - /* We have iframe->padlen == 0, but iframe->frame.hd.flags may have - NGHTTP2_FLAG_PADDED set. This happens when receiving - CONTINUATION frame, since we don't reset flags after HEADERS was - received. */ - if (padlen == 0) { - return 0; - } - return padlen - ((frame->hd.flags & NGHTTP2_FLAG_PADDED) > 0); -} - -void nghttp2_frame_data_init(nghttp2_data *frame, uint8_t flags, - int32_t stream_id) { - /* At this moment, the length of DATA frame is unknown */ - nghttp2_frame_hd_init(&frame->hd, 0, NGHTTP2_DATA, flags, stream_id); - frame->padlen = 0; -} - -void nghttp2_frame_data_free(nghttp2_data *frame) { (void)frame; } - -void nghttp2_frame_extension_init(nghttp2_extension *frame, uint8_t type, - uint8_t flags, int32_t stream_id, - void *payload) { - nghttp2_frame_hd_init(&frame->hd, 0, type, flags, stream_id); - frame->payload = payload; -} - -void nghttp2_frame_extension_free(nghttp2_extension *frame) { (void)frame; } - -void nghttp2_frame_altsvc_init(nghttp2_extension *frame, int32_t stream_id, - uint8_t *origin, size_t origin_len, - uint8_t *field_value, size_t field_value_len) { - nghttp2_ext_altsvc *altsvc; - - nghttp2_frame_hd_init(&frame->hd, 2 + origin_len + field_value_len, - NGHTTP2_ALTSVC, NGHTTP2_FLAG_NONE, stream_id); - - altsvc = frame->payload; - altsvc->origin = origin; - altsvc->origin_len = origin_len; - altsvc->field_value = field_value; - altsvc->field_value_len = field_value_len; -} - -void nghttp2_frame_altsvc_free(nghttp2_extension *frame, nghttp2_mem *mem) { - nghttp2_ext_altsvc *altsvc; - - altsvc = frame->payload; - if (altsvc == NULL) { - return; - } - /* We use the same buffer for altsvc->origin and - altsvc->field_value. */ - nghttp2_mem_free(mem, altsvc->origin); -} - +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * 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_frame.h" + +#include <string.h> +#include <assert.h> +#include <stdio.h> +#include <errno.h> + +#include "nghttp2_helper.h" +#include "nghttp2_net.h" +#include "nghttp2_priority_spec.h" +#include "nghttp2_debug.h" + +void nghttp2_frame_pack_frame_hd(uint8_t *buf, const nghttp2_frame_hd *hd) { + nghttp2_put_uint32be(&buf[0], (uint32_t)(hd->length << 8)); + buf[3] = hd->type; + buf[4] = hd->flags; + nghttp2_put_uint32be(&buf[5], (uint32_t)hd->stream_id); + /* ignore hd->reserved for now */ +} + +void nghttp2_frame_unpack_frame_hd(nghttp2_frame_hd *hd, const uint8_t *buf) { + hd->length = nghttp2_get_uint32(&buf[0]) >> 8; + hd->type = buf[3]; + hd->flags = buf[4]; + hd->stream_id = nghttp2_get_uint32(&buf[5]) & NGHTTP2_STREAM_ID_MASK; + hd->reserved = 0; +} + +void nghttp2_frame_hd_init(nghttp2_frame_hd *hd, size_t length, uint8_t type, + uint8_t flags, int32_t stream_id) { + hd->length = length; + hd->type = type; + hd->flags = flags; + hd->stream_id = stream_id; + hd->reserved = 0; +} + +void nghttp2_frame_headers_init(nghttp2_headers *frame, uint8_t flags, + int32_t stream_id, nghttp2_headers_category cat, + const nghttp2_priority_spec *pri_spec, + nghttp2_nv *nva, size_t nvlen) { + nghttp2_frame_hd_init(&frame->hd, 0, NGHTTP2_HEADERS, flags, stream_id); + frame->padlen = 0; + frame->nva = nva; + frame->nvlen = nvlen; + frame->cat = cat; + + if (pri_spec) { + frame->pri_spec = *pri_spec; + } else { + nghttp2_priority_spec_default_init(&frame->pri_spec); + } +} + +void nghttp2_frame_headers_free(nghttp2_headers *frame, nghttp2_mem *mem) { + nghttp2_nv_array_del(frame->nva, mem); +} + +void nghttp2_frame_priority_init(nghttp2_priority *frame, int32_t stream_id, + const nghttp2_priority_spec *pri_spec) { + nghttp2_frame_hd_init(&frame->hd, NGHTTP2_PRIORITY_SPECLEN, NGHTTP2_PRIORITY, + NGHTTP2_FLAG_NONE, stream_id); + frame->pri_spec = *pri_spec; +} + +void nghttp2_frame_priority_free(nghttp2_priority *frame) { (void)frame; } + +void nghttp2_frame_rst_stream_init(nghttp2_rst_stream *frame, int32_t stream_id, + uint32_t error_code) { + nghttp2_frame_hd_init(&frame->hd, 4, NGHTTP2_RST_STREAM, NGHTTP2_FLAG_NONE, + stream_id); + frame->error_code = error_code; +} + +void nghttp2_frame_rst_stream_free(nghttp2_rst_stream *frame) { (void)frame; } + +void nghttp2_frame_settings_init(nghttp2_settings *frame, uint8_t flags, + nghttp2_settings_entry *iv, size_t niv) { + nghttp2_frame_hd_init(&frame->hd, niv * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH, + NGHTTP2_SETTINGS, flags, 0); + frame->niv = niv; + frame->iv = iv; +} + +void nghttp2_frame_settings_free(nghttp2_settings *frame, nghttp2_mem *mem) { + nghttp2_mem_free(mem, frame->iv); +} + +void nghttp2_frame_push_promise_init(nghttp2_push_promise *frame, uint8_t flags, + int32_t stream_id, + int32_t promised_stream_id, + nghttp2_nv *nva, size_t nvlen) { + nghttp2_frame_hd_init(&frame->hd, 0, NGHTTP2_PUSH_PROMISE, flags, stream_id); + frame->padlen = 0; + frame->nva = nva; + frame->nvlen = nvlen; + frame->promised_stream_id = promised_stream_id; + frame->reserved = 0; +} + +void nghttp2_frame_push_promise_free(nghttp2_push_promise *frame, + nghttp2_mem *mem) { + nghttp2_nv_array_del(frame->nva, mem); +} + +void nghttp2_frame_ping_init(nghttp2_ping *frame, uint8_t flags, + const uint8_t *opaque_data) { + nghttp2_frame_hd_init(&frame->hd, 8, NGHTTP2_PING, flags, 0); + if (opaque_data) { + memcpy(frame->opaque_data, opaque_data, sizeof(frame->opaque_data)); + } else { + memset(frame->opaque_data, 0, sizeof(frame->opaque_data)); + } +} + +void nghttp2_frame_ping_free(nghttp2_ping *frame) { (void)frame; } + +void nghttp2_frame_goaway_init(nghttp2_goaway *frame, int32_t last_stream_id, + uint32_t error_code, uint8_t *opaque_data, + size_t opaque_data_len) { + nghttp2_frame_hd_init(&frame->hd, 8 + opaque_data_len, NGHTTP2_GOAWAY, + NGHTTP2_FLAG_NONE, 0); + frame->last_stream_id = last_stream_id; + frame->error_code = error_code; + frame->opaque_data = opaque_data; + frame->opaque_data_len = opaque_data_len; + frame->reserved = 0; +} + +void nghttp2_frame_goaway_free(nghttp2_goaway *frame, nghttp2_mem *mem) { + nghttp2_mem_free(mem, frame->opaque_data); +} + +void nghttp2_frame_window_update_init(nghttp2_window_update *frame, + uint8_t flags, int32_t stream_id, + int32_t window_size_increment) { + nghttp2_frame_hd_init(&frame->hd, 4, NGHTTP2_WINDOW_UPDATE, flags, stream_id); + frame->window_size_increment = window_size_increment; + frame->reserved = 0; +} + +void nghttp2_frame_window_update_free(nghttp2_window_update *frame) { + (void)frame; +} + +size_t nghttp2_frame_trail_padlen(nghttp2_frame *frame, size_t padlen) { + /* We have iframe->padlen == 0, but iframe->frame.hd.flags may have + NGHTTP2_FLAG_PADDED set. This happens when receiving + CONTINUATION frame, since we don't reset flags after HEADERS was + received. */ + if (padlen == 0) { + return 0; + } + return padlen - ((frame->hd.flags & NGHTTP2_FLAG_PADDED) > 0); +} + +void nghttp2_frame_data_init(nghttp2_data *frame, uint8_t flags, + int32_t stream_id) { + /* At this moment, the length of DATA frame is unknown */ + nghttp2_frame_hd_init(&frame->hd, 0, NGHTTP2_DATA, flags, stream_id); + frame->padlen = 0; +} + +void nghttp2_frame_data_free(nghttp2_data *frame) { (void)frame; } + +void nghttp2_frame_extension_init(nghttp2_extension *frame, uint8_t type, + uint8_t flags, int32_t stream_id, + void *payload) { + nghttp2_frame_hd_init(&frame->hd, 0, type, flags, stream_id); + frame->payload = payload; +} + +void nghttp2_frame_extension_free(nghttp2_extension *frame) { (void)frame; } + +void nghttp2_frame_altsvc_init(nghttp2_extension *frame, int32_t stream_id, + uint8_t *origin, size_t origin_len, + uint8_t *field_value, size_t field_value_len) { + nghttp2_ext_altsvc *altsvc; + + nghttp2_frame_hd_init(&frame->hd, 2 + origin_len + field_value_len, + NGHTTP2_ALTSVC, NGHTTP2_FLAG_NONE, stream_id); + + altsvc = frame->payload; + altsvc->origin = origin; + altsvc->origin_len = origin_len; + altsvc->field_value = field_value; + altsvc->field_value_len = field_value_len; +} + +void nghttp2_frame_altsvc_free(nghttp2_extension *frame, nghttp2_mem *mem) { + nghttp2_ext_altsvc *altsvc; + + altsvc = frame->payload; + if (altsvc == NULL) { + return; + } + /* We use the same buffer for altsvc->origin and + altsvc->field_value. */ + nghttp2_mem_free(mem, altsvc->origin); +} + void nghttp2_frame_origin_init(nghttp2_extension *frame, nghttp2_origin_entry *ov, size_t nov) { nghttp2_ext_origin *origin; @@ -253,529 +253,529 @@ void nghttp2_frame_origin_free(nghttp2_extension *frame, nghttp2_mem *mem) { nghttp2_mem_free(mem, origin->ov); } -size_t nghttp2_frame_priority_len(uint8_t flags) { - if (flags & NGHTTP2_FLAG_PRIORITY) { - return NGHTTP2_PRIORITY_SPECLEN; - } - - return 0; -} - -size_t nghttp2_frame_headers_payload_nv_offset(nghttp2_headers *frame) { - return nghttp2_frame_priority_len(frame->hd.flags); -} - -/* - * Call this function after payload was serialized, but not before - * changing buf->pos and serializing frame header. - * - * This function assumes bufs->cur points to the last buf chain of the - * frame(s). - * - * This function serializes frame header for HEADERS/PUSH_PROMISE and - * handles their successive CONTINUATION frames. - * - * We don't process any padding here. - */ -static int frame_pack_headers_shared(nghttp2_bufs *bufs, - nghttp2_frame_hd *frame_hd) { - nghttp2_buf *buf; - nghttp2_buf_chain *ci, *ce; - nghttp2_frame_hd hd; - - buf = &bufs->head->buf; - - hd = *frame_hd; - hd.length = nghttp2_buf_len(buf); - - DEBUGF("send: HEADERS/PUSH_PROMISE, payloadlen=%zu\n", hd.length); - - /* We have multiple frame buffers, which means one or more - CONTINUATION frame is involved. Remove END_HEADERS flag from the - first frame. */ - if (bufs->head != bufs->cur) { - hd.flags = (uint8_t)(hd.flags & ~NGHTTP2_FLAG_END_HEADERS); - } - - buf->pos -= NGHTTP2_FRAME_HDLEN; - nghttp2_frame_pack_frame_hd(buf->pos, &hd); - - if (bufs->head != bufs->cur) { - /* 2nd and later frames are CONTINUATION frames. */ - hd.type = NGHTTP2_CONTINUATION; - /* We don't have no flags except for last CONTINUATION */ - hd.flags = NGHTTP2_FLAG_NONE; - - ce = bufs->cur; - - for (ci = bufs->head->next; ci != ce; ci = ci->next) { - buf = &ci->buf; - - hd.length = nghttp2_buf_len(buf); - - DEBUGF("send: int CONTINUATION, payloadlen=%zu\n", hd.length); - - buf->pos -= NGHTTP2_FRAME_HDLEN; - nghttp2_frame_pack_frame_hd(buf->pos, &hd); - } - - buf = &ci->buf; - hd.length = nghttp2_buf_len(buf); - /* Set END_HEADERS flag for last CONTINUATION */ - hd.flags = NGHTTP2_FLAG_END_HEADERS; - - DEBUGF("send: last CONTINUATION, payloadlen=%zu\n", hd.length); - - buf->pos -= NGHTTP2_FRAME_HDLEN; - nghttp2_frame_pack_frame_hd(buf->pos, &hd); - } - - return 0; -} - -int nghttp2_frame_pack_headers(nghttp2_bufs *bufs, nghttp2_headers *frame, - nghttp2_hd_deflater *deflater) { - size_t nv_offset; - int rv; - nghttp2_buf *buf; - - assert(bufs->head == bufs->cur); - - nv_offset = nghttp2_frame_headers_payload_nv_offset(frame); - - buf = &bufs->cur->buf; - - buf->pos += nv_offset; - buf->last = buf->pos; - - /* This call will adjust buf->last to the correct position */ - rv = nghttp2_hd_deflate_hd_bufs(deflater, bufs, frame->nva, frame->nvlen); - - if (rv == NGHTTP2_ERR_BUFFER_ERROR) { - rv = NGHTTP2_ERR_HEADER_COMP; - } - - buf->pos -= nv_offset; - - if (rv != 0) { - return rv; - } - - if (frame->hd.flags & NGHTTP2_FLAG_PRIORITY) { - nghttp2_frame_pack_priority_spec(buf->pos, &frame->pri_spec); - } - - frame->padlen = 0; - frame->hd.length = nghttp2_bufs_len(bufs); - - return frame_pack_headers_shared(bufs, &frame->hd); -} - -void nghttp2_frame_pack_priority_spec(uint8_t *buf, - const nghttp2_priority_spec *pri_spec) { - nghttp2_put_uint32be(buf, (uint32_t)pri_spec->stream_id); - if (pri_spec->exclusive) { - buf[0] |= 0x80; - } - buf[4] = (uint8_t)(pri_spec->weight - 1); -} - -void nghttp2_frame_unpack_priority_spec(nghttp2_priority_spec *pri_spec, - const uint8_t *payload) { - int32_t dep_stream_id; - uint8_t exclusive; - int32_t weight; - - dep_stream_id = nghttp2_get_uint32(payload) & NGHTTP2_STREAM_ID_MASK; - exclusive = (payload[0] & 0x80) > 0; - weight = payload[4] + 1; - - nghttp2_priority_spec_init(pri_spec, dep_stream_id, weight, exclusive); -} - -int nghttp2_frame_unpack_headers_payload(nghttp2_headers *frame, - const uint8_t *payload) { - if (frame->hd.flags & NGHTTP2_FLAG_PRIORITY) { - nghttp2_frame_unpack_priority_spec(&frame->pri_spec, payload); - } else { - nghttp2_priority_spec_default_init(&frame->pri_spec); - } - - frame->nva = NULL; - frame->nvlen = 0; - - return 0; -} - -int nghttp2_frame_pack_priority(nghttp2_bufs *bufs, nghttp2_priority *frame) { - nghttp2_buf *buf; - - assert(bufs->head == bufs->cur); - - buf = &bufs->head->buf; - - assert(nghttp2_buf_avail(buf) >= NGHTTP2_PRIORITY_SPECLEN); - - buf->pos -= NGHTTP2_FRAME_HDLEN; - - nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); - - nghttp2_frame_pack_priority_spec(buf->last, &frame->pri_spec); - - buf->last += NGHTTP2_PRIORITY_SPECLEN; - - return 0; -} - -void nghttp2_frame_unpack_priority_payload(nghttp2_priority *frame, - const uint8_t *payload) { - nghttp2_frame_unpack_priority_spec(&frame->pri_spec, payload); -} - -int nghttp2_frame_pack_rst_stream(nghttp2_bufs *bufs, - nghttp2_rst_stream *frame) { - nghttp2_buf *buf; - - assert(bufs->head == bufs->cur); - - buf = &bufs->head->buf; - - assert(nghttp2_buf_avail(buf) >= 4); - - buf->pos -= NGHTTP2_FRAME_HDLEN; - - nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); - - nghttp2_put_uint32be(buf->last, frame->error_code); - buf->last += 4; - - return 0; -} - -void nghttp2_frame_unpack_rst_stream_payload(nghttp2_rst_stream *frame, - const uint8_t *payload) { - frame->error_code = nghttp2_get_uint32(payload); -} - -int nghttp2_frame_pack_settings(nghttp2_bufs *bufs, nghttp2_settings *frame) { - nghttp2_buf *buf; - - assert(bufs->head == bufs->cur); - - buf = &bufs->head->buf; - - if (nghttp2_buf_avail(buf) < frame->hd.length) { - return NGHTTP2_ERR_FRAME_SIZE_ERROR; - } - - buf->pos -= NGHTTP2_FRAME_HDLEN; - - nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); - - buf->last += - nghttp2_frame_pack_settings_payload(buf->last, frame->iv, frame->niv); - - return 0; -} - -size_t nghttp2_frame_pack_settings_payload(uint8_t *buf, - const nghttp2_settings_entry *iv, - size_t niv) { - size_t i; - for (i = 0; i < niv; ++i, buf += NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH) { - nghttp2_put_uint16be(buf, (uint16_t)iv[i].settings_id); - nghttp2_put_uint32be(buf + 2, iv[i].value); - } - return NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH * niv; -} - -void nghttp2_frame_unpack_settings_payload(nghttp2_settings *frame, - nghttp2_settings_entry *iv, - size_t niv) { - frame->iv = iv; - frame->niv = niv; -} - -void nghttp2_frame_unpack_settings_entry(nghttp2_settings_entry *iv, - const uint8_t *payload) { - iv->settings_id = nghttp2_get_uint16(&payload[0]); - iv->value = nghttp2_get_uint32(&payload[2]); -} - -int nghttp2_frame_unpack_settings_payload2(nghttp2_settings_entry **iv_ptr, - size_t *niv_ptr, - const uint8_t *payload, - size_t payloadlen, - nghttp2_mem *mem) { - size_t i; - - *niv_ptr = payloadlen / NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH; - - if (*niv_ptr == 0) { - *iv_ptr = NULL; - - return 0; - } - - *iv_ptr = - nghttp2_mem_malloc(mem, (*niv_ptr) * sizeof(nghttp2_settings_entry)); - - if (*iv_ptr == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - for (i = 0; i < *niv_ptr; ++i) { - size_t off = i * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH; - nghttp2_frame_unpack_settings_entry(&(*iv_ptr)[i], &payload[off]); - } - - return 0; -} - -int nghttp2_frame_pack_push_promise(nghttp2_bufs *bufs, - nghttp2_push_promise *frame, - nghttp2_hd_deflater *deflater) { - size_t nv_offset = 4; - int rv; - nghttp2_buf *buf; - - assert(bufs->head == bufs->cur); - - buf = &bufs->cur->buf; - - buf->pos += nv_offset; - buf->last = buf->pos; - - /* This call will adjust buf->last to the correct position */ - rv = nghttp2_hd_deflate_hd_bufs(deflater, bufs, frame->nva, frame->nvlen); - - if (rv == NGHTTP2_ERR_BUFFER_ERROR) { - rv = NGHTTP2_ERR_HEADER_COMP; - } - - buf->pos -= nv_offset; - - if (rv != 0) { - return rv; - } - - nghttp2_put_uint32be(buf->pos, (uint32_t)frame->promised_stream_id); - - frame->padlen = 0; - frame->hd.length = nghttp2_bufs_len(bufs); - - return frame_pack_headers_shared(bufs, &frame->hd); -} - -int nghttp2_frame_unpack_push_promise_payload(nghttp2_push_promise *frame, - const uint8_t *payload) { - frame->promised_stream_id = - nghttp2_get_uint32(payload) & NGHTTP2_STREAM_ID_MASK; - frame->nva = NULL; - frame->nvlen = 0; - return 0; -} - -int nghttp2_frame_pack_ping(nghttp2_bufs *bufs, nghttp2_ping *frame) { - nghttp2_buf *buf; - - assert(bufs->head == bufs->cur); - - buf = &bufs->head->buf; - - assert(nghttp2_buf_avail(buf) >= 8); - - buf->pos -= NGHTTP2_FRAME_HDLEN; - - nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); - - buf->last = - nghttp2_cpymem(buf->last, frame->opaque_data, sizeof(frame->opaque_data)); - - return 0; -} - -void nghttp2_frame_unpack_ping_payload(nghttp2_ping *frame, - const uint8_t *payload) { - memcpy(frame->opaque_data, payload, sizeof(frame->opaque_data)); -} - -int nghttp2_frame_pack_goaway(nghttp2_bufs *bufs, nghttp2_goaway *frame) { - int rv; - nghttp2_buf *buf; - - assert(bufs->head == bufs->cur); - - buf = &bufs->head->buf; - - buf->pos -= NGHTTP2_FRAME_HDLEN; - - nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); - - nghttp2_put_uint32be(buf->last, (uint32_t)frame->last_stream_id); - buf->last += 4; - - nghttp2_put_uint32be(buf->last, frame->error_code); - buf->last += 4; - - rv = nghttp2_bufs_add(bufs, frame->opaque_data, frame->opaque_data_len); - - if (rv == NGHTTP2_ERR_BUFFER_ERROR) { - return NGHTTP2_ERR_FRAME_SIZE_ERROR; - } - - if (rv != 0) { - return rv; - } - - return 0; -} - -void nghttp2_frame_unpack_goaway_payload(nghttp2_goaway *frame, - const uint8_t *payload, - uint8_t *var_gift_payload, - size_t var_gift_payloadlen) { - frame->last_stream_id = nghttp2_get_uint32(payload) & NGHTTP2_STREAM_ID_MASK; - frame->error_code = nghttp2_get_uint32(payload + 4); - - frame->opaque_data = var_gift_payload; - frame->opaque_data_len = var_gift_payloadlen; -} - -int nghttp2_frame_unpack_goaway_payload2(nghttp2_goaway *frame, - const uint8_t *payload, - size_t payloadlen, nghttp2_mem *mem) { - uint8_t *var_gift_payload; - size_t var_gift_payloadlen; - - if (payloadlen > 8) { - var_gift_payloadlen = payloadlen - 8; - } else { - var_gift_payloadlen = 0; - } - - payloadlen -= var_gift_payloadlen; - - if (!var_gift_payloadlen) { - var_gift_payload = NULL; - } else { - var_gift_payload = nghttp2_mem_malloc(mem, var_gift_payloadlen); - - if (var_gift_payload == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - memcpy(var_gift_payload, payload + 8, var_gift_payloadlen); - } - - nghttp2_frame_unpack_goaway_payload(frame, payload, var_gift_payload, - var_gift_payloadlen); - - return 0; -} - -int nghttp2_frame_pack_window_update(nghttp2_bufs *bufs, - nghttp2_window_update *frame) { - nghttp2_buf *buf; - - assert(bufs->head == bufs->cur); - - buf = &bufs->head->buf; - - assert(nghttp2_buf_avail(buf) >= 4); - - buf->pos -= NGHTTP2_FRAME_HDLEN; - - nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); - - nghttp2_put_uint32be(buf->last, (uint32_t)frame->window_size_increment); - buf->last += 4; - - return 0; -} - -void nghttp2_frame_unpack_window_update_payload(nghttp2_window_update *frame, - const uint8_t *payload) { - frame->window_size_increment = - nghttp2_get_uint32(payload) & NGHTTP2_WINDOW_SIZE_INCREMENT_MASK; -} - -int nghttp2_frame_pack_altsvc(nghttp2_bufs *bufs, nghttp2_extension *frame) { - int rv; - nghttp2_buf *buf; - nghttp2_ext_altsvc *altsvc; - - /* This is required with --disable-assert. */ - (void)rv; - - altsvc = frame->payload; - - buf = &bufs->head->buf; - - assert(nghttp2_buf_avail(buf) >= - 2 + altsvc->origin_len + altsvc->field_value_len); - - buf->pos -= NGHTTP2_FRAME_HDLEN; - - nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); - - nghttp2_put_uint16be(buf->last, (uint16_t)altsvc->origin_len); - buf->last += 2; - - rv = nghttp2_bufs_add(bufs, altsvc->origin, altsvc->origin_len); - - assert(rv == 0); - - rv = nghttp2_bufs_add(bufs, altsvc->field_value, altsvc->field_value_len); - - assert(rv == 0); - - return 0; -} - -void nghttp2_frame_unpack_altsvc_payload(nghttp2_extension *frame, - size_t origin_len, uint8_t *payload, - size_t payloadlen) { - nghttp2_ext_altsvc *altsvc; - uint8_t *p; - - altsvc = frame->payload; - p = payload; - - altsvc->origin = p; - - p += origin_len; - - altsvc->origin_len = origin_len; - - altsvc->field_value = p; - altsvc->field_value_len = (size_t)(payload + payloadlen - p); -} - -int nghttp2_frame_unpack_altsvc_payload2(nghttp2_extension *frame, - const uint8_t *payload, - size_t payloadlen, nghttp2_mem *mem) { - uint8_t *buf; - size_t origin_len; - - if (payloadlen < 2) { - return NGHTTP2_FRAME_SIZE_ERROR; - } - - origin_len = nghttp2_get_uint16(payload); - - buf = nghttp2_mem_malloc(mem, payloadlen - 2); - if (!buf) { - return NGHTTP2_ERR_NOMEM; - } - - nghttp2_cpymem(buf, payload + 2, payloadlen - 2); - - nghttp2_frame_unpack_altsvc_payload(frame, origin_len, buf, payloadlen - 2); - - return 0; -} - +size_t nghttp2_frame_priority_len(uint8_t flags) { + if (flags & NGHTTP2_FLAG_PRIORITY) { + return NGHTTP2_PRIORITY_SPECLEN; + } + + return 0; +} + +size_t nghttp2_frame_headers_payload_nv_offset(nghttp2_headers *frame) { + return nghttp2_frame_priority_len(frame->hd.flags); +} + +/* + * Call this function after payload was serialized, but not before + * changing buf->pos and serializing frame header. + * + * This function assumes bufs->cur points to the last buf chain of the + * frame(s). + * + * This function serializes frame header for HEADERS/PUSH_PROMISE and + * handles their successive CONTINUATION frames. + * + * We don't process any padding here. + */ +static int frame_pack_headers_shared(nghttp2_bufs *bufs, + nghttp2_frame_hd *frame_hd) { + nghttp2_buf *buf; + nghttp2_buf_chain *ci, *ce; + nghttp2_frame_hd hd; + + buf = &bufs->head->buf; + + hd = *frame_hd; + hd.length = nghttp2_buf_len(buf); + + DEBUGF("send: HEADERS/PUSH_PROMISE, payloadlen=%zu\n", hd.length); + + /* We have multiple frame buffers, which means one or more + CONTINUATION frame is involved. Remove END_HEADERS flag from the + first frame. */ + if (bufs->head != bufs->cur) { + hd.flags = (uint8_t)(hd.flags & ~NGHTTP2_FLAG_END_HEADERS); + } + + buf->pos -= NGHTTP2_FRAME_HDLEN; + nghttp2_frame_pack_frame_hd(buf->pos, &hd); + + if (bufs->head != bufs->cur) { + /* 2nd and later frames are CONTINUATION frames. */ + hd.type = NGHTTP2_CONTINUATION; + /* We don't have no flags except for last CONTINUATION */ + hd.flags = NGHTTP2_FLAG_NONE; + + ce = bufs->cur; + + for (ci = bufs->head->next; ci != ce; ci = ci->next) { + buf = &ci->buf; + + hd.length = nghttp2_buf_len(buf); + + DEBUGF("send: int CONTINUATION, payloadlen=%zu\n", hd.length); + + buf->pos -= NGHTTP2_FRAME_HDLEN; + nghttp2_frame_pack_frame_hd(buf->pos, &hd); + } + + buf = &ci->buf; + hd.length = nghttp2_buf_len(buf); + /* Set END_HEADERS flag for last CONTINUATION */ + hd.flags = NGHTTP2_FLAG_END_HEADERS; + + DEBUGF("send: last CONTINUATION, payloadlen=%zu\n", hd.length); + + buf->pos -= NGHTTP2_FRAME_HDLEN; + nghttp2_frame_pack_frame_hd(buf->pos, &hd); + } + + return 0; +} + +int nghttp2_frame_pack_headers(nghttp2_bufs *bufs, nghttp2_headers *frame, + nghttp2_hd_deflater *deflater) { + size_t nv_offset; + int rv; + nghttp2_buf *buf; + + assert(bufs->head == bufs->cur); + + nv_offset = nghttp2_frame_headers_payload_nv_offset(frame); + + buf = &bufs->cur->buf; + + buf->pos += nv_offset; + buf->last = buf->pos; + + /* This call will adjust buf->last to the correct position */ + rv = nghttp2_hd_deflate_hd_bufs(deflater, bufs, frame->nva, frame->nvlen); + + if (rv == NGHTTP2_ERR_BUFFER_ERROR) { + rv = NGHTTP2_ERR_HEADER_COMP; + } + + buf->pos -= nv_offset; + + if (rv != 0) { + return rv; + } + + if (frame->hd.flags & NGHTTP2_FLAG_PRIORITY) { + nghttp2_frame_pack_priority_spec(buf->pos, &frame->pri_spec); + } + + frame->padlen = 0; + frame->hd.length = nghttp2_bufs_len(bufs); + + return frame_pack_headers_shared(bufs, &frame->hd); +} + +void nghttp2_frame_pack_priority_spec(uint8_t *buf, + const nghttp2_priority_spec *pri_spec) { + nghttp2_put_uint32be(buf, (uint32_t)pri_spec->stream_id); + if (pri_spec->exclusive) { + buf[0] |= 0x80; + } + buf[4] = (uint8_t)(pri_spec->weight - 1); +} + +void nghttp2_frame_unpack_priority_spec(nghttp2_priority_spec *pri_spec, + const uint8_t *payload) { + int32_t dep_stream_id; + uint8_t exclusive; + int32_t weight; + + dep_stream_id = nghttp2_get_uint32(payload) & NGHTTP2_STREAM_ID_MASK; + exclusive = (payload[0] & 0x80) > 0; + weight = payload[4] + 1; + + nghttp2_priority_spec_init(pri_spec, dep_stream_id, weight, exclusive); +} + +int nghttp2_frame_unpack_headers_payload(nghttp2_headers *frame, + const uint8_t *payload) { + if (frame->hd.flags & NGHTTP2_FLAG_PRIORITY) { + nghttp2_frame_unpack_priority_spec(&frame->pri_spec, payload); + } else { + nghttp2_priority_spec_default_init(&frame->pri_spec); + } + + frame->nva = NULL; + frame->nvlen = 0; + + return 0; +} + +int nghttp2_frame_pack_priority(nghttp2_bufs *bufs, nghttp2_priority *frame) { + nghttp2_buf *buf; + + assert(bufs->head == bufs->cur); + + buf = &bufs->head->buf; + + assert(nghttp2_buf_avail(buf) >= NGHTTP2_PRIORITY_SPECLEN); + + buf->pos -= NGHTTP2_FRAME_HDLEN; + + nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); + + nghttp2_frame_pack_priority_spec(buf->last, &frame->pri_spec); + + buf->last += NGHTTP2_PRIORITY_SPECLEN; + + return 0; +} + +void nghttp2_frame_unpack_priority_payload(nghttp2_priority *frame, + const uint8_t *payload) { + nghttp2_frame_unpack_priority_spec(&frame->pri_spec, payload); +} + +int nghttp2_frame_pack_rst_stream(nghttp2_bufs *bufs, + nghttp2_rst_stream *frame) { + nghttp2_buf *buf; + + assert(bufs->head == bufs->cur); + + buf = &bufs->head->buf; + + assert(nghttp2_buf_avail(buf) >= 4); + + buf->pos -= NGHTTP2_FRAME_HDLEN; + + nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); + + nghttp2_put_uint32be(buf->last, frame->error_code); + buf->last += 4; + + return 0; +} + +void nghttp2_frame_unpack_rst_stream_payload(nghttp2_rst_stream *frame, + const uint8_t *payload) { + frame->error_code = nghttp2_get_uint32(payload); +} + +int nghttp2_frame_pack_settings(nghttp2_bufs *bufs, nghttp2_settings *frame) { + nghttp2_buf *buf; + + assert(bufs->head == bufs->cur); + + buf = &bufs->head->buf; + + if (nghttp2_buf_avail(buf) < frame->hd.length) { + return NGHTTP2_ERR_FRAME_SIZE_ERROR; + } + + buf->pos -= NGHTTP2_FRAME_HDLEN; + + nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); + + buf->last += + nghttp2_frame_pack_settings_payload(buf->last, frame->iv, frame->niv); + + return 0; +} + +size_t nghttp2_frame_pack_settings_payload(uint8_t *buf, + const nghttp2_settings_entry *iv, + size_t niv) { + size_t i; + for (i = 0; i < niv; ++i, buf += NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH) { + nghttp2_put_uint16be(buf, (uint16_t)iv[i].settings_id); + nghttp2_put_uint32be(buf + 2, iv[i].value); + } + return NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH * niv; +} + +void nghttp2_frame_unpack_settings_payload(nghttp2_settings *frame, + nghttp2_settings_entry *iv, + size_t niv) { + frame->iv = iv; + frame->niv = niv; +} + +void nghttp2_frame_unpack_settings_entry(nghttp2_settings_entry *iv, + const uint8_t *payload) { + iv->settings_id = nghttp2_get_uint16(&payload[0]); + iv->value = nghttp2_get_uint32(&payload[2]); +} + +int nghttp2_frame_unpack_settings_payload2(nghttp2_settings_entry **iv_ptr, + size_t *niv_ptr, + const uint8_t *payload, + size_t payloadlen, + nghttp2_mem *mem) { + size_t i; + + *niv_ptr = payloadlen / NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH; + + if (*niv_ptr == 0) { + *iv_ptr = NULL; + + return 0; + } + + *iv_ptr = + nghttp2_mem_malloc(mem, (*niv_ptr) * sizeof(nghttp2_settings_entry)); + + if (*iv_ptr == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + for (i = 0; i < *niv_ptr; ++i) { + size_t off = i * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH; + nghttp2_frame_unpack_settings_entry(&(*iv_ptr)[i], &payload[off]); + } + + return 0; +} + +int nghttp2_frame_pack_push_promise(nghttp2_bufs *bufs, + nghttp2_push_promise *frame, + nghttp2_hd_deflater *deflater) { + size_t nv_offset = 4; + int rv; + nghttp2_buf *buf; + + assert(bufs->head == bufs->cur); + + buf = &bufs->cur->buf; + + buf->pos += nv_offset; + buf->last = buf->pos; + + /* This call will adjust buf->last to the correct position */ + rv = nghttp2_hd_deflate_hd_bufs(deflater, bufs, frame->nva, frame->nvlen); + + if (rv == NGHTTP2_ERR_BUFFER_ERROR) { + rv = NGHTTP2_ERR_HEADER_COMP; + } + + buf->pos -= nv_offset; + + if (rv != 0) { + return rv; + } + + nghttp2_put_uint32be(buf->pos, (uint32_t)frame->promised_stream_id); + + frame->padlen = 0; + frame->hd.length = nghttp2_bufs_len(bufs); + + return frame_pack_headers_shared(bufs, &frame->hd); +} + +int nghttp2_frame_unpack_push_promise_payload(nghttp2_push_promise *frame, + const uint8_t *payload) { + frame->promised_stream_id = + nghttp2_get_uint32(payload) & NGHTTP2_STREAM_ID_MASK; + frame->nva = NULL; + frame->nvlen = 0; + return 0; +} + +int nghttp2_frame_pack_ping(nghttp2_bufs *bufs, nghttp2_ping *frame) { + nghttp2_buf *buf; + + assert(bufs->head == bufs->cur); + + buf = &bufs->head->buf; + + assert(nghttp2_buf_avail(buf) >= 8); + + buf->pos -= NGHTTP2_FRAME_HDLEN; + + nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); + + buf->last = + nghttp2_cpymem(buf->last, frame->opaque_data, sizeof(frame->opaque_data)); + + return 0; +} + +void nghttp2_frame_unpack_ping_payload(nghttp2_ping *frame, + const uint8_t *payload) { + memcpy(frame->opaque_data, payload, sizeof(frame->opaque_data)); +} + +int nghttp2_frame_pack_goaway(nghttp2_bufs *bufs, nghttp2_goaway *frame) { + int rv; + nghttp2_buf *buf; + + assert(bufs->head == bufs->cur); + + buf = &bufs->head->buf; + + buf->pos -= NGHTTP2_FRAME_HDLEN; + + nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); + + nghttp2_put_uint32be(buf->last, (uint32_t)frame->last_stream_id); + buf->last += 4; + + nghttp2_put_uint32be(buf->last, frame->error_code); + buf->last += 4; + + rv = nghttp2_bufs_add(bufs, frame->opaque_data, frame->opaque_data_len); + + if (rv == NGHTTP2_ERR_BUFFER_ERROR) { + return NGHTTP2_ERR_FRAME_SIZE_ERROR; + } + + if (rv != 0) { + return rv; + } + + return 0; +} + +void nghttp2_frame_unpack_goaway_payload(nghttp2_goaway *frame, + const uint8_t *payload, + uint8_t *var_gift_payload, + size_t var_gift_payloadlen) { + frame->last_stream_id = nghttp2_get_uint32(payload) & NGHTTP2_STREAM_ID_MASK; + frame->error_code = nghttp2_get_uint32(payload + 4); + + frame->opaque_data = var_gift_payload; + frame->opaque_data_len = var_gift_payloadlen; +} + +int nghttp2_frame_unpack_goaway_payload2(nghttp2_goaway *frame, + const uint8_t *payload, + size_t payloadlen, nghttp2_mem *mem) { + uint8_t *var_gift_payload; + size_t var_gift_payloadlen; + + if (payloadlen > 8) { + var_gift_payloadlen = payloadlen - 8; + } else { + var_gift_payloadlen = 0; + } + + payloadlen -= var_gift_payloadlen; + + if (!var_gift_payloadlen) { + var_gift_payload = NULL; + } else { + var_gift_payload = nghttp2_mem_malloc(mem, var_gift_payloadlen); + + if (var_gift_payload == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + memcpy(var_gift_payload, payload + 8, var_gift_payloadlen); + } + + nghttp2_frame_unpack_goaway_payload(frame, payload, var_gift_payload, + var_gift_payloadlen); + + return 0; +} + +int nghttp2_frame_pack_window_update(nghttp2_bufs *bufs, + nghttp2_window_update *frame) { + nghttp2_buf *buf; + + assert(bufs->head == bufs->cur); + + buf = &bufs->head->buf; + + assert(nghttp2_buf_avail(buf) >= 4); + + buf->pos -= NGHTTP2_FRAME_HDLEN; + + nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); + + nghttp2_put_uint32be(buf->last, (uint32_t)frame->window_size_increment); + buf->last += 4; + + return 0; +} + +void nghttp2_frame_unpack_window_update_payload(nghttp2_window_update *frame, + const uint8_t *payload) { + frame->window_size_increment = + nghttp2_get_uint32(payload) & NGHTTP2_WINDOW_SIZE_INCREMENT_MASK; +} + +int nghttp2_frame_pack_altsvc(nghttp2_bufs *bufs, nghttp2_extension *frame) { + int rv; + nghttp2_buf *buf; + nghttp2_ext_altsvc *altsvc; + + /* This is required with --disable-assert. */ + (void)rv; + + altsvc = frame->payload; + + buf = &bufs->head->buf; + + assert(nghttp2_buf_avail(buf) >= + 2 + altsvc->origin_len + altsvc->field_value_len); + + buf->pos -= NGHTTP2_FRAME_HDLEN; + + nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); + + nghttp2_put_uint16be(buf->last, (uint16_t)altsvc->origin_len); + buf->last += 2; + + rv = nghttp2_bufs_add(bufs, altsvc->origin, altsvc->origin_len); + + assert(rv == 0); + + rv = nghttp2_bufs_add(bufs, altsvc->field_value, altsvc->field_value_len); + + assert(rv == 0); + + return 0; +} + +void nghttp2_frame_unpack_altsvc_payload(nghttp2_extension *frame, + size_t origin_len, uint8_t *payload, + size_t payloadlen) { + nghttp2_ext_altsvc *altsvc; + uint8_t *p; + + altsvc = frame->payload; + p = payload; + + altsvc->origin = p; + + p += origin_len; + + altsvc->origin_len = origin_len; + + altsvc->field_value = p; + altsvc->field_value_len = (size_t)(payload + payloadlen - p); +} + +int nghttp2_frame_unpack_altsvc_payload2(nghttp2_extension *frame, + const uint8_t *payload, + size_t payloadlen, nghttp2_mem *mem) { + uint8_t *buf; + size_t origin_len; + + if (payloadlen < 2) { + return NGHTTP2_FRAME_SIZE_ERROR; + } + + origin_len = nghttp2_get_uint16(payload); + + buf = nghttp2_mem_malloc(mem, payloadlen - 2); + if (!buf) { + return NGHTTP2_ERR_NOMEM; + } + + nghttp2_cpymem(buf, payload + 2, payloadlen - 2); + + nghttp2_frame_unpack_altsvc_payload(frame, origin_len, buf, payloadlen - 2); + + return 0; +} + int nghttp2_frame_pack_origin(nghttp2_bufs *bufs, nghttp2_extension *frame) { nghttp2_buf *buf; nghttp2_ext_origin *origin; @@ -878,27 +878,27 @@ int nghttp2_frame_unpack_origin_payload(nghttp2_extension *frame, return 0; } -nghttp2_settings_entry *nghttp2_frame_iv_copy(const nghttp2_settings_entry *iv, - size_t niv, nghttp2_mem *mem) { - nghttp2_settings_entry *iv_copy; - size_t len = niv * sizeof(nghttp2_settings_entry); - - if (len == 0) { - return NULL; - } - - iv_copy = nghttp2_mem_malloc(mem, len); - - if (iv_copy == NULL) { - return NULL; - } - - memcpy(iv_copy, iv, len); - - return iv_copy; -} - -int nghttp2_nv_equal(const nghttp2_nv *a, const nghttp2_nv *b) { +nghttp2_settings_entry *nghttp2_frame_iv_copy(const nghttp2_settings_entry *iv, + size_t niv, nghttp2_mem *mem) { + nghttp2_settings_entry *iv_copy; + size_t len = niv * sizeof(nghttp2_settings_entry); + + if (len == 0) { + return NULL; + } + + iv_copy = nghttp2_mem_malloc(mem, len); + + if (iv_copy == NULL) { + return NULL; + } + + memcpy(iv_copy, iv, len); + + return iv_copy; +} + +int nghttp2_nv_equal(const nghttp2_nv *a, const nghttp2_nv *b) { if (a->namelen != b->namelen || a->valuelen != b->valuelen) { return 0; } @@ -918,235 +918,235 @@ int nghttp2_nv_equal(const nghttp2_nv *a, const nghttp2_nv *b) { } return 1; -} - -void nghttp2_nv_array_del(nghttp2_nv *nva, nghttp2_mem *mem) { - nghttp2_mem_free(mem, nva); -} - -static int bytes_compar(const uint8_t *a, size_t alen, const uint8_t *b, - size_t blen) { - int rv; - - if (alen == blen) { - return memcmp(a, b, alen); - } - - if (alen < blen) { - rv = memcmp(a, b, alen); - - if (rv == 0) { - return -1; - } - - return rv; - } - - rv = memcmp(a, b, blen); - - if (rv == 0) { - return 1; - } - - return rv; -} - -int nghttp2_nv_compare_name(const nghttp2_nv *lhs, const nghttp2_nv *rhs) { - return bytes_compar(lhs->name, lhs->namelen, rhs->name, rhs->namelen); -} - -static int nv_compar(const void *lhs, const void *rhs) { - const nghttp2_nv *a = (const nghttp2_nv *)lhs; - const nghttp2_nv *b = (const nghttp2_nv *)rhs; - int rv; - - rv = bytes_compar(a->name, a->namelen, b->name, b->namelen); - - if (rv == 0) { - return bytes_compar(a->value, a->valuelen, b->value, b->valuelen); - } - - return rv; -} - -void nghttp2_nv_array_sort(nghttp2_nv *nva, size_t nvlen) { - qsort(nva, nvlen, sizeof(nghttp2_nv), nv_compar); -} - -int nghttp2_nv_array_copy(nghttp2_nv **nva_ptr, const nghttp2_nv *nva, - size_t nvlen, nghttp2_mem *mem) { - size_t i; - uint8_t *data = NULL; - size_t buflen = 0; - nghttp2_nv *p; - - if (nvlen == 0) { - *nva_ptr = NULL; - - return 0; - } - - for (i = 0; i < nvlen; ++i) { - /* + 1 for null-termination */ - if ((nva[i].flags & NGHTTP2_NV_FLAG_NO_COPY_NAME) == 0) { - buflen += nva[i].namelen + 1; - } - if ((nva[i].flags & NGHTTP2_NV_FLAG_NO_COPY_VALUE) == 0) { - buflen += nva[i].valuelen + 1; - } - } - - buflen += sizeof(nghttp2_nv) * nvlen; - - *nva_ptr = nghttp2_mem_malloc(mem, buflen); - - if (*nva_ptr == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - p = *nva_ptr; - data = (uint8_t *)(*nva_ptr) + sizeof(nghttp2_nv) * nvlen; - - for (i = 0; i < nvlen; ++i) { - p->flags = nva[i].flags; - - if (nva[i].flags & NGHTTP2_NV_FLAG_NO_COPY_NAME) { - p->name = nva[i].name; - p->namelen = nva[i].namelen; - } else { - if (nva[i].namelen) { - memcpy(data, nva[i].name, nva[i].namelen); - } - p->name = data; - p->namelen = nva[i].namelen; - data[p->namelen] = '\0'; - nghttp2_downcase(p->name, p->namelen); - data += nva[i].namelen + 1; - } - - if (nva[i].flags & NGHTTP2_NV_FLAG_NO_COPY_VALUE) { - p->value = nva[i].value; - p->valuelen = nva[i].valuelen; - } else { - if (nva[i].valuelen) { - memcpy(data, nva[i].value, nva[i].valuelen); - } - p->value = data; - p->valuelen = nva[i].valuelen; - data[p->valuelen] = '\0'; - data += nva[i].valuelen + 1; - } - - ++p; - } - return 0; -} - -int nghttp2_iv_check(const nghttp2_settings_entry *iv, size_t niv) { - size_t i; - for (i = 0; i < niv; ++i) { - switch (iv[i].settings_id) { - case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: - break; - case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: - break; - case NGHTTP2_SETTINGS_ENABLE_PUSH: - if (iv[i].value != 0 && iv[i].value != 1) { - return 0; - } - break; - case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: - if (iv[i].value > (uint32_t)NGHTTP2_MAX_WINDOW_SIZE) { - return 0; - } - break; - case NGHTTP2_SETTINGS_MAX_FRAME_SIZE: - if (iv[i].value < NGHTTP2_MAX_FRAME_SIZE_MIN || - iv[i].value > NGHTTP2_MAX_FRAME_SIZE_MAX) { - return 0; - } - break; - case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: - break; +} + +void nghttp2_nv_array_del(nghttp2_nv *nva, nghttp2_mem *mem) { + nghttp2_mem_free(mem, nva); +} + +static int bytes_compar(const uint8_t *a, size_t alen, const uint8_t *b, + size_t blen) { + int rv; + + if (alen == blen) { + return memcmp(a, b, alen); + } + + if (alen < blen) { + rv = memcmp(a, b, alen); + + if (rv == 0) { + return -1; + } + + return rv; + } + + rv = memcmp(a, b, blen); + + if (rv == 0) { + return 1; + } + + return rv; +} + +int nghttp2_nv_compare_name(const nghttp2_nv *lhs, const nghttp2_nv *rhs) { + return bytes_compar(lhs->name, lhs->namelen, rhs->name, rhs->namelen); +} + +static int nv_compar(const void *lhs, const void *rhs) { + const nghttp2_nv *a = (const nghttp2_nv *)lhs; + const nghttp2_nv *b = (const nghttp2_nv *)rhs; + int rv; + + rv = bytes_compar(a->name, a->namelen, b->name, b->namelen); + + if (rv == 0) { + return bytes_compar(a->value, a->valuelen, b->value, b->valuelen); + } + + return rv; +} + +void nghttp2_nv_array_sort(nghttp2_nv *nva, size_t nvlen) { + qsort(nva, nvlen, sizeof(nghttp2_nv), nv_compar); +} + +int nghttp2_nv_array_copy(nghttp2_nv **nva_ptr, const nghttp2_nv *nva, + size_t nvlen, nghttp2_mem *mem) { + size_t i; + uint8_t *data = NULL; + size_t buflen = 0; + nghttp2_nv *p; + + if (nvlen == 0) { + *nva_ptr = NULL; + + return 0; + } + + for (i = 0; i < nvlen; ++i) { + /* + 1 for null-termination */ + if ((nva[i].flags & NGHTTP2_NV_FLAG_NO_COPY_NAME) == 0) { + buflen += nva[i].namelen + 1; + } + if ((nva[i].flags & NGHTTP2_NV_FLAG_NO_COPY_VALUE) == 0) { + buflen += nva[i].valuelen + 1; + } + } + + buflen += sizeof(nghttp2_nv) * nvlen; + + *nva_ptr = nghttp2_mem_malloc(mem, buflen); + + if (*nva_ptr == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + p = *nva_ptr; + data = (uint8_t *)(*nva_ptr) + sizeof(nghttp2_nv) * nvlen; + + for (i = 0; i < nvlen; ++i) { + p->flags = nva[i].flags; + + if (nva[i].flags & NGHTTP2_NV_FLAG_NO_COPY_NAME) { + p->name = nva[i].name; + p->namelen = nva[i].namelen; + } else { + if (nva[i].namelen) { + memcpy(data, nva[i].name, nva[i].namelen); + } + p->name = data; + p->namelen = nva[i].namelen; + data[p->namelen] = '\0'; + nghttp2_downcase(p->name, p->namelen); + data += nva[i].namelen + 1; + } + + if (nva[i].flags & NGHTTP2_NV_FLAG_NO_COPY_VALUE) { + p->value = nva[i].value; + p->valuelen = nva[i].valuelen; + } else { + if (nva[i].valuelen) { + memcpy(data, nva[i].value, nva[i].valuelen); + } + p->value = data; + p->valuelen = nva[i].valuelen; + data[p->valuelen] = '\0'; + data += nva[i].valuelen + 1; + } + + ++p; + } + return 0; +} + +int nghttp2_iv_check(const nghttp2_settings_entry *iv, size_t niv) { + size_t i; + for (i = 0; i < niv; ++i) { + switch (iv[i].settings_id) { + case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: + break; + case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: + break; + case NGHTTP2_SETTINGS_ENABLE_PUSH: + if (iv[i].value != 0 && iv[i].value != 1) { + return 0; + } + break; + case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: + if (iv[i].value > (uint32_t)NGHTTP2_MAX_WINDOW_SIZE) { + return 0; + } + break; + case NGHTTP2_SETTINGS_MAX_FRAME_SIZE: + if (iv[i].value < NGHTTP2_MAX_FRAME_SIZE_MIN || + iv[i].value > NGHTTP2_MAX_FRAME_SIZE_MAX) { + return 0; + } + break; + case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: + break; case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL: if (iv[i].value != 0 && iv[i].value != 1) { return 0; } break; - } - } - return 1; -} - -static void frame_set_pad(nghttp2_buf *buf, size_t padlen, int framehd_only) { - size_t trail_padlen; - size_t newlen; - - DEBUGF("send: padlen=%zu, shift left 1 bytes\n", padlen); - - memmove(buf->pos - 1, buf->pos, NGHTTP2_FRAME_HDLEN); - - --buf->pos; - - buf->pos[4] |= NGHTTP2_FLAG_PADDED; - - newlen = (nghttp2_get_uint32(buf->pos) >> 8) + padlen; - nghttp2_put_uint32be(buf->pos, (uint32_t)((newlen << 8) + buf->pos[3])); - - if (framehd_only) { - return; - } - - trail_padlen = padlen - 1; - buf->pos[NGHTTP2_FRAME_HDLEN] = (uint8_t)trail_padlen; - - /* zero out padding */ - memset(buf->last, 0, trail_padlen); - /* extend buffers trail_padlen bytes, since we ate previous padlen - - trail_padlen byte(s) */ - buf->last += trail_padlen; -} - -int nghttp2_frame_add_pad(nghttp2_bufs *bufs, nghttp2_frame_hd *hd, - size_t padlen, int framehd_only) { - nghttp2_buf *buf; - - if (padlen == 0) { - DEBUGF("send: padlen = 0, nothing to do\n"); - - return 0; - } - - /* - * We have arranged bufs like this: - * - * 0 1 2 3 - * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | |Frame header | Frame payload... : - * +-+-----------------+-------------------------------------------+ - * | |Frame header | Frame payload... : - * +-+-----------------+-------------------------------------------+ - * | |Frame header | Frame payload... : - * +-+-----------------+-------------------------------------------+ - * - * We arranged padding so that it is included in the first frame - * completely. For padded frame, we are going to adjust buf->pos of - * frame which includes padding and serialize (memmove) frame header - * in the correct position. Also extends buf->last to include - * padding. - */ - - buf = &bufs->head->buf; - - assert(nghttp2_buf_avail(buf) >= padlen - 1); - - frame_set_pad(buf, padlen, framehd_only); - - hd->length += padlen; - hd->flags |= NGHTTP2_FLAG_PADDED; - - DEBUGF("send: final payloadlen=%zu, padlen=%zu\n", hd->length, padlen); - - return 0; -} + } + } + return 1; +} + +static void frame_set_pad(nghttp2_buf *buf, size_t padlen, int framehd_only) { + size_t trail_padlen; + size_t newlen; + + DEBUGF("send: padlen=%zu, shift left 1 bytes\n", padlen); + + memmove(buf->pos - 1, buf->pos, NGHTTP2_FRAME_HDLEN); + + --buf->pos; + + buf->pos[4] |= NGHTTP2_FLAG_PADDED; + + newlen = (nghttp2_get_uint32(buf->pos) >> 8) + padlen; + nghttp2_put_uint32be(buf->pos, (uint32_t)((newlen << 8) + buf->pos[3])); + + if (framehd_only) { + return; + } + + trail_padlen = padlen - 1; + buf->pos[NGHTTP2_FRAME_HDLEN] = (uint8_t)trail_padlen; + + /* zero out padding */ + memset(buf->last, 0, trail_padlen); + /* extend buffers trail_padlen bytes, since we ate previous padlen - + trail_padlen byte(s) */ + buf->last += trail_padlen; +} + +int nghttp2_frame_add_pad(nghttp2_bufs *bufs, nghttp2_frame_hd *hd, + size_t padlen, int framehd_only) { + nghttp2_buf *buf; + + if (padlen == 0) { + DEBUGF("send: padlen = 0, nothing to do\n"); + + return 0; + } + + /* + * We have arranged bufs like this: + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | |Frame header | Frame payload... : + * +-+-----------------+-------------------------------------------+ + * | |Frame header | Frame payload... : + * +-+-----------------+-------------------------------------------+ + * | |Frame header | Frame payload... : + * +-+-----------------+-------------------------------------------+ + * + * We arranged padding so that it is included in the first frame + * completely. For padded frame, we are going to adjust buf->pos of + * frame which includes padding and serialize (memmove) frame header + * in the correct position. Also extends buf->last to include + * padding. + */ + + buf = &bufs->head->buf; + + assert(nghttp2_buf_avail(buf) >= padlen - 1); + + frame_set_pad(buf, padlen, framehd_only); + + hd->length += padlen; + hd->flags |= NGHTTP2_FLAG_PADDED; + + DEBUGF("send: final payloadlen=%zu, padlen=%zu\n", hd->length, padlen); + + return 0; +} diff --git a/contrib/libs/nghttp2/lib/nghttp2_frame.h b/contrib/libs/nghttp2/lib/nghttp2_frame.h index b4ac763be5..4b9222ac6d 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_frame.h +++ b/contrib/libs/nghttp2/lib/nghttp2_frame.h @@ -1,399 +1,399 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * 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_FRAME_H -#define NGHTTP2_FRAME_H - -#ifdef HAVE_CONFIG_H +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * 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_FRAME_H +#define NGHTTP2_FRAME_H + +#ifdef HAVE_CONFIG_H # include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <nghttp2/nghttp2.h> -#include "nghttp2_hd.h" -#include "nghttp2_buf.h" - -#define NGHTTP2_STREAM_ID_MASK ((1u << 31) - 1) -#define NGHTTP2_PRI_GROUP_ID_MASK ((1u << 31) - 1) -#define NGHTTP2_PRIORITY_MASK ((1u << 31) - 1) -#define NGHTTP2_WINDOW_SIZE_INCREMENT_MASK ((1u << 31) - 1) -#define NGHTTP2_SETTINGS_ID_MASK ((1 << 24) - 1) - -/* The number of bytes of frame header. */ -#define NGHTTP2_FRAME_HDLEN 9 - -#define NGHTTP2_MAX_FRAME_SIZE_MAX ((1 << 24) - 1) -#define NGHTTP2_MAX_FRAME_SIZE_MIN (1 << 14) - -#define NGHTTP2_MAX_PAYLOADLEN 16384 -/* The one frame buffer length for tranmission. We may use several of - them to support CONTINUATION. To account for Pad Length field, we - allocate extra 1 byte, which saves extra large memcopying. */ -#define NGHTTP2_FRAMEBUF_CHUNKLEN \ - (NGHTTP2_FRAME_HDLEN + 1 + NGHTTP2_MAX_PAYLOADLEN) - -/* The default length of DATA frame payload. */ -#define NGHTTP2_DATA_PAYLOADLEN NGHTTP2_MAX_FRAME_SIZE_MIN - -/* Maximum headers block size to send, calculated using - nghttp2_hd_deflate_bound(). This is the default value, and can be +#endif /* HAVE_CONFIG_H */ + +#include <nghttp2/nghttp2.h> +#include "nghttp2_hd.h" +#include "nghttp2_buf.h" + +#define NGHTTP2_STREAM_ID_MASK ((1u << 31) - 1) +#define NGHTTP2_PRI_GROUP_ID_MASK ((1u << 31) - 1) +#define NGHTTP2_PRIORITY_MASK ((1u << 31) - 1) +#define NGHTTP2_WINDOW_SIZE_INCREMENT_MASK ((1u << 31) - 1) +#define NGHTTP2_SETTINGS_ID_MASK ((1 << 24) - 1) + +/* The number of bytes of frame header. */ +#define NGHTTP2_FRAME_HDLEN 9 + +#define NGHTTP2_MAX_FRAME_SIZE_MAX ((1 << 24) - 1) +#define NGHTTP2_MAX_FRAME_SIZE_MIN (1 << 14) + +#define NGHTTP2_MAX_PAYLOADLEN 16384 +/* The one frame buffer length for tranmission. We may use several of + them to support CONTINUATION. To account for Pad Length field, we + allocate extra 1 byte, which saves extra large memcopying. */ +#define NGHTTP2_FRAMEBUF_CHUNKLEN \ + (NGHTTP2_FRAME_HDLEN + 1 + NGHTTP2_MAX_PAYLOADLEN) + +/* The default length of DATA frame payload. */ +#define NGHTTP2_DATA_PAYLOADLEN NGHTTP2_MAX_FRAME_SIZE_MIN + +/* Maximum headers block size to send, calculated using + nghttp2_hd_deflate_bound(). This is the default value, and can be overridden by nghttp2_option_set_max_send_header_block_length(). */ -#define NGHTTP2_MAX_HEADERSLEN 65536 - -/* The number of bytes for each SETTINGS entry */ -#define NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH 6 - -/* Length of priority related fields in HEADERS/PRIORITY frames */ -#define NGHTTP2_PRIORITY_SPECLEN 5 - -/* Maximum length of padding in bytes. */ -#define NGHTTP2_MAX_PADLEN 256 - -/* Union of extension frame payload */ -typedef union { - nghttp2_ext_altsvc altsvc; +#define NGHTTP2_MAX_HEADERSLEN 65536 + +/* The number of bytes for each SETTINGS entry */ +#define NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH 6 + +/* Length of priority related fields in HEADERS/PRIORITY frames */ +#define NGHTTP2_PRIORITY_SPECLEN 5 + +/* Maximum length of padding in bytes. */ +#define NGHTTP2_MAX_PADLEN 256 + +/* Union of extension frame payload */ +typedef union { + nghttp2_ext_altsvc altsvc; nghttp2_ext_origin origin; -} nghttp2_ext_frame_payload; - -void nghttp2_frame_pack_frame_hd(uint8_t *buf, const nghttp2_frame_hd *hd); - -void nghttp2_frame_unpack_frame_hd(nghttp2_frame_hd *hd, const uint8_t *buf); - -/** - * Initializes frame header |hd| with given parameters. Reserved bit - * is set to 0. - */ -void nghttp2_frame_hd_init(nghttp2_frame_hd *hd, size_t length, uint8_t type, - uint8_t flags, int32_t stream_id); - -/** - * Returns the number of priority field depending on the |flags|. If - * |flags| has neither NGHTTP2_FLAG_PRIORITY_GROUP nor - * NGHTTP2_FLAG_PRIORITY_DEPENDENCY set, return 0. - */ -size_t nghttp2_frame_priority_len(uint8_t flags); - -/** - * Packs the |pri_spec| in |buf|. This function assumes |buf| has - * enough space for serialization. - */ -void nghttp2_frame_pack_priority_spec(uint8_t *buf, - const nghttp2_priority_spec *pri_spec); - -/** - * Unpacks the priority specification from payload |payload| of length - * |payloadlen| to |pri_spec|. The |flags| is used to determine what - * kind of priority specification is in |payload|. This function - * assumes the |payload| contains whole priority specification. - */ -void nghttp2_frame_unpack_priority_spec(nghttp2_priority_spec *pri_spec, - const uint8_t *payload); - -/* - * Returns the offset from the HEADERS frame payload where the - * compressed header block starts. The frame payload does not include - * frame header. - */ -size_t nghttp2_frame_headers_payload_nv_offset(nghttp2_headers *frame); - -/* - * Packs HEADERS frame |frame| in wire format and store it in |bufs|. - * This function expands |bufs| as necessary to store frame. - * - * The caller must make sure that nghttp2_bufs_reset(bufs) is called - * before calling this function. - * - * frame->hd.length is assigned after length is determined during - * packing process. CONTINUATION frames are also serialized in this - * function. This function does not handle padding. - * - * This function returns 0 if it succeeds, or returns one of the - * following negative error codes: - * - * NGHTTP2_ERR_HEADER_COMP - * The deflate operation failed. - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -int nghttp2_frame_pack_headers(nghttp2_bufs *bufs, nghttp2_headers *frame, - nghttp2_hd_deflater *deflater); - -/* - * Unpacks HEADERS frame byte sequence into |frame|. This function - * only unapcks bytes that come before name/value header block and - * after possible Pad Length field. - * - * This function always succeeds and returns 0. - */ -int nghttp2_frame_unpack_headers_payload(nghttp2_headers *frame, - const uint8_t *payload); - -/* - * Packs PRIORITY frame |frame| in wire 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(nghttp2_bufs *bufs, nghttp2_priority *frame); - -/* - * Unpacks PRIORITY wire format into |frame|. - */ -void nghttp2_frame_unpack_priority_payload(nghttp2_priority *frame, - const uint8_t *payload); - -/* - * Packs RST_STREAM 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_rst_stream(nghttp2_bufs *bufs, - nghttp2_rst_stream *frame); - -/* - * Unpacks RST_STREAM frame byte sequence into |frame|. - */ -void nghttp2_frame_unpack_rst_stream_payload(nghttp2_rst_stream *frame, - const uint8_t *payload); - -/* - * Packs SETTINGS frame |frame| in wire format and store it in - * |bufs|. - * - * The caller must make sure that nghttp2_bufs_reset(bufs) is called - * before calling this function. - * - * This function returns 0 if it succeeds, or returns one of the - * following negative error codes: - * - * NGHTTP2_ERR_FRAME_SIZE_ERROR - * The length of the frame is too large. - */ -int nghttp2_frame_pack_settings(nghttp2_bufs *bufs, nghttp2_settings *frame); - -/* - * Packs the |iv|, which includes |niv| entries, in the |buf|, - * assuming the |buf| has at least 8 * |niv| bytes. - * - * Returns the number of bytes written into the |buf|. - */ -size_t nghttp2_frame_pack_settings_payload(uint8_t *buf, - const nghttp2_settings_entry *iv, - size_t niv); - -void nghttp2_frame_unpack_settings_entry(nghttp2_settings_entry *iv, - const uint8_t *payload); - -/* - * Initializes payload of frame->settings. The |frame| takes - * ownership of |iv|. - */ -void nghttp2_frame_unpack_settings_payload(nghttp2_settings *frame, - nghttp2_settings_entry *iv, - size_t niv); - -/* - * Unpacks SETTINGS payload into |*iv_ptr|. The number of entries are - * assigned to the |*niv_ptr|. This function allocates enough memory - * to store the result in |*iv_ptr|. The caller is responsible to free - * |*iv_ptr| after its use. - * - * This function returns 0 if it succeeds or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -int nghttp2_frame_unpack_settings_payload2(nghttp2_settings_entry **iv_ptr, - size_t *niv_ptr, - const uint8_t *payload, - size_t payloadlen, nghttp2_mem *mem); - -/* - * Packs PUSH_PROMISE frame |frame| in wire format and store it in - * |bufs|. This function expands |bufs| as necessary to store - * frame. - * - * The caller must make sure that nghttp2_bufs_reset(bufs) is called - * before calling this function. - * - * frame->hd.length is assigned after length is determined during - * packing process. CONTINUATION frames are also serialized in this - * function. This function does not handle padding. - * - * This function returns 0 if it succeeds, or returns one of the - * following negative error codes: - * - * NGHTTP2_ERR_HEADER_COMP - * The deflate operation failed. - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -int nghttp2_frame_pack_push_promise(nghttp2_bufs *bufs, - nghttp2_push_promise *frame, - nghttp2_hd_deflater *deflater); - -/* - * Unpacks PUSH_PROMISE frame byte sequence into |frame|. This - * function only unapcks bytes that come before name/value header - * block and after possible Pad Length field. - * - * This function returns 0 if it succeeds or one of the following - * negative error codes: - * - * NGHTTP2_ERR_PROTO - * TODO END_HEADERS flag is not set - */ -int nghttp2_frame_unpack_push_promise_payload(nghttp2_push_promise *frame, - const uint8_t *payload); - -/* - * Packs PING frame |frame| in wire 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_ping(nghttp2_bufs *bufs, nghttp2_ping *frame); - -/* - * Unpacks PING wire format into |frame|. - */ -void nghttp2_frame_unpack_ping_payload(nghttp2_ping *frame, - const uint8_t *payload); - -/* - * Packs GOAWAY frame |frame| in wire format and store it in |bufs|. - * This function expands |bufs| as necessary to store frame. - * - * The caller must make sure that nghttp2_bufs_reset(bufs) is called - * before calling this function. - * - * This function returns 0 if it succeeds or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - * NGHTTP2_ERR_FRAME_SIZE_ERROR - * The length of the frame is too large. - */ -int nghttp2_frame_pack_goaway(nghttp2_bufs *bufs, nghttp2_goaway *frame); - -/* - * Unpacks GOAWAY wire format into |frame|. The |payload| of length - * |payloadlen| contains first 8 bytes of payload. The - * |var_gift_payload| of length |var_gift_payloadlen| contains - * remaining payload and its buffer is gifted to the function and then - * |frame|. The |var_gift_payloadlen| must be freed by - * nghttp2_frame_goaway_free(). - */ -void nghttp2_frame_unpack_goaway_payload(nghttp2_goaway *frame, - const uint8_t *payload, - uint8_t *var_gift_payload, - size_t var_gift_payloadlen); - -/* - * Unpacks GOAWAY wire format into |frame|. This function only exists - * for unit test. After allocating buffer for debug data, this - * function internally calls nghttp2_frame_unpack_goaway_payload(). - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -int nghttp2_frame_unpack_goaway_payload2(nghttp2_goaway *frame, - const uint8_t *payload, - size_t payloadlen, nghttp2_mem *mem); - -/* - * Packs WINDOW_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_window_update(nghttp2_bufs *bufs, - nghttp2_window_update *frame); - -/* - * Unpacks WINDOW_UPDATE frame byte sequence into |frame|. - */ -void nghttp2_frame_unpack_window_update_payload(nghttp2_window_update *frame, - const uint8_t *payload); - -/* - * Packs ALTSVC 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_altsvc(nghttp2_bufs *bufs, nghttp2_extension *ext); - -/* - * Unpacks ALTSVC wire format into |frame|. The |payload| of - * |payloadlen| bytes contains frame payload. This function assumes - * that frame->payload points to the nghttp2_ext_altsvc object. - * - * This function always succeeds and returns 0. - */ -void nghttp2_frame_unpack_altsvc_payload(nghttp2_extension *frame, - size_t origin_len, uint8_t *payload, - size_t payloadlen); - -/* - * Unpacks ALTSVC wire format into |frame|. This function only exists - * for unit test. After allocating buffer for fields, this function - * internally calls nghttp2_frame_unpack_altsvc_payload(). - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - * NGHTTP2_ERR_FRAME_SIZE_ERROR - * The payload is too small. - */ -int nghttp2_frame_unpack_altsvc_payload2(nghttp2_extension *frame, - const uint8_t *payload, - size_t payloadlen, nghttp2_mem *mem); - -/* +} nghttp2_ext_frame_payload; + +void nghttp2_frame_pack_frame_hd(uint8_t *buf, const nghttp2_frame_hd *hd); + +void nghttp2_frame_unpack_frame_hd(nghttp2_frame_hd *hd, const uint8_t *buf); + +/** + * Initializes frame header |hd| with given parameters. Reserved bit + * is set to 0. + */ +void nghttp2_frame_hd_init(nghttp2_frame_hd *hd, size_t length, uint8_t type, + uint8_t flags, int32_t stream_id); + +/** + * Returns the number of priority field depending on the |flags|. If + * |flags| has neither NGHTTP2_FLAG_PRIORITY_GROUP nor + * NGHTTP2_FLAG_PRIORITY_DEPENDENCY set, return 0. + */ +size_t nghttp2_frame_priority_len(uint8_t flags); + +/** + * Packs the |pri_spec| in |buf|. This function assumes |buf| has + * enough space for serialization. + */ +void nghttp2_frame_pack_priority_spec(uint8_t *buf, + const nghttp2_priority_spec *pri_spec); + +/** + * Unpacks the priority specification from payload |payload| of length + * |payloadlen| to |pri_spec|. The |flags| is used to determine what + * kind of priority specification is in |payload|. This function + * assumes the |payload| contains whole priority specification. + */ +void nghttp2_frame_unpack_priority_spec(nghttp2_priority_spec *pri_spec, + const uint8_t *payload); + +/* + * Returns the offset from the HEADERS frame payload where the + * compressed header block starts. The frame payload does not include + * frame header. + */ +size_t nghttp2_frame_headers_payload_nv_offset(nghttp2_headers *frame); + +/* + * Packs HEADERS frame |frame| in wire format and store it in |bufs|. + * This function expands |bufs| as necessary to store frame. + * + * The caller must make sure that nghttp2_bufs_reset(bufs) is called + * before calling this function. + * + * frame->hd.length is assigned after length is determined during + * packing process. CONTINUATION frames are also serialized in this + * function. This function does not handle padding. + * + * This function returns 0 if it succeeds, or returns one of the + * following negative error codes: + * + * NGHTTP2_ERR_HEADER_COMP + * The deflate operation failed. + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_frame_pack_headers(nghttp2_bufs *bufs, nghttp2_headers *frame, + nghttp2_hd_deflater *deflater); + +/* + * Unpacks HEADERS frame byte sequence into |frame|. This function + * only unapcks bytes that come before name/value header block and + * after possible Pad Length field. + * + * This function always succeeds and returns 0. + */ +int nghttp2_frame_unpack_headers_payload(nghttp2_headers *frame, + const uint8_t *payload); + +/* + * Packs PRIORITY frame |frame| in wire 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(nghttp2_bufs *bufs, nghttp2_priority *frame); + +/* + * Unpacks PRIORITY wire format into |frame|. + */ +void nghttp2_frame_unpack_priority_payload(nghttp2_priority *frame, + const uint8_t *payload); + +/* + * Packs RST_STREAM 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_rst_stream(nghttp2_bufs *bufs, + nghttp2_rst_stream *frame); + +/* + * Unpacks RST_STREAM frame byte sequence into |frame|. + */ +void nghttp2_frame_unpack_rst_stream_payload(nghttp2_rst_stream *frame, + const uint8_t *payload); + +/* + * Packs SETTINGS frame |frame| in wire format and store it in + * |bufs|. + * + * The caller must make sure that nghttp2_bufs_reset(bufs) is called + * before calling this function. + * + * This function returns 0 if it succeeds, or returns one of the + * following negative error codes: + * + * NGHTTP2_ERR_FRAME_SIZE_ERROR + * The length of the frame is too large. + */ +int nghttp2_frame_pack_settings(nghttp2_bufs *bufs, nghttp2_settings *frame); + +/* + * Packs the |iv|, which includes |niv| entries, in the |buf|, + * assuming the |buf| has at least 8 * |niv| bytes. + * + * Returns the number of bytes written into the |buf|. + */ +size_t nghttp2_frame_pack_settings_payload(uint8_t *buf, + const nghttp2_settings_entry *iv, + size_t niv); + +void nghttp2_frame_unpack_settings_entry(nghttp2_settings_entry *iv, + const uint8_t *payload); + +/* + * Initializes payload of frame->settings. The |frame| takes + * ownership of |iv|. + */ +void nghttp2_frame_unpack_settings_payload(nghttp2_settings *frame, + nghttp2_settings_entry *iv, + size_t niv); + +/* + * Unpacks SETTINGS payload into |*iv_ptr|. The number of entries are + * assigned to the |*niv_ptr|. This function allocates enough memory + * to store the result in |*iv_ptr|. The caller is responsible to free + * |*iv_ptr| after its use. + * + * This function returns 0 if it succeeds or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_frame_unpack_settings_payload2(nghttp2_settings_entry **iv_ptr, + size_t *niv_ptr, + const uint8_t *payload, + size_t payloadlen, nghttp2_mem *mem); + +/* + * Packs PUSH_PROMISE frame |frame| in wire format and store it in + * |bufs|. This function expands |bufs| as necessary to store + * frame. + * + * The caller must make sure that nghttp2_bufs_reset(bufs) is called + * before calling this function. + * + * frame->hd.length is assigned after length is determined during + * packing process. CONTINUATION frames are also serialized in this + * function. This function does not handle padding. + * + * This function returns 0 if it succeeds, or returns one of the + * following negative error codes: + * + * NGHTTP2_ERR_HEADER_COMP + * The deflate operation failed. + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_frame_pack_push_promise(nghttp2_bufs *bufs, + nghttp2_push_promise *frame, + nghttp2_hd_deflater *deflater); + +/* + * Unpacks PUSH_PROMISE frame byte sequence into |frame|. This + * function only unapcks bytes that come before name/value header + * block and after possible Pad Length field. + * + * This function returns 0 if it succeeds or one of the following + * negative error codes: + * + * NGHTTP2_ERR_PROTO + * TODO END_HEADERS flag is not set + */ +int nghttp2_frame_unpack_push_promise_payload(nghttp2_push_promise *frame, + const uint8_t *payload); + +/* + * Packs PING frame |frame| in wire 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_ping(nghttp2_bufs *bufs, nghttp2_ping *frame); + +/* + * Unpacks PING wire format into |frame|. + */ +void nghttp2_frame_unpack_ping_payload(nghttp2_ping *frame, + const uint8_t *payload); + +/* + * Packs GOAWAY frame |frame| in wire format and store it in |bufs|. + * This function expands |bufs| as necessary to store frame. + * + * The caller must make sure that nghttp2_bufs_reset(bufs) is called + * before calling this function. + * + * This function returns 0 if it succeeds or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_FRAME_SIZE_ERROR + * The length of the frame is too large. + */ +int nghttp2_frame_pack_goaway(nghttp2_bufs *bufs, nghttp2_goaway *frame); + +/* + * Unpacks GOAWAY wire format into |frame|. The |payload| of length + * |payloadlen| contains first 8 bytes of payload. The + * |var_gift_payload| of length |var_gift_payloadlen| contains + * remaining payload and its buffer is gifted to the function and then + * |frame|. The |var_gift_payloadlen| must be freed by + * nghttp2_frame_goaway_free(). + */ +void nghttp2_frame_unpack_goaway_payload(nghttp2_goaway *frame, + const uint8_t *payload, + uint8_t *var_gift_payload, + size_t var_gift_payloadlen); + +/* + * Unpacks GOAWAY wire format into |frame|. This function only exists + * for unit test. After allocating buffer for debug data, this + * function internally calls nghttp2_frame_unpack_goaway_payload(). + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_frame_unpack_goaway_payload2(nghttp2_goaway *frame, + const uint8_t *payload, + size_t payloadlen, nghttp2_mem *mem); + +/* + * Packs WINDOW_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_window_update(nghttp2_bufs *bufs, + nghttp2_window_update *frame); + +/* + * Unpacks WINDOW_UPDATE frame byte sequence into |frame|. + */ +void nghttp2_frame_unpack_window_update_payload(nghttp2_window_update *frame, + const uint8_t *payload); + +/* + * Packs ALTSVC 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_altsvc(nghttp2_bufs *bufs, nghttp2_extension *ext); + +/* + * Unpacks ALTSVC wire format into |frame|. The |payload| of + * |payloadlen| bytes contains frame payload. This function assumes + * that frame->payload points to the nghttp2_ext_altsvc object. + * + * This function always succeeds and returns 0. + */ +void nghttp2_frame_unpack_altsvc_payload(nghttp2_extension *frame, + size_t origin_len, uint8_t *payload, + size_t payloadlen); + +/* + * Unpacks ALTSVC wire format into |frame|. This function only exists + * for unit test. After allocating buffer for fields, this function + * internally calls nghttp2_frame_unpack_altsvc_payload(). + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_FRAME_SIZE_ERROR + * The payload is too small. + */ +int nghttp2_frame_unpack_altsvc_payload2(nghttp2_extension *frame, + const uint8_t *payload, + size_t payloadlen, nghttp2_mem *mem); + +/* * Packs ORIGIN frame |frame| in wire frame format and store it in * |bufs|. * @@ -424,103 +424,103 @@ int nghttp2_frame_unpack_origin_payload(nghttp2_extension *frame, const uint8_t *payload, size_t payloadlen, nghttp2_mem *mem); /* - * Initializes HEADERS frame |frame| with given values. |frame| takes - * ownership of |nva|, so caller must not free it. If |stream_id| is - * not assigned yet, it must be -1. - */ -void nghttp2_frame_headers_init(nghttp2_headers *frame, uint8_t flags, - int32_t stream_id, nghttp2_headers_category cat, - const nghttp2_priority_spec *pri_spec, - nghttp2_nv *nva, size_t nvlen); - -void nghttp2_frame_headers_free(nghttp2_headers *frame, nghttp2_mem *mem); - -void nghttp2_frame_priority_init(nghttp2_priority *frame, int32_t stream_id, - const nghttp2_priority_spec *pri_spec); - -void nghttp2_frame_priority_free(nghttp2_priority *frame); - -void nghttp2_frame_rst_stream_init(nghttp2_rst_stream *frame, int32_t stream_id, - uint32_t error_code); - -void nghttp2_frame_rst_stream_free(nghttp2_rst_stream *frame); - -/* - * Initializes PUSH_PROMISE frame |frame| with given values. |frame| - * takes ownership of |nva|, so caller must not free it. - */ -void nghttp2_frame_push_promise_init(nghttp2_push_promise *frame, uint8_t flags, - int32_t stream_id, - int32_t promised_stream_id, - nghttp2_nv *nva, size_t nvlen); - -void nghttp2_frame_push_promise_free(nghttp2_push_promise *frame, - nghttp2_mem *mem); - -/* - * Initializes SETTINGS frame |frame| with given values. |frame| takes - * ownership of |iv|, so caller must not free it. The |flags| are - * bitwise-OR of one or more of nghttp2_settings_flag. - */ -void nghttp2_frame_settings_init(nghttp2_settings *frame, uint8_t flags, - nghttp2_settings_entry *iv, size_t niv); - -void nghttp2_frame_settings_free(nghttp2_settings *frame, nghttp2_mem *mem); - -/* - * Initializes PING frame |frame| with given values. If the - * |opqeue_data| is not NULL, it must point to 8 bytes memory region - * of data. The data pointed by |opaque_data| is copied. It can be - * NULL. In this case, 8 bytes NULL is used. - */ -void nghttp2_frame_ping_init(nghttp2_ping *frame, uint8_t flags, - const uint8_t *opque_data); - -void nghttp2_frame_ping_free(nghttp2_ping *frame); - -/* - * Initializes GOAWAY frame |frame| with given values. On success, - * this function takes ownership of |opaque_data|, so caller must not - * free it. If the |opaque_data_len| is 0, opaque_data could be NULL. - */ -void nghttp2_frame_goaway_init(nghttp2_goaway *frame, int32_t last_stream_id, - uint32_t error_code, uint8_t *opaque_data, - size_t opaque_data_len); - -void nghttp2_frame_goaway_free(nghttp2_goaway *frame, nghttp2_mem *mem); - -void nghttp2_frame_window_update_init(nghttp2_window_update *frame, - uint8_t flags, int32_t stream_id, - int32_t window_size_increment); - -void nghttp2_frame_window_update_free(nghttp2_window_update *frame); - -void nghttp2_frame_extension_init(nghttp2_extension *frame, uint8_t type, - uint8_t flags, int32_t stream_id, - void *payload); - -void nghttp2_frame_extension_free(nghttp2_extension *frame); - -/* - * Initializes ALTSVC frame |frame| with given values. This function - * assumes that frame->payload points to nghttp2_ext_altsvc object. - * Also |origin| and |field_value| are allocated in single buffer, - * starting |origin|. On success, this function takes ownership of - * |origin|, so caller must not free it. - */ -void nghttp2_frame_altsvc_init(nghttp2_extension *frame, int32_t stream_id, - uint8_t *origin, size_t origin_len, - uint8_t *field_value, size_t field_value_len); - -/* - * Frees up resources under |frame|. This function does not free - * nghttp2_ext_altsvc object pointed by frame->payload. This function - * only frees origin pointed by nghttp2_ext_altsvc.origin. Therefore, - * other fields must be allocated in the same buffer with origin. - */ -void nghttp2_frame_altsvc_free(nghttp2_extension *frame, nghttp2_mem *mem); - -/* + * Initializes HEADERS frame |frame| with given values. |frame| takes + * ownership of |nva|, so caller must not free it. If |stream_id| is + * not assigned yet, it must be -1. + */ +void nghttp2_frame_headers_init(nghttp2_headers *frame, uint8_t flags, + int32_t stream_id, nghttp2_headers_category cat, + const nghttp2_priority_spec *pri_spec, + nghttp2_nv *nva, size_t nvlen); + +void nghttp2_frame_headers_free(nghttp2_headers *frame, nghttp2_mem *mem); + +void nghttp2_frame_priority_init(nghttp2_priority *frame, int32_t stream_id, + const nghttp2_priority_spec *pri_spec); + +void nghttp2_frame_priority_free(nghttp2_priority *frame); + +void nghttp2_frame_rst_stream_init(nghttp2_rst_stream *frame, int32_t stream_id, + uint32_t error_code); + +void nghttp2_frame_rst_stream_free(nghttp2_rst_stream *frame); + +/* + * Initializes PUSH_PROMISE frame |frame| with given values. |frame| + * takes ownership of |nva|, so caller must not free it. + */ +void nghttp2_frame_push_promise_init(nghttp2_push_promise *frame, uint8_t flags, + int32_t stream_id, + int32_t promised_stream_id, + nghttp2_nv *nva, size_t nvlen); + +void nghttp2_frame_push_promise_free(nghttp2_push_promise *frame, + nghttp2_mem *mem); + +/* + * Initializes SETTINGS frame |frame| with given values. |frame| takes + * ownership of |iv|, so caller must not free it. The |flags| are + * bitwise-OR of one or more of nghttp2_settings_flag. + */ +void nghttp2_frame_settings_init(nghttp2_settings *frame, uint8_t flags, + nghttp2_settings_entry *iv, size_t niv); + +void nghttp2_frame_settings_free(nghttp2_settings *frame, nghttp2_mem *mem); + +/* + * Initializes PING frame |frame| with given values. If the + * |opqeue_data| is not NULL, it must point to 8 bytes memory region + * of data. The data pointed by |opaque_data| is copied. It can be + * NULL. In this case, 8 bytes NULL is used. + */ +void nghttp2_frame_ping_init(nghttp2_ping *frame, uint8_t flags, + const uint8_t *opque_data); + +void nghttp2_frame_ping_free(nghttp2_ping *frame); + +/* + * Initializes GOAWAY frame |frame| with given values. On success, + * this function takes ownership of |opaque_data|, so caller must not + * free it. If the |opaque_data_len| is 0, opaque_data could be NULL. + */ +void nghttp2_frame_goaway_init(nghttp2_goaway *frame, int32_t last_stream_id, + uint32_t error_code, uint8_t *opaque_data, + size_t opaque_data_len); + +void nghttp2_frame_goaway_free(nghttp2_goaway *frame, nghttp2_mem *mem); + +void nghttp2_frame_window_update_init(nghttp2_window_update *frame, + uint8_t flags, int32_t stream_id, + int32_t window_size_increment); + +void nghttp2_frame_window_update_free(nghttp2_window_update *frame); + +void nghttp2_frame_extension_init(nghttp2_extension *frame, uint8_t type, + uint8_t flags, int32_t stream_id, + void *payload); + +void nghttp2_frame_extension_free(nghttp2_extension *frame); + +/* + * Initializes ALTSVC frame |frame| with given values. This function + * assumes that frame->payload points to nghttp2_ext_altsvc object. + * Also |origin| and |field_value| are allocated in single buffer, + * starting |origin|. On success, this function takes ownership of + * |origin|, so caller must not free it. + */ +void nghttp2_frame_altsvc_init(nghttp2_extension *frame, int32_t stream_id, + uint8_t *origin, size_t origin_len, + uint8_t *field_value, size_t field_value_len); + +/* + * Frees up resources under |frame|. This function does not free + * nghttp2_ext_altsvc object pointed by frame->payload. This function + * only frees origin pointed by nghttp2_ext_altsvc.origin. Therefore, + * other fields must be allocated in the same buffer with origin. + */ +void nghttp2_frame_altsvc_free(nghttp2_extension *frame, nghttp2_mem *mem); + +/* * Initializes ORIGIN frame |frame| with given values. This function * assumes that frame->payload points to nghttp2_ext_origin object. * Also |ov| and the memory pointed by the field of its elements are @@ -539,86 +539,86 @@ void nghttp2_frame_origin_init(nghttp2_extension *frame, void nghttp2_frame_origin_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 - * returns 0, regardless of frame->hd.flags. - */ -size_t nghttp2_frame_trail_padlen(nghttp2_frame *frame, size_t padlen); - -void nghttp2_frame_data_init(nghttp2_data *frame, uint8_t flags, - int32_t stream_id); - -void nghttp2_frame_data_free(nghttp2_data *frame); - -/* - * Makes copy of |iv| and return the copy. The |niv| is the number of - * entries in |iv|. This function returns the pointer to the copy if - * it succeeds, or NULL. - */ -nghttp2_settings_entry *nghttp2_frame_iv_copy(const nghttp2_settings_entry *iv, - size_t niv, nghttp2_mem *mem); - -/* - * Sorts the |nva| in ascending order of name and value. If names are - * equivalent, sort them by value. - */ -void nghttp2_nv_array_sort(nghttp2_nv *nva, size_t nvlen); - -/* - * Copies name/value pairs from |nva|, which contains |nvlen| pairs, - * to |*nva_ptr|, which is dynamically allocated so that all items can - * be stored. The resultant name and value in nghttp2_nv are - * guaranteed to be NULL-terminated even if the input is not - * null-terminated. - * - * The |*nva_ptr| must be freed using nghttp2_nv_array_del(). - * - * This function returns 0 if it succeeds or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -int nghttp2_nv_array_copy(nghttp2_nv **nva_ptr, const nghttp2_nv *nva, - size_t nvlen, nghttp2_mem *mem); - -/* - * Returns nonzero if the name/value pair |a| equals to |b|. The name - * is compared in case-sensitive, because we ensure that this function - * is called after the name is lower-cased. - */ -int nghttp2_nv_equal(const nghttp2_nv *a, const nghttp2_nv *b); - -/* - * Frees |nva|. - */ -void nghttp2_nv_array_del(nghttp2_nv *nva, nghttp2_mem *mem); - -/* - * Checks that the |iv|, which includes |niv| entries, does not have - * invalid values. - * - * This function returns nonzero if it succeeds, or 0. - */ -int nghttp2_iv_check(const nghttp2_settings_entry *iv, size_t niv); - -/* - * Sets Pad Length field and flags and adjusts frame header position - * of each buffers in |bufs|. The number of padding is given in the - * |padlen| including Pad Length field. The |hd| is the frame header - * for the serialized data. This function fills zeros padding region - * unless framehd_only is nonzero. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - * NGHTTP2_ERR_FRAME_SIZE_ERROR - * The length of the resulting frame is too large. - */ -int nghttp2_frame_add_pad(nghttp2_bufs *bufs, nghttp2_frame_hd *hd, - size_t padlen, int framehd_only); - -#endif /* NGHTTP2_FRAME_H */ + * 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 + * returns 0, regardless of frame->hd.flags. + */ +size_t nghttp2_frame_trail_padlen(nghttp2_frame *frame, size_t padlen); + +void nghttp2_frame_data_init(nghttp2_data *frame, uint8_t flags, + int32_t stream_id); + +void nghttp2_frame_data_free(nghttp2_data *frame); + +/* + * Makes copy of |iv| and return the copy. The |niv| is the number of + * entries in |iv|. This function returns the pointer to the copy if + * it succeeds, or NULL. + */ +nghttp2_settings_entry *nghttp2_frame_iv_copy(const nghttp2_settings_entry *iv, + size_t niv, nghttp2_mem *mem); + +/* + * Sorts the |nva| in ascending order of name and value. If names are + * equivalent, sort them by value. + */ +void nghttp2_nv_array_sort(nghttp2_nv *nva, size_t nvlen); + +/* + * Copies name/value pairs from |nva|, which contains |nvlen| pairs, + * to |*nva_ptr|, which is dynamically allocated so that all items can + * be stored. The resultant name and value in nghttp2_nv are + * guaranteed to be NULL-terminated even if the input is not + * null-terminated. + * + * The |*nva_ptr| must be freed using nghttp2_nv_array_del(). + * + * This function returns 0 if it succeeds or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_nv_array_copy(nghttp2_nv **nva_ptr, const nghttp2_nv *nva, + size_t nvlen, nghttp2_mem *mem); + +/* + * Returns nonzero if the name/value pair |a| equals to |b|. The name + * is compared in case-sensitive, because we ensure that this function + * is called after the name is lower-cased. + */ +int nghttp2_nv_equal(const nghttp2_nv *a, const nghttp2_nv *b); + +/* + * Frees |nva|. + */ +void nghttp2_nv_array_del(nghttp2_nv *nva, nghttp2_mem *mem); + +/* + * Checks that the |iv|, which includes |niv| entries, does not have + * invalid values. + * + * This function returns nonzero if it succeeds, or 0. + */ +int nghttp2_iv_check(const nghttp2_settings_entry *iv, size_t niv); + +/* + * Sets Pad Length field and flags and adjusts frame header position + * of each buffers in |bufs|. The number of padding is given in the + * |padlen| including Pad Length field. The |hd| is the frame header + * for the serialized data. This function fills zeros padding region + * unless framehd_only is nonzero. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_FRAME_SIZE_ERROR + * The length of the resulting frame is too large. + */ +int nghttp2_frame_add_pad(nghttp2_bufs *bufs, nghttp2_frame_hd *hd, + size_t padlen, int framehd_only); + +#endif /* NGHTTP2_FRAME_H */ diff --git a/contrib/libs/nghttp2/lib/nghttp2_hd.c b/contrib/libs/nghttp2/lib/nghttp2_hd.c index 0691c47f68..5e86931525 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_hd.c +++ b/contrib/libs/nghttp2/lib/nghttp2_hd.c @@ -1,276 +1,276 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2013 Tatsuhiro Tsujikawa - * - * 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_hd.h" - -#include <string.h> -#include <assert.h> -#include <stdio.h> - -#include "nghttp2_helper.h" -#include "nghttp2_int.h" -#include "nghttp2_debug.h" - -/* Make scalar initialization form of nghttp2_hd_entry */ -#define MAKE_STATIC_ENT(N, V, T, H) \ - { \ - {NULL, NULL, (uint8_t *)(N), sizeof((N)) - 1, -1}, \ - {NULL, NULL, (uint8_t *)(V), sizeof((V)) - 1, -1}, \ - {(uint8_t *)(N), (uint8_t *)(V), sizeof((N)) - 1, sizeof((V)) - 1, 0}, \ - T, H \ - } - -/* Generated by mkstatictbl.py */ -/* 3rd parameter is nghttp2_token value for header field name. We use - first enum value if same header names are repeated (e.g., - :status). */ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * 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_hd.h" + +#include <string.h> +#include <assert.h> +#include <stdio.h> + +#include "nghttp2_helper.h" +#include "nghttp2_int.h" +#include "nghttp2_debug.h" + +/* Make scalar initialization form of nghttp2_hd_entry */ +#define MAKE_STATIC_ENT(N, V, T, H) \ + { \ + {NULL, NULL, (uint8_t *)(N), sizeof((N)) - 1, -1}, \ + {NULL, NULL, (uint8_t *)(V), sizeof((V)) - 1, -1}, \ + {(uint8_t *)(N), (uint8_t *)(V), sizeof((N)) - 1, sizeof((V)) - 1, 0}, \ + T, H \ + } + +/* Generated by mkstatictbl.py */ +/* 3rd parameter is nghttp2_token value for header field name. We use + first enum value if same header names are repeated (e.g., + :status). */ static const nghttp2_hd_static_entry static_table[] = { - MAKE_STATIC_ENT(":authority", "", 0, 3153725150u), - MAKE_STATIC_ENT(":method", "GET", 1, 695666056u), - MAKE_STATIC_ENT(":method", "POST", 1, 695666056u), - MAKE_STATIC_ENT(":path", "/", 3, 3292848686u), - MAKE_STATIC_ENT(":path", "/index.html", 3, 3292848686u), - MAKE_STATIC_ENT(":scheme", "http", 5, 2510477674u), - MAKE_STATIC_ENT(":scheme", "https", 5, 2510477674u), - MAKE_STATIC_ENT(":status", "200", 7, 4000288983u), - MAKE_STATIC_ENT(":status", "204", 7, 4000288983u), - MAKE_STATIC_ENT(":status", "206", 7, 4000288983u), - MAKE_STATIC_ENT(":status", "304", 7, 4000288983u), - MAKE_STATIC_ENT(":status", "400", 7, 4000288983u), - MAKE_STATIC_ENT(":status", "404", 7, 4000288983u), - MAKE_STATIC_ENT(":status", "500", 7, 4000288983u), - MAKE_STATIC_ENT("accept-charset", "", 14, 3664010344u), - MAKE_STATIC_ENT("accept-encoding", "gzip, deflate", 15, 3379649177u), - MAKE_STATIC_ENT("accept-language", "", 16, 1979086614u), - MAKE_STATIC_ENT("accept-ranges", "", 17, 1713753958u), - MAKE_STATIC_ENT("accept", "", 18, 136609321u), - MAKE_STATIC_ENT("access-control-allow-origin", "", 19, 2710797292u), - MAKE_STATIC_ENT("age", "", 20, 742476188u), - MAKE_STATIC_ENT("allow", "", 21, 2930878514u), - MAKE_STATIC_ENT("authorization", "", 22, 2436257726u), - MAKE_STATIC_ENT("cache-control", "", 23, 1355326669u), - MAKE_STATIC_ENT("content-disposition", "", 24, 3889184348u), - MAKE_STATIC_ENT("content-encoding", "", 25, 65203592u), - MAKE_STATIC_ENT("content-language", "", 26, 24973587u), - MAKE_STATIC_ENT("content-length", "", 27, 1308181789u), - MAKE_STATIC_ENT("content-location", "", 28, 2302364718u), - MAKE_STATIC_ENT("content-range", "", 29, 3555523146u), - MAKE_STATIC_ENT("content-type", "", 30, 4244048277u), - MAKE_STATIC_ENT("cookie", "", 31, 2007449791u), - MAKE_STATIC_ENT("date", "", 32, 3564297305u), - MAKE_STATIC_ENT("etag", "", 33, 113792960u), - MAKE_STATIC_ENT("expect", "", 34, 2530896728u), - MAKE_STATIC_ENT("expires", "", 35, 1049544579u), - MAKE_STATIC_ENT("from", "", 36, 2513272949u), - MAKE_STATIC_ENT("host", "", 37, 2952701295u), - MAKE_STATIC_ENT("if-match", "", 38, 3597694698u), - MAKE_STATIC_ENT("if-modified-since", "", 39, 2213050793u), - MAKE_STATIC_ENT("if-none-match", "", 40, 2536202615u), - MAKE_STATIC_ENT("if-range", "", 41, 2340978238u), - MAKE_STATIC_ENT("if-unmodified-since", "", 42, 3794814858u), - MAKE_STATIC_ENT("last-modified", "", 43, 3226950251u), - MAKE_STATIC_ENT("link", "", 44, 232457833u), - MAKE_STATIC_ENT("location", "", 45, 200649126u), - MAKE_STATIC_ENT("max-forwards", "", 46, 1826162134u), - MAKE_STATIC_ENT("proxy-authenticate", "", 47, 2709445359u), - MAKE_STATIC_ENT("proxy-authorization", "", 48, 2686392507u), - MAKE_STATIC_ENT("range", "", 49, 4208725202u), - MAKE_STATIC_ENT("referer", "", 50, 3969579366u), - MAKE_STATIC_ENT("refresh", "", 51, 3572655668u), - MAKE_STATIC_ENT("retry-after", "", 52, 3336180598u), - MAKE_STATIC_ENT("server", "", 53, 1085029842u), - MAKE_STATIC_ENT("set-cookie", "", 54, 1848371000u), - MAKE_STATIC_ENT("strict-transport-security", "", 55, 4138147361u), - MAKE_STATIC_ENT("transfer-encoding", "", 56, 3719590988u), - MAKE_STATIC_ENT("user-agent", "", 57, 606444526u), - MAKE_STATIC_ENT("vary", "", 58, 1085005381u), - MAKE_STATIC_ENT("via", "", 59, 1762798611u), - MAKE_STATIC_ENT("www-authenticate", "", 60, 779865858u), -}; - -static int memeq(const void *s1, const void *s2, size_t n) { - return memcmp(s1, s2, n) == 0; -} - -/* - * This function was generated by genlibtokenlookup.py. Inspired by - * h2o header lookup. https://github.com/h2o/h2o - */ -static int32_t lookup_token(const uint8_t *name, size_t namelen) { - switch (namelen) { - case 2: - switch (name[1]) { - case 'e': - if (memeq("t", name, 1)) { - return NGHTTP2_TOKEN_TE; - } - break; - } - break; - case 3: - switch (name[2]) { - case 'a': - if (memeq("vi", name, 2)) { - return NGHTTP2_TOKEN_VIA; - } - break; - case 'e': - if (memeq("ag", name, 2)) { - return NGHTTP2_TOKEN_AGE; - } - break; - } - break; - case 4: - switch (name[3]) { - case 'e': - if (memeq("dat", name, 3)) { - return NGHTTP2_TOKEN_DATE; - } - break; - case 'g': - if (memeq("eta", name, 3)) { - return NGHTTP2_TOKEN_ETAG; - } - break; - case 'k': - if (memeq("lin", name, 3)) { - return NGHTTP2_TOKEN_LINK; - } - break; - case 'm': - if (memeq("fro", name, 3)) { - return NGHTTP2_TOKEN_FROM; - } - break; - case 't': - if (memeq("hos", name, 3)) { - return NGHTTP2_TOKEN_HOST; - } - break; - case 'y': - if (memeq("var", name, 3)) { - return NGHTTP2_TOKEN_VARY; - } - break; - } - break; - case 5: - switch (name[4]) { - case 'e': - if (memeq("rang", name, 4)) { - return NGHTTP2_TOKEN_RANGE; - } - break; - case 'h': - if (memeq(":pat", name, 4)) { - return NGHTTP2_TOKEN__PATH; - } - break; - case 'w': - if (memeq("allo", name, 4)) { - return NGHTTP2_TOKEN_ALLOW; - } - break; - } - break; - case 6: - switch (name[5]) { - case 'e': - if (memeq("cooki", name, 5)) { - return NGHTTP2_TOKEN_COOKIE; - } - break; - case 'r': - if (memeq("serve", name, 5)) { - return NGHTTP2_TOKEN_SERVER; - } - break; - case 't': - if (memeq("accep", name, 5)) { - return NGHTTP2_TOKEN_ACCEPT; - } - if (memeq("expec", name, 5)) { - return NGHTTP2_TOKEN_EXPECT; - } - break; - } - break; - case 7: - switch (name[6]) { - case 'd': - if (memeq(":metho", name, 6)) { - return NGHTTP2_TOKEN__METHOD; - } - break; - case 'e': - if (memeq(":schem", name, 6)) { - return NGHTTP2_TOKEN__SCHEME; - } - if (memeq("upgrad", name, 6)) { - return NGHTTP2_TOKEN_UPGRADE; - } - break; - case 'h': - if (memeq("refres", name, 6)) { - return NGHTTP2_TOKEN_REFRESH; - } - break; - case 'r': - if (memeq("refere", name, 6)) { - return NGHTTP2_TOKEN_REFERER; - } - break; - case 's': - if (memeq(":statu", name, 6)) { - return NGHTTP2_TOKEN__STATUS; - } - if (memeq("expire", name, 6)) { - return NGHTTP2_TOKEN_EXPIRES; - } - break; - } - break; - case 8: - switch (name[7]) { - case 'e': - if (memeq("if-rang", name, 7)) { - return NGHTTP2_TOKEN_IF_RANGE; - } - break; - case 'h': - if (memeq("if-matc", name, 7)) { - return NGHTTP2_TOKEN_IF_MATCH; - } - break; - case 'n': - if (memeq("locatio", name, 7)) { - return NGHTTP2_TOKEN_LOCATION; - } - break; - } - break; + MAKE_STATIC_ENT(":authority", "", 0, 3153725150u), + MAKE_STATIC_ENT(":method", "GET", 1, 695666056u), + MAKE_STATIC_ENT(":method", "POST", 1, 695666056u), + MAKE_STATIC_ENT(":path", "/", 3, 3292848686u), + MAKE_STATIC_ENT(":path", "/index.html", 3, 3292848686u), + MAKE_STATIC_ENT(":scheme", "http", 5, 2510477674u), + MAKE_STATIC_ENT(":scheme", "https", 5, 2510477674u), + MAKE_STATIC_ENT(":status", "200", 7, 4000288983u), + MAKE_STATIC_ENT(":status", "204", 7, 4000288983u), + MAKE_STATIC_ENT(":status", "206", 7, 4000288983u), + MAKE_STATIC_ENT(":status", "304", 7, 4000288983u), + MAKE_STATIC_ENT(":status", "400", 7, 4000288983u), + MAKE_STATIC_ENT(":status", "404", 7, 4000288983u), + MAKE_STATIC_ENT(":status", "500", 7, 4000288983u), + MAKE_STATIC_ENT("accept-charset", "", 14, 3664010344u), + MAKE_STATIC_ENT("accept-encoding", "gzip, deflate", 15, 3379649177u), + MAKE_STATIC_ENT("accept-language", "", 16, 1979086614u), + MAKE_STATIC_ENT("accept-ranges", "", 17, 1713753958u), + MAKE_STATIC_ENT("accept", "", 18, 136609321u), + MAKE_STATIC_ENT("access-control-allow-origin", "", 19, 2710797292u), + MAKE_STATIC_ENT("age", "", 20, 742476188u), + MAKE_STATIC_ENT("allow", "", 21, 2930878514u), + MAKE_STATIC_ENT("authorization", "", 22, 2436257726u), + MAKE_STATIC_ENT("cache-control", "", 23, 1355326669u), + MAKE_STATIC_ENT("content-disposition", "", 24, 3889184348u), + MAKE_STATIC_ENT("content-encoding", "", 25, 65203592u), + MAKE_STATIC_ENT("content-language", "", 26, 24973587u), + MAKE_STATIC_ENT("content-length", "", 27, 1308181789u), + MAKE_STATIC_ENT("content-location", "", 28, 2302364718u), + MAKE_STATIC_ENT("content-range", "", 29, 3555523146u), + MAKE_STATIC_ENT("content-type", "", 30, 4244048277u), + MAKE_STATIC_ENT("cookie", "", 31, 2007449791u), + MAKE_STATIC_ENT("date", "", 32, 3564297305u), + MAKE_STATIC_ENT("etag", "", 33, 113792960u), + MAKE_STATIC_ENT("expect", "", 34, 2530896728u), + MAKE_STATIC_ENT("expires", "", 35, 1049544579u), + MAKE_STATIC_ENT("from", "", 36, 2513272949u), + MAKE_STATIC_ENT("host", "", 37, 2952701295u), + MAKE_STATIC_ENT("if-match", "", 38, 3597694698u), + MAKE_STATIC_ENT("if-modified-since", "", 39, 2213050793u), + MAKE_STATIC_ENT("if-none-match", "", 40, 2536202615u), + MAKE_STATIC_ENT("if-range", "", 41, 2340978238u), + MAKE_STATIC_ENT("if-unmodified-since", "", 42, 3794814858u), + MAKE_STATIC_ENT("last-modified", "", 43, 3226950251u), + MAKE_STATIC_ENT("link", "", 44, 232457833u), + MAKE_STATIC_ENT("location", "", 45, 200649126u), + MAKE_STATIC_ENT("max-forwards", "", 46, 1826162134u), + MAKE_STATIC_ENT("proxy-authenticate", "", 47, 2709445359u), + MAKE_STATIC_ENT("proxy-authorization", "", 48, 2686392507u), + MAKE_STATIC_ENT("range", "", 49, 4208725202u), + MAKE_STATIC_ENT("referer", "", 50, 3969579366u), + MAKE_STATIC_ENT("refresh", "", 51, 3572655668u), + MAKE_STATIC_ENT("retry-after", "", 52, 3336180598u), + MAKE_STATIC_ENT("server", "", 53, 1085029842u), + MAKE_STATIC_ENT("set-cookie", "", 54, 1848371000u), + MAKE_STATIC_ENT("strict-transport-security", "", 55, 4138147361u), + MAKE_STATIC_ENT("transfer-encoding", "", 56, 3719590988u), + MAKE_STATIC_ENT("user-agent", "", 57, 606444526u), + MAKE_STATIC_ENT("vary", "", 58, 1085005381u), + MAKE_STATIC_ENT("via", "", 59, 1762798611u), + MAKE_STATIC_ENT("www-authenticate", "", 60, 779865858u), +}; + +static int memeq(const void *s1, const void *s2, size_t n) { + return memcmp(s1, s2, n) == 0; +} + +/* + * This function was generated by genlibtokenlookup.py. Inspired by + * h2o header lookup. https://github.com/h2o/h2o + */ +static int32_t lookup_token(const uint8_t *name, size_t namelen) { + switch (namelen) { + case 2: + switch (name[1]) { + case 'e': + if (memeq("t", name, 1)) { + return NGHTTP2_TOKEN_TE; + } + break; + } + break; + case 3: + switch (name[2]) { + case 'a': + if (memeq("vi", name, 2)) { + return NGHTTP2_TOKEN_VIA; + } + break; + case 'e': + if (memeq("ag", name, 2)) { + return NGHTTP2_TOKEN_AGE; + } + break; + } + break; + case 4: + switch (name[3]) { + case 'e': + if (memeq("dat", name, 3)) { + return NGHTTP2_TOKEN_DATE; + } + break; + case 'g': + if (memeq("eta", name, 3)) { + return NGHTTP2_TOKEN_ETAG; + } + break; + case 'k': + if (memeq("lin", name, 3)) { + return NGHTTP2_TOKEN_LINK; + } + break; + case 'm': + if (memeq("fro", name, 3)) { + return NGHTTP2_TOKEN_FROM; + } + break; + case 't': + if (memeq("hos", name, 3)) { + return NGHTTP2_TOKEN_HOST; + } + break; + case 'y': + if (memeq("var", name, 3)) { + return NGHTTP2_TOKEN_VARY; + } + break; + } + break; + case 5: + switch (name[4]) { + case 'e': + if (memeq("rang", name, 4)) { + return NGHTTP2_TOKEN_RANGE; + } + break; + case 'h': + if (memeq(":pat", name, 4)) { + return NGHTTP2_TOKEN__PATH; + } + break; + case 'w': + if (memeq("allo", name, 4)) { + return NGHTTP2_TOKEN_ALLOW; + } + break; + } + break; + case 6: + switch (name[5]) { + case 'e': + if (memeq("cooki", name, 5)) { + return NGHTTP2_TOKEN_COOKIE; + } + break; + case 'r': + if (memeq("serve", name, 5)) { + return NGHTTP2_TOKEN_SERVER; + } + break; + case 't': + if (memeq("accep", name, 5)) { + return NGHTTP2_TOKEN_ACCEPT; + } + if (memeq("expec", name, 5)) { + return NGHTTP2_TOKEN_EXPECT; + } + break; + } + break; + case 7: + switch (name[6]) { + case 'd': + if (memeq(":metho", name, 6)) { + return NGHTTP2_TOKEN__METHOD; + } + break; + case 'e': + if (memeq(":schem", name, 6)) { + return NGHTTP2_TOKEN__SCHEME; + } + if (memeq("upgrad", name, 6)) { + return NGHTTP2_TOKEN_UPGRADE; + } + break; + case 'h': + if (memeq("refres", name, 6)) { + return NGHTTP2_TOKEN_REFRESH; + } + break; + case 'r': + if (memeq("refere", name, 6)) { + return NGHTTP2_TOKEN_REFERER; + } + break; + case 's': + if (memeq(":statu", name, 6)) { + return NGHTTP2_TOKEN__STATUS; + } + if (memeq("expire", name, 6)) { + return NGHTTP2_TOKEN_EXPIRES; + } + break; + } + break; + case 8: + switch (name[7]) { + case 'e': + if (memeq("if-rang", name, 7)) { + return NGHTTP2_TOKEN_IF_RANGE; + } + break; + case 'h': + if (memeq("if-matc", name, 7)) { + return NGHTTP2_TOKEN_IF_MATCH; + } + break; + case 'n': + if (memeq("locatio", name, 7)) { + return NGHTTP2_TOKEN_LOCATION; + } + break; + } + break; case 9: switch (name[8]) { case 'l': @@ -280,2072 +280,2072 @@ static int32_t lookup_token(const uint8_t *name, size_t namelen) { break; } break; - case 10: - switch (name[9]) { - case 'e': - if (memeq("keep-aliv", name, 9)) { - return NGHTTP2_TOKEN_KEEP_ALIVE; - } - if (memeq("set-cooki", name, 9)) { - return NGHTTP2_TOKEN_SET_COOKIE; - } - break; - case 'n': - if (memeq("connectio", name, 9)) { - return NGHTTP2_TOKEN_CONNECTION; - } - break; - case 't': - if (memeq("user-agen", name, 9)) { - return NGHTTP2_TOKEN_USER_AGENT; - } - break; - case 'y': - if (memeq(":authorit", name, 9)) { - return NGHTTP2_TOKEN__AUTHORITY; - } - break; - } - break; - case 11: - switch (name[10]) { - case 'r': - if (memeq("retry-afte", name, 10)) { - return NGHTTP2_TOKEN_RETRY_AFTER; - } - break; - } - break; - case 12: - switch (name[11]) { - case 'e': - if (memeq("content-typ", name, 11)) { - return NGHTTP2_TOKEN_CONTENT_TYPE; - } - break; - case 's': - if (memeq("max-forward", name, 11)) { - return NGHTTP2_TOKEN_MAX_FORWARDS; - } - break; - } - break; - case 13: - switch (name[12]) { - case 'd': - if (memeq("last-modifie", name, 12)) { - return NGHTTP2_TOKEN_LAST_MODIFIED; - } - break; - case 'e': - if (memeq("content-rang", name, 12)) { - return NGHTTP2_TOKEN_CONTENT_RANGE; - } - break; - case 'h': - if (memeq("if-none-matc", name, 12)) { - return NGHTTP2_TOKEN_IF_NONE_MATCH; - } - break; - case 'l': - if (memeq("cache-contro", name, 12)) { - return NGHTTP2_TOKEN_CACHE_CONTROL; - } - break; - case 'n': - if (memeq("authorizatio", name, 12)) { - return NGHTTP2_TOKEN_AUTHORIZATION; - } - break; - case 's': - if (memeq("accept-range", name, 12)) { - return NGHTTP2_TOKEN_ACCEPT_RANGES; - } - break; - } - break; - case 14: - switch (name[13]) { - case 'h': - if (memeq("content-lengt", name, 13)) { - return NGHTTP2_TOKEN_CONTENT_LENGTH; - } - break; - case 't': - if (memeq("accept-charse", name, 13)) { - return NGHTTP2_TOKEN_ACCEPT_CHARSET; - } - break; - } - break; - case 15: - switch (name[14]) { - case 'e': - if (memeq("accept-languag", name, 14)) { - return NGHTTP2_TOKEN_ACCEPT_LANGUAGE; - } - break; - case 'g': - if (memeq("accept-encodin", name, 14)) { - return NGHTTP2_TOKEN_ACCEPT_ENCODING; - } - break; - } - break; - case 16: - switch (name[15]) { - case 'e': - if (memeq("content-languag", name, 15)) { - return NGHTTP2_TOKEN_CONTENT_LANGUAGE; - } - if (memeq("www-authenticat", name, 15)) { - return NGHTTP2_TOKEN_WWW_AUTHENTICATE; - } - break; - case 'g': - if (memeq("content-encodin", name, 15)) { - return NGHTTP2_TOKEN_CONTENT_ENCODING; - } - break; - case 'n': - if (memeq("content-locatio", name, 15)) { - return NGHTTP2_TOKEN_CONTENT_LOCATION; - } - if (memeq("proxy-connectio", name, 15)) { - return NGHTTP2_TOKEN_PROXY_CONNECTION; - } - break; - } - break; - case 17: - switch (name[16]) { - case 'e': - if (memeq("if-modified-sinc", name, 16)) { - return NGHTTP2_TOKEN_IF_MODIFIED_SINCE; - } - break; - case 'g': - if (memeq("transfer-encodin", name, 16)) { - return NGHTTP2_TOKEN_TRANSFER_ENCODING; - } - break; - } - break; - case 18: - switch (name[17]) { - case 'e': - if (memeq("proxy-authenticat", name, 17)) { - return NGHTTP2_TOKEN_PROXY_AUTHENTICATE; - } - break; - } - break; - case 19: - switch (name[18]) { - case 'e': - if (memeq("if-unmodified-sinc", name, 18)) { - return NGHTTP2_TOKEN_IF_UNMODIFIED_SINCE; - } - break; - case 'n': - if (memeq("content-dispositio", name, 18)) { - return NGHTTP2_TOKEN_CONTENT_DISPOSITION; - } - if (memeq("proxy-authorizatio", name, 18)) { - return NGHTTP2_TOKEN_PROXY_AUTHORIZATION; - } - break; - } - break; - case 25: - switch (name[24]) { - case 'y': - if (memeq("strict-transport-securit", name, 24)) { - return NGHTTP2_TOKEN_STRICT_TRANSPORT_SECURITY; - } - break; - } - break; - case 27: - switch (name[26]) { - case 'n': - if (memeq("access-control-allow-origi", name, 26)) { - return NGHTTP2_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN; - } - break; - } - break; - } - return -1; -} - -void nghttp2_hd_entry_init(nghttp2_hd_entry *ent, nghttp2_hd_nv *nv) { - ent->nv = *nv; - ent->cnv.name = nv->name->base; - ent->cnv.namelen = nv->name->len; - ent->cnv.value = nv->value->base; - ent->cnv.valuelen = nv->value->len; - ent->cnv.flags = nv->flags; - ent->next = NULL; - ent->hash = 0; - - nghttp2_rcbuf_incref(ent->nv.name); - nghttp2_rcbuf_incref(ent->nv.value); -} - -void nghttp2_hd_entry_free(nghttp2_hd_entry *ent) { - nghttp2_rcbuf_decref(ent->nv.value); - nghttp2_rcbuf_decref(ent->nv.name); -} - -static int name_eq(const nghttp2_hd_nv *a, const nghttp2_nv *b) { - return a->name->len == b->namelen && - memeq(a->name->base, b->name, b->namelen); -} - -static int value_eq(const nghttp2_hd_nv *a, const nghttp2_nv *b) { - return a->value->len == b->valuelen && - memeq(a->value->base, b->value, b->valuelen); -} - -static uint32_t name_hash(const nghttp2_nv *nv) { - /* 32 bit FNV-1a: http://isthe.com/chongo/tech/comp/fnv/ */ - uint32_t h = 2166136261u; - size_t i; - - for (i = 0; i < nv->namelen; ++i) { - h ^= nv->name[i]; - h += (h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24); - } - - return h; -} - -static void hd_map_init(nghttp2_hd_map *map) { - memset(map, 0, sizeof(nghttp2_hd_map)); -} - -static void hd_map_insert(nghttp2_hd_map *map, nghttp2_hd_entry *ent) { - nghttp2_hd_entry **bucket; - - bucket = &map->table[ent->hash & (HD_MAP_SIZE - 1)]; - - if (*bucket == NULL) { - *bucket = ent; - return; - } - - /* lower index is linked near the root */ - ent->next = *bucket; - *bucket = ent; -} - -static nghttp2_hd_entry *hd_map_find(nghttp2_hd_map *map, int *exact_match, - const nghttp2_nv *nv, int32_t token, - uint32_t hash, int name_only) { - nghttp2_hd_entry *p; - nghttp2_hd_entry *res = NULL; - - *exact_match = 0; - - for (p = map->table[hash & (HD_MAP_SIZE - 1)]; p; p = p->next) { - if (token != p->nv.token || - (token == -1 && (hash != p->hash || !name_eq(&p->nv, nv)))) { - continue; - } - if (!res) { - res = p; - if (name_only) { - break; - } - } - if (value_eq(&p->nv, nv)) { - res = p; - *exact_match = 1; - break; - } - } - - return res; -} - -static void hd_map_remove(nghttp2_hd_map *map, nghttp2_hd_entry *ent) { - nghttp2_hd_entry **dst; - - dst = &map->table[ent->hash & (HD_MAP_SIZE - 1)]; - - for (; *dst; dst = &(*dst)->next) { - if (*dst != ent) { - continue; - } - - *dst = ent->next; - ent->next = NULL; - return; - } -} - -static int hd_ringbuf_init(nghttp2_hd_ringbuf *ringbuf, size_t bufsize, - nghttp2_mem *mem) { - size_t size; - for (size = 1; size < bufsize; size <<= 1) - ; - ringbuf->buffer = nghttp2_mem_malloc(mem, sizeof(nghttp2_hd_entry *) * size); - if (ringbuf->buffer == NULL) { - return NGHTTP2_ERR_NOMEM; - } - ringbuf->mask = size - 1; - ringbuf->first = 0; - ringbuf->len = 0; - return 0; -} - -static nghttp2_hd_entry *hd_ringbuf_get(nghttp2_hd_ringbuf *ringbuf, - size_t idx) { - assert(idx < ringbuf->len); - return ringbuf->buffer[(ringbuf->first + idx) & ringbuf->mask]; -} - -static int hd_ringbuf_reserve(nghttp2_hd_ringbuf *ringbuf, size_t bufsize, - nghttp2_mem *mem) { - size_t i; - size_t size; - nghttp2_hd_entry **buffer; - - if (ringbuf->mask + 1 >= bufsize) { - return 0; - } - for (size = 1; size < bufsize; size <<= 1) - ; - buffer = nghttp2_mem_malloc(mem, sizeof(nghttp2_hd_entry *) * size); - if (buffer == NULL) { - return NGHTTP2_ERR_NOMEM; - } - for (i = 0; i < ringbuf->len; ++i) { - buffer[i] = hd_ringbuf_get(ringbuf, i); - } - nghttp2_mem_free(mem, ringbuf->buffer); - ringbuf->buffer = buffer; - ringbuf->mask = size - 1; - ringbuf->first = 0; - return 0; -} - -static void hd_ringbuf_free(nghttp2_hd_ringbuf *ringbuf, nghttp2_mem *mem) { - size_t i; - if (ringbuf == NULL) { - return; - } - for (i = 0; i < ringbuf->len; ++i) { - nghttp2_hd_entry *ent = hd_ringbuf_get(ringbuf, i); - - nghttp2_hd_entry_free(ent); - nghttp2_mem_free(mem, ent); - } - nghttp2_mem_free(mem, ringbuf->buffer); -} - -static int hd_ringbuf_push_front(nghttp2_hd_ringbuf *ringbuf, - nghttp2_hd_entry *ent, nghttp2_mem *mem) { - int rv; - - rv = hd_ringbuf_reserve(ringbuf, ringbuf->len + 1, mem); - - if (rv != 0) { - return rv; - } - - ringbuf->buffer[--ringbuf->first & ringbuf->mask] = ent; - ++ringbuf->len; - - return 0; -} - -static void hd_ringbuf_pop_back(nghttp2_hd_ringbuf *ringbuf) { - assert(ringbuf->len > 0); - --ringbuf->len; -} - -static int hd_context_init(nghttp2_hd_context *context, nghttp2_mem *mem) { - int rv; - context->mem = mem; - context->bad = 0; - context->hd_table_bufsize_max = NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE; - rv = hd_ringbuf_init( - &context->hd_table, - context->hd_table_bufsize_max / NGHTTP2_HD_ENTRY_OVERHEAD, mem); - if (rv != 0) { - return rv; - } - - context->hd_table_bufsize = 0; - context->next_seq = 0; - - return 0; -} - -static void hd_context_free(nghttp2_hd_context *context) { - hd_ringbuf_free(&context->hd_table, context->mem); -} - -int nghttp2_hd_deflate_init(nghttp2_hd_deflater *deflater, nghttp2_mem *mem) { - return nghttp2_hd_deflate_init2( - deflater, NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE, mem); -} - -int nghttp2_hd_deflate_init2(nghttp2_hd_deflater *deflater, - size_t max_deflate_dynamic_table_size, - nghttp2_mem *mem) { - int rv; - rv = hd_context_init(&deflater->ctx, mem); - if (rv != 0) { - return rv; - } - - hd_map_init(&deflater->map); - - if (max_deflate_dynamic_table_size < NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE) { - deflater->notify_table_size_change = 1; - deflater->ctx.hd_table_bufsize_max = max_deflate_dynamic_table_size; - } else { - deflater->notify_table_size_change = 0; - } - - deflater->deflate_hd_table_bufsize_max = max_deflate_dynamic_table_size; - deflater->min_hd_table_bufsize_max = UINT32_MAX; - - return 0; -} - -int nghttp2_hd_inflate_init(nghttp2_hd_inflater *inflater, nghttp2_mem *mem) { - int rv; - - rv = hd_context_init(&inflater->ctx, mem); - if (rv != 0) { - goto fail; - } - - inflater->settings_hd_table_bufsize_max = NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE; - inflater->min_hd_table_bufsize_max = UINT32_MAX; - - inflater->nv_name_keep = NULL; - inflater->nv_value_keep = NULL; - - inflater->opcode = NGHTTP2_HD_OPCODE_NONE; - inflater->state = NGHTTP2_HD_STATE_INFLATE_START; - - nghttp2_buf_init(&inflater->namebuf); - nghttp2_buf_init(&inflater->valuebuf); - - inflater->namercbuf = NULL; - inflater->valuercbuf = NULL; - - inflater->huffman_encoded = 0; - inflater->index = 0; - inflater->left = 0; - inflater->shift = 0; - inflater->index_required = 0; - inflater->no_index = 0; - - return 0; - -fail: - return rv; -} - -static void hd_inflate_keep_free(nghttp2_hd_inflater *inflater) { - nghttp2_rcbuf_decref(inflater->nv_value_keep); - nghttp2_rcbuf_decref(inflater->nv_name_keep); - - inflater->nv_value_keep = NULL; - inflater->nv_name_keep = NULL; -} - -void nghttp2_hd_deflate_free(nghttp2_hd_deflater *deflater) { - hd_context_free(&deflater->ctx); -} - -void nghttp2_hd_inflate_free(nghttp2_hd_inflater *inflater) { - hd_inflate_keep_free(inflater); - - nghttp2_rcbuf_decref(inflater->valuercbuf); - nghttp2_rcbuf_decref(inflater->namercbuf); - - hd_context_free(&inflater->ctx); -} - -static size_t entry_room(size_t namelen, size_t valuelen) { - return NGHTTP2_HD_ENTRY_OVERHEAD + namelen + valuelen; -} - -static void emit_header(nghttp2_hd_nv *nv_out, nghttp2_hd_nv *nv) { - DEBUGF("inflatehd: header emission: %s: %s\n", nv->name->base, - nv->value->base); - /* ent->ref may be 0. This happens if the encoder emits literal - block larger than header table capacity with indexing. */ - *nv_out = *nv; -} - -static size_t count_encoded_length(size_t n, size_t prefix) { - size_t k = (size_t)((1 << prefix) - 1); - size_t len = 0; - - if (n < k) { - return 1; - } - - n -= k; - ++len; - - for (; n >= 128; n >>= 7, ++len) - ; - - return len + 1; -} - -static size_t encode_length(uint8_t *buf, size_t n, size_t prefix) { - size_t k = (size_t)((1 << prefix) - 1); - uint8_t *begin = buf; - - *buf = (uint8_t)(*buf & ~k); - - if (n < k) { - *buf = (uint8_t)(*buf | n); - return 1; - } - - *buf = (uint8_t)(*buf | k); - ++buf; - - n -= k; - - for (; n >= 128; n >>= 7) { - *buf++ = (uint8_t)((1 << 7) | (n & 0x7f)); - } - - *buf++ = (uint8_t)n; - - return (size_t)(buf - begin); -} - -/* - * Decodes |prefix| prefixed integer stored from |in|. The |last| - * represents the 1 beyond the last of the valid contiguous memory - * region from |in|. The decoded integer must be less than or equal - * to UINT32_MAX. - * - * If the |initial| is nonzero, it is used as a initial value, this - * function assumes the |in| starts with intermediate data. - * - * An entire integer is decoded successfully, decoded, the |*fin| is - * set to nonzero. - * - * This function stores the decoded integer in |*res| if it succeed, - * including partial decoding (in this case, number of shift to make - * in the next call will be stored in |*shift_ptr|) and returns number - * of bytes processed, or returns -1, indicating decoding error. - */ -static ssize_t decode_length(uint32_t *res, size_t *shift_ptr, int *fin, - uint32_t initial, size_t shift, const uint8_t *in, - const uint8_t *last, size_t prefix) { - uint32_t k = (uint8_t)((1 << prefix) - 1); - uint32_t n = initial; - const uint8_t *start = in; - - *shift_ptr = 0; - *fin = 0; - - if (n == 0) { - if ((*in & k) != k) { - *res = (*in) & k; - *fin = 1; - return 1; - } - - n = k; - - if (++in == last) { - *res = n; - return (ssize_t)(in - start); - } - } - - for (; in != last; ++in, shift += 7) { - uint32_t add = *in & 0x7f; - - if (shift >= 32) { - DEBUGF("inflate: shift exponent overflow\n"); - return -1; - } - - if ((UINT32_MAX >> shift) < add) { - DEBUGF("inflate: integer overflow on shift\n"); - return -1; - } - - add <<= shift; - - if (UINT32_MAX - add < n) { - DEBUGF("inflate: integer overflow on addition\n"); - return -1; - } - - n += add; - - if ((*in & (1 << 7)) == 0) { - break; - } - } - - *shift_ptr = shift; - - if (in == last) { - *res = n; - return (ssize_t)(in - start); - } - - *res = n; - *fin = 1; - return (ssize_t)(in + 1 - start); -} - -static int emit_table_size(nghttp2_bufs *bufs, size_t table_size) { - int rv; - uint8_t *bufp; - size_t blocklen; - uint8_t sb[16]; - - DEBUGF("deflatehd: emit table_size=%zu\n", table_size); - - blocklen = count_encoded_length(table_size, 5); - - if (sizeof(sb) < blocklen) { - return NGHTTP2_ERR_HEADER_COMP; - } - - bufp = sb; - - *bufp = 0x20u; - - encode_length(bufp, table_size, 5); - - rv = nghttp2_bufs_add(bufs, sb, blocklen); - if (rv != 0) { - return rv; - } - - return 0; -} - -static int emit_indexed_block(nghttp2_bufs *bufs, size_t idx) { - int rv; - size_t blocklen; - uint8_t sb[16]; - uint8_t *bufp; - - blocklen = count_encoded_length(idx + 1, 7); - - DEBUGF("deflatehd: emit indexed index=%zu, %zu bytes\n", idx, blocklen); - - if (sizeof(sb) < blocklen) { - return NGHTTP2_ERR_HEADER_COMP; - } - - bufp = sb; - *bufp = 0x80u; - encode_length(bufp, idx + 1, 7); - - rv = nghttp2_bufs_add(bufs, sb, blocklen); - if (rv != 0) { - return rv; - } - - return 0; -} - -static int emit_string(nghttp2_bufs *bufs, const uint8_t *str, size_t len) { - int rv; - uint8_t sb[16]; - uint8_t *bufp; - size_t blocklen; - size_t enclen; - int huffman = 0; - - enclen = nghttp2_hd_huff_encode_count(str, len); - - if (enclen < len) { - huffman = 1; - } else { - enclen = len; - } - - blocklen = count_encoded_length(enclen, 7); - - DEBUGF("deflatehd: emit string str=%.*s, length=%zu, huffman=%d, " - "encoded_length=%zu\n", - (int)len, (const char *)str, len, huffman, enclen); - - if (sizeof(sb) < blocklen) { - return NGHTTP2_ERR_HEADER_COMP; - } - - bufp = sb; - *bufp = huffman ? 1 << 7 : 0; - encode_length(bufp, enclen, 7); - - rv = nghttp2_bufs_add(bufs, sb, blocklen); - if (rv != 0) { - return rv; - } - - if (huffman) { - rv = nghttp2_hd_huff_encode(bufs, str, len); - } else { - assert(enclen == len); - rv = nghttp2_bufs_add(bufs, str, len); - } - - return rv; -} - -static uint8_t pack_first_byte(int indexing_mode) { - switch (indexing_mode) { - case NGHTTP2_HD_WITH_INDEXING: - return 0x40u; - case NGHTTP2_HD_WITHOUT_INDEXING: - return 0; - case NGHTTP2_HD_NEVER_INDEXING: - return 0x10u; - default: - assert(0); - } - /* This is required to compile with android NDK r10d + - --enable-werror */ - return 0; -} - -static int emit_indname_block(nghttp2_bufs *bufs, size_t idx, - const nghttp2_nv *nv, int indexing_mode) { - int rv; - uint8_t *bufp; - size_t blocklen; - uint8_t sb[16]; - size_t prefixlen; - - if (indexing_mode == NGHTTP2_HD_WITH_INDEXING) { - prefixlen = 6; - } else { - prefixlen = 4; - } - - DEBUGF("deflatehd: emit indname index=%zu, valuelen=%zu, indexing_mode=%d\n", - idx, nv->valuelen, indexing_mode); - - blocklen = count_encoded_length(idx + 1, prefixlen); - - if (sizeof(sb) < blocklen) { - return NGHTTP2_ERR_HEADER_COMP; - } - - bufp = sb; - - *bufp = pack_first_byte(indexing_mode); - - encode_length(bufp, idx + 1, prefixlen); - - rv = nghttp2_bufs_add(bufs, sb, blocklen); - if (rv != 0) { - return rv; - } - - rv = emit_string(bufs, nv->value, nv->valuelen); - if (rv != 0) { - return rv; - } - - return 0; -} - -static int emit_newname_block(nghttp2_bufs *bufs, const nghttp2_nv *nv, - int indexing_mode) { - int rv; - - DEBUGF( - "deflatehd: emit newname namelen=%zu, valuelen=%zu, indexing_mode=%d\n", - nv->namelen, nv->valuelen, indexing_mode); - - rv = nghttp2_bufs_addb(bufs, pack_first_byte(indexing_mode)); - if (rv != 0) { - return rv; - } - - rv = emit_string(bufs, nv->name, nv->namelen); - if (rv != 0) { - return rv; - } - - rv = emit_string(bufs, nv->value, nv->valuelen); - if (rv != 0) { - return rv; - } - - return 0; -} - -static int add_hd_table_incremental(nghttp2_hd_context *context, - nghttp2_hd_nv *nv, nghttp2_hd_map *map, - uint32_t hash) { - int rv; - nghttp2_hd_entry *new_ent; - size_t room; - nghttp2_mem *mem; - - mem = context->mem; - room = entry_room(nv->name->len, nv->value->len); - - while (context->hd_table_bufsize + room > context->hd_table_bufsize_max && - context->hd_table.len > 0) { - - size_t idx = context->hd_table.len - 1; - nghttp2_hd_entry *ent = hd_ringbuf_get(&context->hd_table, idx); - - context->hd_table_bufsize -= - entry_room(ent->nv.name->len, ent->nv.value->len); - - DEBUGF("hpack: remove item from header table: %s: %s\n", - (char *)ent->nv.name->base, (char *)ent->nv.value->base); - - hd_ringbuf_pop_back(&context->hd_table); - if (map) { - hd_map_remove(map, ent); - } - - nghttp2_hd_entry_free(ent); - nghttp2_mem_free(mem, ent); - } - - if (room > context->hd_table_bufsize_max) { - /* The entry taking more than NGHTTP2_HD_MAX_BUFFER_SIZE is - immediately evicted. So we don't allocate memory for it. */ - return 0; - } - - new_ent = nghttp2_mem_malloc(mem, sizeof(nghttp2_hd_entry)); - if (new_ent == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - nghttp2_hd_entry_init(new_ent, nv); - - rv = hd_ringbuf_push_front(&context->hd_table, new_ent, mem); - - if (rv != 0) { - nghttp2_hd_entry_free(new_ent); - nghttp2_mem_free(mem, new_ent); - - return rv; - } - - new_ent->seq = context->next_seq++; - new_ent->hash = hash; - - if (map) { - hd_map_insert(map, new_ent); - } - - context->hd_table_bufsize += room; - - return 0; -} - -typedef struct { - ssize_t index; - /* Nonzero if both name and value are matched. */ - int name_value_match; -} search_result; - -static search_result search_static_table(const nghttp2_nv *nv, int32_t token, - int name_only) { - search_result res = {token, 0}; - int i; + case 10: + switch (name[9]) { + case 'e': + if (memeq("keep-aliv", name, 9)) { + return NGHTTP2_TOKEN_KEEP_ALIVE; + } + if (memeq("set-cooki", name, 9)) { + return NGHTTP2_TOKEN_SET_COOKIE; + } + break; + case 'n': + if (memeq("connectio", name, 9)) { + return NGHTTP2_TOKEN_CONNECTION; + } + break; + case 't': + if (memeq("user-agen", name, 9)) { + return NGHTTP2_TOKEN_USER_AGENT; + } + break; + case 'y': + if (memeq(":authorit", name, 9)) { + return NGHTTP2_TOKEN__AUTHORITY; + } + break; + } + break; + case 11: + switch (name[10]) { + case 'r': + if (memeq("retry-afte", name, 10)) { + return NGHTTP2_TOKEN_RETRY_AFTER; + } + break; + } + break; + case 12: + switch (name[11]) { + case 'e': + if (memeq("content-typ", name, 11)) { + return NGHTTP2_TOKEN_CONTENT_TYPE; + } + break; + case 's': + if (memeq("max-forward", name, 11)) { + return NGHTTP2_TOKEN_MAX_FORWARDS; + } + break; + } + break; + case 13: + switch (name[12]) { + case 'd': + if (memeq("last-modifie", name, 12)) { + return NGHTTP2_TOKEN_LAST_MODIFIED; + } + break; + case 'e': + if (memeq("content-rang", name, 12)) { + return NGHTTP2_TOKEN_CONTENT_RANGE; + } + break; + case 'h': + if (memeq("if-none-matc", name, 12)) { + return NGHTTP2_TOKEN_IF_NONE_MATCH; + } + break; + case 'l': + if (memeq("cache-contro", name, 12)) { + return NGHTTP2_TOKEN_CACHE_CONTROL; + } + break; + case 'n': + if (memeq("authorizatio", name, 12)) { + return NGHTTP2_TOKEN_AUTHORIZATION; + } + break; + case 's': + if (memeq("accept-range", name, 12)) { + return NGHTTP2_TOKEN_ACCEPT_RANGES; + } + break; + } + break; + case 14: + switch (name[13]) { + case 'h': + if (memeq("content-lengt", name, 13)) { + return NGHTTP2_TOKEN_CONTENT_LENGTH; + } + break; + case 't': + if (memeq("accept-charse", name, 13)) { + return NGHTTP2_TOKEN_ACCEPT_CHARSET; + } + break; + } + break; + case 15: + switch (name[14]) { + case 'e': + if (memeq("accept-languag", name, 14)) { + return NGHTTP2_TOKEN_ACCEPT_LANGUAGE; + } + break; + case 'g': + if (memeq("accept-encodin", name, 14)) { + return NGHTTP2_TOKEN_ACCEPT_ENCODING; + } + break; + } + break; + case 16: + switch (name[15]) { + case 'e': + if (memeq("content-languag", name, 15)) { + return NGHTTP2_TOKEN_CONTENT_LANGUAGE; + } + if (memeq("www-authenticat", name, 15)) { + return NGHTTP2_TOKEN_WWW_AUTHENTICATE; + } + break; + case 'g': + if (memeq("content-encodin", name, 15)) { + return NGHTTP2_TOKEN_CONTENT_ENCODING; + } + break; + case 'n': + if (memeq("content-locatio", name, 15)) { + return NGHTTP2_TOKEN_CONTENT_LOCATION; + } + if (memeq("proxy-connectio", name, 15)) { + return NGHTTP2_TOKEN_PROXY_CONNECTION; + } + break; + } + break; + case 17: + switch (name[16]) { + case 'e': + if (memeq("if-modified-sinc", name, 16)) { + return NGHTTP2_TOKEN_IF_MODIFIED_SINCE; + } + break; + case 'g': + if (memeq("transfer-encodin", name, 16)) { + return NGHTTP2_TOKEN_TRANSFER_ENCODING; + } + break; + } + break; + case 18: + switch (name[17]) { + case 'e': + if (memeq("proxy-authenticat", name, 17)) { + return NGHTTP2_TOKEN_PROXY_AUTHENTICATE; + } + break; + } + break; + case 19: + switch (name[18]) { + case 'e': + if (memeq("if-unmodified-sinc", name, 18)) { + return NGHTTP2_TOKEN_IF_UNMODIFIED_SINCE; + } + break; + case 'n': + if (memeq("content-dispositio", name, 18)) { + return NGHTTP2_TOKEN_CONTENT_DISPOSITION; + } + if (memeq("proxy-authorizatio", name, 18)) { + return NGHTTP2_TOKEN_PROXY_AUTHORIZATION; + } + break; + } + break; + case 25: + switch (name[24]) { + case 'y': + if (memeq("strict-transport-securit", name, 24)) { + return NGHTTP2_TOKEN_STRICT_TRANSPORT_SECURITY; + } + break; + } + break; + case 27: + switch (name[26]) { + case 'n': + if (memeq("access-control-allow-origi", name, 26)) { + return NGHTTP2_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN; + } + break; + } + break; + } + return -1; +} + +void nghttp2_hd_entry_init(nghttp2_hd_entry *ent, nghttp2_hd_nv *nv) { + ent->nv = *nv; + ent->cnv.name = nv->name->base; + ent->cnv.namelen = nv->name->len; + ent->cnv.value = nv->value->base; + ent->cnv.valuelen = nv->value->len; + ent->cnv.flags = nv->flags; + ent->next = NULL; + ent->hash = 0; + + nghttp2_rcbuf_incref(ent->nv.name); + nghttp2_rcbuf_incref(ent->nv.value); +} + +void nghttp2_hd_entry_free(nghttp2_hd_entry *ent) { + nghttp2_rcbuf_decref(ent->nv.value); + nghttp2_rcbuf_decref(ent->nv.name); +} + +static int name_eq(const nghttp2_hd_nv *a, const nghttp2_nv *b) { + return a->name->len == b->namelen && + memeq(a->name->base, b->name, b->namelen); +} + +static int value_eq(const nghttp2_hd_nv *a, const nghttp2_nv *b) { + return a->value->len == b->valuelen && + memeq(a->value->base, b->value, b->valuelen); +} + +static uint32_t name_hash(const nghttp2_nv *nv) { + /* 32 bit FNV-1a: http://isthe.com/chongo/tech/comp/fnv/ */ + uint32_t h = 2166136261u; + size_t i; + + for (i = 0; i < nv->namelen; ++i) { + h ^= nv->name[i]; + h += (h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24); + } + + return h; +} + +static void hd_map_init(nghttp2_hd_map *map) { + memset(map, 0, sizeof(nghttp2_hd_map)); +} + +static void hd_map_insert(nghttp2_hd_map *map, nghttp2_hd_entry *ent) { + nghttp2_hd_entry **bucket; + + bucket = &map->table[ent->hash & (HD_MAP_SIZE - 1)]; + + if (*bucket == NULL) { + *bucket = ent; + return; + } + + /* lower index is linked near the root */ + ent->next = *bucket; + *bucket = ent; +} + +static nghttp2_hd_entry *hd_map_find(nghttp2_hd_map *map, int *exact_match, + const nghttp2_nv *nv, int32_t token, + uint32_t hash, int name_only) { + nghttp2_hd_entry *p; + nghttp2_hd_entry *res = NULL; + + *exact_match = 0; + + for (p = map->table[hash & (HD_MAP_SIZE - 1)]; p; p = p->next) { + if (token != p->nv.token || + (token == -1 && (hash != p->hash || !name_eq(&p->nv, nv)))) { + continue; + } + if (!res) { + res = p; + if (name_only) { + break; + } + } + if (value_eq(&p->nv, nv)) { + res = p; + *exact_match = 1; + break; + } + } + + return res; +} + +static void hd_map_remove(nghttp2_hd_map *map, nghttp2_hd_entry *ent) { + nghttp2_hd_entry **dst; + + dst = &map->table[ent->hash & (HD_MAP_SIZE - 1)]; + + for (; *dst; dst = &(*dst)->next) { + if (*dst != ent) { + continue; + } + + *dst = ent->next; + ent->next = NULL; + return; + } +} + +static int hd_ringbuf_init(nghttp2_hd_ringbuf *ringbuf, size_t bufsize, + nghttp2_mem *mem) { + size_t size; + for (size = 1; size < bufsize; size <<= 1) + ; + ringbuf->buffer = nghttp2_mem_malloc(mem, sizeof(nghttp2_hd_entry *) * size); + if (ringbuf->buffer == NULL) { + return NGHTTP2_ERR_NOMEM; + } + ringbuf->mask = size - 1; + ringbuf->first = 0; + ringbuf->len = 0; + return 0; +} + +static nghttp2_hd_entry *hd_ringbuf_get(nghttp2_hd_ringbuf *ringbuf, + size_t idx) { + assert(idx < ringbuf->len); + return ringbuf->buffer[(ringbuf->first + idx) & ringbuf->mask]; +} + +static int hd_ringbuf_reserve(nghttp2_hd_ringbuf *ringbuf, size_t bufsize, + nghttp2_mem *mem) { + size_t i; + size_t size; + nghttp2_hd_entry **buffer; + + if (ringbuf->mask + 1 >= bufsize) { + return 0; + } + for (size = 1; size < bufsize; size <<= 1) + ; + buffer = nghttp2_mem_malloc(mem, sizeof(nghttp2_hd_entry *) * size); + if (buffer == NULL) { + return NGHTTP2_ERR_NOMEM; + } + for (i = 0; i < ringbuf->len; ++i) { + buffer[i] = hd_ringbuf_get(ringbuf, i); + } + nghttp2_mem_free(mem, ringbuf->buffer); + ringbuf->buffer = buffer; + ringbuf->mask = size - 1; + ringbuf->first = 0; + return 0; +} + +static void hd_ringbuf_free(nghttp2_hd_ringbuf *ringbuf, nghttp2_mem *mem) { + size_t i; + if (ringbuf == NULL) { + return; + } + for (i = 0; i < ringbuf->len; ++i) { + nghttp2_hd_entry *ent = hd_ringbuf_get(ringbuf, i); + + nghttp2_hd_entry_free(ent); + nghttp2_mem_free(mem, ent); + } + nghttp2_mem_free(mem, ringbuf->buffer); +} + +static int hd_ringbuf_push_front(nghttp2_hd_ringbuf *ringbuf, + nghttp2_hd_entry *ent, nghttp2_mem *mem) { + int rv; + + rv = hd_ringbuf_reserve(ringbuf, ringbuf->len + 1, mem); + + if (rv != 0) { + return rv; + } + + ringbuf->buffer[--ringbuf->first & ringbuf->mask] = ent; + ++ringbuf->len; + + return 0; +} + +static void hd_ringbuf_pop_back(nghttp2_hd_ringbuf *ringbuf) { + assert(ringbuf->len > 0); + --ringbuf->len; +} + +static int hd_context_init(nghttp2_hd_context *context, nghttp2_mem *mem) { + int rv; + context->mem = mem; + context->bad = 0; + context->hd_table_bufsize_max = NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE; + rv = hd_ringbuf_init( + &context->hd_table, + context->hd_table_bufsize_max / NGHTTP2_HD_ENTRY_OVERHEAD, mem); + if (rv != 0) { + return rv; + } + + context->hd_table_bufsize = 0; + context->next_seq = 0; + + return 0; +} + +static void hd_context_free(nghttp2_hd_context *context) { + hd_ringbuf_free(&context->hd_table, context->mem); +} + +int nghttp2_hd_deflate_init(nghttp2_hd_deflater *deflater, nghttp2_mem *mem) { + return nghttp2_hd_deflate_init2( + deflater, NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE, mem); +} + +int nghttp2_hd_deflate_init2(nghttp2_hd_deflater *deflater, + size_t max_deflate_dynamic_table_size, + nghttp2_mem *mem) { + int rv; + rv = hd_context_init(&deflater->ctx, mem); + if (rv != 0) { + return rv; + } + + hd_map_init(&deflater->map); + + if (max_deflate_dynamic_table_size < NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE) { + deflater->notify_table_size_change = 1; + deflater->ctx.hd_table_bufsize_max = max_deflate_dynamic_table_size; + } else { + deflater->notify_table_size_change = 0; + } + + deflater->deflate_hd_table_bufsize_max = max_deflate_dynamic_table_size; + deflater->min_hd_table_bufsize_max = UINT32_MAX; + + return 0; +} + +int nghttp2_hd_inflate_init(nghttp2_hd_inflater *inflater, nghttp2_mem *mem) { + int rv; + + rv = hd_context_init(&inflater->ctx, mem); + if (rv != 0) { + goto fail; + } + + inflater->settings_hd_table_bufsize_max = NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE; + inflater->min_hd_table_bufsize_max = UINT32_MAX; + + inflater->nv_name_keep = NULL; + inflater->nv_value_keep = NULL; + + inflater->opcode = NGHTTP2_HD_OPCODE_NONE; + inflater->state = NGHTTP2_HD_STATE_INFLATE_START; + + nghttp2_buf_init(&inflater->namebuf); + nghttp2_buf_init(&inflater->valuebuf); + + inflater->namercbuf = NULL; + inflater->valuercbuf = NULL; + + inflater->huffman_encoded = 0; + inflater->index = 0; + inflater->left = 0; + inflater->shift = 0; + inflater->index_required = 0; + inflater->no_index = 0; + + return 0; + +fail: + return rv; +} + +static void hd_inflate_keep_free(nghttp2_hd_inflater *inflater) { + nghttp2_rcbuf_decref(inflater->nv_value_keep); + nghttp2_rcbuf_decref(inflater->nv_name_keep); + + inflater->nv_value_keep = NULL; + inflater->nv_name_keep = NULL; +} + +void nghttp2_hd_deflate_free(nghttp2_hd_deflater *deflater) { + hd_context_free(&deflater->ctx); +} + +void nghttp2_hd_inflate_free(nghttp2_hd_inflater *inflater) { + hd_inflate_keep_free(inflater); + + nghttp2_rcbuf_decref(inflater->valuercbuf); + nghttp2_rcbuf_decref(inflater->namercbuf); + + hd_context_free(&inflater->ctx); +} + +static size_t entry_room(size_t namelen, size_t valuelen) { + return NGHTTP2_HD_ENTRY_OVERHEAD + namelen + valuelen; +} + +static void emit_header(nghttp2_hd_nv *nv_out, nghttp2_hd_nv *nv) { + DEBUGF("inflatehd: header emission: %s: %s\n", nv->name->base, + nv->value->base); + /* ent->ref may be 0. This happens if the encoder emits literal + block larger than header table capacity with indexing. */ + *nv_out = *nv; +} + +static size_t count_encoded_length(size_t n, size_t prefix) { + size_t k = (size_t)((1 << prefix) - 1); + size_t len = 0; + + if (n < k) { + return 1; + } + + n -= k; + ++len; + + for (; n >= 128; n >>= 7, ++len) + ; + + return len + 1; +} + +static size_t encode_length(uint8_t *buf, size_t n, size_t prefix) { + size_t k = (size_t)((1 << prefix) - 1); + uint8_t *begin = buf; + + *buf = (uint8_t)(*buf & ~k); + + if (n < k) { + *buf = (uint8_t)(*buf | n); + return 1; + } + + *buf = (uint8_t)(*buf | k); + ++buf; + + n -= k; + + for (; n >= 128; n >>= 7) { + *buf++ = (uint8_t)((1 << 7) | (n & 0x7f)); + } + + *buf++ = (uint8_t)n; + + return (size_t)(buf - begin); +} + +/* + * Decodes |prefix| prefixed integer stored from |in|. The |last| + * represents the 1 beyond the last of the valid contiguous memory + * region from |in|. The decoded integer must be less than or equal + * to UINT32_MAX. + * + * If the |initial| is nonzero, it is used as a initial value, this + * function assumes the |in| starts with intermediate data. + * + * An entire integer is decoded successfully, decoded, the |*fin| is + * set to nonzero. + * + * This function stores the decoded integer in |*res| if it succeed, + * including partial decoding (in this case, number of shift to make + * in the next call will be stored in |*shift_ptr|) and returns number + * of bytes processed, or returns -1, indicating decoding error. + */ +static ssize_t decode_length(uint32_t *res, size_t *shift_ptr, int *fin, + uint32_t initial, size_t shift, const uint8_t *in, + const uint8_t *last, size_t prefix) { + uint32_t k = (uint8_t)((1 << prefix) - 1); + uint32_t n = initial; + const uint8_t *start = in; + + *shift_ptr = 0; + *fin = 0; + + if (n == 0) { + if ((*in & k) != k) { + *res = (*in) & k; + *fin = 1; + return 1; + } + + n = k; + + if (++in == last) { + *res = n; + return (ssize_t)(in - start); + } + } + + for (; in != last; ++in, shift += 7) { + uint32_t add = *in & 0x7f; + + if (shift >= 32) { + DEBUGF("inflate: shift exponent overflow\n"); + return -1; + } + + if ((UINT32_MAX >> shift) < add) { + DEBUGF("inflate: integer overflow on shift\n"); + return -1; + } + + add <<= shift; + + if (UINT32_MAX - add < n) { + DEBUGF("inflate: integer overflow on addition\n"); + return -1; + } + + n += add; + + if ((*in & (1 << 7)) == 0) { + break; + } + } + + *shift_ptr = shift; + + if (in == last) { + *res = n; + return (ssize_t)(in - start); + } + + *res = n; + *fin = 1; + return (ssize_t)(in + 1 - start); +} + +static int emit_table_size(nghttp2_bufs *bufs, size_t table_size) { + int rv; + uint8_t *bufp; + size_t blocklen; + uint8_t sb[16]; + + DEBUGF("deflatehd: emit table_size=%zu\n", table_size); + + blocklen = count_encoded_length(table_size, 5); + + if (sizeof(sb) < blocklen) { + return NGHTTP2_ERR_HEADER_COMP; + } + + bufp = sb; + + *bufp = 0x20u; + + encode_length(bufp, table_size, 5); + + rv = nghttp2_bufs_add(bufs, sb, blocklen); + if (rv != 0) { + return rv; + } + + return 0; +} + +static int emit_indexed_block(nghttp2_bufs *bufs, size_t idx) { + int rv; + size_t blocklen; + uint8_t sb[16]; + uint8_t *bufp; + + blocklen = count_encoded_length(idx + 1, 7); + + DEBUGF("deflatehd: emit indexed index=%zu, %zu bytes\n", idx, blocklen); + + if (sizeof(sb) < blocklen) { + return NGHTTP2_ERR_HEADER_COMP; + } + + bufp = sb; + *bufp = 0x80u; + encode_length(bufp, idx + 1, 7); + + rv = nghttp2_bufs_add(bufs, sb, blocklen); + if (rv != 0) { + return rv; + } + + return 0; +} + +static int emit_string(nghttp2_bufs *bufs, const uint8_t *str, size_t len) { + int rv; + uint8_t sb[16]; + uint8_t *bufp; + size_t blocklen; + size_t enclen; + int huffman = 0; + + enclen = nghttp2_hd_huff_encode_count(str, len); + + if (enclen < len) { + huffman = 1; + } else { + enclen = len; + } + + blocklen = count_encoded_length(enclen, 7); + + DEBUGF("deflatehd: emit string str=%.*s, length=%zu, huffman=%d, " + "encoded_length=%zu\n", + (int)len, (const char *)str, len, huffman, enclen); + + if (sizeof(sb) < blocklen) { + return NGHTTP2_ERR_HEADER_COMP; + } + + bufp = sb; + *bufp = huffman ? 1 << 7 : 0; + encode_length(bufp, enclen, 7); + + rv = nghttp2_bufs_add(bufs, sb, blocklen); + if (rv != 0) { + return rv; + } + + if (huffman) { + rv = nghttp2_hd_huff_encode(bufs, str, len); + } else { + assert(enclen == len); + rv = nghttp2_bufs_add(bufs, str, len); + } + + return rv; +} + +static uint8_t pack_first_byte(int indexing_mode) { + switch (indexing_mode) { + case NGHTTP2_HD_WITH_INDEXING: + return 0x40u; + case NGHTTP2_HD_WITHOUT_INDEXING: + return 0; + case NGHTTP2_HD_NEVER_INDEXING: + return 0x10u; + default: + assert(0); + } + /* This is required to compile with android NDK r10d + + --enable-werror */ + return 0; +} + +static int emit_indname_block(nghttp2_bufs *bufs, size_t idx, + const nghttp2_nv *nv, int indexing_mode) { + int rv; + uint8_t *bufp; + size_t blocklen; + uint8_t sb[16]; + size_t prefixlen; + + if (indexing_mode == NGHTTP2_HD_WITH_INDEXING) { + prefixlen = 6; + } else { + prefixlen = 4; + } + + DEBUGF("deflatehd: emit indname index=%zu, valuelen=%zu, indexing_mode=%d\n", + idx, nv->valuelen, indexing_mode); + + blocklen = count_encoded_length(idx + 1, prefixlen); + + if (sizeof(sb) < blocklen) { + return NGHTTP2_ERR_HEADER_COMP; + } + + bufp = sb; + + *bufp = pack_first_byte(indexing_mode); + + encode_length(bufp, idx + 1, prefixlen); + + rv = nghttp2_bufs_add(bufs, sb, blocklen); + if (rv != 0) { + return rv; + } + + rv = emit_string(bufs, nv->value, nv->valuelen); + if (rv != 0) { + return rv; + } + + return 0; +} + +static int emit_newname_block(nghttp2_bufs *bufs, const nghttp2_nv *nv, + int indexing_mode) { + int rv; + + DEBUGF( + "deflatehd: emit newname namelen=%zu, valuelen=%zu, indexing_mode=%d\n", + nv->namelen, nv->valuelen, indexing_mode); + + rv = nghttp2_bufs_addb(bufs, pack_first_byte(indexing_mode)); + if (rv != 0) { + return rv; + } + + rv = emit_string(bufs, nv->name, nv->namelen); + if (rv != 0) { + return rv; + } + + rv = emit_string(bufs, nv->value, nv->valuelen); + if (rv != 0) { + return rv; + } + + return 0; +} + +static int add_hd_table_incremental(nghttp2_hd_context *context, + nghttp2_hd_nv *nv, nghttp2_hd_map *map, + uint32_t hash) { + int rv; + nghttp2_hd_entry *new_ent; + size_t room; + nghttp2_mem *mem; + + mem = context->mem; + room = entry_room(nv->name->len, nv->value->len); + + while (context->hd_table_bufsize + room > context->hd_table_bufsize_max && + context->hd_table.len > 0) { + + size_t idx = context->hd_table.len - 1; + nghttp2_hd_entry *ent = hd_ringbuf_get(&context->hd_table, idx); + + context->hd_table_bufsize -= + entry_room(ent->nv.name->len, ent->nv.value->len); + + DEBUGF("hpack: remove item from header table: %s: %s\n", + (char *)ent->nv.name->base, (char *)ent->nv.value->base); + + hd_ringbuf_pop_back(&context->hd_table); + if (map) { + hd_map_remove(map, ent); + } + + nghttp2_hd_entry_free(ent); + nghttp2_mem_free(mem, ent); + } + + if (room > context->hd_table_bufsize_max) { + /* The entry taking more than NGHTTP2_HD_MAX_BUFFER_SIZE is + immediately evicted. So we don't allocate memory for it. */ + return 0; + } + + new_ent = nghttp2_mem_malloc(mem, sizeof(nghttp2_hd_entry)); + if (new_ent == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + nghttp2_hd_entry_init(new_ent, nv); + + rv = hd_ringbuf_push_front(&context->hd_table, new_ent, mem); + + if (rv != 0) { + nghttp2_hd_entry_free(new_ent); + nghttp2_mem_free(mem, new_ent); + + return rv; + } + + new_ent->seq = context->next_seq++; + new_ent->hash = hash; + + if (map) { + hd_map_insert(map, new_ent); + } + + context->hd_table_bufsize += room; + + return 0; +} + +typedef struct { + ssize_t index; + /* Nonzero if both name and value are matched. */ + int name_value_match; +} search_result; + +static search_result search_static_table(const nghttp2_nv *nv, int32_t token, + int name_only) { + search_result res = {token, 0}; + int i; const nghttp2_hd_static_entry *ent; - - if (name_only) { - return res; - } - - for (i = token; - i <= NGHTTP2_TOKEN_WWW_AUTHENTICATE && static_table[i].token == token; - ++i) { - ent = &static_table[i]; - if (ent->value.len == nv->valuelen && - memcmp(ent->value.base, nv->value, nv->valuelen) == 0) { - res.index = i; - res.name_value_match = 1; - return res; - } - } - return res; -} - -static search_result search_hd_table(nghttp2_hd_context *context, - const nghttp2_nv *nv, int32_t token, - int indexing_mode, nghttp2_hd_map *map, - uint32_t hash) { - search_result res = {-1, 0}; + + if (name_only) { + return res; + } + + for (i = token; + i <= NGHTTP2_TOKEN_WWW_AUTHENTICATE && static_table[i].token == token; + ++i) { + ent = &static_table[i]; + if (ent->value.len == nv->valuelen && + memcmp(ent->value.base, nv->value, nv->valuelen) == 0) { + res.index = i; + res.name_value_match = 1; + return res; + } + } + return res; +} + +static search_result search_hd_table(nghttp2_hd_context *context, + const nghttp2_nv *nv, int32_t token, + int indexing_mode, nghttp2_hd_map *map, + uint32_t hash) { + search_result res = {-1, 0}; const nghttp2_hd_entry *ent; - int exact_match; - int name_only = indexing_mode == NGHTTP2_HD_NEVER_INDEXING; - - exact_match = 0; - ent = hd_map_find(map, &exact_match, nv, token, hash, name_only); - - if (!exact_match && token >= 0 && token <= NGHTTP2_TOKEN_WWW_AUTHENTICATE) { - return search_static_table(nv, token, name_only); - } - - if (ent == NULL) { - return res; - } - - res.index = - (ssize_t)(context->next_seq - 1 - ent->seq + NGHTTP2_STATIC_TABLE_LENGTH); - res.name_value_match = exact_match; - - return res; -} - -static void hd_context_shrink_table_size(nghttp2_hd_context *context, - nghttp2_hd_map *map) { - nghttp2_mem *mem; - - mem = context->mem; - - while (context->hd_table_bufsize > context->hd_table_bufsize_max && - context->hd_table.len > 0) { - size_t idx = context->hd_table.len - 1; - nghttp2_hd_entry *ent = hd_ringbuf_get(&context->hd_table, idx); - context->hd_table_bufsize -= - entry_room(ent->nv.name->len, ent->nv.value->len); - hd_ringbuf_pop_back(&context->hd_table); - if (map) { - hd_map_remove(map, ent); - } - - nghttp2_hd_entry_free(ent); - nghttp2_mem_free(mem, ent); - } -} - -int nghttp2_hd_deflate_change_table_size( - nghttp2_hd_deflater *deflater, size_t settings_max_dynamic_table_size) { - size_t next_bufsize = nghttp2_min(settings_max_dynamic_table_size, - deflater->deflate_hd_table_bufsize_max); - - deflater->ctx.hd_table_bufsize_max = next_bufsize; - - deflater->min_hd_table_bufsize_max = - nghttp2_min(deflater->min_hd_table_bufsize_max, next_bufsize); - - deflater->notify_table_size_change = 1; - - hd_context_shrink_table_size(&deflater->ctx, &deflater->map); - return 0; -} - -int nghttp2_hd_inflate_change_table_size( - nghttp2_hd_inflater *inflater, size_t settings_max_dynamic_table_size) { - switch (inflater->state) { - case NGHTTP2_HD_STATE_EXPECT_TABLE_SIZE: - case NGHTTP2_HD_STATE_INFLATE_START: - break; - default: - return NGHTTP2_ERR_INVALID_STATE; - } - - /* It seems that encoder is not required to send dynamic table size - update if the table size is not changed after applying - SETTINGS_HEADER_TABLE_SIZE. RFC 7541 is ambiguous here, but this - is the intention of the editor. If new maximum table size is - strictly smaller than the current negotiated maximum size, - encoder must send dynamic table size update. In other cases, we - cannot expect it to do so. */ - if (inflater->ctx.hd_table_bufsize_max > settings_max_dynamic_table_size) { - inflater->state = NGHTTP2_HD_STATE_EXPECT_TABLE_SIZE; - /* Remember minimum value, and validate that encoder sends the - value less than or equal to this. */ - inflater->min_hd_table_bufsize_max = settings_max_dynamic_table_size; - } - - inflater->settings_hd_table_bufsize_max = settings_max_dynamic_table_size; - - inflater->ctx.hd_table_bufsize_max = settings_max_dynamic_table_size; - - hd_context_shrink_table_size(&inflater->ctx, NULL); - return 0; -} - -#define INDEX_RANGE_VALID(context, idx) \ - ((idx) < (context)->hd_table.len + NGHTTP2_STATIC_TABLE_LENGTH) - -static size_t get_max_index(nghttp2_hd_context *context) { - return context->hd_table.len + NGHTTP2_STATIC_TABLE_LENGTH; -} - -nghttp2_hd_nv nghttp2_hd_table_get(nghttp2_hd_context *context, size_t idx) { - assert(INDEX_RANGE_VALID(context, idx)); - if (idx >= NGHTTP2_STATIC_TABLE_LENGTH) { - return hd_ringbuf_get(&context->hd_table, idx - NGHTTP2_STATIC_TABLE_LENGTH) - ->nv; - } else { + int exact_match; + int name_only = indexing_mode == NGHTTP2_HD_NEVER_INDEXING; + + exact_match = 0; + ent = hd_map_find(map, &exact_match, nv, token, hash, name_only); + + if (!exact_match && token >= 0 && token <= NGHTTP2_TOKEN_WWW_AUTHENTICATE) { + return search_static_table(nv, token, name_only); + } + + if (ent == NULL) { + return res; + } + + res.index = + (ssize_t)(context->next_seq - 1 - ent->seq + NGHTTP2_STATIC_TABLE_LENGTH); + res.name_value_match = exact_match; + + return res; +} + +static void hd_context_shrink_table_size(nghttp2_hd_context *context, + nghttp2_hd_map *map) { + nghttp2_mem *mem; + + mem = context->mem; + + while (context->hd_table_bufsize > context->hd_table_bufsize_max && + context->hd_table.len > 0) { + size_t idx = context->hd_table.len - 1; + nghttp2_hd_entry *ent = hd_ringbuf_get(&context->hd_table, idx); + context->hd_table_bufsize -= + entry_room(ent->nv.name->len, ent->nv.value->len); + hd_ringbuf_pop_back(&context->hd_table); + if (map) { + hd_map_remove(map, ent); + } + + nghttp2_hd_entry_free(ent); + nghttp2_mem_free(mem, ent); + } +} + +int nghttp2_hd_deflate_change_table_size( + nghttp2_hd_deflater *deflater, size_t settings_max_dynamic_table_size) { + size_t next_bufsize = nghttp2_min(settings_max_dynamic_table_size, + deflater->deflate_hd_table_bufsize_max); + + deflater->ctx.hd_table_bufsize_max = next_bufsize; + + deflater->min_hd_table_bufsize_max = + nghttp2_min(deflater->min_hd_table_bufsize_max, next_bufsize); + + deflater->notify_table_size_change = 1; + + hd_context_shrink_table_size(&deflater->ctx, &deflater->map); + return 0; +} + +int nghttp2_hd_inflate_change_table_size( + nghttp2_hd_inflater *inflater, size_t settings_max_dynamic_table_size) { + switch (inflater->state) { + case NGHTTP2_HD_STATE_EXPECT_TABLE_SIZE: + case NGHTTP2_HD_STATE_INFLATE_START: + break; + default: + return NGHTTP2_ERR_INVALID_STATE; + } + + /* It seems that encoder is not required to send dynamic table size + update if the table size is not changed after applying + SETTINGS_HEADER_TABLE_SIZE. RFC 7541 is ambiguous here, but this + is the intention of the editor. If new maximum table size is + strictly smaller than the current negotiated maximum size, + encoder must send dynamic table size update. In other cases, we + cannot expect it to do so. */ + if (inflater->ctx.hd_table_bufsize_max > settings_max_dynamic_table_size) { + inflater->state = NGHTTP2_HD_STATE_EXPECT_TABLE_SIZE; + /* Remember minimum value, and validate that encoder sends the + value less than or equal to this. */ + inflater->min_hd_table_bufsize_max = settings_max_dynamic_table_size; + } + + inflater->settings_hd_table_bufsize_max = settings_max_dynamic_table_size; + + inflater->ctx.hd_table_bufsize_max = settings_max_dynamic_table_size; + + hd_context_shrink_table_size(&inflater->ctx, NULL); + return 0; +} + +#define INDEX_RANGE_VALID(context, idx) \ + ((idx) < (context)->hd_table.len + NGHTTP2_STATIC_TABLE_LENGTH) + +static size_t get_max_index(nghttp2_hd_context *context) { + return context->hd_table.len + NGHTTP2_STATIC_TABLE_LENGTH; +} + +nghttp2_hd_nv nghttp2_hd_table_get(nghttp2_hd_context *context, size_t idx) { + assert(INDEX_RANGE_VALID(context, idx)); + if (idx >= NGHTTP2_STATIC_TABLE_LENGTH) { + return hd_ringbuf_get(&context->hd_table, idx - NGHTTP2_STATIC_TABLE_LENGTH) + ->nv; + } else { const nghttp2_hd_static_entry *ent = &static_table[idx]; nghttp2_hd_nv nv = {(nghttp2_rcbuf *)&ent->name, (nghttp2_rcbuf *)&ent->value, ent->token, - NGHTTP2_NV_FLAG_NONE}; - return nv; - } -} - -static const nghttp2_nv *nghttp2_hd_table_get2(nghttp2_hd_context *context, - size_t idx) { - assert(INDEX_RANGE_VALID(context, idx)); - if (idx >= NGHTTP2_STATIC_TABLE_LENGTH) { - return &hd_ringbuf_get(&context->hd_table, - idx - NGHTTP2_STATIC_TABLE_LENGTH) - ->cnv; - } - - return &static_table[idx].cnv; -} - -static int hd_deflate_decide_indexing(nghttp2_hd_deflater *deflater, - const nghttp2_nv *nv, int32_t token) { - if (token == NGHTTP2_TOKEN__PATH || token == NGHTTP2_TOKEN_AGE || - token == NGHTTP2_TOKEN_CONTENT_LENGTH || token == NGHTTP2_TOKEN_ETAG || - token == NGHTTP2_TOKEN_IF_MODIFIED_SINCE || - token == NGHTTP2_TOKEN_IF_NONE_MATCH || token == NGHTTP2_TOKEN_LOCATION || - token == NGHTTP2_TOKEN_SET_COOKIE || - entry_room(nv->namelen, nv->valuelen) > - deflater->ctx.hd_table_bufsize_max * 3 / 4) { - return NGHTTP2_HD_WITHOUT_INDEXING; - } - - return NGHTTP2_HD_WITH_INDEXING; -} - -static int deflate_nv(nghttp2_hd_deflater *deflater, nghttp2_bufs *bufs, - const nghttp2_nv *nv) { - int rv; - search_result res; - ssize_t idx; - int indexing_mode; - int32_t token; - nghttp2_mem *mem; - uint32_t hash = 0; - - DEBUGF("deflatehd: deflating %.*s: %.*s\n", (int)nv->namelen, nv->name, - (int)nv->valuelen, nv->value); - - mem = deflater->ctx.mem; - - token = lookup_token(nv->name, nv->namelen); - if (token == -1) { - hash = name_hash(nv); - } else if (token <= NGHTTP2_TOKEN_WWW_AUTHENTICATE) { - hash = static_table[token].hash; - } - - /* Don't index authorization header field since it may contain low - entropy secret data (e.g., id/password). Also cookie header - field with less than 20 bytes value is also never indexed. This - is the same criteria used in Firefox codebase. */ - indexing_mode = - token == NGHTTP2_TOKEN_AUTHORIZATION || - (token == NGHTTP2_TOKEN_COOKIE && nv->valuelen < 20) || - (nv->flags & NGHTTP2_NV_FLAG_NO_INDEX) - ? NGHTTP2_HD_NEVER_INDEXING - : hd_deflate_decide_indexing(deflater, nv, token); - - res = search_hd_table(&deflater->ctx, nv, token, indexing_mode, - &deflater->map, hash); - - idx = res.index; - - if (res.name_value_match) { - - DEBUGF("deflatehd: name/value match index=%zd\n", idx); - - rv = emit_indexed_block(bufs, (size_t)idx); - if (rv != 0) { - return rv; - } - - return 0; - } - - if (res.index != -1) { - DEBUGF("deflatehd: name match index=%zd\n", res.index); - } - - if (indexing_mode == NGHTTP2_HD_WITH_INDEXING) { - nghttp2_hd_nv hd_nv; - + NGHTTP2_NV_FLAG_NONE}; + return nv; + } +} + +static const nghttp2_nv *nghttp2_hd_table_get2(nghttp2_hd_context *context, + size_t idx) { + assert(INDEX_RANGE_VALID(context, idx)); + if (idx >= NGHTTP2_STATIC_TABLE_LENGTH) { + return &hd_ringbuf_get(&context->hd_table, + idx - NGHTTP2_STATIC_TABLE_LENGTH) + ->cnv; + } + + return &static_table[idx].cnv; +} + +static int hd_deflate_decide_indexing(nghttp2_hd_deflater *deflater, + const nghttp2_nv *nv, int32_t token) { + if (token == NGHTTP2_TOKEN__PATH || token == NGHTTP2_TOKEN_AGE || + token == NGHTTP2_TOKEN_CONTENT_LENGTH || token == NGHTTP2_TOKEN_ETAG || + token == NGHTTP2_TOKEN_IF_MODIFIED_SINCE || + token == NGHTTP2_TOKEN_IF_NONE_MATCH || token == NGHTTP2_TOKEN_LOCATION || + token == NGHTTP2_TOKEN_SET_COOKIE || + entry_room(nv->namelen, nv->valuelen) > + deflater->ctx.hd_table_bufsize_max * 3 / 4) { + return NGHTTP2_HD_WITHOUT_INDEXING; + } + + return NGHTTP2_HD_WITH_INDEXING; +} + +static int deflate_nv(nghttp2_hd_deflater *deflater, nghttp2_bufs *bufs, + const nghttp2_nv *nv) { + int rv; + search_result res; + ssize_t idx; + int indexing_mode; + int32_t token; + nghttp2_mem *mem; + uint32_t hash = 0; + + DEBUGF("deflatehd: deflating %.*s: %.*s\n", (int)nv->namelen, nv->name, + (int)nv->valuelen, nv->value); + + mem = deflater->ctx.mem; + + token = lookup_token(nv->name, nv->namelen); + if (token == -1) { + hash = name_hash(nv); + } else if (token <= NGHTTP2_TOKEN_WWW_AUTHENTICATE) { + hash = static_table[token].hash; + } + + /* Don't index authorization header field since it may contain low + entropy secret data (e.g., id/password). Also cookie header + field with less than 20 bytes value is also never indexed. This + is the same criteria used in Firefox codebase. */ + indexing_mode = + token == NGHTTP2_TOKEN_AUTHORIZATION || + (token == NGHTTP2_TOKEN_COOKIE && nv->valuelen < 20) || + (nv->flags & NGHTTP2_NV_FLAG_NO_INDEX) + ? NGHTTP2_HD_NEVER_INDEXING + : hd_deflate_decide_indexing(deflater, nv, token); + + res = search_hd_table(&deflater->ctx, nv, token, indexing_mode, + &deflater->map, hash); + + idx = res.index; + + if (res.name_value_match) { + + DEBUGF("deflatehd: name/value match index=%zd\n", idx); + + rv = emit_indexed_block(bufs, (size_t)idx); + if (rv != 0) { + return rv; + } + + return 0; + } + + if (res.index != -1) { + DEBUGF("deflatehd: name match index=%zd\n", res.index); + } + + if (indexing_mode == NGHTTP2_HD_WITH_INDEXING) { + nghttp2_hd_nv hd_nv; + if (idx != -1) { - hd_nv.name = nghttp2_hd_table_get(&deflater->ctx, (size_t)idx).name; - nghttp2_rcbuf_incref(hd_nv.name); - } else { - rv = nghttp2_rcbuf_new2(&hd_nv.name, nv->name, nv->namelen, mem); - if (rv != 0) { - return rv; - } - } - - rv = nghttp2_rcbuf_new2(&hd_nv.value, nv->value, nv->valuelen, mem); - - if (rv != 0) { - nghttp2_rcbuf_decref(hd_nv.name); - return rv; - } - - hd_nv.token = token; - hd_nv.flags = NGHTTP2_NV_FLAG_NONE; - - rv = add_hd_table_incremental(&deflater->ctx, &hd_nv, &deflater->map, hash); - - nghttp2_rcbuf_decref(hd_nv.value); - nghttp2_rcbuf_decref(hd_nv.name); - - if (rv != 0) { - return NGHTTP2_ERR_HEADER_COMP; - } - } - if (idx == -1) { - rv = emit_newname_block(bufs, nv, indexing_mode); - } else { - rv = emit_indname_block(bufs, (size_t)idx, nv, indexing_mode); - } - if (rv != 0) { - return rv; - } - - return 0; -} - -int nghttp2_hd_deflate_hd_bufs(nghttp2_hd_deflater *deflater, - nghttp2_bufs *bufs, const nghttp2_nv *nv, - size_t nvlen) { - size_t i; - int rv = 0; - - if (deflater->ctx.bad) { - return NGHTTP2_ERR_HEADER_COMP; - } - - if (deflater->notify_table_size_change) { - size_t min_hd_table_bufsize_max; - - min_hd_table_bufsize_max = deflater->min_hd_table_bufsize_max; - - deflater->notify_table_size_change = 0; - deflater->min_hd_table_bufsize_max = UINT32_MAX; - - if (deflater->ctx.hd_table_bufsize_max > min_hd_table_bufsize_max) { - - rv = emit_table_size(bufs, min_hd_table_bufsize_max); - - if (rv != 0) { - goto fail; - } - } - - rv = emit_table_size(bufs, deflater->ctx.hd_table_bufsize_max); - - if (rv != 0) { - goto fail; - } - } - - for (i = 0; i < nvlen; ++i) { - rv = deflate_nv(deflater, bufs, &nv[i]); - if (rv != 0) { - goto fail; - } - } - - DEBUGF("deflatehd: all input name/value pairs were deflated\n"); - - return 0; -fail: - DEBUGF("deflatehd: error return %d\n", rv); - - deflater->ctx.bad = 1; - return rv; -} - -ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, uint8_t *buf, - size_t buflen, const nghttp2_nv *nv, - size_t nvlen) { - nghttp2_bufs bufs; - int rv; - nghttp2_mem *mem; - - mem = deflater->ctx.mem; - - rv = nghttp2_bufs_wrap_init(&bufs, buf, buflen, mem); - - if (rv != 0) { - return rv; - } - - rv = nghttp2_hd_deflate_hd_bufs(deflater, &bufs, nv, nvlen); - - buflen = nghttp2_bufs_len(&bufs); - - nghttp2_bufs_wrap_free(&bufs); - - if (rv == NGHTTP2_ERR_BUFFER_ERROR) { - return NGHTTP2_ERR_INSUFF_BUFSIZE; - } - - if (rv != 0) { - return rv; - } - - return (ssize_t)buflen; -} - -ssize_t nghttp2_hd_deflate_hd_vec(nghttp2_hd_deflater *deflater, - const nghttp2_vec *vec, size_t veclen, - const nghttp2_nv *nv, size_t nvlen) { - nghttp2_bufs bufs; - int rv; - nghttp2_mem *mem; - size_t buflen; - - mem = deflater->ctx.mem; - - rv = nghttp2_bufs_wrap_init2(&bufs, vec, veclen, mem); - - if (rv != 0) { - return rv; - } - - rv = nghttp2_hd_deflate_hd_bufs(deflater, &bufs, nv, nvlen); - - buflen = nghttp2_bufs_len(&bufs); - - nghttp2_bufs_wrap_free(&bufs); - - if (rv == NGHTTP2_ERR_BUFFER_ERROR) { - return NGHTTP2_ERR_INSUFF_BUFSIZE; - } - - if (rv != 0) { - return rv; - } - - return (ssize_t)buflen; -} - -size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater, - const nghttp2_nv *nva, size_t nvlen) { - size_t n = 0; - size_t i; - (void)deflater; - - /* Possible Maximum Header Table Size Change. Encoding (1u << 31) - - 1 using 4 bit prefix requires 6 bytes. We may emit this at most - twice. */ - n += 12; - - /* Use Literal Header Field without indexing - New Name, since it is - most space consuming format. Also we choose the less one between - non-huffman and huffman, so using literal byte count is - sufficient for upper bound. - - Encoding (1u << 31) - 1 using 7 bit prefix requires 6 bytes. We - need 2 of this for |nvlen| header fields. */ - n += 6 * 2 * nvlen; - - for (i = 0; i < nvlen; ++i) { - n += nva[i].namelen + nva[i].valuelen; - } - - return n; -} - -int nghttp2_hd_deflate_new(nghttp2_hd_deflater **deflater_ptr, - size_t deflate_hd_table_bufsize_max) { - return nghttp2_hd_deflate_new2(deflater_ptr, deflate_hd_table_bufsize_max, - NULL); -} - -int nghttp2_hd_deflate_new2(nghttp2_hd_deflater **deflater_ptr, - size_t deflate_hd_table_bufsize_max, - nghttp2_mem *mem) { - int rv; - nghttp2_hd_deflater *deflater; - - if (mem == NULL) { - mem = nghttp2_mem_default(); - } - - deflater = nghttp2_mem_malloc(mem, sizeof(nghttp2_hd_deflater)); - - if (deflater == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - rv = nghttp2_hd_deflate_init2(deflater, deflate_hd_table_bufsize_max, mem); - - if (rv != 0) { - nghttp2_mem_free(mem, deflater); - - return rv; - } - - *deflater_ptr = deflater; - - return 0; -} - -void nghttp2_hd_deflate_del(nghttp2_hd_deflater *deflater) { - nghttp2_mem *mem; - - mem = deflater->ctx.mem; - - nghttp2_hd_deflate_free(deflater); - - nghttp2_mem_free(mem, deflater); -} - -static void hd_inflate_set_huffman_encoded(nghttp2_hd_inflater *inflater, - const uint8_t *in) { - inflater->huffman_encoded = (*in & (1 << 7)) != 0; -} - -/* - * Decodes the integer from the range [in, last). The result is - * assigned to |inflater->left|. If the |inflater->left| is 0, then - * it performs variable integer decoding from scratch. Otherwise, it - * uses the |inflater->left| as the initial value and continues to - * decode assuming that [in, last) begins with intermediary sequence. - * - * This function returns the number of bytes read if it succeeds, or - * one of the following negative error codes: - * - * NGHTTP2_ERR_HEADER_COMP - * Integer decoding failed - */ -static ssize_t hd_inflate_read_len(nghttp2_hd_inflater *inflater, int *rfin, - const uint8_t *in, const uint8_t *last, - size_t prefix, size_t maxlen) { - ssize_t rv; - uint32_t out; - - *rfin = 0; - - rv = decode_length(&out, &inflater->shift, rfin, (uint32_t)inflater->left, - inflater->shift, in, last, prefix); - - if (rv == -1) { - DEBUGF("inflatehd: integer decoding failed\n"); - return NGHTTP2_ERR_HEADER_COMP; - } - - if (out > maxlen) { - DEBUGF("inflatehd: integer exceeded the maximum value %zu\n", maxlen); - return NGHTTP2_ERR_HEADER_COMP; - } - - inflater->left = out; - - DEBUGF("inflatehd: decoded integer is %u\n", out); - - return rv; -} - -/* - * Reads |inflater->left| bytes from the range [in, last) and performs - * huffman decoding against them and pushes the result into the - * |buffer|. - * - * This function returns the number of bytes read if it succeeds, or - * one of the following negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory - * NGHTTP2_ERR_HEADER_COMP - * Huffman decoding failed - */ -static ssize_t hd_inflate_read_huff(nghttp2_hd_inflater *inflater, - nghttp2_buf *buf, const uint8_t *in, - const uint8_t *last) { - ssize_t readlen; - int fin = 0; - if ((size_t)(last - in) >= inflater->left) { - last = in + inflater->left; - fin = 1; - } - readlen = nghttp2_hd_huff_decode(&inflater->huff_decode_ctx, buf, in, - (size_t)(last - in), fin); - - if (readlen < 0) { - DEBUGF("inflatehd: huffman decoding failed\n"); - return readlen; - } + hd_nv.name = nghttp2_hd_table_get(&deflater->ctx, (size_t)idx).name; + nghttp2_rcbuf_incref(hd_nv.name); + } else { + rv = nghttp2_rcbuf_new2(&hd_nv.name, nv->name, nv->namelen, mem); + if (rv != 0) { + return rv; + } + } + + rv = nghttp2_rcbuf_new2(&hd_nv.value, nv->value, nv->valuelen, mem); + + if (rv != 0) { + nghttp2_rcbuf_decref(hd_nv.name); + return rv; + } + + hd_nv.token = token; + hd_nv.flags = NGHTTP2_NV_FLAG_NONE; + + rv = add_hd_table_incremental(&deflater->ctx, &hd_nv, &deflater->map, hash); + + nghttp2_rcbuf_decref(hd_nv.value); + nghttp2_rcbuf_decref(hd_nv.name); + + if (rv != 0) { + return NGHTTP2_ERR_HEADER_COMP; + } + } + if (idx == -1) { + rv = emit_newname_block(bufs, nv, indexing_mode); + } else { + rv = emit_indname_block(bufs, (size_t)idx, nv, indexing_mode); + } + if (rv != 0) { + return rv; + } + + return 0; +} + +int nghttp2_hd_deflate_hd_bufs(nghttp2_hd_deflater *deflater, + nghttp2_bufs *bufs, const nghttp2_nv *nv, + size_t nvlen) { + size_t i; + int rv = 0; + + if (deflater->ctx.bad) { + return NGHTTP2_ERR_HEADER_COMP; + } + + if (deflater->notify_table_size_change) { + size_t min_hd_table_bufsize_max; + + min_hd_table_bufsize_max = deflater->min_hd_table_bufsize_max; + + deflater->notify_table_size_change = 0; + deflater->min_hd_table_bufsize_max = UINT32_MAX; + + if (deflater->ctx.hd_table_bufsize_max > min_hd_table_bufsize_max) { + + rv = emit_table_size(bufs, min_hd_table_bufsize_max); + + if (rv != 0) { + goto fail; + } + } + + rv = emit_table_size(bufs, deflater->ctx.hd_table_bufsize_max); + + if (rv != 0) { + goto fail; + } + } + + for (i = 0; i < nvlen; ++i) { + rv = deflate_nv(deflater, bufs, &nv[i]); + if (rv != 0) { + goto fail; + } + } + + DEBUGF("deflatehd: all input name/value pairs were deflated\n"); + + return 0; +fail: + DEBUGF("deflatehd: error return %d\n", rv); + + deflater->ctx.bad = 1; + return rv; +} + +ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, uint8_t *buf, + size_t buflen, const nghttp2_nv *nv, + size_t nvlen) { + nghttp2_bufs bufs; + int rv; + nghttp2_mem *mem; + + mem = deflater->ctx.mem; + + rv = nghttp2_bufs_wrap_init(&bufs, buf, buflen, mem); + + if (rv != 0) { + return rv; + } + + rv = nghttp2_hd_deflate_hd_bufs(deflater, &bufs, nv, nvlen); + + buflen = nghttp2_bufs_len(&bufs); + + nghttp2_bufs_wrap_free(&bufs); + + if (rv == NGHTTP2_ERR_BUFFER_ERROR) { + return NGHTTP2_ERR_INSUFF_BUFSIZE; + } + + if (rv != 0) { + return rv; + } + + return (ssize_t)buflen; +} + +ssize_t nghttp2_hd_deflate_hd_vec(nghttp2_hd_deflater *deflater, + const nghttp2_vec *vec, size_t veclen, + const nghttp2_nv *nv, size_t nvlen) { + nghttp2_bufs bufs; + int rv; + nghttp2_mem *mem; + size_t buflen; + + mem = deflater->ctx.mem; + + rv = nghttp2_bufs_wrap_init2(&bufs, vec, veclen, mem); + + if (rv != 0) { + return rv; + } + + rv = nghttp2_hd_deflate_hd_bufs(deflater, &bufs, nv, nvlen); + + buflen = nghttp2_bufs_len(&bufs); + + nghttp2_bufs_wrap_free(&bufs); + + if (rv == NGHTTP2_ERR_BUFFER_ERROR) { + return NGHTTP2_ERR_INSUFF_BUFSIZE; + } + + if (rv != 0) { + return rv; + } + + return (ssize_t)buflen; +} + +size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater, + const nghttp2_nv *nva, size_t nvlen) { + size_t n = 0; + size_t i; + (void)deflater; + + /* Possible Maximum Header Table Size Change. Encoding (1u << 31) - + 1 using 4 bit prefix requires 6 bytes. We may emit this at most + twice. */ + n += 12; + + /* Use Literal Header Field without indexing - New Name, since it is + most space consuming format. Also we choose the less one between + non-huffman and huffman, so using literal byte count is + sufficient for upper bound. + + Encoding (1u << 31) - 1 using 7 bit prefix requires 6 bytes. We + need 2 of this for |nvlen| header fields. */ + n += 6 * 2 * nvlen; + + for (i = 0; i < nvlen; ++i) { + n += nva[i].namelen + nva[i].valuelen; + } + + return n; +} + +int nghttp2_hd_deflate_new(nghttp2_hd_deflater **deflater_ptr, + size_t deflate_hd_table_bufsize_max) { + return nghttp2_hd_deflate_new2(deflater_ptr, deflate_hd_table_bufsize_max, + NULL); +} + +int nghttp2_hd_deflate_new2(nghttp2_hd_deflater **deflater_ptr, + size_t deflate_hd_table_bufsize_max, + nghttp2_mem *mem) { + int rv; + nghttp2_hd_deflater *deflater; + + if (mem == NULL) { + mem = nghttp2_mem_default(); + } + + deflater = nghttp2_mem_malloc(mem, sizeof(nghttp2_hd_deflater)); + + if (deflater == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + rv = nghttp2_hd_deflate_init2(deflater, deflate_hd_table_bufsize_max, mem); + + if (rv != 0) { + nghttp2_mem_free(mem, deflater); + + return rv; + } + + *deflater_ptr = deflater; + + return 0; +} + +void nghttp2_hd_deflate_del(nghttp2_hd_deflater *deflater) { + nghttp2_mem *mem; + + mem = deflater->ctx.mem; + + nghttp2_hd_deflate_free(deflater); + + nghttp2_mem_free(mem, deflater); +} + +static void hd_inflate_set_huffman_encoded(nghttp2_hd_inflater *inflater, + const uint8_t *in) { + inflater->huffman_encoded = (*in & (1 << 7)) != 0; +} + +/* + * Decodes the integer from the range [in, last). The result is + * assigned to |inflater->left|. If the |inflater->left| is 0, then + * it performs variable integer decoding from scratch. Otherwise, it + * uses the |inflater->left| as the initial value and continues to + * decode assuming that [in, last) begins with intermediary sequence. + * + * This function returns the number of bytes read if it succeeds, or + * one of the following negative error codes: + * + * NGHTTP2_ERR_HEADER_COMP + * Integer decoding failed + */ +static ssize_t hd_inflate_read_len(nghttp2_hd_inflater *inflater, int *rfin, + const uint8_t *in, const uint8_t *last, + size_t prefix, size_t maxlen) { + ssize_t rv; + uint32_t out; + + *rfin = 0; + + rv = decode_length(&out, &inflater->shift, rfin, (uint32_t)inflater->left, + inflater->shift, in, last, prefix); + + if (rv == -1) { + DEBUGF("inflatehd: integer decoding failed\n"); + return NGHTTP2_ERR_HEADER_COMP; + } + + if (out > maxlen) { + DEBUGF("inflatehd: integer exceeded the maximum value %zu\n", maxlen); + return NGHTTP2_ERR_HEADER_COMP; + } + + inflater->left = out; + + DEBUGF("inflatehd: decoded integer is %u\n", out); + + return rv; +} + +/* + * Reads |inflater->left| bytes from the range [in, last) and performs + * huffman decoding against them and pushes the result into the + * |buffer|. + * + * This function returns the number of bytes read if it succeeds, or + * one of the following negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + * NGHTTP2_ERR_HEADER_COMP + * Huffman decoding failed + */ +static ssize_t hd_inflate_read_huff(nghttp2_hd_inflater *inflater, + nghttp2_buf *buf, const uint8_t *in, + const uint8_t *last) { + ssize_t readlen; + int fin = 0; + if ((size_t)(last - in) >= inflater->left) { + last = in + inflater->left; + fin = 1; + } + readlen = nghttp2_hd_huff_decode(&inflater->huff_decode_ctx, buf, in, + (size_t)(last - in), fin); + + if (readlen < 0) { + DEBUGF("inflatehd: huffman decoding failed\n"); + return readlen; + } if (nghttp2_hd_huff_decode_failure_state(&inflater->huff_decode_ctx)) { DEBUGF("inflatehd: huffman decoding failed\n"); return NGHTTP2_ERR_HEADER_COMP; } - inflater->left -= (size_t)readlen; - return readlen; -} - -/* - * Reads |inflater->left| bytes from the range [in, last) and copies - * them into the |buffer|. - * - * This function returns the number of bytes read if it succeeds, or - * one of the following negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory - * NGHTTP2_ERR_HEADER_COMP - * Header decompression failed - */ -static ssize_t hd_inflate_read(nghttp2_hd_inflater *inflater, nghttp2_buf *buf, - const uint8_t *in, const uint8_t *last) { - size_t len = nghttp2_min((size_t)(last - in), inflater->left); - - buf->last = nghttp2_cpymem(buf->last, in, len); - - inflater->left -= len; - return (ssize_t)len; -} - -/* - * Finalize indexed header representation reception. The referenced - * header is always emitted, and |*nv_out| is filled with that value. - */ -static void hd_inflate_commit_indexed(nghttp2_hd_inflater *inflater, - nghttp2_hd_nv *nv_out) { - nghttp2_hd_nv nv = nghttp2_hd_table_get(&inflater->ctx, inflater->index); - - emit_header(nv_out, &nv); -} - -/* - * Finalize literal header representation - new name- reception. If - * header is emitted, |*nv_out| is filled with that value and 0 is - * returned. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory - */ -static int hd_inflate_commit_newname(nghttp2_hd_inflater *inflater, - nghttp2_hd_nv *nv_out) { - nghttp2_hd_nv nv; - int rv; - - if (inflater->no_index) { - nv.flags = NGHTTP2_NV_FLAG_NO_INDEX; - } else { - nv.flags = NGHTTP2_NV_FLAG_NONE; - } - - nv.name = inflater->namercbuf; - nv.value = inflater->valuercbuf; - nv.token = lookup_token(inflater->namercbuf->base, inflater->namercbuf->len); - - if (inflater->index_required) { - rv = add_hd_table_incremental(&inflater->ctx, &nv, NULL, 0); - - if (rv != 0) { - return rv; - } - } - - emit_header(nv_out, &nv); - - inflater->nv_name_keep = nv.name; - inflater->nv_value_keep = nv.value; - - inflater->namercbuf = NULL; - inflater->valuercbuf = NULL; - - return 0; -} - -/* - * Finalize literal header representation - indexed name- - * reception. If header is emitted, |*nv_out| is filled with that - * value and 0 is returned. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory - */ -static int hd_inflate_commit_indname(nghttp2_hd_inflater *inflater, - nghttp2_hd_nv *nv_out) { - nghttp2_hd_nv nv; - int rv; - - nv = nghttp2_hd_table_get(&inflater->ctx, inflater->index); - - if (inflater->no_index) { - nv.flags = NGHTTP2_NV_FLAG_NO_INDEX; - } else { - nv.flags = NGHTTP2_NV_FLAG_NONE; - } - - nghttp2_rcbuf_incref(nv.name); - - nv.value = inflater->valuercbuf; - - if (inflater->index_required) { - rv = add_hd_table_incremental(&inflater->ctx, &nv, NULL, 0); - if (rv != 0) { - nghttp2_rcbuf_decref(nv.name); - return NGHTTP2_ERR_NOMEM; - } - } - - emit_header(nv_out, &nv); - - inflater->nv_name_keep = nv.name; - inflater->nv_value_keep = nv.value; - - inflater->valuercbuf = NULL; - - return 0; -} - -ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out, - int *inflate_flags, uint8_t *in, size_t inlen, - int in_final) { - return nghttp2_hd_inflate_hd2(inflater, nv_out, inflate_flags, in, inlen, - in_final); -} - -ssize_t nghttp2_hd_inflate_hd2(nghttp2_hd_inflater *inflater, - nghttp2_nv *nv_out, int *inflate_flags, - const uint8_t *in, size_t inlen, int in_final) { - ssize_t rv; - nghttp2_hd_nv hd_nv; - - rv = nghttp2_hd_inflate_hd_nv(inflater, &hd_nv, inflate_flags, in, inlen, - in_final); - - if (rv < 0) { - return rv; - } - - if (*inflate_flags & NGHTTP2_HD_INFLATE_EMIT) { - nv_out->name = hd_nv.name->base; - nv_out->namelen = hd_nv.name->len; - - nv_out->value = hd_nv.value->base; - nv_out->valuelen = hd_nv.value->len; - - nv_out->flags = hd_nv.flags; - } - - return rv; -} - -ssize_t nghttp2_hd_inflate_hd_nv(nghttp2_hd_inflater *inflater, - nghttp2_hd_nv *nv_out, int *inflate_flags, - const uint8_t *in, size_t inlen, - int in_final) { - ssize_t rv = 0; - const uint8_t *first = in; - const uint8_t *last = in + inlen; - int rfin = 0; - int busy = 0; - nghttp2_mem *mem; - - mem = inflater->ctx.mem; - - if (inflater->ctx.bad) { - return NGHTTP2_ERR_HEADER_COMP; - } - - DEBUGF("inflatehd: start state=%d\n", inflater->state); - hd_inflate_keep_free(inflater); - *inflate_flags = NGHTTP2_HD_INFLATE_NONE; - for (; in != last || busy;) { - busy = 0; - switch (inflater->state) { - case NGHTTP2_HD_STATE_EXPECT_TABLE_SIZE: - if ((*in & 0xe0u) != 0x20u) { - DEBUGF("inflatehd: header table size change was expected, but saw " - "0x%02x as first byte", - *in); - rv = NGHTTP2_ERR_HEADER_COMP; - goto fail; - } - /* fall through */ - case NGHTTP2_HD_STATE_INFLATE_START: - case NGHTTP2_HD_STATE_OPCODE: - if ((*in & 0xe0u) == 0x20u) { - DEBUGF("inflatehd: header table size change\n"); - if (inflater->state == NGHTTP2_HD_STATE_OPCODE) { - DEBUGF("inflatehd: header table size change must appear at the head " - "of header block\n"); - rv = NGHTTP2_ERR_HEADER_COMP; - goto fail; - } - inflater->opcode = NGHTTP2_HD_OPCODE_INDEXED; - inflater->state = NGHTTP2_HD_STATE_READ_TABLE_SIZE; - } else if (*in & 0x80u) { - DEBUGF("inflatehd: indexed repr\n"); - inflater->opcode = NGHTTP2_HD_OPCODE_INDEXED; - inflater->state = NGHTTP2_HD_STATE_READ_INDEX; - } else { - if (*in == 0x40u || *in == 0 || *in == 0x10u) { - DEBUGF("inflatehd: literal header repr - new name\n"); - inflater->opcode = NGHTTP2_HD_OPCODE_NEWNAME; - inflater->state = NGHTTP2_HD_STATE_NEWNAME_CHECK_NAMELEN; - } else { - DEBUGF("inflatehd: literal header repr - indexed name\n"); - inflater->opcode = NGHTTP2_HD_OPCODE_INDNAME; - inflater->state = NGHTTP2_HD_STATE_READ_INDEX; - } - inflater->index_required = (*in & 0x40) != 0; - inflater->no_index = (*in & 0xf0u) == 0x10u; - DEBUGF("inflatehd: indexing required=%d, no_index=%d\n", - inflater->index_required, inflater->no_index); - if (inflater->opcode == NGHTTP2_HD_OPCODE_NEWNAME) { - ++in; - } - } - inflater->left = 0; - inflater->shift = 0; - break; - case NGHTTP2_HD_STATE_READ_TABLE_SIZE: - rfin = 0; - rv = hd_inflate_read_len( - inflater, &rfin, in, last, 5, - nghttp2_min(inflater->min_hd_table_bufsize_max, - inflater->settings_hd_table_bufsize_max)); - if (rv < 0) { - goto fail; - } - in += rv; - if (!rfin) { - goto almost_ok; - } - DEBUGF("inflatehd: table_size=%zu\n", inflater->left); - inflater->min_hd_table_bufsize_max = UINT32_MAX; - inflater->ctx.hd_table_bufsize_max = inflater->left; - hd_context_shrink_table_size(&inflater->ctx, NULL); - inflater->state = NGHTTP2_HD_STATE_INFLATE_START; - break; - case NGHTTP2_HD_STATE_READ_INDEX: { - size_t prefixlen; - - if (inflater->opcode == NGHTTP2_HD_OPCODE_INDEXED) { - prefixlen = 7; - } else if (inflater->index_required) { - prefixlen = 6; - } else { - prefixlen = 4; - } - - rfin = 0; - rv = hd_inflate_read_len(inflater, &rfin, in, last, prefixlen, - get_max_index(&inflater->ctx)); - if (rv < 0) { - goto fail; - } - - in += rv; - - if (!rfin) { - goto almost_ok; - } - - if (inflater->left == 0) { - rv = NGHTTP2_ERR_HEADER_COMP; - goto fail; - } - - DEBUGF("inflatehd: index=%zu\n", inflater->left); - if (inflater->opcode == NGHTTP2_HD_OPCODE_INDEXED) { - inflater->index = inflater->left; - --inflater->index; - - hd_inflate_commit_indexed(inflater, nv_out); - - inflater->state = NGHTTP2_HD_STATE_OPCODE; - *inflate_flags |= NGHTTP2_HD_INFLATE_EMIT; - return (ssize_t)(in - first); - } else { - inflater->index = inflater->left; - --inflater->index; - - inflater->state = NGHTTP2_HD_STATE_CHECK_VALUELEN; - } - break; - } - case NGHTTP2_HD_STATE_NEWNAME_CHECK_NAMELEN: - hd_inflate_set_huffman_encoded(inflater, in); - inflater->state = NGHTTP2_HD_STATE_NEWNAME_READ_NAMELEN; - inflater->left = 0; - inflater->shift = 0; - DEBUGF("inflatehd: huffman encoded=%d\n", inflater->huffman_encoded != 0); - /* Fall through */ - case NGHTTP2_HD_STATE_NEWNAME_READ_NAMELEN: - rfin = 0; - rv = hd_inflate_read_len(inflater, &rfin, in, last, 7, NGHTTP2_HD_MAX_NV); - if (rv < 0) { - goto fail; - } - in += rv; - if (!rfin) { - DEBUGF("inflatehd: integer not fully decoded. current=%zu\n", - inflater->left); - - goto almost_ok; - } - - if (inflater->huffman_encoded) { - nghttp2_hd_huff_decode_context_init(&inflater->huff_decode_ctx); - - inflater->state = NGHTTP2_HD_STATE_NEWNAME_READ_NAMEHUFF; - - rv = nghttp2_rcbuf_new(&inflater->namercbuf, inflater->left * 2 + 1, - mem); - } else { - inflater->state = NGHTTP2_HD_STATE_NEWNAME_READ_NAME; - rv = nghttp2_rcbuf_new(&inflater->namercbuf, inflater->left + 1, mem); - } - - if (rv != 0) { - goto fail; - } - - nghttp2_buf_wrap_init(&inflater->namebuf, inflater->namercbuf->base, - inflater->namercbuf->len); - - break; - case NGHTTP2_HD_STATE_NEWNAME_READ_NAMEHUFF: - rv = hd_inflate_read_huff(inflater, &inflater->namebuf, in, last); - if (rv < 0) { - goto fail; - } - - in += rv; - - DEBUGF("inflatehd: %zd bytes read\n", rv); - - if (inflater->left) { - DEBUGF("inflatehd: still %zu bytes to go\n", inflater->left); - - goto almost_ok; - } - - *inflater->namebuf.last = '\0'; - inflater->namercbuf->len = nghttp2_buf_len(&inflater->namebuf); - - inflater->state = NGHTTP2_HD_STATE_CHECK_VALUELEN; - - break; - case NGHTTP2_HD_STATE_NEWNAME_READ_NAME: - rv = hd_inflate_read(inflater, &inflater->namebuf, in, last); - if (rv < 0) { - goto fail; - } - - in += rv; - - DEBUGF("inflatehd: %zd bytes read\n", rv); - if (inflater->left) { - DEBUGF("inflatehd: still %zu bytes to go\n", inflater->left); - - goto almost_ok; - } - - *inflater->namebuf.last = '\0'; - inflater->namercbuf->len = nghttp2_buf_len(&inflater->namebuf); - - inflater->state = NGHTTP2_HD_STATE_CHECK_VALUELEN; - - break; - case NGHTTP2_HD_STATE_CHECK_VALUELEN: - hd_inflate_set_huffman_encoded(inflater, in); - inflater->state = NGHTTP2_HD_STATE_READ_VALUELEN; - inflater->left = 0; - inflater->shift = 0; - DEBUGF("inflatehd: huffman encoded=%d\n", inflater->huffman_encoded != 0); - /* Fall through */ - case NGHTTP2_HD_STATE_READ_VALUELEN: - rfin = 0; - rv = hd_inflate_read_len(inflater, &rfin, in, last, 7, NGHTTP2_HD_MAX_NV); - if (rv < 0) { - goto fail; - } - - in += rv; - - if (!rfin) { - goto almost_ok; - } - - DEBUGF("inflatehd: valuelen=%zu\n", inflater->left); - - if (inflater->huffman_encoded) { - nghttp2_hd_huff_decode_context_init(&inflater->huff_decode_ctx); - - inflater->state = NGHTTP2_HD_STATE_READ_VALUEHUFF; - - rv = nghttp2_rcbuf_new(&inflater->valuercbuf, inflater->left * 2 + 1, - mem); - } else { - inflater->state = NGHTTP2_HD_STATE_READ_VALUE; - - rv = nghttp2_rcbuf_new(&inflater->valuercbuf, inflater->left + 1, mem); - } - - if (rv != 0) { - goto fail; - } - - nghttp2_buf_wrap_init(&inflater->valuebuf, inflater->valuercbuf->base, - inflater->valuercbuf->len); - - busy = 1; - - break; - case NGHTTP2_HD_STATE_READ_VALUEHUFF: - rv = hd_inflate_read_huff(inflater, &inflater->valuebuf, in, last); - if (rv < 0) { - goto fail; - } - - in += rv; - - DEBUGF("inflatehd: %zd bytes read\n", rv); - - if (inflater->left) { - DEBUGF("inflatehd: still %zu bytes to go\n", inflater->left); - - goto almost_ok; - } - - *inflater->valuebuf.last = '\0'; - inflater->valuercbuf->len = nghttp2_buf_len(&inflater->valuebuf); - - if (inflater->opcode == NGHTTP2_HD_OPCODE_NEWNAME) { - rv = hd_inflate_commit_newname(inflater, nv_out); - } else { - rv = hd_inflate_commit_indname(inflater, nv_out); - } - - if (rv != 0) { - goto fail; - } - - inflater->state = NGHTTP2_HD_STATE_OPCODE; - *inflate_flags |= NGHTTP2_HD_INFLATE_EMIT; - - return (ssize_t)(in - first); - case NGHTTP2_HD_STATE_READ_VALUE: - rv = hd_inflate_read(inflater, &inflater->valuebuf, in, last); - if (rv < 0) { - DEBUGF("inflatehd: value read failure %zd: %s\n", rv, - nghttp2_strerror((int)rv)); - goto fail; - } - - in += rv; - - DEBUGF("inflatehd: %zd bytes read\n", rv); - - if (inflater->left) { - DEBUGF("inflatehd: still %zu bytes to go\n", inflater->left); - goto almost_ok; - } - - *inflater->valuebuf.last = '\0'; - inflater->valuercbuf->len = nghttp2_buf_len(&inflater->valuebuf); - - if (inflater->opcode == NGHTTP2_HD_OPCODE_NEWNAME) { - rv = hd_inflate_commit_newname(inflater, nv_out); - } else { - rv = hd_inflate_commit_indname(inflater, nv_out); - } - - if (rv != 0) { - goto fail; - } - - inflater->state = NGHTTP2_HD_STATE_OPCODE; - *inflate_flags |= NGHTTP2_HD_INFLATE_EMIT; - - return (ssize_t)(in - first); - } - } - - assert(in == last); - - DEBUGF("inflatehd: all input bytes were processed\n"); - - if (in_final) { - DEBUGF("inflatehd: in_final set\n"); - - if (inflater->state != NGHTTP2_HD_STATE_OPCODE && - inflater->state != NGHTTP2_HD_STATE_INFLATE_START) { - DEBUGF("inflatehd: unacceptable state=%d\n", inflater->state); - rv = NGHTTP2_ERR_HEADER_COMP; - - goto fail; - } - *inflate_flags |= NGHTTP2_HD_INFLATE_FINAL; - } - return (ssize_t)(in - first); - -almost_ok: - if (in_final) { - DEBUGF("inflatehd: input ended prematurely\n"); - - rv = NGHTTP2_ERR_HEADER_COMP; - - goto fail; - } - return (ssize_t)(in - first); - -fail: - DEBUGF("inflatehd: error return %zd\n", rv); - - inflater->ctx.bad = 1; - return rv; -} - -int nghttp2_hd_inflate_end_headers(nghttp2_hd_inflater *inflater) { - hd_inflate_keep_free(inflater); - inflater->state = NGHTTP2_HD_STATE_INFLATE_START; - return 0; -} - -int nghttp2_hd_inflate_new(nghttp2_hd_inflater **inflater_ptr) { - return nghttp2_hd_inflate_new2(inflater_ptr, NULL); -} - -int nghttp2_hd_inflate_new2(nghttp2_hd_inflater **inflater_ptr, - nghttp2_mem *mem) { - int rv; - nghttp2_hd_inflater *inflater; - - if (mem == NULL) { - mem = nghttp2_mem_default(); - } - - inflater = nghttp2_mem_malloc(mem, sizeof(nghttp2_hd_inflater)); - - if (inflater == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - rv = nghttp2_hd_inflate_init(inflater, mem); - - if (rv != 0) { - nghttp2_mem_free(mem, inflater); - - return rv; - } - - *inflater_ptr = inflater; - - return 0; -} - -void nghttp2_hd_inflate_del(nghttp2_hd_inflater *inflater) { - nghttp2_mem *mem; - - mem = inflater->ctx.mem; - nghttp2_hd_inflate_free(inflater); - - nghttp2_mem_free(mem, inflater); -} - -int nghttp2_hd_emit_indname_block(nghttp2_bufs *bufs, size_t idx, - nghttp2_nv *nv, int indexing_mode) { - - return emit_indname_block(bufs, idx, nv, indexing_mode); -} - -int nghttp2_hd_emit_newname_block(nghttp2_bufs *bufs, nghttp2_nv *nv, - int indexing_mode) { - return emit_newname_block(bufs, nv, indexing_mode); -} - -int nghttp2_hd_emit_table_size(nghttp2_bufs *bufs, size_t table_size) { - return emit_table_size(bufs, table_size); -} - -ssize_t nghttp2_hd_decode_length(uint32_t *res, size_t *shift_ptr, int *fin, - uint32_t initial, size_t shift, uint8_t *in, - uint8_t *last, size_t prefix) { - return decode_length(res, shift_ptr, fin, initial, shift, in, last, prefix); -} - -static const nghttp2_nv *hd_get_table_entry(nghttp2_hd_context *context, - size_t idx) { - if (idx == 0) { - return NULL; - } - - --idx; - - if (!INDEX_RANGE_VALID(context, idx)) { - return NULL; - } - - return nghttp2_hd_table_get2(context, idx); -} - -size_t nghttp2_hd_deflate_get_num_table_entries(nghttp2_hd_deflater *deflater) { - return get_max_index(&deflater->ctx); -} - -const nghttp2_nv * -nghttp2_hd_deflate_get_table_entry(nghttp2_hd_deflater *deflater, size_t idx) { - return hd_get_table_entry(&deflater->ctx, idx); -} - -size_t -nghttp2_hd_deflate_get_dynamic_table_size(nghttp2_hd_deflater *deflater) { - return deflater->ctx.hd_table_bufsize; -} - -size_t -nghttp2_hd_deflate_get_max_dynamic_table_size(nghttp2_hd_deflater *deflater) { - return deflater->ctx.hd_table_bufsize_max; -} - -size_t nghttp2_hd_inflate_get_num_table_entries(nghttp2_hd_inflater *inflater) { - return get_max_index(&inflater->ctx); -} - -const nghttp2_nv * -nghttp2_hd_inflate_get_table_entry(nghttp2_hd_inflater *inflater, size_t idx) { - return hd_get_table_entry(&inflater->ctx, idx); -} - -size_t -nghttp2_hd_inflate_get_dynamic_table_size(nghttp2_hd_inflater *inflater) { - return inflater->ctx.hd_table_bufsize; -} - -size_t -nghttp2_hd_inflate_get_max_dynamic_table_size(nghttp2_hd_inflater *inflater) { - return inflater->ctx.hd_table_bufsize_max; -} + inflater->left -= (size_t)readlen; + return readlen; +} + +/* + * Reads |inflater->left| bytes from the range [in, last) and copies + * them into the |buffer|. + * + * This function returns the number of bytes read if it succeeds, or + * one of the following negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + * NGHTTP2_ERR_HEADER_COMP + * Header decompression failed + */ +static ssize_t hd_inflate_read(nghttp2_hd_inflater *inflater, nghttp2_buf *buf, + const uint8_t *in, const uint8_t *last) { + size_t len = nghttp2_min((size_t)(last - in), inflater->left); + + buf->last = nghttp2_cpymem(buf->last, in, len); + + inflater->left -= len; + return (ssize_t)len; +} + +/* + * Finalize indexed header representation reception. The referenced + * header is always emitted, and |*nv_out| is filled with that value. + */ +static void hd_inflate_commit_indexed(nghttp2_hd_inflater *inflater, + nghttp2_hd_nv *nv_out) { + nghttp2_hd_nv nv = nghttp2_hd_table_get(&inflater->ctx, inflater->index); + + emit_header(nv_out, &nv); +} + +/* + * Finalize literal header representation - new name- reception. If + * header is emitted, |*nv_out| is filled with that value and 0 is + * returned. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +static int hd_inflate_commit_newname(nghttp2_hd_inflater *inflater, + nghttp2_hd_nv *nv_out) { + nghttp2_hd_nv nv; + int rv; + + if (inflater->no_index) { + nv.flags = NGHTTP2_NV_FLAG_NO_INDEX; + } else { + nv.flags = NGHTTP2_NV_FLAG_NONE; + } + + nv.name = inflater->namercbuf; + nv.value = inflater->valuercbuf; + nv.token = lookup_token(inflater->namercbuf->base, inflater->namercbuf->len); + + if (inflater->index_required) { + rv = add_hd_table_incremental(&inflater->ctx, &nv, NULL, 0); + + if (rv != 0) { + return rv; + } + } + + emit_header(nv_out, &nv); + + inflater->nv_name_keep = nv.name; + inflater->nv_value_keep = nv.value; + + inflater->namercbuf = NULL; + inflater->valuercbuf = NULL; + + return 0; +} + +/* + * Finalize literal header representation - indexed name- + * reception. If header is emitted, |*nv_out| is filled with that + * value and 0 is returned. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +static int hd_inflate_commit_indname(nghttp2_hd_inflater *inflater, + nghttp2_hd_nv *nv_out) { + nghttp2_hd_nv nv; + int rv; + + nv = nghttp2_hd_table_get(&inflater->ctx, inflater->index); + + if (inflater->no_index) { + nv.flags = NGHTTP2_NV_FLAG_NO_INDEX; + } else { + nv.flags = NGHTTP2_NV_FLAG_NONE; + } + + nghttp2_rcbuf_incref(nv.name); + + nv.value = inflater->valuercbuf; + + if (inflater->index_required) { + rv = add_hd_table_incremental(&inflater->ctx, &nv, NULL, 0); + if (rv != 0) { + nghttp2_rcbuf_decref(nv.name); + return NGHTTP2_ERR_NOMEM; + } + } + + emit_header(nv_out, &nv); + + inflater->nv_name_keep = nv.name; + inflater->nv_value_keep = nv.value; + + inflater->valuercbuf = NULL; + + return 0; +} + +ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out, + int *inflate_flags, uint8_t *in, size_t inlen, + int in_final) { + return nghttp2_hd_inflate_hd2(inflater, nv_out, inflate_flags, in, inlen, + in_final); +} + +ssize_t nghttp2_hd_inflate_hd2(nghttp2_hd_inflater *inflater, + nghttp2_nv *nv_out, int *inflate_flags, + const uint8_t *in, size_t inlen, int in_final) { + ssize_t rv; + nghttp2_hd_nv hd_nv; + + rv = nghttp2_hd_inflate_hd_nv(inflater, &hd_nv, inflate_flags, in, inlen, + in_final); + + if (rv < 0) { + return rv; + } + + if (*inflate_flags & NGHTTP2_HD_INFLATE_EMIT) { + nv_out->name = hd_nv.name->base; + nv_out->namelen = hd_nv.name->len; + + nv_out->value = hd_nv.value->base; + nv_out->valuelen = hd_nv.value->len; + + nv_out->flags = hd_nv.flags; + } + + return rv; +} + +ssize_t nghttp2_hd_inflate_hd_nv(nghttp2_hd_inflater *inflater, + nghttp2_hd_nv *nv_out, int *inflate_flags, + const uint8_t *in, size_t inlen, + int in_final) { + ssize_t rv = 0; + const uint8_t *first = in; + const uint8_t *last = in + inlen; + int rfin = 0; + int busy = 0; + nghttp2_mem *mem; + + mem = inflater->ctx.mem; + + if (inflater->ctx.bad) { + return NGHTTP2_ERR_HEADER_COMP; + } + + DEBUGF("inflatehd: start state=%d\n", inflater->state); + hd_inflate_keep_free(inflater); + *inflate_flags = NGHTTP2_HD_INFLATE_NONE; + for (; in != last || busy;) { + busy = 0; + switch (inflater->state) { + case NGHTTP2_HD_STATE_EXPECT_TABLE_SIZE: + if ((*in & 0xe0u) != 0x20u) { + DEBUGF("inflatehd: header table size change was expected, but saw " + "0x%02x as first byte", + *in); + rv = NGHTTP2_ERR_HEADER_COMP; + goto fail; + } + /* fall through */ + case NGHTTP2_HD_STATE_INFLATE_START: + case NGHTTP2_HD_STATE_OPCODE: + if ((*in & 0xe0u) == 0x20u) { + DEBUGF("inflatehd: header table size change\n"); + if (inflater->state == NGHTTP2_HD_STATE_OPCODE) { + DEBUGF("inflatehd: header table size change must appear at the head " + "of header block\n"); + rv = NGHTTP2_ERR_HEADER_COMP; + goto fail; + } + inflater->opcode = NGHTTP2_HD_OPCODE_INDEXED; + inflater->state = NGHTTP2_HD_STATE_READ_TABLE_SIZE; + } else if (*in & 0x80u) { + DEBUGF("inflatehd: indexed repr\n"); + inflater->opcode = NGHTTP2_HD_OPCODE_INDEXED; + inflater->state = NGHTTP2_HD_STATE_READ_INDEX; + } else { + if (*in == 0x40u || *in == 0 || *in == 0x10u) { + DEBUGF("inflatehd: literal header repr - new name\n"); + inflater->opcode = NGHTTP2_HD_OPCODE_NEWNAME; + inflater->state = NGHTTP2_HD_STATE_NEWNAME_CHECK_NAMELEN; + } else { + DEBUGF("inflatehd: literal header repr - indexed name\n"); + inflater->opcode = NGHTTP2_HD_OPCODE_INDNAME; + inflater->state = NGHTTP2_HD_STATE_READ_INDEX; + } + inflater->index_required = (*in & 0x40) != 0; + inflater->no_index = (*in & 0xf0u) == 0x10u; + DEBUGF("inflatehd: indexing required=%d, no_index=%d\n", + inflater->index_required, inflater->no_index); + if (inflater->opcode == NGHTTP2_HD_OPCODE_NEWNAME) { + ++in; + } + } + inflater->left = 0; + inflater->shift = 0; + break; + case NGHTTP2_HD_STATE_READ_TABLE_SIZE: + rfin = 0; + rv = hd_inflate_read_len( + inflater, &rfin, in, last, 5, + nghttp2_min(inflater->min_hd_table_bufsize_max, + inflater->settings_hd_table_bufsize_max)); + if (rv < 0) { + goto fail; + } + in += rv; + if (!rfin) { + goto almost_ok; + } + DEBUGF("inflatehd: table_size=%zu\n", inflater->left); + inflater->min_hd_table_bufsize_max = UINT32_MAX; + inflater->ctx.hd_table_bufsize_max = inflater->left; + hd_context_shrink_table_size(&inflater->ctx, NULL); + inflater->state = NGHTTP2_HD_STATE_INFLATE_START; + break; + case NGHTTP2_HD_STATE_READ_INDEX: { + size_t prefixlen; + + if (inflater->opcode == NGHTTP2_HD_OPCODE_INDEXED) { + prefixlen = 7; + } else if (inflater->index_required) { + prefixlen = 6; + } else { + prefixlen = 4; + } + + rfin = 0; + rv = hd_inflate_read_len(inflater, &rfin, in, last, prefixlen, + get_max_index(&inflater->ctx)); + if (rv < 0) { + goto fail; + } + + in += rv; + + if (!rfin) { + goto almost_ok; + } + + if (inflater->left == 0) { + rv = NGHTTP2_ERR_HEADER_COMP; + goto fail; + } + + DEBUGF("inflatehd: index=%zu\n", inflater->left); + if (inflater->opcode == NGHTTP2_HD_OPCODE_INDEXED) { + inflater->index = inflater->left; + --inflater->index; + + hd_inflate_commit_indexed(inflater, nv_out); + + inflater->state = NGHTTP2_HD_STATE_OPCODE; + *inflate_flags |= NGHTTP2_HD_INFLATE_EMIT; + return (ssize_t)(in - first); + } else { + inflater->index = inflater->left; + --inflater->index; + + inflater->state = NGHTTP2_HD_STATE_CHECK_VALUELEN; + } + break; + } + case NGHTTP2_HD_STATE_NEWNAME_CHECK_NAMELEN: + hd_inflate_set_huffman_encoded(inflater, in); + inflater->state = NGHTTP2_HD_STATE_NEWNAME_READ_NAMELEN; + inflater->left = 0; + inflater->shift = 0; + DEBUGF("inflatehd: huffman encoded=%d\n", inflater->huffman_encoded != 0); + /* Fall through */ + case NGHTTP2_HD_STATE_NEWNAME_READ_NAMELEN: + rfin = 0; + rv = hd_inflate_read_len(inflater, &rfin, in, last, 7, NGHTTP2_HD_MAX_NV); + if (rv < 0) { + goto fail; + } + in += rv; + if (!rfin) { + DEBUGF("inflatehd: integer not fully decoded. current=%zu\n", + inflater->left); + + goto almost_ok; + } + + if (inflater->huffman_encoded) { + nghttp2_hd_huff_decode_context_init(&inflater->huff_decode_ctx); + + inflater->state = NGHTTP2_HD_STATE_NEWNAME_READ_NAMEHUFF; + + rv = nghttp2_rcbuf_new(&inflater->namercbuf, inflater->left * 2 + 1, + mem); + } else { + inflater->state = NGHTTP2_HD_STATE_NEWNAME_READ_NAME; + rv = nghttp2_rcbuf_new(&inflater->namercbuf, inflater->left + 1, mem); + } + + if (rv != 0) { + goto fail; + } + + nghttp2_buf_wrap_init(&inflater->namebuf, inflater->namercbuf->base, + inflater->namercbuf->len); + + break; + case NGHTTP2_HD_STATE_NEWNAME_READ_NAMEHUFF: + rv = hd_inflate_read_huff(inflater, &inflater->namebuf, in, last); + if (rv < 0) { + goto fail; + } + + in += rv; + + DEBUGF("inflatehd: %zd bytes read\n", rv); + + if (inflater->left) { + DEBUGF("inflatehd: still %zu bytes to go\n", inflater->left); + + goto almost_ok; + } + + *inflater->namebuf.last = '\0'; + inflater->namercbuf->len = nghttp2_buf_len(&inflater->namebuf); + + inflater->state = NGHTTP2_HD_STATE_CHECK_VALUELEN; + + break; + case NGHTTP2_HD_STATE_NEWNAME_READ_NAME: + rv = hd_inflate_read(inflater, &inflater->namebuf, in, last); + if (rv < 0) { + goto fail; + } + + in += rv; + + DEBUGF("inflatehd: %zd bytes read\n", rv); + if (inflater->left) { + DEBUGF("inflatehd: still %zu bytes to go\n", inflater->left); + + goto almost_ok; + } + + *inflater->namebuf.last = '\0'; + inflater->namercbuf->len = nghttp2_buf_len(&inflater->namebuf); + + inflater->state = NGHTTP2_HD_STATE_CHECK_VALUELEN; + + break; + case NGHTTP2_HD_STATE_CHECK_VALUELEN: + hd_inflate_set_huffman_encoded(inflater, in); + inflater->state = NGHTTP2_HD_STATE_READ_VALUELEN; + inflater->left = 0; + inflater->shift = 0; + DEBUGF("inflatehd: huffman encoded=%d\n", inflater->huffman_encoded != 0); + /* Fall through */ + case NGHTTP2_HD_STATE_READ_VALUELEN: + rfin = 0; + rv = hd_inflate_read_len(inflater, &rfin, in, last, 7, NGHTTP2_HD_MAX_NV); + if (rv < 0) { + goto fail; + } + + in += rv; + + if (!rfin) { + goto almost_ok; + } + + DEBUGF("inflatehd: valuelen=%zu\n", inflater->left); + + if (inflater->huffman_encoded) { + nghttp2_hd_huff_decode_context_init(&inflater->huff_decode_ctx); + + inflater->state = NGHTTP2_HD_STATE_READ_VALUEHUFF; + + rv = nghttp2_rcbuf_new(&inflater->valuercbuf, inflater->left * 2 + 1, + mem); + } else { + inflater->state = NGHTTP2_HD_STATE_READ_VALUE; + + rv = nghttp2_rcbuf_new(&inflater->valuercbuf, inflater->left + 1, mem); + } + + if (rv != 0) { + goto fail; + } + + nghttp2_buf_wrap_init(&inflater->valuebuf, inflater->valuercbuf->base, + inflater->valuercbuf->len); + + busy = 1; + + break; + case NGHTTP2_HD_STATE_READ_VALUEHUFF: + rv = hd_inflate_read_huff(inflater, &inflater->valuebuf, in, last); + if (rv < 0) { + goto fail; + } + + in += rv; + + DEBUGF("inflatehd: %zd bytes read\n", rv); + + if (inflater->left) { + DEBUGF("inflatehd: still %zu bytes to go\n", inflater->left); + + goto almost_ok; + } + + *inflater->valuebuf.last = '\0'; + inflater->valuercbuf->len = nghttp2_buf_len(&inflater->valuebuf); + + if (inflater->opcode == NGHTTP2_HD_OPCODE_NEWNAME) { + rv = hd_inflate_commit_newname(inflater, nv_out); + } else { + rv = hd_inflate_commit_indname(inflater, nv_out); + } + + if (rv != 0) { + goto fail; + } + + inflater->state = NGHTTP2_HD_STATE_OPCODE; + *inflate_flags |= NGHTTP2_HD_INFLATE_EMIT; + + return (ssize_t)(in - first); + case NGHTTP2_HD_STATE_READ_VALUE: + rv = hd_inflate_read(inflater, &inflater->valuebuf, in, last); + if (rv < 0) { + DEBUGF("inflatehd: value read failure %zd: %s\n", rv, + nghttp2_strerror((int)rv)); + goto fail; + } + + in += rv; + + DEBUGF("inflatehd: %zd bytes read\n", rv); + + if (inflater->left) { + DEBUGF("inflatehd: still %zu bytes to go\n", inflater->left); + goto almost_ok; + } + + *inflater->valuebuf.last = '\0'; + inflater->valuercbuf->len = nghttp2_buf_len(&inflater->valuebuf); + + if (inflater->opcode == NGHTTP2_HD_OPCODE_NEWNAME) { + rv = hd_inflate_commit_newname(inflater, nv_out); + } else { + rv = hd_inflate_commit_indname(inflater, nv_out); + } + + if (rv != 0) { + goto fail; + } + + inflater->state = NGHTTP2_HD_STATE_OPCODE; + *inflate_flags |= NGHTTP2_HD_INFLATE_EMIT; + + return (ssize_t)(in - first); + } + } + + assert(in == last); + + DEBUGF("inflatehd: all input bytes were processed\n"); + + if (in_final) { + DEBUGF("inflatehd: in_final set\n"); + + if (inflater->state != NGHTTP2_HD_STATE_OPCODE && + inflater->state != NGHTTP2_HD_STATE_INFLATE_START) { + DEBUGF("inflatehd: unacceptable state=%d\n", inflater->state); + rv = NGHTTP2_ERR_HEADER_COMP; + + goto fail; + } + *inflate_flags |= NGHTTP2_HD_INFLATE_FINAL; + } + return (ssize_t)(in - first); + +almost_ok: + if (in_final) { + DEBUGF("inflatehd: input ended prematurely\n"); + + rv = NGHTTP2_ERR_HEADER_COMP; + + goto fail; + } + return (ssize_t)(in - first); + +fail: + DEBUGF("inflatehd: error return %zd\n", rv); + + inflater->ctx.bad = 1; + return rv; +} + +int nghttp2_hd_inflate_end_headers(nghttp2_hd_inflater *inflater) { + hd_inflate_keep_free(inflater); + inflater->state = NGHTTP2_HD_STATE_INFLATE_START; + return 0; +} + +int nghttp2_hd_inflate_new(nghttp2_hd_inflater **inflater_ptr) { + return nghttp2_hd_inflate_new2(inflater_ptr, NULL); +} + +int nghttp2_hd_inflate_new2(nghttp2_hd_inflater **inflater_ptr, + nghttp2_mem *mem) { + int rv; + nghttp2_hd_inflater *inflater; + + if (mem == NULL) { + mem = nghttp2_mem_default(); + } + + inflater = nghttp2_mem_malloc(mem, sizeof(nghttp2_hd_inflater)); + + if (inflater == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + rv = nghttp2_hd_inflate_init(inflater, mem); + + if (rv != 0) { + nghttp2_mem_free(mem, inflater); + + return rv; + } + + *inflater_ptr = inflater; + + return 0; +} + +void nghttp2_hd_inflate_del(nghttp2_hd_inflater *inflater) { + nghttp2_mem *mem; + + mem = inflater->ctx.mem; + nghttp2_hd_inflate_free(inflater); + + nghttp2_mem_free(mem, inflater); +} + +int nghttp2_hd_emit_indname_block(nghttp2_bufs *bufs, size_t idx, + nghttp2_nv *nv, int indexing_mode) { + + return emit_indname_block(bufs, idx, nv, indexing_mode); +} + +int nghttp2_hd_emit_newname_block(nghttp2_bufs *bufs, nghttp2_nv *nv, + int indexing_mode) { + return emit_newname_block(bufs, nv, indexing_mode); +} + +int nghttp2_hd_emit_table_size(nghttp2_bufs *bufs, size_t table_size) { + return emit_table_size(bufs, table_size); +} + +ssize_t nghttp2_hd_decode_length(uint32_t *res, size_t *shift_ptr, int *fin, + uint32_t initial, size_t shift, uint8_t *in, + uint8_t *last, size_t prefix) { + return decode_length(res, shift_ptr, fin, initial, shift, in, last, prefix); +} + +static const nghttp2_nv *hd_get_table_entry(nghttp2_hd_context *context, + size_t idx) { + if (idx == 0) { + return NULL; + } + + --idx; + + if (!INDEX_RANGE_VALID(context, idx)) { + return NULL; + } + + return nghttp2_hd_table_get2(context, idx); +} + +size_t nghttp2_hd_deflate_get_num_table_entries(nghttp2_hd_deflater *deflater) { + return get_max_index(&deflater->ctx); +} + +const nghttp2_nv * +nghttp2_hd_deflate_get_table_entry(nghttp2_hd_deflater *deflater, size_t idx) { + return hd_get_table_entry(&deflater->ctx, idx); +} + +size_t +nghttp2_hd_deflate_get_dynamic_table_size(nghttp2_hd_deflater *deflater) { + return deflater->ctx.hd_table_bufsize; +} + +size_t +nghttp2_hd_deflate_get_max_dynamic_table_size(nghttp2_hd_deflater *deflater) { + return deflater->ctx.hd_table_bufsize_max; +} + +size_t nghttp2_hd_inflate_get_num_table_entries(nghttp2_hd_inflater *inflater) { + return get_max_index(&inflater->ctx); +} + +const nghttp2_nv * +nghttp2_hd_inflate_get_table_entry(nghttp2_hd_inflater *inflater, size_t idx) { + return hd_get_table_entry(&inflater->ctx, idx); +} + +size_t +nghttp2_hd_inflate_get_dynamic_table_size(nghttp2_hd_inflater *inflater) { + return inflater->ctx.hd_table_bufsize; +} + +size_t +nghttp2_hd_inflate_get_max_dynamic_table_size(nghttp2_hd_inflater *inflater) { + return inflater->ctx.hd_table_bufsize_max; +} diff --git a/contrib/libs/nghttp2/lib/nghttp2_hd.h b/contrib/libs/nghttp2/lib/nghttp2_hd.h index 9743530084..267402881f 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_hd.h +++ b/contrib/libs/nghttp2/lib/nghttp2_hd.h @@ -1,439 +1,439 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2013 Tatsuhiro Tsujikawa - * - * 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_HD_H -#define NGHTTP2_HD_H - -#ifdef HAVE_CONFIG_H +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * 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_HD_H +#define NGHTTP2_HD_H + +#ifdef HAVE_CONFIG_H # include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <nghttp2/nghttp2.h> - -#include "nghttp2_hd_huffman.h" -#include "nghttp2_buf.h" -#include "nghttp2_mem.h" -#include "nghttp2_rcbuf.h" - -#define NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE NGHTTP2_DEFAULT_HEADER_TABLE_SIZE -#define NGHTTP2_HD_ENTRY_OVERHEAD 32 - -/* The maximum length of one name/value pair. This is the sum of the - length of name and value. This is not specified by the spec. We - just chose the arbitrary size */ -#define NGHTTP2_HD_MAX_NV 65536 - -/* Default size of maximum table buffer size for encoder. Even if - remote decoder notifies larger buffer size for its decoding, - encoder only uses the memory up to this value. */ -#define NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE (1 << 12) - -/* Exported for unit test */ -#define NGHTTP2_STATIC_TABLE_LENGTH 61 - -/* Generated by genlibtokenlookup.py */ -typedef enum { - NGHTTP2_TOKEN__AUTHORITY = 0, - NGHTTP2_TOKEN__METHOD = 1, - NGHTTP2_TOKEN__PATH = 3, - NGHTTP2_TOKEN__SCHEME = 5, - NGHTTP2_TOKEN__STATUS = 7, - NGHTTP2_TOKEN_ACCEPT_CHARSET = 14, - NGHTTP2_TOKEN_ACCEPT_ENCODING = 15, - NGHTTP2_TOKEN_ACCEPT_LANGUAGE = 16, - NGHTTP2_TOKEN_ACCEPT_RANGES = 17, - NGHTTP2_TOKEN_ACCEPT = 18, - NGHTTP2_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN = 19, - NGHTTP2_TOKEN_AGE = 20, - NGHTTP2_TOKEN_ALLOW = 21, - NGHTTP2_TOKEN_AUTHORIZATION = 22, - NGHTTP2_TOKEN_CACHE_CONTROL = 23, - NGHTTP2_TOKEN_CONTENT_DISPOSITION = 24, - NGHTTP2_TOKEN_CONTENT_ENCODING = 25, - NGHTTP2_TOKEN_CONTENT_LANGUAGE = 26, - NGHTTP2_TOKEN_CONTENT_LENGTH = 27, - NGHTTP2_TOKEN_CONTENT_LOCATION = 28, - NGHTTP2_TOKEN_CONTENT_RANGE = 29, - NGHTTP2_TOKEN_CONTENT_TYPE = 30, - NGHTTP2_TOKEN_COOKIE = 31, - NGHTTP2_TOKEN_DATE = 32, - NGHTTP2_TOKEN_ETAG = 33, - NGHTTP2_TOKEN_EXPECT = 34, - NGHTTP2_TOKEN_EXPIRES = 35, - NGHTTP2_TOKEN_FROM = 36, - NGHTTP2_TOKEN_HOST = 37, - NGHTTP2_TOKEN_IF_MATCH = 38, - NGHTTP2_TOKEN_IF_MODIFIED_SINCE = 39, - NGHTTP2_TOKEN_IF_NONE_MATCH = 40, - NGHTTP2_TOKEN_IF_RANGE = 41, - NGHTTP2_TOKEN_IF_UNMODIFIED_SINCE = 42, - NGHTTP2_TOKEN_LAST_MODIFIED = 43, - NGHTTP2_TOKEN_LINK = 44, - NGHTTP2_TOKEN_LOCATION = 45, - NGHTTP2_TOKEN_MAX_FORWARDS = 46, - NGHTTP2_TOKEN_PROXY_AUTHENTICATE = 47, - NGHTTP2_TOKEN_PROXY_AUTHORIZATION = 48, - NGHTTP2_TOKEN_RANGE = 49, - NGHTTP2_TOKEN_REFERER = 50, - NGHTTP2_TOKEN_REFRESH = 51, - NGHTTP2_TOKEN_RETRY_AFTER = 52, - NGHTTP2_TOKEN_SERVER = 53, - NGHTTP2_TOKEN_SET_COOKIE = 54, - NGHTTP2_TOKEN_STRICT_TRANSPORT_SECURITY = 55, - NGHTTP2_TOKEN_TRANSFER_ENCODING = 56, - NGHTTP2_TOKEN_USER_AGENT = 57, - NGHTTP2_TOKEN_VARY = 58, - NGHTTP2_TOKEN_VIA = 59, - NGHTTP2_TOKEN_WWW_AUTHENTICATE = 60, - NGHTTP2_TOKEN_TE, - NGHTTP2_TOKEN_CONNECTION, - NGHTTP2_TOKEN_KEEP_ALIVE, - NGHTTP2_TOKEN_PROXY_CONNECTION, - NGHTTP2_TOKEN_UPGRADE, +#endif /* HAVE_CONFIG_H */ + +#include <nghttp2/nghttp2.h> + +#include "nghttp2_hd_huffman.h" +#include "nghttp2_buf.h" +#include "nghttp2_mem.h" +#include "nghttp2_rcbuf.h" + +#define NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE NGHTTP2_DEFAULT_HEADER_TABLE_SIZE +#define NGHTTP2_HD_ENTRY_OVERHEAD 32 + +/* The maximum length of one name/value pair. This is the sum of the + length of name and value. This is not specified by the spec. We + just chose the arbitrary size */ +#define NGHTTP2_HD_MAX_NV 65536 + +/* Default size of maximum table buffer size for encoder. Even if + remote decoder notifies larger buffer size for its decoding, + encoder only uses the memory up to this value. */ +#define NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE (1 << 12) + +/* Exported for unit test */ +#define NGHTTP2_STATIC_TABLE_LENGTH 61 + +/* Generated by genlibtokenlookup.py */ +typedef enum { + NGHTTP2_TOKEN__AUTHORITY = 0, + NGHTTP2_TOKEN__METHOD = 1, + NGHTTP2_TOKEN__PATH = 3, + NGHTTP2_TOKEN__SCHEME = 5, + NGHTTP2_TOKEN__STATUS = 7, + NGHTTP2_TOKEN_ACCEPT_CHARSET = 14, + NGHTTP2_TOKEN_ACCEPT_ENCODING = 15, + NGHTTP2_TOKEN_ACCEPT_LANGUAGE = 16, + NGHTTP2_TOKEN_ACCEPT_RANGES = 17, + NGHTTP2_TOKEN_ACCEPT = 18, + NGHTTP2_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN = 19, + NGHTTP2_TOKEN_AGE = 20, + NGHTTP2_TOKEN_ALLOW = 21, + NGHTTP2_TOKEN_AUTHORIZATION = 22, + NGHTTP2_TOKEN_CACHE_CONTROL = 23, + NGHTTP2_TOKEN_CONTENT_DISPOSITION = 24, + NGHTTP2_TOKEN_CONTENT_ENCODING = 25, + NGHTTP2_TOKEN_CONTENT_LANGUAGE = 26, + NGHTTP2_TOKEN_CONTENT_LENGTH = 27, + NGHTTP2_TOKEN_CONTENT_LOCATION = 28, + NGHTTP2_TOKEN_CONTENT_RANGE = 29, + NGHTTP2_TOKEN_CONTENT_TYPE = 30, + NGHTTP2_TOKEN_COOKIE = 31, + NGHTTP2_TOKEN_DATE = 32, + NGHTTP2_TOKEN_ETAG = 33, + NGHTTP2_TOKEN_EXPECT = 34, + NGHTTP2_TOKEN_EXPIRES = 35, + NGHTTP2_TOKEN_FROM = 36, + NGHTTP2_TOKEN_HOST = 37, + NGHTTP2_TOKEN_IF_MATCH = 38, + NGHTTP2_TOKEN_IF_MODIFIED_SINCE = 39, + NGHTTP2_TOKEN_IF_NONE_MATCH = 40, + NGHTTP2_TOKEN_IF_RANGE = 41, + NGHTTP2_TOKEN_IF_UNMODIFIED_SINCE = 42, + NGHTTP2_TOKEN_LAST_MODIFIED = 43, + NGHTTP2_TOKEN_LINK = 44, + NGHTTP2_TOKEN_LOCATION = 45, + NGHTTP2_TOKEN_MAX_FORWARDS = 46, + NGHTTP2_TOKEN_PROXY_AUTHENTICATE = 47, + NGHTTP2_TOKEN_PROXY_AUTHORIZATION = 48, + NGHTTP2_TOKEN_RANGE = 49, + NGHTTP2_TOKEN_REFERER = 50, + NGHTTP2_TOKEN_REFRESH = 51, + NGHTTP2_TOKEN_RETRY_AFTER = 52, + NGHTTP2_TOKEN_SERVER = 53, + NGHTTP2_TOKEN_SET_COOKIE = 54, + NGHTTP2_TOKEN_STRICT_TRANSPORT_SECURITY = 55, + NGHTTP2_TOKEN_TRANSFER_ENCODING = 56, + NGHTTP2_TOKEN_USER_AGENT = 57, + NGHTTP2_TOKEN_VARY = 58, + NGHTTP2_TOKEN_VIA = 59, + NGHTTP2_TOKEN_WWW_AUTHENTICATE = 60, + NGHTTP2_TOKEN_TE, + NGHTTP2_TOKEN_CONNECTION, + NGHTTP2_TOKEN_KEEP_ALIVE, + NGHTTP2_TOKEN_PROXY_CONNECTION, + NGHTTP2_TOKEN_UPGRADE, NGHTTP2_TOKEN__PROTOCOL, -} nghttp2_token; - -struct nghttp2_hd_entry; -typedef struct nghttp2_hd_entry nghttp2_hd_entry; - -typedef struct { - /* The buffer containing header field name. NULL-termination is - guaranteed. */ - nghttp2_rcbuf *name; - /* The buffer containing header field value. NULL-termination is - guaranteed. */ - nghttp2_rcbuf *value; - /* nghttp2_token value for name. It could be -1 if we have no token - for that header field name. */ - int32_t token; - /* Bitwise OR of one or more of nghttp2_nv_flag. */ - uint8_t flags; -} nghttp2_hd_nv; - -struct nghttp2_hd_entry { - /* The header field name/value pair */ - nghttp2_hd_nv nv; - /* This is solely for nghttp2_hd_{deflate,inflate}_get_table_entry - APIs to keep backward compatibility. */ - nghttp2_nv cnv; - /* The next entry which shares same bucket in hash table. */ - nghttp2_hd_entry *next; - /* The sequence number. We will increment it by one whenever we - store nghttp2_hd_entry to dynamic header table. */ - uint32_t seq; - /* The hash value for header name (nv.name). */ - uint32_t hash; -}; - -/* The entry used for static header table. */ -typedef struct { - nghttp2_rcbuf name; - nghttp2_rcbuf value; - nghttp2_nv cnv; - int32_t token; - uint32_t hash; -} nghttp2_hd_static_entry; - -typedef struct { - nghttp2_hd_entry **buffer; - size_t mask; - size_t first; - size_t len; -} nghttp2_hd_ringbuf; - -typedef enum { - NGHTTP2_HD_OPCODE_NONE, - NGHTTP2_HD_OPCODE_INDEXED, - NGHTTP2_HD_OPCODE_NEWNAME, - NGHTTP2_HD_OPCODE_INDNAME -} nghttp2_hd_opcode; - -typedef enum { - NGHTTP2_HD_STATE_EXPECT_TABLE_SIZE, - NGHTTP2_HD_STATE_INFLATE_START, - NGHTTP2_HD_STATE_OPCODE, - NGHTTP2_HD_STATE_READ_TABLE_SIZE, - NGHTTP2_HD_STATE_READ_INDEX, - NGHTTP2_HD_STATE_NEWNAME_CHECK_NAMELEN, - NGHTTP2_HD_STATE_NEWNAME_READ_NAMELEN, - NGHTTP2_HD_STATE_NEWNAME_READ_NAMEHUFF, - NGHTTP2_HD_STATE_NEWNAME_READ_NAME, - NGHTTP2_HD_STATE_CHECK_VALUELEN, - NGHTTP2_HD_STATE_READ_VALUELEN, - NGHTTP2_HD_STATE_READ_VALUEHUFF, - NGHTTP2_HD_STATE_READ_VALUE -} nghttp2_hd_inflate_state; - -typedef enum { - NGHTTP2_HD_WITH_INDEXING, - NGHTTP2_HD_WITHOUT_INDEXING, - NGHTTP2_HD_NEVER_INDEXING -} nghttp2_hd_indexing_mode; - -typedef struct { - /* dynamic header table */ - nghttp2_hd_ringbuf hd_table; - /* Memory allocator */ - nghttp2_mem *mem; - /* Abstract buffer size of hd_table as described in the spec. This - is the sum of length of name/value in hd_table + - NGHTTP2_HD_ENTRY_OVERHEAD bytes overhead per each entry. */ - size_t hd_table_bufsize; - /* The effective header table size. */ - size_t hd_table_bufsize_max; - /* Next sequence number for nghttp2_hd_entry */ - uint32_t next_seq; - /* If inflate/deflate error occurred, this value is set to 1 and - further invocation of inflate/deflate will fail with - NGHTTP2_ERR_HEADER_COMP. */ - uint8_t bad; -} nghttp2_hd_context; - -#define HD_MAP_SIZE 128 - -typedef struct { - nghttp2_hd_entry *table[HD_MAP_SIZE]; -} nghttp2_hd_map; - -struct nghttp2_hd_deflater { - nghttp2_hd_context ctx; - nghttp2_hd_map map; - /* The upper limit of the header table size the deflater accepts. */ - size_t deflate_hd_table_bufsize_max; - /* Minimum header table size notified in the next context update */ - size_t min_hd_table_bufsize_max; - /* If nonzero, send header table size using encoding context update - in the next deflate process */ - uint8_t notify_table_size_change; -}; - -struct nghttp2_hd_inflater { - nghttp2_hd_context ctx; - /* Stores current state of huffman decoding */ - nghttp2_hd_huff_decode_context huff_decode_ctx; - /* header buffer */ - nghttp2_buf namebuf, valuebuf; - nghttp2_rcbuf *namercbuf, *valuercbuf; - /* Pointer to the name/value pair which are used in the current - header emission. */ - nghttp2_rcbuf *nv_name_keep, *nv_value_keep; - /* The number of bytes to read */ - size_t left; - /* The index in indexed repr or indexed name */ - size_t index; - /* The maximum header table size the inflater supports. This is the - same value transmitted in SETTINGS_HEADER_TABLE_SIZE */ - size_t settings_hd_table_bufsize_max; - /* Minimum header table size set by nghttp2_hd_inflate_change_table_size */ - size_t min_hd_table_bufsize_max; - /* The number of next shift to decode integer */ - size_t shift; - nghttp2_hd_opcode opcode; - nghttp2_hd_inflate_state state; - /* nonzero if string is huffman encoded */ - uint8_t huffman_encoded; - /* nonzero if deflater requires that current entry is indexed */ - uint8_t index_required; - /* nonzero if deflater requires that current entry must not be - indexed */ - uint8_t no_index; -}; - -/* - * Initializes the |ent| members. The reference counts of nv->name - * and nv->value are increased by one for each. - */ -void nghttp2_hd_entry_init(nghttp2_hd_entry *ent, nghttp2_hd_nv *nv); - -/* - * This function decreases the reference counts of nv->name and - * nv->value. - */ -void nghttp2_hd_entry_free(nghttp2_hd_entry *ent); - -/* - * Initializes |deflater| for deflating name/values pairs. - * - * The encoder only uses up to - * NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE bytes for header table - * even if the larger value is specified later in - * nghttp2_hd_change_table_size(). - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -int nghttp2_hd_deflate_init(nghttp2_hd_deflater *deflater, nghttp2_mem *mem); - -/* - * Initializes |deflater| for deflating name/values pairs. - * - * The encoder only uses up to |max_deflate_dynamic_table_size| bytes - * for header table even if the larger value is specified later in - * nghttp2_hd_change_table_size(). - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -int nghttp2_hd_deflate_init2(nghttp2_hd_deflater *deflater, - size_t max_deflate_dynamic_table_size, - nghttp2_mem *mem); - -/* - * Deallocates any resources allocated for |deflater|. - */ -void nghttp2_hd_deflate_free(nghttp2_hd_deflater *deflater); - -/* - * Deflates the |nva|, which has the |nvlen| name/value pairs, into - * the |bufs|. - * - * This function expands |bufs| as necessary to store the result. If - * buffers is full and the process still requires more space, this - * function fails and returns NGHTTP2_ERR_HEADER_COMP. - * - * After this function returns, it is safe to delete the |nva|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - * NGHTTP2_ERR_HEADER_COMP - * Deflation process has failed. - * NGHTTP2_ERR_BUFFER_ERROR - * Out of buffer space. - */ -int nghttp2_hd_deflate_hd_bufs(nghttp2_hd_deflater *deflater, - nghttp2_bufs *bufs, const nghttp2_nv *nva, - size_t nvlen); - -/* - * Initializes |inflater| for inflating name/values pairs. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * :enum:`NGHTTP2_ERR_NOMEM` - * Out of memory. - */ -int nghttp2_hd_inflate_init(nghttp2_hd_inflater *inflater, nghttp2_mem *mem); - -/* - * Deallocates any resources allocated for |inflater|. - */ -void nghttp2_hd_inflate_free(nghttp2_hd_inflater *inflater); - -/* - * Similar to nghttp2_hd_inflate_hd(), but this takes nghttp2_hd_nv - * instead of nghttp2_nv as output parameter |nv_out|. Other than - * that return values and semantics are the same as - * nghttp2_hd_inflate_hd(). - */ -ssize_t nghttp2_hd_inflate_hd_nv(nghttp2_hd_inflater *inflater, - nghttp2_hd_nv *nv_out, int *inflate_flags, - const uint8_t *in, size_t inlen, int in_final); - -/* For unittesting purpose */ -int nghttp2_hd_emit_indname_block(nghttp2_bufs *bufs, size_t index, - nghttp2_nv *nv, int indexing_mode); - -/* For unittesting purpose */ -int nghttp2_hd_emit_newname_block(nghttp2_bufs *bufs, nghttp2_nv *nv, - int indexing_mode); - -/* For unittesting purpose */ -int nghttp2_hd_emit_table_size(nghttp2_bufs *bufs, size_t table_size); - -/* For unittesting purpose */ -nghttp2_hd_nv nghttp2_hd_table_get(nghttp2_hd_context *context, size_t index); - -/* For unittesting purpose */ -ssize_t nghttp2_hd_decode_length(uint32_t *res, size_t *shift_ptr, int *fin, - uint32_t initial, size_t shift, uint8_t *in, - uint8_t *last, size_t prefix); - -/* Huffman encoding/decoding functions */ - -/* - * Counts the required bytes to encode |src| with length |len|. - * - * This function returns the number of required bytes to encode given - * data, including padding of prefix of terminal symbol code. This - * function always succeeds. - */ -size_t nghttp2_hd_huff_encode_count(const uint8_t *src, size_t len); - -/* - * Encodes the given data |src| with length |srclen| to the |bufs|. - * This function expands extra buffers in |bufs| if necessary. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - * NGHTTP2_ERR_BUFFER_ERROR - * Out of buffer space. - */ -int nghttp2_hd_huff_encode(nghttp2_bufs *bufs, const uint8_t *src, - size_t srclen); - -void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx); - -/* - * Decodes the given data |src| with length |srclen|. The |ctx| must - * be initialized by nghttp2_hd_huff_decode_context_init(). The result - * will be written to |buf|. This function assumes that |buf| has the - * enough room to store the decoded byte string. - * - * The caller must set the |fin| to nonzero if the given input is the - * final block. - * - * This function returns the number of read bytes from the |in|. - * - * If this function fails, it returns one of the following negative - * return codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - * NGHTTP2_ERR_HEADER_COMP - * Decoding process has failed. - */ -ssize_t nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx, - nghttp2_buf *buf, const uint8_t *src, - size_t srclen, int fin); - +} nghttp2_token; + +struct nghttp2_hd_entry; +typedef struct nghttp2_hd_entry nghttp2_hd_entry; + +typedef struct { + /* The buffer containing header field name. NULL-termination is + guaranteed. */ + nghttp2_rcbuf *name; + /* The buffer containing header field value. NULL-termination is + guaranteed. */ + nghttp2_rcbuf *value; + /* nghttp2_token value for name. It could be -1 if we have no token + for that header field name. */ + int32_t token; + /* Bitwise OR of one or more of nghttp2_nv_flag. */ + uint8_t flags; +} nghttp2_hd_nv; + +struct nghttp2_hd_entry { + /* The header field name/value pair */ + nghttp2_hd_nv nv; + /* This is solely for nghttp2_hd_{deflate,inflate}_get_table_entry + APIs to keep backward compatibility. */ + nghttp2_nv cnv; + /* The next entry which shares same bucket in hash table. */ + nghttp2_hd_entry *next; + /* The sequence number. We will increment it by one whenever we + store nghttp2_hd_entry to dynamic header table. */ + uint32_t seq; + /* The hash value for header name (nv.name). */ + uint32_t hash; +}; + +/* The entry used for static header table. */ +typedef struct { + nghttp2_rcbuf name; + nghttp2_rcbuf value; + nghttp2_nv cnv; + int32_t token; + uint32_t hash; +} nghttp2_hd_static_entry; + +typedef struct { + nghttp2_hd_entry **buffer; + size_t mask; + size_t first; + size_t len; +} nghttp2_hd_ringbuf; + +typedef enum { + NGHTTP2_HD_OPCODE_NONE, + NGHTTP2_HD_OPCODE_INDEXED, + NGHTTP2_HD_OPCODE_NEWNAME, + NGHTTP2_HD_OPCODE_INDNAME +} nghttp2_hd_opcode; + +typedef enum { + NGHTTP2_HD_STATE_EXPECT_TABLE_SIZE, + NGHTTP2_HD_STATE_INFLATE_START, + NGHTTP2_HD_STATE_OPCODE, + NGHTTP2_HD_STATE_READ_TABLE_SIZE, + NGHTTP2_HD_STATE_READ_INDEX, + NGHTTP2_HD_STATE_NEWNAME_CHECK_NAMELEN, + NGHTTP2_HD_STATE_NEWNAME_READ_NAMELEN, + NGHTTP2_HD_STATE_NEWNAME_READ_NAMEHUFF, + NGHTTP2_HD_STATE_NEWNAME_READ_NAME, + NGHTTP2_HD_STATE_CHECK_VALUELEN, + NGHTTP2_HD_STATE_READ_VALUELEN, + NGHTTP2_HD_STATE_READ_VALUEHUFF, + NGHTTP2_HD_STATE_READ_VALUE +} nghttp2_hd_inflate_state; + +typedef enum { + NGHTTP2_HD_WITH_INDEXING, + NGHTTP2_HD_WITHOUT_INDEXING, + NGHTTP2_HD_NEVER_INDEXING +} nghttp2_hd_indexing_mode; + +typedef struct { + /* dynamic header table */ + nghttp2_hd_ringbuf hd_table; + /* Memory allocator */ + nghttp2_mem *mem; + /* Abstract buffer size of hd_table as described in the spec. This + is the sum of length of name/value in hd_table + + NGHTTP2_HD_ENTRY_OVERHEAD bytes overhead per each entry. */ + size_t hd_table_bufsize; + /* The effective header table size. */ + size_t hd_table_bufsize_max; + /* Next sequence number for nghttp2_hd_entry */ + uint32_t next_seq; + /* If inflate/deflate error occurred, this value is set to 1 and + further invocation of inflate/deflate will fail with + NGHTTP2_ERR_HEADER_COMP. */ + uint8_t bad; +} nghttp2_hd_context; + +#define HD_MAP_SIZE 128 + +typedef struct { + nghttp2_hd_entry *table[HD_MAP_SIZE]; +} nghttp2_hd_map; + +struct nghttp2_hd_deflater { + nghttp2_hd_context ctx; + nghttp2_hd_map map; + /* The upper limit of the header table size the deflater accepts. */ + size_t deflate_hd_table_bufsize_max; + /* Minimum header table size notified in the next context update */ + size_t min_hd_table_bufsize_max; + /* If nonzero, send header table size using encoding context update + in the next deflate process */ + uint8_t notify_table_size_change; +}; + +struct nghttp2_hd_inflater { + nghttp2_hd_context ctx; + /* Stores current state of huffman decoding */ + nghttp2_hd_huff_decode_context huff_decode_ctx; + /* header buffer */ + nghttp2_buf namebuf, valuebuf; + nghttp2_rcbuf *namercbuf, *valuercbuf; + /* Pointer to the name/value pair which are used in the current + header emission. */ + nghttp2_rcbuf *nv_name_keep, *nv_value_keep; + /* The number of bytes to read */ + size_t left; + /* The index in indexed repr or indexed name */ + size_t index; + /* The maximum header table size the inflater supports. This is the + same value transmitted in SETTINGS_HEADER_TABLE_SIZE */ + size_t settings_hd_table_bufsize_max; + /* Minimum header table size set by nghttp2_hd_inflate_change_table_size */ + size_t min_hd_table_bufsize_max; + /* The number of next shift to decode integer */ + size_t shift; + nghttp2_hd_opcode opcode; + nghttp2_hd_inflate_state state; + /* nonzero if string is huffman encoded */ + uint8_t huffman_encoded; + /* nonzero if deflater requires that current entry is indexed */ + uint8_t index_required; + /* nonzero if deflater requires that current entry must not be + indexed */ + uint8_t no_index; +}; + +/* + * Initializes the |ent| members. The reference counts of nv->name + * and nv->value are increased by one for each. + */ +void nghttp2_hd_entry_init(nghttp2_hd_entry *ent, nghttp2_hd_nv *nv); + +/* + * This function decreases the reference counts of nv->name and + * nv->value. + */ +void nghttp2_hd_entry_free(nghttp2_hd_entry *ent); + +/* + * Initializes |deflater| for deflating name/values pairs. + * + * The encoder only uses up to + * NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE bytes for header table + * even if the larger value is specified later in + * nghttp2_hd_change_table_size(). + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_hd_deflate_init(nghttp2_hd_deflater *deflater, nghttp2_mem *mem); + +/* + * Initializes |deflater| for deflating name/values pairs. + * + * The encoder only uses up to |max_deflate_dynamic_table_size| bytes + * for header table even if the larger value is specified later in + * nghttp2_hd_change_table_size(). + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_hd_deflate_init2(nghttp2_hd_deflater *deflater, + size_t max_deflate_dynamic_table_size, + nghttp2_mem *mem); + +/* + * Deallocates any resources allocated for |deflater|. + */ +void nghttp2_hd_deflate_free(nghttp2_hd_deflater *deflater); + +/* + * Deflates the |nva|, which has the |nvlen| name/value pairs, into + * the |bufs|. + * + * This function expands |bufs| as necessary to store the result. If + * buffers is full and the process still requires more space, this + * function fails and returns NGHTTP2_ERR_HEADER_COMP. + * + * After this function returns, it is safe to delete the |nva|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_HEADER_COMP + * Deflation process has failed. + * NGHTTP2_ERR_BUFFER_ERROR + * Out of buffer space. + */ +int nghttp2_hd_deflate_hd_bufs(nghttp2_hd_deflater *deflater, + nghttp2_bufs *bufs, const nghttp2_nv *nva, + size_t nvlen); + +/* + * Initializes |inflater| for inflating name/values pairs. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +int nghttp2_hd_inflate_init(nghttp2_hd_inflater *inflater, nghttp2_mem *mem); + +/* + * Deallocates any resources allocated for |inflater|. + */ +void nghttp2_hd_inflate_free(nghttp2_hd_inflater *inflater); + +/* + * Similar to nghttp2_hd_inflate_hd(), but this takes nghttp2_hd_nv + * instead of nghttp2_nv as output parameter |nv_out|. Other than + * that return values and semantics are the same as + * nghttp2_hd_inflate_hd(). + */ +ssize_t nghttp2_hd_inflate_hd_nv(nghttp2_hd_inflater *inflater, + nghttp2_hd_nv *nv_out, int *inflate_flags, + const uint8_t *in, size_t inlen, int in_final); + +/* For unittesting purpose */ +int nghttp2_hd_emit_indname_block(nghttp2_bufs *bufs, size_t index, + nghttp2_nv *nv, int indexing_mode); + +/* For unittesting purpose */ +int nghttp2_hd_emit_newname_block(nghttp2_bufs *bufs, nghttp2_nv *nv, + int indexing_mode); + +/* For unittesting purpose */ +int nghttp2_hd_emit_table_size(nghttp2_bufs *bufs, size_t table_size); + +/* For unittesting purpose */ +nghttp2_hd_nv nghttp2_hd_table_get(nghttp2_hd_context *context, size_t index); + +/* For unittesting purpose */ +ssize_t nghttp2_hd_decode_length(uint32_t *res, size_t *shift_ptr, int *fin, + uint32_t initial, size_t shift, uint8_t *in, + uint8_t *last, size_t prefix); + +/* Huffman encoding/decoding functions */ + +/* + * Counts the required bytes to encode |src| with length |len|. + * + * This function returns the number of required bytes to encode given + * data, including padding of prefix of terminal symbol code. This + * function always succeeds. + */ +size_t nghttp2_hd_huff_encode_count(const uint8_t *src, size_t len); + +/* + * Encodes the given data |src| with length |srclen| to the |bufs|. + * This function expands extra buffers in |bufs| if necessary. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_BUFFER_ERROR + * Out of buffer space. + */ +int nghttp2_hd_huff_encode(nghttp2_bufs *bufs, const uint8_t *src, + size_t srclen); + +void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx); + +/* + * Decodes the given data |src| with length |srclen|. The |ctx| must + * be initialized by nghttp2_hd_huff_decode_context_init(). The result + * will be written to |buf|. This function assumes that |buf| has the + * enough room to store the decoded byte string. + * + * The caller must set the |fin| to nonzero if the given input is the + * final block. + * + * This function returns the number of read bytes from the |in|. + * + * If this function fails, it returns one of the following negative + * return codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_HEADER_COMP + * Decoding process has failed. + */ +ssize_t nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx, + nghttp2_buf *buf, const uint8_t *src, + size_t srclen, int fin); + /* * nghttp2_hd_huff_decode_failure_state returns nonzero if |ctx| * indicates that huffman decoding context is in failure state. */ int nghttp2_hd_huff_decode_failure_state(nghttp2_hd_huff_decode_context *ctx); -#endif /* NGHTTP2_HD_H */ +#endif /* NGHTTP2_HD_H */ diff --git a/contrib/libs/nghttp2/lib/nghttp2_hd_huffman.c b/contrib/libs/nghttp2/lib/nghttp2_hd_huffman.c index 6e5a22ea12..ac90f49c44 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_hd_huffman.c +++ b/contrib/libs/nghttp2/lib/nghttp2_hd_huffman.c @@ -1,59 +1,59 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2013 Tatsuhiro Tsujikawa - * - * 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_hd_huffman.h" - -#include <string.h> -#include <assert.h> -#include <stdio.h> - -#include "nghttp2_hd.h" +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * 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_hd_huffman.h" + +#include <string.h> +#include <assert.h> +#include <stdio.h> + +#include "nghttp2_hd.h" #include "nghttp2_net.h" - -size_t nghttp2_hd_huff_encode_count(const uint8_t *src, size_t len) { - size_t i; - size_t nbits = 0; - - for (i = 0; i < len; ++i) { - nbits += huff_sym_table[src[i]].nbits; - } - /* pad the prefix of EOS (256) */ - return (nbits + 7) / 8; -} - -int nghttp2_hd_huff_encode(nghttp2_bufs *bufs, const uint8_t *src, - size_t srclen) { + +size_t nghttp2_hd_huff_encode_count(const uint8_t *src, size_t len) { + size_t i; + size_t nbits = 0; + + for (i = 0; i < len; ++i) { + nbits += huff_sym_table[src[i]].nbits; + } + /* pad the prefix of EOS (256) */ + return (nbits + 7) / 8; +} + +int nghttp2_hd_huff_encode(nghttp2_bufs *bufs, const uint8_t *src, + size_t srclen) { const nghttp2_huff_sym *sym; const uint8_t *end = src + srclen; uint64_t code = 0; uint32_t x; size_t nbits = 0; size_t avail; - int rv; - - avail = nghttp2_bufs_cur_avail(bufs); - + int rv; + + avail = nghttp2_bufs_cur_avail(bufs); + for (; src != end;) { sym = &huff_sym_table[*src++]; code |= (uint64_t)sym->code << (32 - nbits); @@ -75,10 +75,10 @@ int nghttp2_hd_huff_encode(nghttp2_bufs *bufs, const uint8_t *src, rv = nghttp2_bufs_addb(bufs, (uint8_t)(code >> 56)); if (rv != 0) { return rv; - } + } code <<= 8; nbits -= 8; - } + } avail = nghttp2_bufs_cur_avail(bufs); } @@ -87,10 +87,10 @@ int nghttp2_hd_huff_encode(nghttp2_bufs *bufs, const uint8_t *src, rv = nghttp2_bufs_addb(bufs, (uint8_t)(code >> 56)); if (rv != 0) { return rv; - } + } code <<= 8; nbits -= 8; - } + } if (nbits) { rv = nghttp2_bufs_addb( @@ -98,46 +98,46 @@ int nghttp2_hd_huff_encode(nghttp2_bufs *bufs, const uint8_t *src, if (rv != 0) { return rv; } - } - - return 0; -} - -void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx) { + } + + return 0; +} + +void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx) { ctx->fstate = NGHTTP2_HUFF_ACCEPTED; -} - -ssize_t nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx, - nghttp2_buf *buf, const uint8_t *src, - size_t srclen, int final) { +} + +ssize_t nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx, + nghttp2_buf *buf, const uint8_t *src, + size_t srclen, int final) { const uint8_t *end = src + srclen; nghttp2_huff_decode node = {ctx->fstate, 0}; const nghttp2_huff_decode *t = &node; uint8_t c; - - /* We use the decoding algorithm described in - http://graphics.ics.uci.edu/pub/Prefix.pdf */ + + /* We use the decoding algorithm described in + http://graphics.ics.uci.edu/pub/Prefix.pdf */ for (; src != end;) { c = *src++; t = &huff_decode_table[t->fstate & 0x1ff][c >> 4]; if (t->fstate & NGHTTP2_HUFF_SYM) { - *buf->last++ = t->sym; - } - + *buf->last++ = t->sym; + } + t = &huff_decode_table[t->fstate & 0x1ff][c & 0xf]; if (t->fstate & NGHTTP2_HUFF_SYM) { - *buf->last++ = t->sym; - } + *buf->last++ = t->sym; + } } - + ctx->fstate = t->fstate; if (final && !(ctx->fstate & NGHTTP2_HUFF_ACCEPTED)) { - return NGHTTP2_ERR_HEADER_COMP; - } + return NGHTTP2_ERR_HEADER_COMP; + } return (ssize_t)srclen; -} +} int nghttp2_hd_huff_decode_failure_state(nghttp2_hd_huff_decode_context *ctx) { return ctx->fstate == 0x100; diff --git a/contrib/libs/nghttp2/lib/nghttp2_hd_huffman.h b/contrib/libs/nghttp2/lib/nghttp2_hd_huffman.h index b9c8aeb704..2bfd531816 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_hd_huffman.h +++ b/contrib/libs/nghttp2/lib/nghttp2_hd_huffman.h @@ -1,45 +1,45 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2013 Tatsuhiro Tsujikawa - * - * 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_HD_HUFFMAN_H -#define NGHTTP2_HD_HUFFMAN_H - -#ifdef HAVE_CONFIG_H +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * 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_HD_HUFFMAN_H +#define NGHTTP2_HD_HUFFMAN_H + +#ifdef HAVE_CONFIG_H # include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <nghttp2/nghttp2.h> - -typedef enum { - /* FSA accepts this state as the end of huffman encoding - sequence. */ +#endif /* HAVE_CONFIG_H */ + +#include <nghttp2/nghttp2.h> + +typedef enum { + /* FSA accepts this state as the end of huffman encoding + sequence. */ NGHTTP2_HUFF_ACCEPTED = 1 << 14, - /* This state emits symbol */ + /* This state emits symbol */ NGHTTP2_HUFF_SYM = 1 << 15, -} nghttp2_huff_decode_flag; - -typedef struct { +} nghttp2_huff_decode_flag; + +typedef struct { /* fstate is the current huffman decoding state, which is actually the node ID of internal huffman tree with nghttp2_huff_decode_flag OR-ed. We have 257 leaf nodes, but they @@ -48,25 +48,25 @@ typedef struct { a special node and it is a terminal state that means decoding failed. */ uint16_t fstate; - /* symbol if NGHTTP2_HUFF_SYM flag set */ - uint8_t sym; -} nghttp2_huff_decode; - -typedef nghttp2_huff_decode huff_decode_table_type[16]; - -typedef struct { + /* symbol if NGHTTP2_HUFF_SYM flag set */ + uint8_t sym; +} nghttp2_huff_decode; + +typedef nghttp2_huff_decode huff_decode_table_type[16]; + +typedef struct { /* fstate is the current huffman decoding state. */ uint16_t fstate; -} nghttp2_hd_huff_decode_context; - -typedef struct { - /* The number of bits in this code */ - uint32_t nbits; - /* Huffman code aligned to LSB */ - uint32_t code; -} nghttp2_huff_sym; - -extern const nghttp2_huff_sym huff_sym_table[]; -extern const nghttp2_huff_decode huff_decode_table[][16]; - -#endif /* NGHTTP2_HD_HUFFMAN_H */ +} nghttp2_hd_huff_decode_context; + +typedef struct { + /* The number of bits in this code */ + uint32_t nbits; + /* Huffman code aligned to LSB */ + uint32_t code; +} nghttp2_huff_sym; + +extern const nghttp2_huff_sym huff_sym_table[]; +extern const nghttp2_huff_decode huff_decode_table[][16]; + +#endif /* NGHTTP2_HD_HUFFMAN_H */ diff --git a/contrib/libs/nghttp2/lib/nghttp2_hd_huffman_data.c b/contrib/libs/nghttp2/lib/nghttp2_hd_huffman_data.c index c07f5ad1c9..2e2e13f7be 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_hd_huffman_data.c +++ b/contrib/libs/nghttp2/lib/nghttp2_hd_huffman_data.c @@ -1,32 +1,32 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2013 Tatsuhiro Tsujikawa - * - * 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_hd_huffman.h" - -/* Generated by mkhufftbl.py */ - -const nghttp2_huff_sym huff_sym_table[] = { +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2013 Tatsuhiro Tsujikawa + * + * 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_hd_huffman.h" + +/* Generated by mkhufftbl.py */ + +const nghttp2_huff_sym huff_sym_table[] = { {13, 0xffc00000u}, {23, 0xffffb000u}, {28, 0xfffffe20u}, {28, 0xfffffe30u}, {28, 0xfffffe40u}, {28, 0xfffffe50u}, {28, 0xfffffe60u}, {28, 0xfffffe70u}, {28, 0xfffffe80u}, {24, 0xffffea00u}, {30, 0xfffffff0u}, {28, 0xfffffe90u}, @@ -92,10 +92,10 @@ const nghttp2_huff_sym huff_sym_table[] = { {27, 0xfffffd60u}, {28, 0xffffffe0u}, {27, 0xfffffd80u}, {27, 0xfffffda0u}, {27, 0xfffffdc0u}, {27, 0xfffffde0u}, {27, 0xfffffe00u}, {26, 0xfffffb80u}, {30, 0xfffffffcu}}; - -const nghttp2_huff_decode huff_decode_table[][16] = { - /* 0 */ - { + +const nghttp2_huff_decode huff_decode_table[][16] = { + /* 0 */ + { {0x04, 0}, {0x05, 0}, {0x07, 0}, @@ -112,9 +112,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x31, 0}, {0x39, 0}, {0x4040, 0}, - }, - /* 1 */ - { + }, + /* 1 */ + { {0xc000, 48}, {0xc000, 49}, {0xc000, 50}, @@ -131,9 +131,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x12, 0}, {0x14, 0}, {0x15, 0}, - }, - /* 2 */ - { + }, + /* 2 */ + { {0x8001, 48}, {0xc016, 48}, {0x8001, 49}, @@ -150,9 +150,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xc016, 105}, {0x8001, 111}, {0xc016, 111}, - }, - /* 3 */ - { + }, + /* 3 */ + { {0x8002, 48}, {0x8009, 48}, {0x8017, 48}, @@ -169,9 +169,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 97}, {0x8017, 97}, {0xc028, 97}, - }, - /* 4 */ - { + }, + /* 4 */ + { {0x8003, 48}, {0x8006, 48}, {0x800a, 48}, @@ -188,9 +188,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 49}, {0x8029, 49}, {0xc038, 49}, - }, - /* 5 */ - { + }, + /* 5 */ + { {0x8003, 50}, {0x8006, 50}, {0x800a, 50}, @@ -207,9 +207,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 97}, {0x8029, 97}, {0xc038, 97}, - }, - /* 6 */ - { + }, + /* 6 */ + { {0x8002, 99}, {0x8009, 99}, {0x8017, 99}, @@ -226,9 +226,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 111}, {0x8017, 111}, {0xc028, 111}, - }, - /* 7 */ - { + }, + /* 7 */ + { {0x8003, 99}, {0x8006, 99}, {0x800a, 99}, @@ -245,9 +245,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 101}, {0x8029, 101}, {0xc038, 101}, - }, - /* 8 */ - { + }, + /* 8 */ + { {0x8003, 105}, {0x8006, 105}, {0x800a, 105}, @@ -264,9 +264,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 111}, {0x8029, 111}, {0xc038, 111}, - }, - /* 9 */ - { + }, + /* 9 */ + { {0x8001, 115}, {0xc016, 115}, {0x8001, 116}, @@ -283,9 +283,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xc000, 55}, {0xc000, 56}, {0xc000, 57}, - }, - /* 10 */ - { + }, + /* 10 */ + { {0x8002, 115}, {0x8009, 115}, {0x8017, 115}, @@ -302,9 +302,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xc016, 45}, {0x8001, 46}, {0xc016, 46}, - }, - /* 11 */ - { + }, + /* 11 */ + { {0x8003, 115}, {0x8006, 115}, {0x800a, 115}, @@ -321,9 +321,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 116}, {0x8029, 116}, {0xc038, 116}, - }, - /* 12 */ - { + }, + /* 12 */ + { {0x8002, 32}, {0x8009, 32}, {0x8017, 32}, @@ -340,9 +340,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 46}, {0x8017, 46}, {0xc028, 46}, - }, - /* 13 */ - { + }, + /* 13 */ + { {0x8003, 32}, {0x8006, 32}, {0x800a, 32}, @@ -359,9 +359,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 37}, {0x8029, 37}, {0xc038, 37}, - }, - /* 14 */ - { + }, + /* 14 */ + { {0x8003, 45}, {0x8006, 45}, {0x800a, 45}, @@ -378,9 +378,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 46}, {0x8029, 46}, {0xc038, 46}, - }, - /* 15 */ - { + }, + /* 15 */ + { {0x8001, 47}, {0xc016, 47}, {0x8001, 51}, @@ -397,9 +397,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xc016, 56}, {0x8001, 57}, {0xc016, 57}, - }, - /* 16 */ - { + }, + /* 16 */ + { {0x8002, 47}, {0x8009, 47}, {0x8017, 47}, @@ -416,9 +416,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 53}, {0x8017, 53}, {0xc028, 53}, - }, - /* 17 */ - { + }, + /* 17 */ + { {0x8003, 47}, {0x8006, 47}, {0x800a, 47}, @@ -435,9 +435,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 51}, {0x8029, 51}, {0xc038, 51}, - }, - /* 18 */ - { + }, + /* 18 */ + { {0x8003, 52}, {0x8006, 52}, {0x800a, 52}, @@ -454,9 +454,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 53}, {0x8029, 53}, {0xc038, 53}, - }, - /* 19 */ - { + }, + /* 19 */ + { {0x8002, 54}, {0x8009, 54}, {0x8017, 54}, @@ -473,9 +473,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 57}, {0x8017, 57}, {0xc028, 57}, - }, - /* 20 */ - { + }, + /* 20 */ + { {0x8003, 54}, {0x8006, 54}, {0x800a, 54}, @@ -492,9 +492,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 55}, {0x8029, 55}, {0xc038, 55}, - }, - /* 21 */ - { + }, + /* 21 */ + { {0x8003, 56}, {0x8006, 56}, {0x800a, 56}, @@ -511,9 +511,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 57}, {0x8029, 57}, {0xc038, 57}, - }, - /* 22 */ - { + }, + /* 22 */ + { {0x1a, 0}, {0x1b, 0}, {0x1d, 0}, @@ -530,9 +530,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x3d, 0}, {0x41, 0}, {0x4044, 0}, - }, - /* 23 */ - { + }, + /* 23 */ + { {0xc000, 61}, {0xc000, 65}, {0xc000, 95}, @@ -549,9 +549,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xc000, 117}, {0x26, 0}, {0x27, 0}, - }, - /* 24 */ - { + }, + /* 24 */ + { {0x8001, 61}, {0xc016, 61}, {0x8001, 65}, @@ -568,9 +568,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xc016, 103}, {0x8001, 104}, {0xc016, 104}, - }, - /* 25 */ - { + }, + /* 25 */ + { {0x8002, 61}, {0x8009, 61}, {0x8017, 61}, @@ -587,9 +587,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 98}, {0x8017, 98}, {0xc028, 98}, - }, - /* 26 */ - { + }, + /* 26 */ + { {0x8003, 61}, {0x8006, 61}, {0x800a, 61}, @@ -606,9 +606,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 65}, {0x8029, 65}, {0xc038, 65}, - }, - /* 27 */ - { + }, + /* 27 */ + { {0x8003, 95}, {0x8006, 95}, {0x800a, 95}, @@ -625,9 +625,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 98}, {0x8029, 98}, {0xc038, 98}, - }, - /* 28 */ - { + }, + /* 28 */ + { {0x8002, 100}, {0x8009, 100}, {0x8017, 100}, @@ -644,9 +644,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 104}, {0x8017, 104}, {0xc028, 104}, - }, - /* 29 */ - { + }, + /* 29 */ + { {0x8003, 100}, {0x8006, 100}, {0x800a, 100}, @@ -663,9 +663,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 102}, {0x8029, 102}, {0xc038, 102}, - }, - /* 30 */ - { + }, + /* 30 */ + { {0x8003, 103}, {0x8006, 103}, {0x800a, 103}, @@ -682,9 +682,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 104}, {0x8029, 104}, {0xc038, 104}, - }, - /* 31 */ - { + }, + /* 31 */ + { {0x8001, 108}, {0xc016, 108}, {0x8001, 109}, @@ -701,9 +701,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xc000, 66}, {0xc000, 67}, {0xc000, 68}, - }, - /* 32 */ - { + }, + /* 32 */ + { {0x8002, 108}, {0x8009, 108}, {0x8017, 108}, @@ -720,9 +720,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 112}, {0x8017, 112}, {0xc028, 112}, - }, - /* 33 */ - { + }, + /* 33 */ + { {0x8003, 108}, {0x8006, 108}, {0x800a, 108}, @@ -739,9 +739,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 109}, {0x8029, 109}, {0xc038, 109}, - }, - /* 34 */ - { + }, + /* 34 */ + { {0x8003, 110}, {0x8006, 110}, {0x800a, 110}, @@ -758,9 +758,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 112}, {0x8029, 112}, {0xc038, 112}, - }, - /* 35 */ - { + }, + /* 35 */ + { {0x8002, 114}, {0x8009, 114}, {0x8017, 114}, @@ -777,9 +777,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xc016, 67}, {0x8001, 68}, {0xc016, 68}, - }, - /* 36 */ - { + }, + /* 36 */ + { {0x8003, 114}, {0x8006, 114}, {0x800a, 114}, @@ -796,9 +796,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 117}, {0x8029, 117}, {0xc038, 117}, - }, - /* 37 */ - { + }, + /* 37 */ + { {0x8002, 58}, {0x8009, 58}, {0x8017, 58}, @@ -815,9 +815,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 68}, {0x8017, 68}, {0xc028, 68}, - }, - /* 38 */ - { + }, + /* 38 */ + { {0x8003, 58}, {0x8006, 58}, {0x800a, 58}, @@ -834,9 +834,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 66}, {0x8029, 66}, {0xc038, 66}, - }, - /* 39 */ - { + }, + /* 39 */ + { {0x8003, 67}, {0x8006, 67}, {0x800a, 67}, @@ -853,9 +853,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 68}, {0x8029, 68}, {0xc038, 68}, - }, - /* 40 */ - { + }, + /* 40 */ + { {0x2c, 0}, {0x2d, 0}, {0x2f, 0}, @@ -872,9 +872,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x43, 0}, {0x45, 0}, {0x4048, 0}, - }, - /* 41 */ - { + }, + /* 41 */ + { {0xc000, 69}, {0xc000, 70}, {0xc000, 71}, @@ -891,9 +891,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xc000, 82}, {0xc000, 83}, {0xc000, 84}, - }, - /* 42 */ - { + }, + /* 42 */ + { {0x8001, 69}, {0xc016, 69}, {0x8001, 70}, @@ -910,9 +910,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xc016, 75}, {0x8001, 76}, {0xc016, 76}, - }, - /* 43 */ - { + }, + /* 43 */ + { {0x8002, 69}, {0x8009, 69}, {0x8017, 69}, @@ -929,9 +929,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 72}, {0x8017, 72}, {0xc028, 72}, - }, - /* 44 */ - { + }, + /* 44 */ + { {0x8003, 69}, {0x8006, 69}, {0x800a, 69}, @@ -948,9 +948,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 70}, {0x8029, 70}, {0xc038, 70}, - }, - /* 45 */ - { + }, + /* 45 */ + { {0x8003, 71}, {0x8006, 71}, {0x800a, 71}, @@ -967,9 +967,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 72}, {0x8029, 72}, {0xc038, 72}, - }, - /* 46 */ - { + }, + /* 46 */ + { {0x8002, 73}, {0x8009, 73}, {0x8017, 73}, @@ -986,9 +986,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 76}, {0x8017, 76}, {0xc028, 76}, - }, - /* 47 */ - { + }, + /* 47 */ + { {0x8003, 73}, {0x8006, 73}, {0x800a, 73}, @@ -1005,9 +1005,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 74}, {0x8029, 74}, {0xc038, 74}, - }, - /* 48 */ - { + }, + /* 48 */ + { {0x8003, 75}, {0x8006, 75}, {0x800a, 75}, @@ -1024,9 +1024,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 76}, {0x8029, 76}, {0xc038, 76}, - }, - /* 49 */ - { + }, + /* 49 */ + { {0x8001, 77}, {0xc016, 77}, {0x8001, 78}, @@ -1043,9 +1043,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xc016, 83}, {0x8001, 84}, {0xc016, 84}, - }, - /* 50 */ - { + }, + /* 50 */ + { {0x8002, 77}, {0x8009, 77}, {0x8017, 77}, @@ -1062,9 +1062,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 80}, {0x8017, 80}, {0xc028, 80}, - }, - /* 51 */ - { + }, + /* 51 */ + { {0x8003, 77}, {0x8006, 77}, {0x800a, 77}, @@ -1081,9 +1081,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 78}, {0x8029, 78}, {0xc038, 78}, - }, - /* 52 */ - { + }, + /* 52 */ + { {0x8003, 79}, {0x8006, 79}, {0x800a, 79}, @@ -1100,9 +1100,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 80}, {0x8029, 80}, {0xc038, 80}, - }, - /* 53 */ - { + }, + /* 53 */ + { {0x8002, 81}, {0x8009, 81}, {0x8017, 81}, @@ -1119,9 +1119,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 84}, {0x8017, 84}, {0xc028, 84}, - }, - /* 54 */ - { + }, + /* 54 */ + { {0x8003, 81}, {0x8006, 81}, {0x800a, 81}, @@ -1138,9 +1138,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 82}, {0x8029, 82}, {0xc038, 82}, - }, - /* 55 */ - { + }, + /* 55 */ + { {0x8003, 83}, {0x8006, 83}, {0x800a, 83}, @@ -1157,9 +1157,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 84}, {0x8029, 84}, {0xc038, 84}, - }, - /* 56 */ - { + }, + /* 56 */ + { {0xc000, 85}, {0xc000, 86}, {0xc000, 87}, @@ -1176,9 +1176,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x47, 0}, {0x49, 0}, {0x404a, 0}, - }, - /* 57 */ - { + }, + /* 57 */ + { {0x8001, 85}, {0xc016, 85}, {0x8001, 86}, @@ -1195,9 +1195,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xc016, 113}, {0x8001, 118}, {0xc016, 118}, - }, - /* 58 */ - { + }, + /* 58 */ + { {0x8002, 85}, {0x8009, 85}, {0x8017, 85}, @@ -1214,9 +1214,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 89}, {0x8017, 89}, {0xc028, 89}, - }, - /* 59 */ - { + }, + /* 59 */ + { {0x8003, 85}, {0x8006, 85}, {0x800a, 85}, @@ -1233,9 +1233,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 86}, {0x8029, 86}, {0xc038, 86}, - }, - /* 60 */ - { + }, + /* 60 */ + { {0x8003, 87}, {0x8006, 87}, {0x800a, 87}, @@ -1252,9 +1252,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 89}, {0x8029, 89}, {0xc038, 89}, - }, - /* 61 */ - { + }, + /* 61 */ + { {0x8002, 106}, {0x8009, 106}, {0x8017, 106}, @@ -1271,9 +1271,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 118}, {0x8017, 118}, {0xc028, 118}, - }, - /* 62 */ - { + }, + /* 62 */ + { {0x8003, 106}, {0x8006, 106}, {0x800a, 106}, @@ -1290,9 +1290,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 107}, {0x8029, 107}, {0xc038, 107}, - }, - /* 63 */ - { + }, + /* 63 */ + { {0x8003, 113}, {0x8006, 113}, {0x800a, 113}, @@ -1309,9 +1309,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 118}, {0x8029, 118}, {0xc038, 118}, - }, - /* 64 */ - { + }, + /* 64 */ + { {0x8001, 119}, {0xc016, 119}, {0x8001, 120}, @@ -1328,9 +1328,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xc000, 90}, {0x4b, 0}, {0x4e, 0}, - }, - /* 65 */ - { + }, + /* 65 */ + { {0x8002, 119}, {0x8009, 119}, {0x8017, 119}, @@ -1347,9 +1347,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 122}, {0x8017, 122}, {0xc028, 122}, - }, - /* 66 */ - { + }, + /* 66 */ + { {0x8003, 119}, {0x8006, 119}, {0x800a, 119}, @@ -1366,9 +1366,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 120}, {0x8029, 120}, {0xc038, 120}, - }, - /* 67 */ - { + }, + /* 67 */ + { {0x8003, 121}, {0x8006, 121}, {0x800a, 121}, @@ -1385,9 +1385,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 122}, {0x8029, 122}, {0xc038, 122}, - }, - /* 68 */ - { + }, + /* 68 */ + { {0x8001, 38}, {0xc016, 38}, {0x8001, 42}, @@ -1404,9 +1404,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x4d, 0}, {0x4f, 0}, {0x51, 0}, - }, - /* 69 */ - { + }, + /* 69 */ + { {0x8002, 38}, {0x8009, 38}, {0x8017, 38}, @@ -1423,9 +1423,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 59}, {0x8017, 59}, {0xc028, 59}, - }, - /* 70 */ - { + }, + /* 70 */ + { {0x8003, 38}, {0x8006, 38}, {0x800a, 38}, @@ -1442,9 +1442,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 42}, {0x8029, 42}, {0xc038, 42}, - }, - /* 71 */ - { + }, + /* 71 */ + { {0x8003, 44}, {0x8006, 44}, {0x800a, 44}, @@ -1461,9 +1461,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 59}, {0x8029, 59}, {0xc038, 59}, - }, - /* 72 */ - { + }, + /* 72 */ + { {0x8002, 88}, {0x8009, 88}, {0x8017, 88}, @@ -1480,9 +1480,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x50, 0}, {0x52, 0}, {0x54, 0}, - }, - /* 73 */ - { + }, + /* 73 */ + { {0x8003, 88}, {0x8006, 88}, {0x800a, 88}, @@ -1499,9 +1499,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 90}, {0x8029, 90}, {0xc038, 90}, - }, - /* 74 */ - { + }, + /* 74 */ + { {0x8001, 33}, {0xc016, 33}, {0x8001, 34}, @@ -1518,9 +1518,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x53, 0}, {0x55, 0}, {0x58, 0}, - }, - /* 75 */ - { + }, + /* 75 */ + { {0x8002, 33}, {0x8009, 33}, {0x8017, 33}, @@ -1537,9 +1537,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 41}, {0x8017, 41}, {0xc028, 41}, - }, - /* 76 */ - { + }, + /* 76 */ + { {0x8003, 33}, {0x8006, 33}, {0x800a, 33}, @@ -1556,9 +1556,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 34}, {0x8029, 34}, {0xc038, 34}, - }, - /* 77 */ - { + }, + /* 77 */ + { {0x8003, 40}, {0x8006, 40}, {0x800a, 40}, @@ -1575,9 +1575,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 41}, {0x8029, 41}, {0xc038, 41}, - }, - /* 78 */ - { + }, + /* 78 */ + { {0x8002, 63}, {0x8009, 63}, {0x8017, 63}, @@ -1594,9 +1594,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x57, 0}, {0x59, 0}, {0x5a, 0}, - }, - /* 79 */ - { + }, + /* 79 */ + { {0x8003, 63}, {0x8006, 63}, {0x800a, 63}, @@ -1613,9 +1613,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 43}, {0x8017, 43}, {0xc028, 43}, - }, - /* 80 */ - { + }, + /* 80 */ + { {0x8003, 39}, {0x8006, 39}, {0x800a, 39}, @@ -1632,9 +1632,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 43}, {0x8029, 43}, {0xc038, 43}, - }, - /* 81 */ - { + }, + /* 81 */ + { {0x8002, 124}, {0x8009, 124}, {0x8017, 124}, @@ -1651,9 +1651,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xc000, 126}, {0x5b, 0}, {0x5c, 0}, - }, - /* 82 */ - { + }, + /* 82 */ + { {0x8003, 124}, {0x8006, 124}, {0x800a, 124}, @@ -1670,9 +1670,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 62}, {0x8017, 62}, {0xc028, 62}, - }, - /* 83 */ - { + }, + /* 83 */ + { {0x8003, 35}, {0x8006, 35}, {0x800a, 35}, @@ -1689,9 +1689,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 62}, {0x8029, 62}, {0xc038, 62}, - }, - /* 84 */ - { + }, + /* 84 */ + { {0x8001, 0}, {0xc016, 0}, {0x8001, 36}, @@ -1708,9 +1708,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xc000, 125}, {0x5d, 0}, {0x5e, 0}, - }, - /* 85 */ - { + }, + /* 85 */ + { {0x8002, 0}, {0x8009, 0}, {0x8017, 0}, @@ -1727,9 +1727,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 91}, {0x8017, 91}, {0xc028, 91}, - }, - /* 86 */ - { + }, + /* 86 */ + { {0x8003, 0}, {0x8006, 0}, {0x800a, 0}, @@ -1746,9 +1746,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 36}, {0x8029, 36}, {0xc038, 36}, - }, - /* 87 */ - { + }, + /* 87 */ + { {0x8003, 64}, {0x8006, 64}, {0x800a, 64}, @@ -1765,9 +1765,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 91}, {0x8029, 91}, {0xc038, 91}, - }, - /* 88 */ - { + }, + /* 88 */ + { {0x8002, 93}, {0x8009, 93}, {0x8017, 93}, @@ -1784,9 +1784,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xc000, 96}, {0xc000, 123}, {0x5f, 0}, - }, - /* 89 */ - { + }, + /* 89 */ + { {0x8003, 93}, {0x8006, 93}, {0x800a, 93}, @@ -1803,9 +1803,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 126}, {0x8029, 126}, {0xc038, 126}, - }, - /* 90 */ - { + }, + /* 90 */ + { {0x8002, 94}, {0x8009, 94}, {0x8017, 94}, @@ -1822,9 +1822,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xc016, 123}, {0x60, 0}, {0x6e, 0}, - }, - /* 91 */ - { + }, + /* 91 */ + { {0x8003, 94}, {0x8006, 94}, {0x800a, 94}, @@ -1841,9 +1841,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 125}, {0x8029, 125}, {0xc038, 125}, - }, - /* 92 */ - { + }, + /* 92 */ + { {0x8002, 60}, {0x8009, 60}, {0x8017, 60}, @@ -1860,9 +1860,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x65, 0}, {0x6f, 0}, {0x85, 0}, - }, - /* 93 */ - { + }, + /* 93 */ + { {0x8003, 60}, {0x8006, 60}, {0x800a, 60}, @@ -1879,9 +1879,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 96}, {0x8029, 96}, {0xc038, 96}, - }, - /* 94 */ - { + }, + /* 94 */ + { {0x8003, 123}, {0x8006, 123}, {0x800a, 123}, @@ -1898,9 +1898,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x77, 0}, {0x86, 0}, {0x99, 0}, - }, - /* 95 */ - { + }, + /* 95 */ + { {0xc000, 92}, {0xc000, 195}, {0xc000, 208}, @@ -1917,9 +1917,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8e, 0}, {0x9a, 0}, {0xa9, 0}, - }, - /* 96 */ - { + }, + /* 96 */ + { {0x8001, 92}, {0xc016, 92}, {0x8001, 195}, @@ -1936,9 +1936,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xc000, 226}, {0x6c, 0}, {0x6d, 0}, - }, - /* 97 */ - { + }, + /* 97 */ + { {0x8002, 92}, {0x8009, 92}, {0x8017, 92}, @@ -1955,9 +1955,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xc016, 128}, {0x8001, 130}, {0xc016, 130}, - }, - /* 98 */ - { + }, + /* 98 */ + { {0x8003, 92}, {0x8006, 92}, {0x800a, 92}, @@ -1974,9 +1974,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 195}, {0x8029, 195}, {0xc038, 195}, - }, - /* 99 */ - { + }, + /* 99 */ + { {0x8003, 208}, {0x8006, 208}, {0x800a, 208}, @@ -1993,9 +1993,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 130}, {0x8017, 130}, {0xc028, 130}, - }, - /* 100 */ - { + }, + /* 100 */ + { {0x8003, 128}, {0x8006, 128}, {0x800a, 128}, @@ -2012,9 +2012,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 130}, {0x8029, 130}, {0xc038, 130}, - }, - /* 101 */ - { + }, + /* 101 */ + { {0x8001, 131}, {0xc016, 131}, {0x8001, 162}, @@ -2031,9 +2031,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xc000, 161}, {0xc000, 167}, {0xc000, 172}, - }, - /* 102 */ - { + }, + /* 102 */ + { {0x8002, 131}, {0x8009, 131}, {0x8017, 131}, @@ -2050,9 +2050,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 194}, {0x8017, 194}, {0xc028, 194}, - }, - /* 103 */ - { + }, + /* 103 */ + { {0x8003, 131}, {0x8006, 131}, {0x800a, 131}, @@ -2069,9 +2069,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 162}, {0x8029, 162}, {0xc038, 162}, - }, - /* 104 */ - { + }, + /* 104 */ + { {0x8003, 184}, {0x8006, 184}, {0x800a, 184}, @@ -2088,9 +2088,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 194}, {0x8029, 194}, {0xc038, 194}, - }, - /* 105 */ - { + }, + /* 105 */ + { {0x8002, 224}, {0x8009, 224}, {0x8017, 224}, @@ -2107,9 +2107,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xc016, 167}, {0x8001, 172}, {0xc016, 172}, - }, - /* 106 */ - { + }, + /* 106 */ + { {0x8003, 224}, {0x8006, 224}, {0x800a, 224}, @@ -2126,9 +2126,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 226}, {0x8029, 226}, {0xc038, 226}, - }, - /* 107 */ - { + }, + /* 107 */ + { {0x8002, 153}, {0x8009, 153}, {0x8017, 153}, @@ -2145,9 +2145,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 172}, {0x8017, 172}, {0xc028, 172}, - }, - /* 108 */ - { + }, + /* 108 */ + { {0x8003, 153}, {0x8006, 153}, {0x800a, 153}, @@ -2164,9 +2164,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 161}, {0x8029, 161}, {0xc038, 161}, - }, - /* 109 */ - { + }, + /* 109 */ + { {0x8003, 167}, {0x8006, 167}, {0x800a, 167}, @@ -2183,9 +2183,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 172}, {0x8029, 172}, {0xc038, 172}, - }, - /* 110 */ - { + }, + /* 110 */ + { {0x72, 0}, {0x73, 0}, {0x75, 0}, @@ -2202,9 +2202,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xa2, 0}, {0xaa, 0}, {0xb4, 0}, - }, - /* 111 */ - { + }, + /* 111 */ + { {0xc000, 176}, {0xc000, 177}, {0xc000, 179}, @@ -2221,9 +2221,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x81, 0}, {0x83, 0}, {0x84, 0}, - }, - /* 112 */ - { + }, + /* 112 */ + { {0x8001, 176}, {0xc016, 176}, {0x8001, 177}, @@ -2240,9 +2240,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xc016, 227}, {0x8001, 229}, {0xc016, 229}, - }, - /* 113 */ - { + }, + /* 113 */ + { {0x8002, 176}, {0x8009, 176}, {0x8017, 176}, @@ -2259,9 +2259,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 209}, {0x8017, 209}, {0xc028, 209}, - }, - /* 114 */ - { + }, + /* 114 */ + { {0x8003, 176}, {0x8006, 176}, {0x800a, 176}, @@ -2278,9 +2278,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 177}, {0x8029, 177}, {0xc038, 177}, - }, - /* 115 */ - { + }, + /* 115 */ + { {0x8003, 179}, {0x8006, 179}, {0x800a, 179}, @@ -2297,9 +2297,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 209}, {0x8029, 209}, {0xc038, 209}, - }, - /* 116 */ - { + }, + /* 116 */ + { {0x8002, 216}, {0x8009, 216}, {0x8017, 216}, @@ -2316,9 +2316,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 229}, {0x8017, 229}, {0xc028, 229}, - }, - /* 117 */ - { + }, + /* 117 */ + { {0x8003, 216}, {0x8006, 216}, {0x800a, 216}, @@ -2335,9 +2335,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 217}, {0x8029, 217}, {0xc038, 217}, - }, - /* 118 */ - { + }, + /* 118 */ + { {0x8003, 227}, {0x8006, 227}, {0x800a, 227}, @@ -2354,9 +2354,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 229}, {0x8029, 229}, {0xc038, 229}, - }, - /* 119 */ - { + }, + /* 119 */ + { {0x8001, 230}, {0xc016, 230}, {0xc000, 129}, @@ -2373,9 +2373,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xc000, 169}, {0xc000, 170}, {0xc000, 173}, - }, - /* 120 */ - { + }, + /* 120 */ + { {0x8002, 230}, {0x8009, 230}, {0x8017, 230}, @@ -2392,9 +2392,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xc016, 136}, {0x8001, 146}, {0xc016, 146}, - }, - /* 121 */ - { + }, + /* 121 */ + { {0x8003, 230}, {0x8006, 230}, {0x800a, 230}, @@ -2411,9 +2411,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 132}, {0x8017, 132}, {0xc028, 132}, - }, - /* 122 */ - { + }, + /* 122 */ + { {0x8003, 129}, {0x8006, 129}, {0x800a, 129}, @@ -2430,9 +2430,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 132}, {0x8029, 132}, {0xc038, 132}, - }, - /* 123 */ - { + }, + /* 123 */ + { {0x8002, 133}, {0x8009, 133}, {0x8017, 133}, @@ -2449,9 +2449,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 146}, {0x8017, 146}, {0xc028, 146}, - }, - /* 124 */ - { + }, + /* 124 */ + { {0x8003, 133}, {0x8006, 133}, {0x800a, 133}, @@ -2468,9 +2468,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 134}, {0x8029, 134}, {0xc038, 134}, - }, - /* 125 */ - { + }, + /* 125 */ + { {0x8003, 136}, {0x8006, 136}, {0x800a, 136}, @@ -2487,9 +2487,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 146}, {0x8029, 146}, {0xc038, 146}, - }, - /* 126 */ - { + }, + /* 126 */ + { {0x8001, 154}, {0xc016, 154}, {0x8001, 156}, @@ -2506,9 +2506,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xc016, 170}, {0x8001, 173}, {0xc016, 173}, - }, - /* 127 */ - { + }, + /* 127 */ + { {0x8002, 154}, {0x8009, 154}, {0x8017, 154}, @@ -2525,9 +2525,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 163}, {0x8017, 163}, {0xc028, 163}, - }, - /* 128 */ - { + }, + /* 128 */ + { {0x8003, 154}, {0x8006, 154}, {0x800a, 154}, @@ -2544,9 +2544,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 156}, {0x8029, 156}, {0xc038, 156}, - }, - /* 129 */ - { + }, + /* 129 */ + { {0x8003, 160}, {0x8006, 160}, {0x800a, 160}, @@ -2563,9 +2563,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 163}, {0x8029, 163}, {0xc038, 163}, - }, - /* 130 */ - { + }, + /* 130 */ + { {0x8002, 164}, {0x8009, 164}, {0x8017, 164}, @@ -2582,9 +2582,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 173}, {0x8017, 173}, {0xc028, 173}, - }, - /* 131 */ - { + }, + /* 131 */ + { {0x8003, 164}, {0x8006, 164}, {0x800a, 164}, @@ -2601,9 +2601,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 169}, {0x8029, 169}, {0xc038, 169}, - }, - /* 132 */ - { + }, + /* 132 */ + { {0x8003, 170}, {0x8006, 170}, {0x800a, 170}, @@ -2620,9 +2620,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 173}, {0x8029, 173}, {0xc038, 173}, - }, - /* 133 */ - { + }, + /* 133 */ + { {0x89, 0}, {0x8a, 0}, {0x8c, 0}, @@ -2639,9 +2639,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xae, 0}, {0xb5, 0}, {0xbe, 0}, - }, - /* 134 */ - { + }, + /* 134 */ + { {0xc000, 178}, {0xc000, 181}, {0xc000, 185}, @@ -2658,9 +2658,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x95, 0}, {0x97, 0}, {0x98, 0}, - }, - /* 135 */ - { + }, + /* 135 */ + { {0x8001, 178}, {0xc016, 178}, {0x8001, 181}, @@ -2677,9 +2677,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xc016, 190}, {0x8001, 196}, {0xc016, 196}, - }, - /* 136 */ - { + }, + /* 136 */ + { {0x8002, 178}, {0x8009, 178}, {0x8017, 178}, @@ -2696,9 +2696,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 186}, {0x8017, 186}, {0xc028, 186}, - }, - /* 137 */ - { + }, + /* 137 */ + { {0x8003, 178}, {0x8006, 178}, {0x800a, 178}, @@ -2715,9 +2715,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 181}, {0x8029, 181}, {0xc038, 181}, - }, - /* 138 */ - { + }, + /* 138 */ + { {0x8003, 185}, {0x8006, 185}, {0x800a, 185}, @@ -2734,9 +2734,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 186}, {0x8029, 186}, {0xc038, 186}, - }, - /* 139 */ - { + }, + /* 139 */ + { {0x8002, 187}, {0x8009, 187}, {0x8017, 187}, @@ -2753,9 +2753,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 196}, {0x8017, 196}, {0xc028, 196}, - }, - /* 140 */ - { + }, + /* 140 */ + { {0x8003, 187}, {0x8006, 187}, {0x800a, 187}, @@ -2772,9 +2772,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 189}, {0x8029, 189}, {0xc038, 189}, - }, - /* 141 */ - { + }, + /* 141 */ + { {0x8003, 190}, {0x8006, 190}, {0x800a, 190}, @@ -2791,9 +2791,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 196}, {0x8029, 196}, {0xc038, 196}, - }, - /* 142 */ - { + }, + /* 142 */ + { {0x8001, 198}, {0xc016, 198}, {0x8001, 228}, @@ -2810,9 +2810,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xc000, 140}, {0xc000, 141}, {0xc000, 143}, - }, - /* 143 */ - { + }, + /* 143 */ + { {0x8002, 198}, {0x8009, 198}, {0x8017, 198}, @@ -2829,9 +2829,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 233}, {0x8017, 233}, {0xc028, 233}, - }, - /* 144 */ - { + }, + /* 144 */ + { {0x8003, 198}, {0x8006, 198}, {0x800a, 198}, @@ -2848,9 +2848,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 228}, {0x8029, 228}, {0xc038, 228}, - }, - /* 145 */ - { + }, + /* 145 */ + { {0x8003, 232}, {0x8006, 232}, {0x800a, 232}, @@ -2867,9 +2867,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 233}, {0x8029, 233}, {0xc038, 233}, - }, - /* 146 */ - { + }, + /* 146 */ + { {0x8001, 1}, {0xc016, 1}, {0x8001, 135}, @@ -2886,9 +2886,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xc016, 141}, {0x8001, 143}, {0xc016, 143}, - }, - /* 147 */ - { + }, + /* 147 */ + { {0x8002, 1}, {0x8009, 1}, {0x8017, 1}, @@ -2905,9 +2905,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 138}, {0x8017, 138}, {0xc028, 138}, - }, - /* 148 */ - { + }, + /* 148 */ + { {0x8003, 1}, {0x8006, 1}, {0x800a, 1}, @@ -2924,9 +2924,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 135}, {0x8029, 135}, {0xc038, 135}, - }, - /* 149 */ - { + }, + /* 149 */ + { {0x8003, 137}, {0x8006, 137}, {0x800a, 137}, @@ -2943,9 +2943,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 138}, {0x8029, 138}, {0xc038, 138}, - }, - /* 150 */ - { + }, + /* 150 */ + { {0x8002, 139}, {0x8009, 139}, {0x8017, 139}, @@ -2962,9 +2962,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 143}, {0x8017, 143}, {0xc028, 143}, - }, - /* 151 */ - { + }, + /* 151 */ + { {0x8003, 139}, {0x8006, 139}, {0x800a, 139}, @@ -2981,9 +2981,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 140}, {0x8029, 140}, {0xc038, 140}, - }, - /* 152 */ - { + }, + /* 152 */ + { {0x8003, 141}, {0x8006, 141}, {0x800a, 141}, @@ -3000,9 +3000,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 143}, {0x8029, 143}, {0xc038, 143}, - }, - /* 153 */ - { + }, + /* 153 */ + { {0x9d, 0}, {0x9e, 0}, {0xa0, 0}, @@ -3019,9 +3019,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xb9, 0}, {0xbf, 0}, {0xcf, 0}, - }, - /* 154 */ - { + }, + /* 154 */ + { {0xc000, 147}, {0xc000, 149}, {0xc000, 150}, @@ -3038,9 +3038,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xc000, 180}, {0xc000, 182}, {0xc000, 183}, - }, - /* 155 */ - { + }, + /* 155 */ + { {0x8001, 147}, {0xc016, 147}, {0x8001, 149}, @@ -3057,9 +3057,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xc016, 157}, {0x8001, 158}, {0xc016, 158}, - }, - /* 156 */ - { + }, + /* 156 */ + { {0x8002, 147}, {0x8009, 147}, {0x8017, 147}, @@ -3076,9 +3076,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 151}, {0x8017, 151}, {0xc028, 151}, - }, - /* 157 */ - { + }, + /* 157 */ + { {0x8003, 147}, {0x8006, 147}, {0x800a, 147}, @@ -3095,9 +3095,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 149}, {0x8029, 149}, {0xc038, 149}, - }, - /* 158 */ - { + }, + /* 158 */ + { {0x8003, 150}, {0x8006, 150}, {0x800a, 150}, @@ -3114,9 +3114,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 151}, {0x8029, 151}, {0xc038, 151}, - }, - /* 159 */ - { + }, + /* 159 */ + { {0x8002, 152}, {0x8009, 152}, {0x8017, 152}, @@ -3133,9 +3133,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 158}, {0x8017, 158}, {0xc028, 158}, - }, - /* 160 */ - { + }, + /* 160 */ + { {0x8003, 152}, {0x8006, 152}, {0x800a, 152}, @@ -3152,9 +3152,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 155}, {0x8029, 155}, {0xc038, 155}, - }, - /* 161 */ - { + }, + /* 161 */ + { {0x8003, 157}, {0x8006, 157}, {0x800a, 157}, @@ -3171,9 +3171,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 158}, {0x8029, 158}, {0xc038, 158}, - }, - /* 162 */ - { + }, + /* 162 */ + { {0x8001, 165}, {0xc016, 165}, {0x8001, 166}, @@ -3190,9 +3190,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xc016, 182}, {0x8001, 183}, {0xc016, 183}, - }, - /* 163 */ - { + }, + /* 163 */ + { {0x8002, 165}, {0x8009, 165}, {0x8017, 165}, @@ -3209,9 +3209,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 174}, {0x8017, 174}, {0xc028, 174}, - }, - /* 164 */ - { + }, + /* 164 */ + { {0x8003, 165}, {0x8006, 165}, {0x800a, 165}, @@ -3228,9 +3228,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 166}, {0x8029, 166}, {0xc038, 166}, - }, - /* 165 */ - { + }, + /* 165 */ + { {0x8003, 168}, {0x8006, 168}, {0x800a, 168}, @@ -3247,9 +3247,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 174}, {0x8029, 174}, {0xc038, 174}, - }, - /* 166 */ - { + }, + /* 166 */ + { {0x8002, 175}, {0x8009, 175}, {0x8017, 175}, @@ -3266,9 +3266,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 183}, {0x8017, 183}, {0xc028, 183}, - }, - /* 167 */ - { + }, + /* 167 */ + { {0x8003, 175}, {0x8006, 175}, {0x800a, 175}, @@ -3285,9 +3285,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 180}, {0x8029, 180}, {0xc038, 180}, - }, - /* 168 */ - { + }, + /* 168 */ + { {0x8003, 182}, {0x8006, 182}, {0x800a, 182}, @@ -3304,9 +3304,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 183}, {0x8029, 183}, {0xc038, 183}, - }, - /* 169 */ - { + }, + /* 169 */ + { {0xc000, 188}, {0xc000, 191}, {0xc000, 197}, @@ -3323,9 +3323,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xc7, 0}, {0xd0, 0}, {0xdf, 0}, - }, - /* 170 */ - { + }, + /* 170 */ + { {0x8001, 188}, {0xc016, 188}, {0x8001, 191}, @@ -3342,9 +3342,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xc000, 145}, {0xc000, 148}, {0xc000, 159}, - }, - /* 171 */ - { + }, + /* 171 */ + { {0x8002, 188}, {0x8009, 188}, {0x8017, 188}, @@ -3361,9 +3361,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 231}, {0x8017, 231}, {0xc028, 231}, - }, - /* 172 */ - { + }, + /* 172 */ + { {0x8003, 188}, {0x8006, 188}, {0x800a, 188}, @@ -3380,9 +3380,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 191}, {0x8029, 191}, {0xc038, 191}, - }, - /* 173 */ - { + }, + /* 173 */ + { {0x8003, 197}, {0x8006, 197}, {0x800a, 197}, @@ -3399,9 +3399,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 231}, {0x8029, 231}, {0xc038, 231}, - }, - /* 174 */ - { + }, + /* 174 */ + { {0x8002, 239}, {0x8009, 239}, {0x8017, 239}, @@ -3418,9 +3418,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xc016, 148}, {0x8001, 159}, {0xc016, 159}, - }, - /* 175 */ - { + }, + /* 175 */ + { {0x8003, 239}, {0x8006, 239}, {0x800a, 239}, @@ -3437,9 +3437,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 142}, {0x8017, 142}, {0xc028, 142}, - }, - /* 176 */ - { + }, + /* 176 */ + { {0x8003, 9}, {0x8006, 9}, {0x800a, 9}, @@ -3456,9 +3456,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 142}, {0x8029, 142}, {0xc038, 142}, - }, - /* 177 */ - { + }, + /* 177 */ + { {0x8002, 144}, {0x8009, 144}, {0x8017, 144}, @@ -3475,9 +3475,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 159}, {0x8017, 159}, {0xc028, 159}, - }, - /* 178 */ - { + }, + /* 178 */ + { {0x8003, 144}, {0x8006, 144}, {0x800a, 144}, @@ -3494,9 +3494,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 145}, {0x8029, 145}, {0xc038, 145}, - }, - /* 179 */ - { + }, + /* 179 */ + { {0x8003, 148}, {0x8006, 148}, {0x800a, 148}, @@ -3513,9 +3513,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 159}, {0x8029, 159}, {0xc038, 159}, - }, - /* 180 */ - { + }, + /* 180 */ + { {0xc000, 171}, {0xc000, 206}, {0xc000, 215}, @@ -3532,9 +3532,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xd8, 0}, {0xe0, 0}, {0xee, 0}, - }, - /* 181 */ - { + }, + /* 181 */ + { {0x8001, 171}, {0xc016, 171}, {0x8001, 206}, @@ -3551,9 +3551,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xc000, 207}, {0xc000, 234}, {0xc000, 235}, - }, - /* 182 */ - { + }, + /* 182 */ + { {0x8002, 171}, {0x8009, 171}, {0x8017, 171}, @@ -3570,9 +3570,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 225}, {0x8017, 225}, {0xc028, 225}, - }, - /* 183 */ - { + }, + /* 183 */ + { {0x8003, 171}, {0x8006, 171}, {0x800a, 171}, @@ -3589,9 +3589,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 206}, {0x8029, 206}, {0xc038, 206}, - }, - /* 184 */ - { + }, + /* 184 */ + { {0x8003, 215}, {0x8006, 215}, {0x800a, 215}, @@ -3608,9 +3608,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 225}, {0x8029, 225}, {0xc038, 225}, - }, - /* 185 */ - { + }, + /* 185 */ + { {0x8002, 236}, {0x8009, 236}, {0x8017, 236}, @@ -3627,9 +3627,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xc016, 234}, {0x8001, 235}, {0xc016, 235}, - }, - /* 186 */ - { + }, + /* 186 */ + { {0x8003, 236}, {0x8006, 236}, {0x800a, 236}, @@ -3646,9 +3646,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 237}, {0x8029, 237}, {0xc038, 237}, - }, - /* 187 */ - { + }, + /* 187 */ + { {0x8002, 199}, {0x8009, 199}, {0x8017, 199}, @@ -3665,9 +3665,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 235}, {0x8017, 235}, {0xc028, 235}, - }, - /* 188 */ - { + }, + /* 188 */ + { {0x8003, 199}, {0x8006, 199}, {0x800a, 199}, @@ -3684,9 +3684,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 207}, {0x8029, 207}, {0xc038, 207}, - }, - /* 189 */ - { + }, + /* 189 */ + { {0x8003, 234}, {0x8006, 234}, {0x800a, 234}, @@ -3703,9 +3703,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 235}, {0x8029, 235}, {0xc038, 235}, - }, - /* 190 */ - { + }, + /* 190 */ + { {0xc2, 0}, {0xc3, 0}, {0xc5, 0}, @@ -3722,9 +3722,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xe7, 0}, {0xef, 0}, {0xf6, 0}, - }, - /* 191 */ - { + }, + /* 191 */ + { {0xc000, 192}, {0xc000, 193}, {0xc000, 200}, @@ -3741,9 +3741,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xc000, 243}, {0xc000, 255}, {0xce, 0}, - }, - /* 192 */ - { + }, + /* 192 */ + { {0x8001, 192}, {0xc016, 192}, {0x8001, 193}, @@ -3760,9 +3760,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xc016, 210}, {0x8001, 213}, {0xc016, 213}, - }, - /* 193 */ - { + }, + /* 193 */ + { {0x8002, 192}, {0x8009, 192}, {0x8017, 192}, @@ -3779,9 +3779,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 201}, {0x8017, 201}, {0xc028, 201}, - }, - /* 194 */ - { + }, + /* 194 */ + { {0x8003, 192}, {0x8006, 192}, {0x800a, 192}, @@ -3798,9 +3798,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 193}, {0x8029, 193}, {0xc038, 193}, - }, - /* 195 */ - { + }, + /* 195 */ + { {0x8003, 200}, {0x8006, 200}, {0x800a, 200}, @@ -3817,9 +3817,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 201}, {0x8029, 201}, {0xc038, 201}, - }, - /* 196 */ - { + }, + /* 196 */ + { {0x8002, 202}, {0x8009, 202}, {0x8017, 202}, @@ -3836,9 +3836,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 213}, {0x8017, 213}, {0xc028, 213}, - }, - /* 197 */ - { + }, + /* 197 */ + { {0x8003, 202}, {0x8006, 202}, {0x800a, 202}, @@ -3855,9 +3855,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 205}, {0x8029, 205}, {0xc038, 205}, - }, - /* 198 */ - { + }, + /* 198 */ + { {0x8003, 210}, {0x8006, 210}, {0x800a, 210}, @@ -3874,9 +3874,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 213}, {0x8029, 213}, {0xc038, 213}, - }, - /* 199 */ - { + }, + /* 199 */ + { {0x8001, 218}, {0xc016, 218}, {0x8001, 219}, @@ -3893,9 +3893,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xc016, 255}, {0xc000, 203}, {0xc000, 204}, - }, - /* 200 */ - { + }, + /* 200 */ + { {0x8002, 218}, {0x8009, 218}, {0x8017, 218}, @@ -3912,9 +3912,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 240}, {0x8017, 240}, {0xc028, 240}, - }, - /* 201 */ - { + }, + /* 201 */ + { {0x8003, 218}, {0x8006, 218}, {0x800a, 218}, @@ -3931,9 +3931,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 219}, {0x8029, 219}, {0xc038, 219}, - }, - /* 202 */ - { + }, + /* 202 */ + { {0x8003, 238}, {0x8006, 238}, {0x800a, 238}, @@ -3950,9 +3950,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 240}, {0x8029, 240}, {0xc038, 240}, - }, - /* 203 */ - { + }, + /* 203 */ + { {0x8002, 242}, {0x8009, 242}, {0x8017, 242}, @@ -3969,9 +3969,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xc016, 203}, {0x8001, 204}, {0xc016, 204}, - }, - /* 204 */ - { + }, + /* 204 */ + { {0x8003, 242}, {0x8006, 242}, {0x800a, 242}, @@ -3988,9 +3988,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 243}, {0x8029, 243}, {0xc038, 243}, - }, - /* 205 */ - { + }, + /* 205 */ + { {0x8003, 255}, {0x8006, 255}, {0x800a, 255}, @@ -4007,9 +4007,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 204}, {0x8017, 204}, {0xc028, 204}, - }, - /* 206 */ - { + }, + /* 206 */ + { {0x8003, 203}, {0x8006, 203}, {0x800a, 203}, @@ -4026,9 +4026,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 204}, {0x8029, 204}, {0xc038, 204}, - }, - /* 207 */ - { + }, + /* 207 */ + { {0xd3, 0}, {0xd4, 0}, {0xd6, 0}, @@ -4045,9 +4045,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xf3, 0}, {0xf7, 0}, {0xfa, 0}, - }, - /* 208 */ - { + }, + /* 208 */ + { {0xc000, 211}, {0xc000, 212}, {0xc000, 214}, @@ -4064,9 +4064,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xc000, 251}, {0xc000, 252}, {0xc000, 253}, - }, - /* 209 */ - { + }, + /* 209 */ + { {0x8001, 211}, {0xc016, 211}, {0x8001, 212}, @@ -4083,9 +4083,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xc016, 241}, {0x8001, 244}, {0xc016, 244}, - }, - /* 210 */ - { + }, + /* 210 */ + { {0x8002, 211}, {0x8009, 211}, {0x8017, 211}, @@ -4102,9 +4102,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 221}, {0x8017, 221}, {0xc028, 221}, - }, - /* 211 */ - { + }, + /* 211 */ + { {0x8003, 211}, {0x8006, 211}, {0x800a, 211}, @@ -4121,9 +4121,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 212}, {0x8029, 212}, {0xc038, 212}, - }, - /* 212 */ - { + }, + /* 212 */ + { {0x8003, 214}, {0x8006, 214}, {0x800a, 214}, @@ -4140,9 +4140,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 221}, {0x8029, 221}, {0xc038, 221}, - }, - /* 213 */ - { + }, + /* 213 */ + { {0x8002, 222}, {0x8009, 222}, {0x8017, 222}, @@ -4159,9 +4159,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 244}, {0x8017, 244}, {0xc028, 244}, - }, - /* 214 */ - { + }, + /* 214 */ + { {0x8003, 222}, {0x8006, 222}, {0x800a, 222}, @@ -4178,9 +4178,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 223}, {0x8029, 223}, {0xc038, 223}, - }, - /* 215 */ - { + }, + /* 215 */ + { {0x8003, 241}, {0x8006, 241}, {0x800a, 241}, @@ -4197,9 +4197,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 244}, {0x8029, 244}, {0xc038, 244}, - }, - /* 216 */ - { + }, + /* 216 */ + { {0x8001, 245}, {0xc016, 245}, {0x8001, 246}, @@ -4216,9 +4216,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xc016, 252}, {0x8001, 253}, {0xc016, 253}, - }, - /* 217 */ - { + }, + /* 217 */ + { {0x8002, 245}, {0x8009, 245}, {0x8017, 245}, @@ -4235,9 +4235,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 248}, {0x8017, 248}, {0xc028, 248}, - }, - /* 218 */ - { + }, + /* 218 */ + { {0x8003, 245}, {0x8006, 245}, {0x800a, 245}, @@ -4254,9 +4254,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 246}, {0x8029, 246}, {0xc038, 246}, - }, - /* 219 */ - { + }, + /* 219 */ + { {0x8003, 247}, {0x8006, 247}, {0x800a, 247}, @@ -4273,9 +4273,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 248}, {0x8029, 248}, {0xc038, 248}, - }, - /* 220 */ - { + }, + /* 220 */ + { {0x8002, 250}, {0x8009, 250}, {0x8017, 250}, @@ -4292,9 +4292,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 253}, {0x8017, 253}, {0xc028, 253}, - }, - /* 221 */ - { + }, + /* 221 */ + { {0x8003, 250}, {0x8006, 250}, {0x800a, 250}, @@ -4311,9 +4311,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 251}, {0x8029, 251}, {0xc038, 251}, - }, - /* 222 */ - { + }, + /* 222 */ + { {0x8003, 252}, {0x8006, 252}, {0x800a, 252}, @@ -4330,9 +4330,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 253}, {0x8029, 253}, {0xc038, 253}, - }, - /* 223 */ - { + }, + /* 223 */ + { {0xc000, 254}, {0xe3, 0}, {0xe5, 0}, @@ -4349,9 +4349,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xf9, 0}, {0xfb, 0}, {0xfc, 0}, - }, - /* 224 */ - { + }, + /* 224 */ + { {0x8001, 254}, {0xc016, 254}, {0xc000, 2}, @@ -4368,9 +4368,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xc000, 16}, {0xc000, 17}, {0xc000, 18}, - }, - /* 225 */ - { + }, + /* 225 */ + { {0x8002, 254}, {0x8009, 254}, {0x8017, 254}, @@ -4387,9 +4387,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xc016, 6}, {0x8001, 7}, {0xc016, 7}, - }, - /* 226 */ - { + }, + /* 226 */ + { {0x8003, 254}, {0x8006, 254}, {0x800a, 254}, @@ -4406,9 +4406,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 3}, {0x8017, 3}, {0xc028, 3}, - }, - /* 227 */ - { + }, + /* 227 */ + { {0x8003, 2}, {0x8006, 2}, {0x800a, 2}, @@ -4425,9 +4425,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 3}, {0x8029, 3}, {0xc038, 3}, - }, - /* 228 */ - { + }, + /* 228 */ + { {0x8002, 4}, {0x8009, 4}, {0x8017, 4}, @@ -4444,9 +4444,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 7}, {0x8017, 7}, {0xc028, 7}, - }, - /* 229 */ - { + }, + /* 229 */ + { {0x8003, 4}, {0x8006, 4}, {0x800a, 4}, @@ -4463,9 +4463,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 5}, {0x8029, 5}, {0xc038, 5}, - }, - /* 230 */ - { + }, + /* 230 */ + { {0x8003, 6}, {0x8006, 6}, {0x800a, 6}, @@ -4482,9 +4482,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 7}, {0x8029, 7}, {0xc038, 7}, - }, - /* 231 */ - { + }, + /* 231 */ + { {0x8001, 8}, {0xc016, 8}, {0x8001, 11}, @@ -4501,9 +4501,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xc016, 17}, {0x8001, 18}, {0xc016, 18}, - }, - /* 232 */ - { + }, + /* 232 */ + { {0x8002, 8}, {0x8009, 8}, {0x8017, 8}, @@ -4520,9 +4520,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 14}, {0x8017, 14}, {0xc028, 14}, - }, - /* 233 */ - { + }, + /* 233 */ + { {0x8003, 8}, {0x8006, 8}, {0x800a, 8}, @@ -4539,9 +4539,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 11}, {0x8029, 11}, {0xc038, 11}, - }, - /* 234 */ - { + }, + /* 234 */ + { {0x8003, 12}, {0x8006, 12}, {0x800a, 12}, @@ -4558,9 +4558,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 14}, {0x8029, 14}, {0xc038, 14}, - }, - /* 235 */ - { + }, + /* 235 */ + { {0x8002, 15}, {0x8009, 15}, {0x8017, 15}, @@ -4577,9 +4577,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 18}, {0x8017, 18}, {0xc028, 18}, - }, - /* 236 */ - { + }, + /* 236 */ + { {0x8003, 15}, {0x8006, 15}, {0x800a, 15}, @@ -4596,9 +4596,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 16}, {0x8029, 16}, {0xc038, 16}, - }, - /* 237 */ - { + }, + /* 237 */ + { {0x8003, 17}, {0x8006, 17}, {0x800a, 17}, @@ -4615,9 +4615,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 18}, {0x8029, 18}, {0xc038, 18}, - }, - /* 238 */ - { + }, + /* 238 */ + { {0xc000, 19}, {0xc000, 20}, {0xc000, 21}, @@ -4634,9 +4634,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xc000, 220}, {0xc000, 249}, {0xfd, 0}, - }, - /* 239 */ - { + }, + /* 239 */ + { {0x8001, 19}, {0xc016, 19}, {0x8001, 20}, @@ -4653,9 +4653,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xc016, 26}, {0x8001, 27}, {0xc016, 27}, - }, - /* 240 */ - { + }, + /* 240 */ + { {0x8002, 19}, {0x8009, 19}, {0x8017, 19}, @@ -4672,9 +4672,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 23}, {0x8017, 23}, {0xc028, 23}, - }, - /* 241 */ - { + }, + /* 241 */ + { {0x8003, 19}, {0x8006, 19}, {0x800a, 19}, @@ -4691,9 +4691,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 20}, {0x8029, 20}, {0xc038, 20}, - }, - /* 242 */ - { + }, + /* 242 */ + { {0x8003, 21}, {0x8006, 21}, {0x800a, 21}, @@ -4710,9 +4710,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 23}, {0x8029, 23}, {0xc038, 23}, - }, - /* 243 */ - { + }, + /* 243 */ + { {0x8002, 24}, {0x8009, 24}, {0x8017, 24}, @@ -4729,9 +4729,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 27}, {0x8017, 27}, {0xc028, 27}, - }, - /* 244 */ - { + }, + /* 244 */ + { {0x8003, 24}, {0x8006, 24}, {0x800a, 24}, @@ -4748,9 +4748,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 25}, {0x8029, 25}, {0xc038, 25}, - }, - /* 245 */ - { + }, + /* 245 */ + { {0x8003, 26}, {0x8006, 26}, {0x800a, 26}, @@ -4767,9 +4767,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 27}, {0x8029, 27}, {0xc038, 27}, - }, - /* 246 */ - { + }, + /* 246 */ + { {0x8001, 28}, {0xc016, 28}, {0x8001, 29}, @@ -4786,9 +4786,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xc016, 249}, {0xfe, 0}, {0xff, 0}, - }, - /* 247 */ - { + }, + /* 247 */ + { {0x8002, 28}, {0x8009, 28}, {0x8017, 28}, @@ -4805,9 +4805,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x8009, 31}, {0x8017, 31}, {0xc028, 31}, - }, - /* 248 */ - { + }, + /* 248 */ + { {0x8003, 28}, {0x8006, 28}, {0x800a, 28}, @@ -4824,9 +4824,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 29}, {0x8029, 29}, {0xc038, 29}, - }, - /* 249 */ - { + }, + /* 249 */ + { {0x8003, 30}, {0x8006, 30}, {0x800a, 30}, @@ -4843,9 +4843,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 31}, {0x8029, 31}, {0xc038, 31}, - }, - /* 250 */ - { + }, + /* 250 */ + { {0x8002, 127}, {0x8009, 127}, {0x8017, 127}, @@ -4862,9 +4862,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xc000, 13}, {0xc000, 22}, {0x100, 0}, - }, - /* 251 */ - { + }, + /* 251 */ + { {0x8003, 127}, {0x8006, 127}, {0x800a, 127}, @@ -4881,9 +4881,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 220}, {0x8029, 220}, {0xc038, 220}, - }, - /* 252 */ - { + }, + /* 252 */ + { {0x8003, 249}, {0x8006, 249}, {0x800a, 249}, @@ -4900,9 +4900,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0xc016, 22}, {0x100, 0}, {0x100, 0}, - }, - /* 253 */ - { + }, + /* 253 */ + { {0x8002, 10}, {0x8009, 10}, {0x8017, 10}, @@ -4919,9 +4919,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x100, 0}, {0x100, 0}, {0x100, 0}, - }, - /* 254 */ - { + }, + /* 254 */ + { {0x8003, 10}, {0x8006, 10}, {0x800a, 10}, @@ -4938,9 +4938,9 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x801f, 13}, {0x8029, 13}, {0xc038, 13}, - }, - /* 255 */ - { + }, + /* 255 */ + { {0x8003, 22}, {0x8006, 22}, {0x800a, 22}, @@ -4957,7 +4957,7 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x100, 0}, {0x100, 0}, {0x100, 0}, - }, + }, /* 256 */ { {0x100, 0}, @@ -4977,4 +4977,4 @@ const nghttp2_huff_decode huff_decode_table[][16] = { {0x100, 0}, {0x100, 0}, }, -}; +}; diff --git a/contrib/libs/nghttp2/lib/nghttp2_helper.c b/contrib/libs/nghttp2/lib/nghttp2_helper.c index 9d240538a4..588e269c34 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_helper.c +++ b/contrib/libs/nghttp2/lib/nghttp2_helper.c @@ -1,512 +1,512 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * 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_helper.h" - -#include <assert.h> -#include <string.h> - -#include "nghttp2_net.h" - -void nghttp2_put_uint16be(uint8_t *buf, uint16_t n) { - uint16_t x = htons(n); - memcpy(buf, &x, sizeof(uint16_t)); -} - -void nghttp2_put_uint32be(uint8_t *buf, uint32_t n) { - uint32_t x = htonl(n); - memcpy(buf, &x, sizeof(uint32_t)); -} - -uint16_t nghttp2_get_uint16(const uint8_t *data) { - uint16_t n; - memcpy(&n, data, sizeof(uint16_t)); - return ntohs(n); -} - -uint32_t nghttp2_get_uint32(const uint8_t *data) { - uint32_t n; - memcpy(&n, data, sizeof(uint32_t)); - return ntohl(n); -} - -/* Generated by gendowncasetbl.py */ -static const uint8_t DOWNCASE_TBL[] = { - 0 /* NUL */, 1 /* SOH */, 2 /* STX */, 3 /* ETX */, - 4 /* EOT */, 5 /* ENQ */, 6 /* ACK */, 7 /* BEL */, - 8 /* BS */, 9 /* HT */, 10 /* LF */, 11 /* VT */, - 12 /* FF */, 13 /* CR */, 14 /* SO */, 15 /* SI */, - 16 /* DLE */, 17 /* DC1 */, 18 /* DC2 */, 19 /* DC3 */, - 20 /* DC4 */, 21 /* NAK */, 22 /* SYN */, 23 /* ETB */, - 24 /* CAN */, 25 /* EM */, 26 /* SUB */, 27 /* ESC */, - 28 /* FS */, 29 /* GS */, 30 /* RS */, 31 /* US */, - 32 /* SPC */, 33 /* ! */, 34 /* " */, 35 /* # */, - 36 /* $ */, 37 /* % */, 38 /* & */, 39 /* ' */, - 40 /* ( */, 41 /* ) */, 42 /* * */, 43 /* + */, - 44 /* , */, 45 /* - */, 46 /* . */, 47 /* / */, - 48 /* 0 */, 49 /* 1 */, 50 /* 2 */, 51 /* 3 */, - 52 /* 4 */, 53 /* 5 */, 54 /* 6 */, 55 /* 7 */, - 56 /* 8 */, 57 /* 9 */, 58 /* : */, 59 /* ; */, - 60 /* < */, 61 /* = */, 62 /* > */, 63 /* ? */, - 64 /* @ */, 97 /* A */, 98 /* B */, 99 /* C */, - 100 /* D */, 101 /* E */, 102 /* F */, 103 /* G */, - 104 /* H */, 105 /* I */, 106 /* J */, 107 /* K */, - 108 /* L */, 109 /* M */, 110 /* N */, 111 /* O */, - 112 /* P */, 113 /* Q */, 114 /* R */, 115 /* S */, - 116 /* T */, 117 /* U */, 118 /* V */, 119 /* W */, - 120 /* X */, 121 /* Y */, 122 /* Z */, 91 /* [ */, - 92 /* \ */, 93 /* ] */, 94 /* ^ */, 95 /* _ */, - 96 /* ` */, 97 /* a */, 98 /* b */, 99 /* c */, - 100 /* d */, 101 /* e */, 102 /* f */, 103 /* g */, - 104 /* h */, 105 /* i */, 106 /* j */, 107 /* k */, - 108 /* l */, 109 /* m */, 110 /* n */, 111 /* o */, - 112 /* p */, 113 /* q */, 114 /* r */, 115 /* s */, - 116 /* t */, 117 /* u */, 118 /* v */, 119 /* w */, - 120 /* x */, 121 /* y */, 122 /* z */, 123 /* { */, - 124 /* | */, 125 /* } */, 126 /* ~ */, 127 /* DEL */, - 128 /* 0x80 */, 129 /* 0x81 */, 130 /* 0x82 */, 131 /* 0x83 */, - 132 /* 0x84 */, 133 /* 0x85 */, 134 /* 0x86 */, 135 /* 0x87 */, - 136 /* 0x88 */, 137 /* 0x89 */, 138 /* 0x8a */, 139 /* 0x8b */, - 140 /* 0x8c */, 141 /* 0x8d */, 142 /* 0x8e */, 143 /* 0x8f */, - 144 /* 0x90 */, 145 /* 0x91 */, 146 /* 0x92 */, 147 /* 0x93 */, - 148 /* 0x94 */, 149 /* 0x95 */, 150 /* 0x96 */, 151 /* 0x97 */, - 152 /* 0x98 */, 153 /* 0x99 */, 154 /* 0x9a */, 155 /* 0x9b */, - 156 /* 0x9c */, 157 /* 0x9d */, 158 /* 0x9e */, 159 /* 0x9f */, - 160 /* 0xa0 */, 161 /* 0xa1 */, 162 /* 0xa2 */, 163 /* 0xa3 */, - 164 /* 0xa4 */, 165 /* 0xa5 */, 166 /* 0xa6 */, 167 /* 0xa7 */, - 168 /* 0xa8 */, 169 /* 0xa9 */, 170 /* 0xaa */, 171 /* 0xab */, - 172 /* 0xac */, 173 /* 0xad */, 174 /* 0xae */, 175 /* 0xaf */, - 176 /* 0xb0 */, 177 /* 0xb1 */, 178 /* 0xb2 */, 179 /* 0xb3 */, - 180 /* 0xb4 */, 181 /* 0xb5 */, 182 /* 0xb6 */, 183 /* 0xb7 */, - 184 /* 0xb8 */, 185 /* 0xb9 */, 186 /* 0xba */, 187 /* 0xbb */, - 188 /* 0xbc */, 189 /* 0xbd */, 190 /* 0xbe */, 191 /* 0xbf */, - 192 /* 0xc0 */, 193 /* 0xc1 */, 194 /* 0xc2 */, 195 /* 0xc3 */, - 196 /* 0xc4 */, 197 /* 0xc5 */, 198 /* 0xc6 */, 199 /* 0xc7 */, - 200 /* 0xc8 */, 201 /* 0xc9 */, 202 /* 0xca */, 203 /* 0xcb */, - 204 /* 0xcc */, 205 /* 0xcd */, 206 /* 0xce */, 207 /* 0xcf */, - 208 /* 0xd0 */, 209 /* 0xd1 */, 210 /* 0xd2 */, 211 /* 0xd3 */, - 212 /* 0xd4 */, 213 /* 0xd5 */, 214 /* 0xd6 */, 215 /* 0xd7 */, - 216 /* 0xd8 */, 217 /* 0xd9 */, 218 /* 0xda */, 219 /* 0xdb */, - 220 /* 0xdc */, 221 /* 0xdd */, 222 /* 0xde */, 223 /* 0xdf */, - 224 /* 0xe0 */, 225 /* 0xe1 */, 226 /* 0xe2 */, 227 /* 0xe3 */, - 228 /* 0xe4 */, 229 /* 0xe5 */, 230 /* 0xe6 */, 231 /* 0xe7 */, - 232 /* 0xe8 */, 233 /* 0xe9 */, 234 /* 0xea */, 235 /* 0xeb */, - 236 /* 0xec */, 237 /* 0xed */, 238 /* 0xee */, 239 /* 0xef */, - 240 /* 0xf0 */, 241 /* 0xf1 */, 242 /* 0xf2 */, 243 /* 0xf3 */, - 244 /* 0xf4 */, 245 /* 0xf5 */, 246 /* 0xf6 */, 247 /* 0xf7 */, - 248 /* 0xf8 */, 249 /* 0xf9 */, 250 /* 0xfa */, 251 /* 0xfb */, - 252 /* 0xfc */, 253 /* 0xfd */, 254 /* 0xfe */, 255 /* 0xff */, -}; - -void nghttp2_downcase(uint8_t *s, size_t len) { - size_t i; - for (i = 0; i < len; ++i) { - s[i] = DOWNCASE_TBL[s[i]]; - } -} - -/* - * local_window_size - * ^ * - * | * recv_window_size - * | * * ^ - * | * * | - * 0+++++++++ - * | * * \ - * | * * | This rage is hidden in flow control. But it must be - * v * * / kept in order to restore it when window size is enlarged. - * recv_reduction - * (+ for negative direction) - * - * recv_window_size could be negative if we decrease - * local_window_size more than recv_window_size: - * - * local_window_size - * ^ * - * | * - * | * - * 0++++++++ - * | * ^ recv_window_size (negative) - * | * | - * v * * - * recv_reduction - */ -int nghttp2_adjust_local_window_size(int32_t *local_window_size_ptr, - int32_t *recv_window_size_ptr, - int32_t *recv_reduction_ptr, - int32_t *delta_ptr) { - if (*delta_ptr > 0) { - int32_t recv_reduction_delta; - int32_t delta; - int32_t new_recv_window_size = - nghttp2_max(0, *recv_window_size_ptr) - *delta_ptr; - - if (new_recv_window_size >= 0) { - *recv_window_size_ptr = new_recv_window_size; - return 0; - } - - delta = -new_recv_window_size; - - /* The delta size is strictly more than received bytes. Increase - local_window_size by that difference |delta|. */ - if (*local_window_size_ptr > NGHTTP2_MAX_WINDOW_SIZE - delta) { - return NGHTTP2_ERR_FLOW_CONTROL; - } - *local_window_size_ptr += delta; - /* If there is recv_reduction due to earlier window_size - reduction, we have to adjust it too. */ - recv_reduction_delta = nghttp2_min(*recv_reduction_ptr, delta); - *recv_reduction_ptr -= recv_reduction_delta; - if (*recv_window_size_ptr < 0) { - *recv_window_size_ptr += recv_reduction_delta; - } else { - /* If *recv_window_size_ptr > 0, then those bytes are going to - be returned to the remote peer (by WINDOW_UPDATE with the - adjusted *delta_ptr), so it is effectively 0 now. We set to - *recv_reduction_delta, because caller does not take into - account it in *delta_ptr. */ - *recv_window_size_ptr = recv_reduction_delta; - } - /* recv_reduction_delta must be paid from *delta_ptr, since it was - added in window size reduction (see below). */ - *delta_ptr -= recv_reduction_delta; - - return 0; - } - - if (*local_window_size_ptr + *delta_ptr < 0 || - *recv_window_size_ptr < INT32_MIN - *delta_ptr || - *recv_reduction_ptr > INT32_MAX + *delta_ptr) { - return NGHTTP2_ERR_FLOW_CONTROL; - } - /* Decreasing local window size. Note that we achieve this without - noticing to the remote peer. To do this, we cut - recv_window_size by -delta. This means that we don't send - WINDOW_UPDATE for -delta bytes. */ - *local_window_size_ptr += *delta_ptr; - *recv_window_size_ptr += *delta_ptr; - *recv_reduction_ptr -= *delta_ptr; - *delta_ptr = 0; - - return 0; -} - -int nghttp2_increase_local_window_size(int32_t *local_window_size_ptr, - int32_t *recv_window_size_ptr, - int32_t *recv_reduction_ptr, - int32_t *delta_ptr) { - int32_t recv_reduction_delta; - int32_t delta; - - delta = *delta_ptr; - - assert(delta >= 0); - - /* The delta size is strictly more than received bytes. Increase - local_window_size by that difference |delta|. */ - if (*local_window_size_ptr > NGHTTP2_MAX_WINDOW_SIZE - delta) { - return NGHTTP2_ERR_FLOW_CONTROL; - } - - *local_window_size_ptr += delta; - /* If there is recv_reduction due to earlier window_size - reduction, we have to adjust it too. */ - recv_reduction_delta = nghttp2_min(*recv_reduction_ptr, delta); - *recv_reduction_ptr -= recv_reduction_delta; - - *recv_window_size_ptr += recv_reduction_delta; - - /* recv_reduction_delta must be paid from *delta_ptr, since it was - added in window size reduction (see below). */ - *delta_ptr -= recv_reduction_delta; - - return 0; -} - -int nghttp2_should_send_window_update(int32_t local_window_size, - int32_t recv_window_size) { - return recv_window_size > 0 && recv_window_size >= local_window_size / 2; -} - -const char *nghttp2_strerror(int error_code) { - switch (error_code) { - case 0: - return "Success"; - case NGHTTP2_ERR_INVALID_ARGUMENT: - return "Invalid argument"; - case NGHTTP2_ERR_BUFFER_ERROR: - return "Out of buffer space"; - case NGHTTP2_ERR_UNSUPPORTED_VERSION: - return "Unsupported SPDY version"; - case NGHTTP2_ERR_WOULDBLOCK: - return "Operation would block"; - case NGHTTP2_ERR_PROTO: - return "Protocol error"; - case NGHTTP2_ERR_INVALID_FRAME: - return "Invalid frame octets"; - case NGHTTP2_ERR_EOF: - return "EOF"; - case NGHTTP2_ERR_DEFERRED: - return "Data transfer deferred"; - case NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE: - return "No more Stream ID available"; - case NGHTTP2_ERR_STREAM_CLOSED: - return "Stream was already closed or invalid"; - case NGHTTP2_ERR_STREAM_CLOSING: - return "Stream is closing"; - case NGHTTP2_ERR_STREAM_SHUT_WR: - return "The transmission is not allowed for this stream"; - case NGHTTP2_ERR_INVALID_STREAM_ID: - return "Stream ID is invalid"; - case NGHTTP2_ERR_INVALID_STREAM_STATE: - return "Invalid stream state"; - case NGHTTP2_ERR_DEFERRED_DATA_EXIST: - return "Another DATA frame has already been deferred"; - case NGHTTP2_ERR_START_STREAM_NOT_ALLOWED: - return "request HEADERS is not allowed"; - case NGHTTP2_ERR_GOAWAY_ALREADY_SENT: - return "GOAWAY has already been sent"; - case NGHTTP2_ERR_INVALID_HEADER_BLOCK: - return "Invalid header block"; - case NGHTTP2_ERR_INVALID_STATE: - return "Invalid state"; - case NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE: - return "The user callback function failed due to the temporal error"; - case NGHTTP2_ERR_FRAME_SIZE_ERROR: - return "The length of the frame is invalid"; - case NGHTTP2_ERR_HEADER_COMP: - return "Header compression/decompression error"; - case NGHTTP2_ERR_FLOW_CONTROL: - return "Flow control error"; - case NGHTTP2_ERR_INSUFF_BUFSIZE: - return "Insufficient buffer size given to function"; - case NGHTTP2_ERR_PAUSE: - return "Callback was paused by the application"; - case NGHTTP2_ERR_TOO_MANY_INFLIGHT_SETTINGS: - return "Too many inflight SETTINGS"; - case NGHTTP2_ERR_PUSH_DISABLED: - return "Server push is disabled by peer"; - case NGHTTP2_ERR_DATA_EXIST: - return "DATA or HEADERS frame has already been submitted for the stream"; - case NGHTTP2_ERR_SESSION_CLOSING: - return "The current session is closing"; - case NGHTTP2_ERR_HTTP_HEADER: - return "Invalid HTTP header field was received"; - case NGHTTP2_ERR_HTTP_MESSAGING: - return "Violation in HTTP messaging rule"; - case NGHTTP2_ERR_REFUSED_STREAM: - return "Stream was refused"; - case NGHTTP2_ERR_INTERNAL: - return "Internal error"; - case NGHTTP2_ERR_CANCEL: - return "Cancel"; - case NGHTTP2_ERR_SETTINGS_EXPECTED: - return "When a local endpoint expects to receive SETTINGS frame, it " - "receives an other type of frame"; - case NGHTTP2_ERR_NOMEM: - return "Out of memory"; - case NGHTTP2_ERR_CALLBACK_FAILURE: - return "The user callback function failed"; - case NGHTTP2_ERR_BAD_CLIENT_MAGIC: - return "Received bad client magic byte string"; - case NGHTTP2_ERR_FLOODED: - return "Flooding was detected in this HTTP/2 session, and it must be " - "closed"; +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * 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_helper.h" + +#include <assert.h> +#include <string.h> + +#include "nghttp2_net.h" + +void nghttp2_put_uint16be(uint8_t *buf, uint16_t n) { + uint16_t x = htons(n); + memcpy(buf, &x, sizeof(uint16_t)); +} + +void nghttp2_put_uint32be(uint8_t *buf, uint32_t n) { + uint32_t x = htonl(n); + memcpy(buf, &x, sizeof(uint32_t)); +} + +uint16_t nghttp2_get_uint16(const uint8_t *data) { + uint16_t n; + memcpy(&n, data, sizeof(uint16_t)); + return ntohs(n); +} + +uint32_t nghttp2_get_uint32(const uint8_t *data) { + uint32_t n; + memcpy(&n, data, sizeof(uint32_t)); + return ntohl(n); +} + +/* Generated by gendowncasetbl.py */ +static const uint8_t DOWNCASE_TBL[] = { + 0 /* NUL */, 1 /* SOH */, 2 /* STX */, 3 /* ETX */, + 4 /* EOT */, 5 /* ENQ */, 6 /* ACK */, 7 /* BEL */, + 8 /* BS */, 9 /* HT */, 10 /* LF */, 11 /* VT */, + 12 /* FF */, 13 /* CR */, 14 /* SO */, 15 /* SI */, + 16 /* DLE */, 17 /* DC1 */, 18 /* DC2 */, 19 /* DC3 */, + 20 /* DC4 */, 21 /* NAK */, 22 /* SYN */, 23 /* ETB */, + 24 /* CAN */, 25 /* EM */, 26 /* SUB */, 27 /* ESC */, + 28 /* FS */, 29 /* GS */, 30 /* RS */, 31 /* US */, + 32 /* SPC */, 33 /* ! */, 34 /* " */, 35 /* # */, + 36 /* $ */, 37 /* % */, 38 /* & */, 39 /* ' */, + 40 /* ( */, 41 /* ) */, 42 /* * */, 43 /* + */, + 44 /* , */, 45 /* - */, 46 /* . */, 47 /* / */, + 48 /* 0 */, 49 /* 1 */, 50 /* 2 */, 51 /* 3 */, + 52 /* 4 */, 53 /* 5 */, 54 /* 6 */, 55 /* 7 */, + 56 /* 8 */, 57 /* 9 */, 58 /* : */, 59 /* ; */, + 60 /* < */, 61 /* = */, 62 /* > */, 63 /* ? */, + 64 /* @ */, 97 /* A */, 98 /* B */, 99 /* C */, + 100 /* D */, 101 /* E */, 102 /* F */, 103 /* G */, + 104 /* H */, 105 /* I */, 106 /* J */, 107 /* K */, + 108 /* L */, 109 /* M */, 110 /* N */, 111 /* O */, + 112 /* P */, 113 /* Q */, 114 /* R */, 115 /* S */, + 116 /* T */, 117 /* U */, 118 /* V */, 119 /* W */, + 120 /* X */, 121 /* Y */, 122 /* Z */, 91 /* [ */, + 92 /* \ */, 93 /* ] */, 94 /* ^ */, 95 /* _ */, + 96 /* ` */, 97 /* a */, 98 /* b */, 99 /* c */, + 100 /* d */, 101 /* e */, 102 /* f */, 103 /* g */, + 104 /* h */, 105 /* i */, 106 /* j */, 107 /* k */, + 108 /* l */, 109 /* m */, 110 /* n */, 111 /* o */, + 112 /* p */, 113 /* q */, 114 /* r */, 115 /* s */, + 116 /* t */, 117 /* u */, 118 /* v */, 119 /* w */, + 120 /* x */, 121 /* y */, 122 /* z */, 123 /* { */, + 124 /* | */, 125 /* } */, 126 /* ~ */, 127 /* DEL */, + 128 /* 0x80 */, 129 /* 0x81 */, 130 /* 0x82 */, 131 /* 0x83 */, + 132 /* 0x84 */, 133 /* 0x85 */, 134 /* 0x86 */, 135 /* 0x87 */, + 136 /* 0x88 */, 137 /* 0x89 */, 138 /* 0x8a */, 139 /* 0x8b */, + 140 /* 0x8c */, 141 /* 0x8d */, 142 /* 0x8e */, 143 /* 0x8f */, + 144 /* 0x90 */, 145 /* 0x91 */, 146 /* 0x92 */, 147 /* 0x93 */, + 148 /* 0x94 */, 149 /* 0x95 */, 150 /* 0x96 */, 151 /* 0x97 */, + 152 /* 0x98 */, 153 /* 0x99 */, 154 /* 0x9a */, 155 /* 0x9b */, + 156 /* 0x9c */, 157 /* 0x9d */, 158 /* 0x9e */, 159 /* 0x9f */, + 160 /* 0xa0 */, 161 /* 0xa1 */, 162 /* 0xa2 */, 163 /* 0xa3 */, + 164 /* 0xa4 */, 165 /* 0xa5 */, 166 /* 0xa6 */, 167 /* 0xa7 */, + 168 /* 0xa8 */, 169 /* 0xa9 */, 170 /* 0xaa */, 171 /* 0xab */, + 172 /* 0xac */, 173 /* 0xad */, 174 /* 0xae */, 175 /* 0xaf */, + 176 /* 0xb0 */, 177 /* 0xb1 */, 178 /* 0xb2 */, 179 /* 0xb3 */, + 180 /* 0xb4 */, 181 /* 0xb5 */, 182 /* 0xb6 */, 183 /* 0xb7 */, + 184 /* 0xb8 */, 185 /* 0xb9 */, 186 /* 0xba */, 187 /* 0xbb */, + 188 /* 0xbc */, 189 /* 0xbd */, 190 /* 0xbe */, 191 /* 0xbf */, + 192 /* 0xc0 */, 193 /* 0xc1 */, 194 /* 0xc2 */, 195 /* 0xc3 */, + 196 /* 0xc4 */, 197 /* 0xc5 */, 198 /* 0xc6 */, 199 /* 0xc7 */, + 200 /* 0xc8 */, 201 /* 0xc9 */, 202 /* 0xca */, 203 /* 0xcb */, + 204 /* 0xcc */, 205 /* 0xcd */, 206 /* 0xce */, 207 /* 0xcf */, + 208 /* 0xd0 */, 209 /* 0xd1 */, 210 /* 0xd2 */, 211 /* 0xd3 */, + 212 /* 0xd4 */, 213 /* 0xd5 */, 214 /* 0xd6 */, 215 /* 0xd7 */, + 216 /* 0xd8 */, 217 /* 0xd9 */, 218 /* 0xda */, 219 /* 0xdb */, + 220 /* 0xdc */, 221 /* 0xdd */, 222 /* 0xde */, 223 /* 0xdf */, + 224 /* 0xe0 */, 225 /* 0xe1 */, 226 /* 0xe2 */, 227 /* 0xe3 */, + 228 /* 0xe4 */, 229 /* 0xe5 */, 230 /* 0xe6 */, 231 /* 0xe7 */, + 232 /* 0xe8 */, 233 /* 0xe9 */, 234 /* 0xea */, 235 /* 0xeb */, + 236 /* 0xec */, 237 /* 0xed */, 238 /* 0xee */, 239 /* 0xef */, + 240 /* 0xf0 */, 241 /* 0xf1 */, 242 /* 0xf2 */, 243 /* 0xf3 */, + 244 /* 0xf4 */, 245 /* 0xf5 */, 246 /* 0xf6 */, 247 /* 0xf7 */, + 248 /* 0xf8 */, 249 /* 0xf9 */, 250 /* 0xfa */, 251 /* 0xfb */, + 252 /* 0xfc */, 253 /* 0xfd */, 254 /* 0xfe */, 255 /* 0xff */, +}; + +void nghttp2_downcase(uint8_t *s, size_t len) { + size_t i; + for (i = 0; i < len; ++i) { + s[i] = DOWNCASE_TBL[s[i]]; + } +} + +/* + * local_window_size + * ^ * + * | * recv_window_size + * | * * ^ + * | * * | + * 0+++++++++ + * | * * \ + * | * * | This rage is hidden in flow control. But it must be + * v * * / kept in order to restore it when window size is enlarged. + * recv_reduction + * (+ for negative direction) + * + * recv_window_size could be negative if we decrease + * local_window_size more than recv_window_size: + * + * local_window_size + * ^ * + * | * + * | * + * 0++++++++ + * | * ^ recv_window_size (negative) + * | * | + * v * * + * recv_reduction + */ +int nghttp2_adjust_local_window_size(int32_t *local_window_size_ptr, + int32_t *recv_window_size_ptr, + int32_t *recv_reduction_ptr, + int32_t *delta_ptr) { + if (*delta_ptr > 0) { + int32_t recv_reduction_delta; + int32_t delta; + int32_t new_recv_window_size = + nghttp2_max(0, *recv_window_size_ptr) - *delta_ptr; + + if (new_recv_window_size >= 0) { + *recv_window_size_ptr = new_recv_window_size; + return 0; + } + + delta = -new_recv_window_size; + + /* The delta size is strictly more than received bytes. Increase + local_window_size by that difference |delta|. */ + if (*local_window_size_ptr > NGHTTP2_MAX_WINDOW_SIZE - delta) { + return NGHTTP2_ERR_FLOW_CONTROL; + } + *local_window_size_ptr += delta; + /* If there is recv_reduction due to earlier window_size + reduction, we have to adjust it too. */ + recv_reduction_delta = nghttp2_min(*recv_reduction_ptr, delta); + *recv_reduction_ptr -= recv_reduction_delta; + if (*recv_window_size_ptr < 0) { + *recv_window_size_ptr += recv_reduction_delta; + } else { + /* If *recv_window_size_ptr > 0, then those bytes are going to + be returned to the remote peer (by WINDOW_UPDATE with the + adjusted *delta_ptr), so it is effectively 0 now. We set to + *recv_reduction_delta, because caller does not take into + account it in *delta_ptr. */ + *recv_window_size_ptr = recv_reduction_delta; + } + /* recv_reduction_delta must be paid from *delta_ptr, since it was + added in window size reduction (see below). */ + *delta_ptr -= recv_reduction_delta; + + return 0; + } + + if (*local_window_size_ptr + *delta_ptr < 0 || + *recv_window_size_ptr < INT32_MIN - *delta_ptr || + *recv_reduction_ptr > INT32_MAX + *delta_ptr) { + return NGHTTP2_ERR_FLOW_CONTROL; + } + /* Decreasing local window size. Note that we achieve this without + noticing to the remote peer. To do this, we cut + recv_window_size by -delta. This means that we don't send + WINDOW_UPDATE for -delta bytes. */ + *local_window_size_ptr += *delta_ptr; + *recv_window_size_ptr += *delta_ptr; + *recv_reduction_ptr -= *delta_ptr; + *delta_ptr = 0; + + return 0; +} + +int nghttp2_increase_local_window_size(int32_t *local_window_size_ptr, + int32_t *recv_window_size_ptr, + int32_t *recv_reduction_ptr, + int32_t *delta_ptr) { + int32_t recv_reduction_delta; + int32_t delta; + + delta = *delta_ptr; + + assert(delta >= 0); + + /* The delta size is strictly more than received bytes. Increase + local_window_size by that difference |delta|. */ + if (*local_window_size_ptr > NGHTTP2_MAX_WINDOW_SIZE - delta) { + return NGHTTP2_ERR_FLOW_CONTROL; + } + + *local_window_size_ptr += delta; + /* If there is recv_reduction due to earlier window_size + reduction, we have to adjust it too. */ + recv_reduction_delta = nghttp2_min(*recv_reduction_ptr, delta); + *recv_reduction_ptr -= recv_reduction_delta; + + *recv_window_size_ptr += recv_reduction_delta; + + /* recv_reduction_delta must be paid from *delta_ptr, since it was + added in window size reduction (see below). */ + *delta_ptr -= recv_reduction_delta; + + return 0; +} + +int nghttp2_should_send_window_update(int32_t local_window_size, + int32_t recv_window_size) { + return recv_window_size > 0 && recv_window_size >= local_window_size / 2; +} + +const char *nghttp2_strerror(int error_code) { + switch (error_code) { + case 0: + return "Success"; + case NGHTTP2_ERR_INVALID_ARGUMENT: + return "Invalid argument"; + case NGHTTP2_ERR_BUFFER_ERROR: + return "Out of buffer space"; + case NGHTTP2_ERR_UNSUPPORTED_VERSION: + return "Unsupported SPDY version"; + case NGHTTP2_ERR_WOULDBLOCK: + return "Operation would block"; + case NGHTTP2_ERR_PROTO: + return "Protocol error"; + case NGHTTP2_ERR_INVALID_FRAME: + return "Invalid frame octets"; + case NGHTTP2_ERR_EOF: + return "EOF"; + case NGHTTP2_ERR_DEFERRED: + return "Data transfer deferred"; + case NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE: + return "No more Stream ID available"; + case NGHTTP2_ERR_STREAM_CLOSED: + return "Stream was already closed or invalid"; + case NGHTTP2_ERR_STREAM_CLOSING: + return "Stream is closing"; + case NGHTTP2_ERR_STREAM_SHUT_WR: + return "The transmission is not allowed for this stream"; + case NGHTTP2_ERR_INVALID_STREAM_ID: + return "Stream ID is invalid"; + case NGHTTP2_ERR_INVALID_STREAM_STATE: + return "Invalid stream state"; + case NGHTTP2_ERR_DEFERRED_DATA_EXIST: + return "Another DATA frame has already been deferred"; + case NGHTTP2_ERR_START_STREAM_NOT_ALLOWED: + return "request HEADERS is not allowed"; + case NGHTTP2_ERR_GOAWAY_ALREADY_SENT: + return "GOAWAY has already been sent"; + case NGHTTP2_ERR_INVALID_HEADER_BLOCK: + return "Invalid header block"; + case NGHTTP2_ERR_INVALID_STATE: + return "Invalid state"; + case NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE: + return "The user callback function failed due to the temporal error"; + case NGHTTP2_ERR_FRAME_SIZE_ERROR: + return "The length of the frame is invalid"; + case NGHTTP2_ERR_HEADER_COMP: + return "Header compression/decompression error"; + case NGHTTP2_ERR_FLOW_CONTROL: + return "Flow control error"; + case NGHTTP2_ERR_INSUFF_BUFSIZE: + return "Insufficient buffer size given to function"; + case NGHTTP2_ERR_PAUSE: + return "Callback was paused by the application"; + case NGHTTP2_ERR_TOO_MANY_INFLIGHT_SETTINGS: + return "Too many inflight SETTINGS"; + case NGHTTP2_ERR_PUSH_DISABLED: + return "Server push is disabled by peer"; + case NGHTTP2_ERR_DATA_EXIST: + return "DATA or HEADERS frame has already been submitted for the stream"; + case NGHTTP2_ERR_SESSION_CLOSING: + return "The current session is closing"; + case NGHTTP2_ERR_HTTP_HEADER: + return "Invalid HTTP header field was received"; + case NGHTTP2_ERR_HTTP_MESSAGING: + return "Violation in HTTP messaging rule"; + case NGHTTP2_ERR_REFUSED_STREAM: + return "Stream was refused"; + case NGHTTP2_ERR_INTERNAL: + return "Internal error"; + case NGHTTP2_ERR_CANCEL: + return "Cancel"; + case NGHTTP2_ERR_SETTINGS_EXPECTED: + return "When a local endpoint expects to receive SETTINGS frame, it " + "receives an other type of frame"; + case NGHTTP2_ERR_NOMEM: + return "Out of memory"; + case NGHTTP2_ERR_CALLBACK_FAILURE: + return "The user callback function failed"; + case NGHTTP2_ERR_BAD_CLIENT_MAGIC: + return "Received bad client magic byte string"; + case NGHTTP2_ERR_FLOODED: + return "Flooding was detected in this HTTP/2 session, and it must be " + "closed"; case NGHTTP2_ERR_TOO_MANY_SETTINGS: return "SETTINGS frame contained more than the maximum allowed entries"; - default: - return "Unknown error code"; - } -} - -/* Generated by gennmchartbl.py */ + default: + return "Unknown error code"; + } +} + +/* Generated by gennmchartbl.py */ static const int VALID_HD_NAME_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 /* . */, 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 /* ] */, 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 */ -}; - -int nghttp2_check_header_name(const uint8_t *name, size_t len) { - const uint8_t *last; - if (len == 0) { - return 0; - } - if (*name == ':') { - if (len == 1) { - return 0; - } - ++name; - --len; - } - for (last = name + len; name != last; ++name) { - if (!VALID_HD_NAME_CHARS[*name]) { - return 0; - } - } - return 1; -} - -/* Generated by genvchartbl.py */ + 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 /* . */, 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 /* ] */, 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 */ +}; + +int nghttp2_check_header_name(const uint8_t *name, size_t len) { + const uint8_t *last; + if (len == 0) { + return 0; + } + if (*name == ':') { + if (len == 1) { + return 0; + } + ++name; + --len; + } + for (last = name + len; name != last; ++name) { + if (!VALID_HD_NAME_CHARS[*name]) { + return 0; + } + } + return 1; +} + +/* Generated by genvchartbl.py */ static const int VALID_HD_VALUE_CHARS[] = { - 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, - 0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, - 0 /* BS */, 1 /* 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 /* ! */, 1 /* " */, 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 /* [ */, - 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 /* { */, - 1 /* | */, 1 /* } */, 1 /* ~ */, 0 /* DEL */, - 1 /* 0x80 */, 1 /* 0x81 */, 1 /* 0x82 */, 1 /* 0x83 */, - 1 /* 0x84 */, 1 /* 0x85 */, 1 /* 0x86 */, 1 /* 0x87 */, - 1 /* 0x88 */, 1 /* 0x89 */, 1 /* 0x8a */, 1 /* 0x8b */, - 1 /* 0x8c */, 1 /* 0x8d */, 1 /* 0x8e */, 1 /* 0x8f */, - 1 /* 0x90 */, 1 /* 0x91 */, 1 /* 0x92 */, 1 /* 0x93 */, - 1 /* 0x94 */, 1 /* 0x95 */, 1 /* 0x96 */, 1 /* 0x97 */, - 1 /* 0x98 */, 1 /* 0x99 */, 1 /* 0x9a */, 1 /* 0x9b */, - 1 /* 0x9c */, 1 /* 0x9d */, 1 /* 0x9e */, 1 /* 0x9f */, - 1 /* 0xa0 */, 1 /* 0xa1 */, 1 /* 0xa2 */, 1 /* 0xa3 */, - 1 /* 0xa4 */, 1 /* 0xa5 */, 1 /* 0xa6 */, 1 /* 0xa7 */, - 1 /* 0xa8 */, 1 /* 0xa9 */, 1 /* 0xaa */, 1 /* 0xab */, - 1 /* 0xac */, 1 /* 0xad */, 1 /* 0xae */, 1 /* 0xaf */, - 1 /* 0xb0 */, 1 /* 0xb1 */, 1 /* 0xb2 */, 1 /* 0xb3 */, - 1 /* 0xb4 */, 1 /* 0xb5 */, 1 /* 0xb6 */, 1 /* 0xb7 */, - 1 /* 0xb8 */, 1 /* 0xb9 */, 1 /* 0xba */, 1 /* 0xbb */, - 1 /* 0xbc */, 1 /* 0xbd */, 1 /* 0xbe */, 1 /* 0xbf */, - 1 /* 0xc0 */, 1 /* 0xc1 */, 1 /* 0xc2 */, 1 /* 0xc3 */, - 1 /* 0xc4 */, 1 /* 0xc5 */, 1 /* 0xc6 */, 1 /* 0xc7 */, - 1 /* 0xc8 */, 1 /* 0xc9 */, 1 /* 0xca */, 1 /* 0xcb */, - 1 /* 0xcc */, 1 /* 0xcd */, 1 /* 0xce */, 1 /* 0xcf */, - 1 /* 0xd0 */, 1 /* 0xd1 */, 1 /* 0xd2 */, 1 /* 0xd3 */, - 1 /* 0xd4 */, 1 /* 0xd5 */, 1 /* 0xd6 */, 1 /* 0xd7 */, - 1 /* 0xd8 */, 1 /* 0xd9 */, 1 /* 0xda */, 1 /* 0xdb */, - 1 /* 0xdc */, 1 /* 0xdd */, 1 /* 0xde */, 1 /* 0xdf */, - 1 /* 0xe0 */, 1 /* 0xe1 */, 1 /* 0xe2 */, 1 /* 0xe3 */, - 1 /* 0xe4 */, 1 /* 0xe5 */, 1 /* 0xe6 */, 1 /* 0xe7 */, - 1 /* 0xe8 */, 1 /* 0xe9 */, 1 /* 0xea */, 1 /* 0xeb */, - 1 /* 0xec */, 1 /* 0xed */, 1 /* 0xee */, 1 /* 0xef */, - 1 /* 0xf0 */, 1 /* 0xf1 */, 1 /* 0xf2 */, 1 /* 0xf3 */, - 1 /* 0xf4 */, 1 /* 0xf5 */, 1 /* 0xf6 */, 1 /* 0xf7 */, - 1 /* 0xf8 */, 1 /* 0xf9 */, 1 /* 0xfa */, 1 /* 0xfb */, - 1 /* 0xfc */, 1 /* 0xfd */, 1 /* 0xfe */, 1 /* 0xff */ -}; - -int nghttp2_check_header_value(const uint8_t *value, size_t len) { - const uint8_t *last; - for (last = value + len; value != last; ++value) { - if (!VALID_HD_VALUE_CHARS[*value]) { - return 0; - } - } - return 1; -} - + 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, + 0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, + 0 /* BS */, 1 /* 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 /* ! */, 1 /* " */, 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 /* [ */, + 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 /* { */, + 1 /* | */, 1 /* } */, 1 /* ~ */, 0 /* DEL */, + 1 /* 0x80 */, 1 /* 0x81 */, 1 /* 0x82 */, 1 /* 0x83 */, + 1 /* 0x84 */, 1 /* 0x85 */, 1 /* 0x86 */, 1 /* 0x87 */, + 1 /* 0x88 */, 1 /* 0x89 */, 1 /* 0x8a */, 1 /* 0x8b */, + 1 /* 0x8c */, 1 /* 0x8d */, 1 /* 0x8e */, 1 /* 0x8f */, + 1 /* 0x90 */, 1 /* 0x91 */, 1 /* 0x92 */, 1 /* 0x93 */, + 1 /* 0x94 */, 1 /* 0x95 */, 1 /* 0x96 */, 1 /* 0x97 */, + 1 /* 0x98 */, 1 /* 0x99 */, 1 /* 0x9a */, 1 /* 0x9b */, + 1 /* 0x9c */, 1 /* 0x9d */, 1 /* 0x9e */, 1 /* 0x9f */, + 1 /* 0xa0 */, 1 /* 0xa1 */, 1 /* 0xa2 */, 1 /* 0xa3 */, + 1 /* 0xa4 */, 1 /* 0xa5 */, 1 /* 0xa6 */, 1 /* 0xa7 */, + 1 /* 0xa8 */, 1 /* 0xa9 */, 1 /* 0xaa */, 1 /* 0xab */, + 1 /* 0xac */, 1 /* 0xad */, 1 /* 0xae */, 1 /* 0xaf */, + 1 /* 0xb0 */, 1 /* 0xb1 */, 1 /* 0xb2 */, 1 /* 0xb3 */, + 1 /* 0xb4 */, 1 /* 0xb5 */, 1 /* 0xb6 */, 1 /* 0xb7 */, + 1 /* 0xb8 */, 1 /* 0xb9 */, 1 /* 0xba */, 1 /* 0xbb */, + 1 /* 0xbc */, 1 /* 0xbd */, 1 /* 0xbe */, 1 /* 0xbf */, + 1 /* 0xc0 */, 1 /* 0xc1 */, 1 /* 0xc2 */, 1 /* 0xc3 */, + 1 /* 0xc4 */, 1 /* 0xc5 */, 1 /* 0xc6 */, 1 /* 0xc7 */, + 1 /* 0xc8 */, 1 /* 0xc9 */, 1 /* 0xca */, 1 /* 0xcb */, + 1 /* 0xcc */, 1 /* 0xcd */, 1 /* 0xce */, 1 /* 0xcf */, + 1 /* 0xd0 */, 1 /* 0xd1 */, 1 /* 0xd2 */, 1 /* 0xd3 */, + 1 /* 0xd4 */, 1 /* 0xd5 */, 1 /* 0xd6 */, 1 /* 0xd7 */, + 1 /* 0xd8 */, 1 /* 0xd9 */, 1 /* 0xda */, 1 /* 0xdb */, + 1 /* 0xdc */, 1 /* 0xdd */, 1 /* 0xde */, 1 /* 0xdf */, + 1 /* 0xe0 */, 1 /* 0xe1 */, 1 /* 0xe2 */, 1 /* 0xe3 */, + 1 /* 0xe4 */, 1 /* 0xe5 */, 1 /* 0xe6 */, 1 /* 0xe7 */, + 1 /* 0xe8 */, 1 /* 0xe9 */, 1 /* 0xea */, 1 /* 0xeb */, + 1 /* 0xec */, 1 /* 0xed */, 1 /* 0xee */, 1 /* 0xef */, + 1 /* 0xf0 */, 1 /* 0xf1 */, 1 /* 0xf2 */, 1 /* 0xf3 */, + 1 /* 0xf4 */, 1 /* 0xf5 */, 1 /* 0xf6 */, 1 /* 0xf7 */, + 1 /* 0xf8 */, 1 /* 0xf9 */, 1 /* 0xfa */, 1 /* 0xfb */, + 1 /* 0xfc */, 1 /* 0xfd */, 1 /* 0xfe */, 1 /* 0xff */ +}; + +int nghttp2_check_header_value(const uint8_t *value, size_t len) { + const uint8_t *last; + for (last = value + len; value != last; ++value) { + if (!VALID_HD_VALUE_CHARS[*value]) { + return 0; + } + } + return 1; +} + /* Generated by genmethodchartbl.py */ static char VALID_METHOD_CHARS[] = { 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, @@ -744,47 +744,47 @@ int nghttp2_check_authority(const uint8_t *value, size_t len) { return 1; } -uint8_t *nghttp2_cpymem(uint8_t *dest, const void *src, size_t len) { - if (len == 0) { - return dest; - } - - memcpy(dest, src, len); - - return dest + len; -} - -const char *nghttp2_http2_strerror(uint32_t error_code) { - switch (error_code) { - case NGHTTP2_NO_ERROR: - return "NO_ERROR"; - case NGHTTP2_PROTOCOL_ERROR: - return "PROTOCOL_ERROR"; - case NGHTTP2_INTERNAL_ERROR: - return "INTERNAL_ERROR"; - case NGHTTP2_FLOW_CONTROL_ERROR: - return "FLOW_CONTROL_ERROR"; - case NGHTTP2_SETTINGS_TIMEOUT: - return "SETTINGS_TIMEOUT"; - case NGHTTP2_STREAM_CLOSED: - return "STREAM_CLOSED"; - case NGHTTP2_FRAME_SIZE_ERROR: - return "FRAME_SIZE_ERROR"; - case NGHTTP2_REFUSED_STREAM: - return "REFUSED_STREAM"; - case NGHTTP2_CANCEL: - return "CANCEL"; - case NGHTTP2_COMPRESSION_ERROR: - return "COMPRESSION_ERROR"; - case NGHTTP2_CONNECT_ERROR: - return "CONNECT_ERROR"; - case NGHTTP2_ENHANCE_YOUR_CALM: - return "ENHANCE_YOUR_CALM"; - case NGHTTP2_INADEQUATE_SECURITY: - return "INADEQUATE_SECURITY"; - case NGHTTP2_HTTP_1_1_REQUIRED: - return "HTTP_1_1_REQUIRED"; - default: - return "unknown"; - } -} +uint8_t *nghttp2_cpymem(uint8_t *dest, const void *src, size_t len) { + if (len == 0) { + return dest; + } + + memcpy(dest, src, len); + + return dest + len; +} + +const char *nghttp2_http2_strerror(uint32_t error_code) { + switch (error_code) { + case NGHTTP2_NO_ERROR: + return "NO_ERROR"; + case NGHTTP2_PROTOCOL_ERROR: + return "PROTOCOL_ERROR"; + case NGHTTP2_INTERNAL_ERROR: + return "INTERNAL_ERROR"; + case NGHTTP2_FLOW_CONTROL_ERROR: + return "FLOW_CONTROL_ERROR"; + case NGHTTP2_SETTINGS_TIMEOUT: + return "SETTINGS_TIMEOUT"; + case NGHTTP2_STREAM_CLOSED: + return "STREAM_CLOSED"; + case NGHTTP2_FRAME_SIZE_ERROR: + return "FRAME_SIZE_ERROR"; + case NGHTTP2_REFUSED_STREAM: + return "REFUSED_STREAM"; + case NGHTTP2_CANCEL: + return "CANCEL"; + case NGHTTP2_COMPRESSION_ERROR: + return "COMPRESSION_ERROR"; + case NGHTTP2_CONNECT_ERROR: + return "CONNECT_ERROR"; + case NGHTTP2_ENHANCE_YOUR_CALM: + return "ENHANCE_YOUR_CALM"; + case NGHTTP2_INADEQUATE_SECURITY: + return "INADEQUATE_SECURITY"; + case NGHTTP2_HTTP_1_1_REQUIRED: + return "HTTP_1_1_REQUIRED"; + default: + return "unknown"; + } +} diff --git a/contrib/libs/nghttp2/lib/nghttp2_helper.h b/contrib/libs/nghttp2/lib/nghttp2_helper.h index abfee45433..b1f18ce541 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_helper.h +++ b/contrib/libs/nghttp2/lib/nghttp2_helper.h @@ -1,122 +1,122 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * 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_HELPER_H -#define NGHTTP2_HELPER_H - -#ifdef HAVE_CONFIG_H +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * 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_HELPER_H +#define NGHTTP2_HELPER_H + +#ifdef HAVE_CONFIG_H # include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <string.h> -#include <stddef.h> - -#include <nghttp2/nghttp2.h> -#include "nghttp2_mem.h" - -#define nghttp2_min(A, B) ((A) < (B) ? (A) : (B)) -#define nghttp2_max(A, B) ((A) > (B) ? (A) : (B)) - -#define lstreq(A, B, N) ((sizeof((A)) - 1) == (N) && memcmp((A), (B), (N)) == 0) - -#define nghttp2_struct_of(ptr, type, member) \ - ((type *)(void *)((char *)(ptr)-offsetof(type, member))) - -/* - * Copies 2 byte unsigned integer |n| in host byte order to |buf| in - * network byte order. - */ -void nghttp2_put_uint16be(uint8_t *buf, uint16_t n); - -/* - * Copies 4 byte unsigned integer |n| in host byte order to |buf| in - * network byte order. - */ -void nghttp2_put_uint32be(uint8_t *buf, uint32_t n); - -/* - * Retrieves 2 byte unsigned integer stored in |data| in network byte - * order and returns it in host byte order. - */ -uint16_t nghttp2_get_uint16(const uint8_t *data); - -/* - * Retrieves 4 byte unsigned integer stored in |data| in network byte - * order and returns it in host byte order. - */ -uint32_t nghttp2_get_uint32(const uint8_t *data); - -void nghttp2_downcase(uint8_t *s, size_t len); - -/* - * Adjusts |*local_window_size_ptr|, |*recv_window_size_ptr|, - * |*recv_reduction_ptr| with |*delta_ptr| which is the - * WINDOW_UPDATE's window_size_increment sent from local side. If - * |delta| is strictly larger than |*recv_window_size_ptr|, - * |*local_window_size_ptr| is increased by delta - - * *recv_window_size_ptr. If |delta| is negative, - * |*local_window_size_ptr| is decreased by delta. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_FLOW_CONTROL - * local_window_size overflow or gets negative. - */ -int nghttp2_adjust_local_window_size(int32_t *local_window_size_ptr, - int32_t *recv_window_size_ptr, - int32_t *recv_reduction_ptr, - int32_t *delta_ptr); - -/* - * This function works like nghttp2_adjust_local_window_size(). The - * difference is that this function assumes *delta_ptr >= 0, and - * *recv_window_size_ptr is not decreased by *delta_ptr. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_FLOW_CONTROL - * local_window_size overflow or gets negative. - */ -int nghttp2_increase_local_window_size(int32_t *local_window_size_ptr, - int32_t *recv_window_size_ptr, - int32_t *recv_reduction_ptr, - int32_t *delta_ptr); - -/* - * Returns non-zero if the function decided that WINDOW_UPDATE should - * be sent. - */ -int nghttp2_should_send_window_update(int32_t local_window_size, - int32_t recv_window_size); - -/* - * Copies the buffer |src| of length |len| to the destination pointed - * by the |dest|, assuming that the |dest| is at lest |len| bytes long - * . Returns dest + len. - */ -uint8_t *nghttp2_cpymem(uint8_t *dest, const void *src, size_t len); - -#endif /* NGHTTP2_HELPER_H */ +#endif /* HAVE_CONFIG_H */ + +#include <string.h> +#include <stddef.h> + +#include <nghttp2/nghttp2.h> +#include "nghttp2_mem.h" + +#define nghttp2_min(A, B) ((A) < (B) ? (A) : (B)) +#define nghttp2_max(A, B) ((A) > (B) ? (A) : (B)) + +#define lstreq(A, B, N) ((sizeof((A)) - 1) == (N) && memcmp((A), (B), (N)) == 0) + +#define nghttp2_struct_of(ptr, type, member) \ + ((type *)(void *)((char *)(ptr)-offsetof(type, member))) + +/* + * Copies 2 byte unsigned integer |n| in host byte order to |buf| in + * network byte order. + */ +void nghttp2_put_uint16be(uint8_t *buf, uint16_t n); + +/* + * Copies 4 byte unsigned integer |n| in host byte order to |buf| in + * network byte order. + */ +void nghttp2_put_uint32be(uint8_t *buf, uint32_t n); + +/* + * Retrieves 2 byte unsigned integer stored in |data| in network byte + * order and returns it in host byte order. + */ +uint16_t nghttp2_get_uint16(const uint8_t *data); + +/* + * Retrieves 4 byte unsigned integer stored in |data| in network byte + * order and returns it in host byte order. + */ +uint32_t nghttp2_get_uint32(const uint8_t *data); + +void nghttp2_downcase(uint8_t *s, size_t len); + +/* + * Adjusts |*local_window_size_ptr|, |*recv_window_size_ptr|, + * |*recv_reduction_ptr| with |*delta_ptr| which is the + * WINDOW_UPDATE's window_size_increment sent from local side. If + * |delta| is strictly larger than |*recv_window_size_ptr|, + * |*local_window_size_ptr| is increased by delta - + * *recv_window_size_ptr. If |delta| is negative, + * |*local_window_size_ptr| is decreased by delta. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_FLOW_CONTROL + * local_window_size overflow or gets negative. + */ +int nghttp2_adjust_local_window_size(int32_t *local_window_size_ptr, + int32_t *recv_window_size_ptr, + int32_t *recv_reduction_ptr, + int32_t *delta_ptr); + +/* + * This function works like nghttp2_adjust_local_window_size(). The + * difference is that this function assumes *delta_ptr >= 0, and + * *recv_window_size_ptr is not decreased by *delta_ptr. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_FLOW_CONTROL + * local_window_size overflow or gets negative. + */ +int nghttp2_increase_local_window_size(int32_t *local_window_size_ptr, + int32_t *recv_window_size_ptr, + int32_t *recv_reduction_ptr, + int32_t *delta_ptr); + +/* + * Returns non-zero if the function decided that WINDOW_UPDATE should + * be sent. + */ +int nghttp2_should_send_window_update(int32_t local_window_size, + int32_t recv_window_size); + +/* + * Copies the buffer |src| of length |len| to the destination pointed + * by the |dest|, assuming that the |dest| is at lest |len| bytes long + * . Returns dest + len. + */ +uint8_t *nghttp2_cpymem(uint8_t *dest, const void *src, size_t len); + +#endif /* NGHTTP2_HELPER_H */ diff --git a/contrib/libs/nghttp2/lib/nghttp2_http.c b/contrib/libs/nghttp2/lib/nghttp2_http.c index 597671f29c..a2bcd2c0a1 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_http.c +++ b/contrib/libs/nghttp2/lib/nghttp2_http.c @@ -1,181 +1,181 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2015 Tatsuhiro Tsujikawa - * - * 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_http.h" - -#include <string.h> -#include <assert.h> -#include <stdio.h> - -#include "nghttp2_hd.h" -#include "nghttp2_helper.h" - -static uint8_t downcase(uint8_t c) { - return 'A' <= c && c <= 'Z' ? (uint8_t)(c - 'A' + 'a') : c; -} - -static int memieq(const void *a, const void *b, size_t n) { - size_t i; - const uint8_t *aa = a, *bb = b; - - for (i = 0; i < n; ++i) { - if (downcase(aa[i]) != downcase(bb[i])) { - return 0; - } - } - return 1; -} - -#define lstrieq(A, B, N) ((sizeof((A)) - 1) == (N) && memieq((A), (B), (N))) - -static int64_t parse_uint(const uint8_t *s, size_t len) { - int64_t n = 0; - size_t i; - if (len == 0) { - return -1; - } - for (i = 0; i < len; ++i) { - if ('0' <= s[i] && s[i] <= '9') { - if (n > INT64_MAX / 10) { - return -1; - } - n *= 10; - if (n > INT64_MAX - (s[i] - '0')) { - return -1; - } - n += s[i] - '0'; - continue; - } - return -1; - } - return n; -} - -static int lws(const uint8_t *s, size_t n) { - size_t i; - for (i = 0; i < n; ++i) { - if (s[i] != ' ' && s[i] != '\t') { - return 0; - } - } - return 1; -} - -static int check_pseudo_header(nghttp2_stream *stream, const nghttp2_hd_nv *nv, - int flag) { - if (stream->http_flags & flag) { - return 0; - } - if (lws(nv->value->base, nv->value->len)) { - return 0; - } - stream->http_flags = (uint16_t)(stream->http_flags | flag); - return 1; -} - -static int expect_response_body(nghttp2_stream *stream) { - return (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_HEAD) == 0 && - stream->status_code / 100 != 1 && stream->status_code != 304 && - stream->status_code != 204; -} - -/* For "http" or "https" URIs, OPTIONS request may have "*" in :path - header field to represent system-wide OPTIONS request. Otherwise, - :path header field value must start with "/". This function must - be called after ":method" header field was received. This function - returns nonzero if path is valid.*/ -static int check_path(nghttp2_stream *stream) { - return (stream->http_flags & NGHTTP2_HTTP_FLAG_SCHEME_HTTP) == 0 || - ((stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_REGULAR) || - ((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_OPTIONS) && - (stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_ASTERISK))); -} - -static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv, +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * 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_http.h" + +#include <string.h> +#include <assert.h> +#include <stdio.h> + +#include "nghttp2_hd.h" +#include "nghttp2_helper.h" + +static uint8_t downcase(uint8_t c) { + return 'A' <= c && c <= 'Z' ? (uint8_t)(c - 'A' + 'a') : c; +} + +static int memieq(const void *a, const void *b, size_t n) { + size_t i; + const uint8_t *aa = a, *bb = b; + + for (i = 0; i < n; ++i) { + if (downcase(aa[i]) != downcase(bb[i])) { + return 0; + } + } + return 1; +} + +#define lstrieq(A, B, N) ((sizeof((A)) - 1) == (N) && memieq((A), (B), (N))) + +static int64_t parse_uint(const uint8_t *s, size_t len) { + int64_t n = 0; + size_t i; + if (len == 0) { + return -1; + } + for (i = 0; i < len; ++i) { + if ('0' <= s[i] && s[i] <= '9') { + if (n > INT64_MAX / 10) { + return -1; + } + n *= 10; + if (n > INT64_MAX - (s[i] - '0')) { + return -1; + } + n += s[i] - '0'; + continue; + } + return -1; + } + return n; +} + +static int lws(const uint8_t *s, size_t n) { + size_t i; + for (i = 0; i < n; ++i) { + if (s[i] != ' ' && s[i] != '\t') { + return 0; + } + } + return 1; +} + +static int check_pseudo_header(nghttp2_stream *stream, const nghttp2_hd_nv *nv, + int flag) { + if (stream->http_flags & flag) { + return 0; + } + if (lws(nv->value->base, nv->value->len)) { + return 0; + } + stream->http_flags = (uint16_t)(stream->http_flags | flag); + return 1; +} + +static int expect_response_body(nghttp2_stream *stream) { + return (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_HEAD) == 0 && + stream->status_code / 100 != 1 && stream->status_code != 304 && + stream->status_code != 204; +} + +/* For "http" or "https" URIs, OPTIONS request may have "*" in :path + header field to represent system-wide OPTIONS request. Otherwise, + :path header field value must start with "/". This function must + be called after ":method" header field was received. This function + returns nonzero if path is valid.*/ +static int check_path(nghttp2_stream *stream) { + return (stream->http_flags & NGHTTP2_HTTP_FLAG_SCHEME_HTTP) == 0 || + ((stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_REGULAR) || + ((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_OPTIONS) && + (stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_ASTERISK))); +} + +static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv, int trailer, int connect_protocol) { - if (nv->name->base[0] == ':') { - if (trailer || - (stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) { - return NGHTTP2_ERR_HTTP_HEADER; - } - } - - switch (nv->token) { - case NGHTTP2_TOKEN__AUTHORITY: - if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__AUTHORITY)) { - return NGHTTP2_ERR_HTTP_HEADER; - } - break; - case NGHTTP2_TOKEN__METHOD: - if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__METHOD)) { - return NGHTTP2_ERR_HTTP_HEADER; - } - switch (nv->value->len) { - case 4: - if (lstreq("HEAD", nv->value->base, nv->value->len)) { - stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD; - } - break; - case 7: - switch (nv->value->base[6]) { - case 'T': - if (lstreq("CONNECT", nv->value->base, nv->value->len)) { - if (stream->stream_id % 2 == 0) { - /* we won't allow CONNECT for push */ - return NGHTTP2_ERR_HTTP_HEADER; - } - stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT; - } - break; - case 'S': - if (lstreq("OPTIONS", nv->value->base, nv->value->len)) { - stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_OPTIONS; - } - break; - } - break; - } - break; - case NGHTTP2_TOKEN__PATH: - if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PATH)) { - return NGHTTP2_ERR_HTTP_HEADER; - } - if (nv->value->base[0] == '/') { - stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_REGULAR; - } else if (nv->value->len == 1 && nv->value->base[0] == '*') { - stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_ASTERISK; - } - break; - case NGHTTP2_TOKEN__SCHEME: - if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__SCHEME)) { - return NGHTTP2_ERR_HTTP_HEADER; - } - if ((nv->value->len == 4 && memieq("http", nv->value->base, 4)) || - (nv->value->len == 5 && memieq("https", nv->value->base, 5))) { - stream->http_flags |= NGHTTP2_HTTP_FLAG_SCHEME_HTTP; - } - break; + if (nv->name->base[0] == ':') { + if (trailer || + (stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) { + return NGHTTP2_ERR_HTTP_HEADER; + } + } + + switch (nv->token) { + case NGHTTP2_TOKEN__AUTHORITY: + if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__AUTHORITY)) { + return NGHTTP2_ERR_HTTP_HEADER; + } + break; + case NGHTTP2_TOKEN__METHOD: + if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__METHOD)) { + return NGHTTP2_ERR_HTTP_HEADER; + } + switch (nv->value->len) { + case 4: + if (lstreq("HEAD", nv->value->base, nv->value->len)) { + stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD; + } + break; + case 7: + switch (nv->value->base[6]) { + case 'T': + if (lstreq("CONNECT", nv->value->base, nv->value->len)) { + if (stream->stream_id % 2 == 0) { + /* we won't allow CONNECT for push */ + return NGHTTP2_ERR_HTTP_HEADER; + } + stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT; + } + break; + case 'S': + if (lstreq("OPTIONS", nv->value->base, nv->value->len)) { + stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_OPTIONS; + } + break; + } + break; + } + break; + case NGHTTP2_TOKEN__PATH: + if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PATH)) { + return NGHTTP2_ERR_HTTP_HEADER; + } + if (nv->value->base[0] == '/') { + stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_REGULAR; + } else if (nv->value->len == 1 && nv->value->base[0] == '*') { + stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_ASTERISK; + } + break; + case NGHTTP2_TOKEN__SCHEME: + if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__SCHEME)) { + return NGHTTP2_ERR_HTTP_HEADER; + } + if ((nv->value->len == 4 && memieq("http", nv->value->base, 4)) || + (nv->value->len == 5 && memieq("https", nv->value->base, 5))) { + stream->http_flags |= NGHTTP2_HTTP_FLAG_SCHEME_HTTP; + } + break; case NGHTTP2_TOKEN__PROTOCOL: if (!connect_protocol) { return NGHTTP2_ERR_HTTP_HEADER; @@ -185,181 +185,181 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv, return NGHTTP2_ERR_HTTP_HEADER; } break; - case NGHTTP2_TOKEN_HOST: - if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG_HOST)) { - return NGHTTP2_ERR_HTTP_HEADER; - } - break; - case NGHTTP2_TOKEN_CONTENT_LENGTH: { - if (stream->content_length != -1) { - return NGHTTP2_ERR_HTTP_HEADER; - } - stream->content_length = parse_uint(nv->value->base, nv->value->len); - if (stream->content_length == -1) { - return NGHTTP2_ERR_HTTP_HEADER; - } - break; - } - /* disallowed header fields */ - case NGHTTP2_TOKEN_CONNECTION: - case NGHTTP2_TOKEN_KEEP_ALIVE: - case NGHTTP2_TOKEN_PROXY_CONNECTION: - case NGHTTP2_TOKEN_TRANSFER_ENCODING: - case NGHTTP2_TOKEN_UPGRADE: - return NGHTTP2_ERR_HTTP_HEADER; - case NGHTTP2_TOKEN_TE: - if (!lstrieq("trailers", nv->value->base, nv->value->len)) { - return NGHTTP2_ERR_HTTP_HEADER; - } - break; - default: - if (nv->name->base[0] == ':') { - return NGHTTP2_ERR_HTTP_HEADER; - } - } - - if (nv->name->base[0] != ':') { - stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED; - } - - return 0; -} - -static int http_response_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv, - int trailer) { - if (nv->name->base[0] == ':') { - if (trailer || - (stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) { - return NGHTTP2_ERR_HTTP_HEADER; - } - } - - switch (nv->token) { - case NGHTTP2_TOKEN__STATUS: { - if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__STATUS)) { - return NGHTTP2_ERR_HTTP_HEADER; - } - if (nv->value->len != 3) { - return NGHTTP2_ERR_HTTP_HEADER; - } - stream->status_code = (int16_t)parse_uint(nv->value->base, nv->value->len); - if (stream->status_code == -1 || stream->status_code == 101) { - return NGHTTP2_ERR_HTTP_HEADER; - } - break; - } - case NGHTTP2_TOKEN_CONTENT_LENGTH: { - if (stream->status_code == 204) { - /* content-length header field in 204 response is prohibited by - RFC 7230. But some widely used servers send content-length: - 0. Until they get fixed, we ignore it. */ - if (stream->content_length != -1) { - /* Found multiple content-length field */ - return NGHTTP2_ERR_HTTP_HEADER; - } - if (!lstrieq("0", nv->value->base, nv->value->len)) { - return NGHTTP2_ERR_HTTP_HEADER; - } - stream->content_length = 0; - return NGHTTP2_ERR_REMOVE_HTTP_HEADER; - } + case NGHTTP2_TOKEN_HOST: + if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG_HOST)) { + return NGHTTP2_ERR_HTTP_HEADER; + } + break; + case NGHTTP2_TOKEN_CONTENT_LENGTH: { + if (stream->content_length != -1) { + return NGHTTP2_ERR_HTTP_HEADER; + } + stream->content_length = parse_uint(nv->value->base, nv->value->len); + if (stream->content_length == -1) { + return NGHTTP2_ERR_HTTP_HEADER; + } + break; + } + /* disallowed header fields */ + case NGHTTP2_TOKEN_CONNECTION: + case NGHTTP2_TOKEN_KEEP_ALIVE: + case NGHTTP2_TOKEN_PROXY_CONNECTION: + case NGHTTP2_TOKEN_TRANSFER_ENCODING: + case NGHTTP2_TOKEN_UPGRADE: + return NGHTTP2_ERR_HTTP_HEADER; + case NGHTTP2_TOKEN_TE: + if (!lstrieq("trailers", nv->value->base, nv->value->len)) { + return NGHTTP2_ERR_HTTP_HEADER; + } + break; + default: + if (nv->name->base[0] == ':') { + return NGHTTP2_ERR_HTTP_HEADER; + } + } + + if (nv->name->base[0] != ':') { + stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED; + } + + return 0; +} + +static int http_response_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv, + int trailer) { + if (nv->name->base[0] == ':') { + if (trailer || + (stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) { + return NGHTTP2_ERR_HTTP_HEADER; + } + } + + switch (nv->token) { + case NGHTTP2_TOKEN__STATUS: { + if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__STATUS)) { + return NGHTTP2_ERR_HTTP_HEADER; + } + if (nv->value->len != 3) { + return NGHTTP2_ERR_HTTP_HEADER; + } + stream->status_code = (int16_t)parse_uint(nv->value->base, nv->value->len); + if (stream->status_code == -1 || stream->status_code == 101) { + return NGHTTP2_ERR_HTTP_HEADER; + } + break; + } + case NGHTTP2_TOKEN_CONTENT_LENGTH: { + if (stream->status_code == 204) { + /* content-length header field in 204 response is prohibited by + RFC 7230. But some widely used servers send content-length: + 0. Until they get fixed, we ignore it. */ + if (stream->content_length != -1) { + /* Found multiple content-length field */ + return NGHTTP2_ERR_HTTP_HEADER; + } + if (!lstrieq("0", nv->value->base, nv->value->len)) { + return NGHTTP2_ERR_HTTP_HEADER; + } + stream->content_length = 0; + return NGHTTP2_ERR_REMOVE_HTTP_HEADER; + } if (stream->status_code / 100 == 1) { - return NGHTTP2_ERR_HTTP_HEADER; - } + return NGHTTP2_ERR_HTTP_HEADER; + } /* https://tools.ietf.org/html/rfc7230#section-3.3.3 */ if (stream->status_code / 100 == 2 && (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT)) { return NGHTTP2_ERR_REMOVE_HTTP_HEADER; } - if (stream->content_length != -1) { - return NGHTTP2_ERR_HTTP_HEADER; - } - stream->content_length = parse_uint(nv->value->base, nv->value->len); - if (stream->content_length == -1) { - return NGHTTP2_ERR_HTTP_HEADER; - } - break; - } - /* disallowed header fields */ - case NGHTTP2_TOKEN_CONNECTION: - case NGHTTP2_TOKEN_KEEP_ALIVE: - case NGHTTP2_TOKEN_PROXY_CONNECTION: - case NGHTTP2_TOKEN_TRANSFER_ENCODING: - case NGHTTP2_TOKEN_UPGRADE: - return NGHTTP2_ERR_HTTP_HEADER; - case NGHTTP2_TOKEN_TE: - if (!lstrieq("trailers", nv->value->base, nv->value->len)) { - return NGHTTP2_ERR_HTTP_HEADER; - } - break; - default: - if (nv->name->base[0] == ':') { - return NGHTTP2_ERR_HTTP_HEADER; - } - } - - if (nv->name->base[0] != ':') { - stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED; - } - - return 0; -} - -static int check_scheme(const uint8_t *value, size_t len) { - const uint8_t *last; - if (len == 0) { - return 0; - } - - if (!(('A' <= *value && *value <= 'Z') || ('a' <= *value && *value <= 'z'))) { - return 0; - } - - last = value + len; - ++value; - - for (; value != last; ++value) { - if (!(('A' <= *value && *value <= 'Z') || - ('a' <= *value && *value <= 'z') || - ('0' <= *value && *value <= '9') || *value == '+' || *value == '-' || - *value == '.')) { - return 0; - } - } - return 1; -} - -int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream, - nghttp2_frame *frame, nghttp2_hd_nv *nv, - int trailer) { - int rv; - - /* We are strict for pseudo header field. One bad character should - lead to fail. OTOH, we should be a bit forgiving for regular - headers, since existing public internet has so much illegal - headers floating around and if we kill the stream because of - this, we may disrupt many web sites and/or libraries. So we - become conservative here, and just ignore those illegal regular - headers. */ - if (!nghttp2_check_header_name(nv->name->base, nv->name->len)) { - size_t i; - if (nv->name->len > 0 && nv->name->base[0] == ':') { - return NGHTTP2_ERR_HTTP_HEADER; - } - /* header field name must be lower-cased without exception */ - for (i = 0; i < nv->name->len; ++i) { - uint8_t c = nv->name->base[i]; - if ('A' <= c && c <= 'Z') { - return NGHTTP2_ERR_HTTP_HEADER; - } - } - /* When ignoring regular headers, we set this flag so that we - still enforce header field ordering rule for pseudo header - fields. */ - stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED; - return NGHTTP2_ERR_IGN_HTTP_HEADER; - } - + if (stream->content_length != -1) { + return NGHTTP2_ERR_HTTP_HEADER; + } + stream->content_length = parse_uint(nv->value->base, nv->value->len); + if (stream->content_length == -1) { + return NGHTTP2_ERR_HTTP_HEADER; + } + break; + } + /* disallowed header fields */ + case NGHTTP2_TOKEN_CONNECTION: + case NGHTTP2_TOKEN_KEEP_ALIVE: + case NGHTTP2_TOKEN_PROXY_CONNECTION: + case NGHTTP2_TOKEN_TRANSFER_ENCODING: + case NGHTTP2_TOKEN_UPGRADE: + return NGHTTP2_ERR_HTTP_HEADER; + case NGHTTP2_TOKEN_TE: + if (!lstrieq("trailers", nv->value->base, nv->value->len)) { + return NGHTTP2_ERR_HTTP_HEADER; + } + break; + default: + if (nv->name->base[0] == ':') { + return NGHTTP2_ERR_HTTP_HEADER; + } + } + + if (nv->name->base[0] != ':') { + stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED; + } + + return 0; +} + +static int check_scheme(const uint8_t *value, size_t len) { + const uint8_t *last; + if (len == 0) { + return 0; + } + + if (!(('A' <= *value && *value <= 'Z') || ('a' <= *value && *value <= 'z'))) { + return 0; + } + + last = value + len; + ++value; + + for (; value != last; ++value) { + if (!(('A' <= *value && *value <= 'Z') || + ('a' <= *value && *value <= 'z') || + ('0' <= *value && *value <= '9') || *value == '+' || *value == '-' || + *value == '.')) { + return 0; + } + } + return 1; +} + +int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream, + nghttp2_frame *frame, nghttp2_hd_nv *nv, + int trailer) { + int rv; + + /* We are strict for pseudo header field. One bad character should + lead to fail. OTOH, we should be a bit forgiving for regular + headers, since existing public internet has so much illegal + headers floating around and if we kill the stream because of + this, we may disrupt many web sites and/or libraries. So we + become conservative here, and just ignore those illegal regular + headers. */ + if (!nghttp2_check_header_name(nv->name->base, nv->name->len)) { + size_t i; + if (nv->name->len > 0 && nv->name->base[0] == ':') { + return NGHTTP2_ERR_HTTP_HEADER; + } + /* header field name must be lower-cased without exception */ + for (i = 0; i < nv->name->len; ++i) { + uint8_t c = nv->name->base[i]; + if ('A' <= c && c <= 'Z') { + return NGHTTP2_ERR_HTTP_HEADER; + } + } + /* When ignoring regular headers, we set this flag so that we + still enforce header field ordering rule for pseudo header + fields. */ + stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED; + return NGHTTP2_ERR_IGN_HTTP_HEADER; + } + switch (nv->token) { case NGHTTP2_TOKEN__METHOD: rv = nghttp2_check_method(nv->value->base, nv->value->len); @@ -372,168 +372,168 @@ int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream, rv = nghttp2_check_authority(nv->value->base, nv->value->len); break; case NGHTTP2_TOKEN__SCHEME: - rv = check_scheme(nv->value->base, nv->value->len); + rv = check_scheme(nv->value->base, nv->value->len); break; default: - rv = nghttp2_check_header_value(nv->value->base, nv->value->len); - } - - if (rv == 0) { - assert(nv->name->len > 0); - if (nv->name->base[0] == ':') { - return NGHTTP2_ERR_HTTP_HEADER; - } - /* When ignoring regular headers, we set this flag so that we - still enforce header field ordering rule for pseudo header - fields. */ - stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED; - return NGHTTP2_ERR_IGN_HTTP_HEADER; - } - - if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) { + rv = nghttp2_check_header_value(nv->value->base, nv->value->len); + } + + if (rv == 0) { + assert(nv->name->len > 0); + if (nv->name->base[0] == ':') { + return NGHTTP2_ERR_HTTP_HEADER; + } + /* When ignoring regular headers, we set this flag so that we + still enforce header field ordering rule for pseudo header + fields. */ + stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED; + return NGHTTP2_ERR_IGN_HTTP_HEADER; + } + + if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) { return http_request_on_header(stream, nv, trailer, session->server && session->pending_enable_connect_protocol); - } - - return http_response_on_header(stream, nv, trailer); -} - -int nghttp2_http_on_request_headers(nghttp2_stream *stream, - nghttp2_frame *frame) { + } + + return http_response_on_header(stream, nv, trailer); +} + +int nghttp2_http_on_request_headers(nghttp2_stream *stream, + nghttp2_frame *frame) { if (!(stream->http_flags & NGHTTP2_HTTP_FLAG__PROTOCOL) && (stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT)) { if ((stream->http_flags & (NGHTTP2_HTTP_FLAG__SCHEME | NGHTTP2_HTTP_FLAG__PATH)) || (stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0) { - return -1; - } - stream->content_length = -1; - } else { - if ((stream->http_flags & NGHTTP2_HTTP_FLAG_REQ_HEADERS) != - NGHTTP2_HTTP_FLAG_REQ_HEADERS || - (stream->http_flags & - (NGHTTP2_HTTP_FLAG__AUTHORITY | NGHTTP2_HTTP_FLAG_HOST)) == 0) { - return -1; - } + return -1; + } + stream->content_length = -1; + } else { + if ((stream->http_flags & NGHTTP2_HTTP_FLAG_REQ_HEADERS) != + NGHTTP2_HTTP_FLAG_REQ_HEADERS || + (stream->http_flags & + (NGHTTP2_HTTP_FLAG__AUTHORITY | NGHTTP2_HTTP_FLAG_HOST)) == 0) { + return -1; + } if ((stream->http_flags & NGHTTP2_HTTP_FLAG__PROTOCOL) && ((stream->http_flags & NGHTTP2_HTTP_FLAG_METH_CONNECT) == 0 || (stream->http_flags & NGHTTP2_HTTP_FLAG__AUTHORITY) == 0)) { return -1; } - if (!check_path(stream)) { - return -1; - } - } - - if (frame->hd.type == NGHTTP2_PUSH_PROMISE) { - /* we are going to reuse data fields for upcoming response. Clear - them now, except for method flags. */ - stream->http_flags &= NGHTTP2_HTTP_FLAG_METH_ALL; - stream->content_length = -1; - } - - return 0; -} - -int nghttp2_http_on_response_headers(nghttp2_stream *stream) { - if ((stream->http_flags & NGHTTP2_HTTP_FLAG__STATUS) == 0) { - return -1; - } - - 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->content_length = -1; - stream->status_code = -1; - return 0; - } - - stream->http_flags = - (uint16_t)(stream->http_flags & ~NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE); - - if (!expect_response_body(stream)) { - stream->content_length = 0; - } else if (stream->http_flags & (NGHTTP2_HTTP_FLAG_METH_CONNECT | - NGHTTP2_HTTP_FLAG_METH_UPGRADE_WORKAROUND)) { - stream->content_length = -1; - } - - return 0; -} - -int nghttp2_http_on_trailer_headers(nghttp2_stream *stream, - nghttp2_frame *frame) { - (void)stream; - - if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) { - return -1; - } - - return 0; -} - -int nghttp2_http_on_remote_end_stream(nghttp2_stream *stream) { - if (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) { - return -1; - } - - if (stream->content_length != -1 && - stream->content_length != stream->recv_content_length) { - return -1; - } - - return 0; -} - -int nghttp2_http_on_data_chunk(nghttp2_stream *stream, size_t n) { - stream->recv_content_length += (int64_t)n; - - if ((stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) || - (stream->content_length != -1 && - stream->recv_content_length > stream->content_length)) { - return -1; - } - - return 0; -} - -void nghttp2_http_record_request_method(nghttp2_stream *stream, - nghttp2_frame *frame) { - const nghttp2_nv *nva; - size_t nvlen; - size_t i; - - switch (frame->hd.type) { - case NGHTTP2_HEADERS: - nva = frame->headers.nva; - nvlen = frame->headers.nvlen; - break; - case NGHTTP2_PUSH_PROMISE: - nva = frame->push_promise.nva; - nvlen = frame->push_promise.nvlen; - break; - default: - return; - } - - /* TODO we should do this strictly. */ - for (i = 0; i < nvlen; ++i) { - const nghttp2_nv *nv = &nva[i]; - if (!(nv->namelen == 7 && nv->name[6] == 'd' && - memcmp(":metho", nv->name, nv->namelen - 1) == 0)) { - continue; - } - if (lstreq("CONNECT", nv->value, nv->valuelen)) { - stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT; - return; - } - if (lstreq("HEAD", nv->value, nv->valuelen)) { - stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD; - return; - } - return; - } -} + if (!check_path(stream)) { + return -1; + } + } + + if (frame->hd.type == NGHTTP2_PUSH_PROMISE) { + /* we are going to reuse data fields for upcoming response. Clear + them now, except for method flags. */ + stream->http_flags &= NGHTTP2_HTTP_FLAG_METH_ALL; + stream->content_length = -1; + } + + return 0; +} + +int nghttp2_http_on_response_headers(nghttp2_stream *stream) { + if ((stream->http_flags & NGHTTP2_HTTP_FLAG__STATUS) == 0) { + return -1; + } + + 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->content_length = -1; + stream->status_code = -1; + return 0; + } + + stream->http_flags = + (uint16_t)(stream->http_flags & ~NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE); + + if (!expect_response_body(stream)) { + stream->content_length = 0; + } else if (stream->http_flags & (NGHTTP2_HTTP_FLAG_METH_CONNECT | + NGHTTP2_HTTP_FLAG_METH_UPGRADE_WORKAROUND)) { + stream->content_length = -1; + } + + return 0; +} + +int nghttp2_http_on_trailer_headers(nghttp2_stream *stream, + nghttp2_frame *frame) { + (void)stream; + + if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) { + return -1; + } + + return 0; +} + +int nghttp2_http_on_remote_end_stream(nghttp2_stream *stream) { + if (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) { + return -1; + } + + if (stream->content_length != -1 && + stream->content_length != stream->recv_content_length) { + return -1; + } + + return 0; +} + +int nghttp2_http_on_data_chunk(nghttp2_stream *stream, size_t n) { + stream->recv_content_length += (int64_t)n; + + if ((stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) || + (stream->content_length != -1 && + stream->recv_content_length > stream->content_length)) { + return -1; + } + + return 0; +} + +void nghttp2_http_record_request_method(nghttp2_stream *stream, + nghttp2_frame *frame) { + const nghttp2_nv *nva; + size_t nvlen; + size_t i; + + switch (frame->hd.type) { + case NGHTTP2_HEADERS: + nva = frame->headers.nva; + nvlen = frame->headers.nvlen; + break; + case NGHTTP2_PUSH_PROMISE: + nva = frame->push_promise.nva; + nvlen = frame->push_promise.nvlen; + break; + default: + return; + } + + /* TODO we should do this strictly. */ + for (i = 0; i < nvlen; ++i) { + const nghttp2_nv *nv = &nva[i]; + if (!(nv->namelen == 7 && nv->name[6] == 'd' && + memcmp(":metho", nv->name, nv->namelen - 1) == 0)) { + continue; + } + if (lstreq("CONNECT", nv->value, nv->valuelen)) { + stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_CONNECT; + return; + } + if (lstreq("HEAD", nv->value, nv->valuelen)) { + stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD; + return; + } + return; + } +} diff --git a/contrib/libs/nghttp2/lib/nghttp2_http.h b/contrib/libs/nghttp2/lib/nghttp2_http.h index b5374f03c5..dd057cdb60 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_http.h +++ b/contrib/libs/nghttp2/lib/nghttp2_http.h @@ -1,97 +1,97 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2015 Tatsuhiro Tsujikawa - * - * 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_HTTP_H -#define NGHTTP2_HTTP_H - -#ifdef HAVE_CONFIG_H +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2015 Tatsuhiro Tsujikawa + * + * 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_HTTP_H +#define NGHTTP2_HTTP_H + +#ifdef HAVE_CONFIG_H # include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <nghttp2/nghttp2.h> -#include "nghttp2_session.h" -#include "nghttp2_stream.h" - -/* - * This function is called when HTTP header field |nv| in |frame| is - * received for |stream|. This function will validate |nv| against - * the current state of stream. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_HTTP_HEADER - * Invalid HTTP header field was received. - * NGHTTP2_ERR_IGN_HTTP_HEADER - * Invalid HTTP header field was received but it can be treated as - * if it was not received because of compatibility reasons. - */ -int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream, - nghttp2_frame *frame, nghttp2_hd_nv *nv, - int trailer); - -/* - * This function is called when request header is received. This - * function performs validation and returns 0 if it succeeds, or -1. - */ -int nghttp2_http_on_request_headers(nghttp2_stream *stream, - nghttp2_frame *frame); - -/* - * This function is called when response header is received. This - * function performs validation and returns 0 if it succeeds, or -1. - */ -int nghttp2_http_on_response_headers(nghttp2_stream *stream); - -/* - * This function is called trailer header (for both request and - * response) is received. This function performs validation and - * returns 0 if it succeeds, or -1. - */ -int nghttp2_http_on_trailer_headers(nghttp2_stream *stream, - nghttp2_frame *frame); - -/* - * This function is called when END_STREAM flag is seen in incoming - * frame. This function performs validation and returns 0 if it - * succeeds, or -1. - */ -int nghttp2_http_on_remote_end_stream(nghttp2_stream *stream); - -/* - * This function is called when chunk of data is received. This - * function performs validation and returns 0 if it succeeds, or -1. - */ -int nghttp2_http_on_data_chunk(nghttp2_stream *stream, size_t n); - -/* - * This function inspects header field in |frame| and records its - * method in stream->http_flags. If frame->hd.type is neither - * NGHTTP2_HEADERS nor NGHTTP2_PUSH_PROMISE, this function does - * nothing. - */ -void nghttp2_http_record_request_method(nghttp2_stream *stream, - nghttp2_frame *frame); - -#endif /* NGHTTP2_HTTP_H */ +#endif /* HAVE_CONFIG_H */ + +#include <nghttp2/nghttp2.h> +#include "nghttp2_session.h" +#include "nghttp2_stream.h" + +/* + * This function is called when HTTP header field |nv| in |frame| is + * received for |stream|. This function will validate |nv| against + * the current state of stream. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_HTTP_HEADER + * Invalid HTTP header field was received. + * NGHTTP2_ERR_IGN_HTTP_HEADER + * Invalid HTTP header field was received but it can be treated as + * if it was not received because of compatibility reasons. + */ +int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream, + nghttp2_frame *frame, nghttp2_hd_nv *nv, + int trailer); + +/* + * This function is called when request header is received. This + * function performs validation and returns 0 if it succeeds, or -1. + */ +int nghttp2_http_on_request_headers(nghttp2_stream *stream, + nghttp2_frame *frame); + +/* + * This function is called when response header is received. This + * function performs validation and returns 0 if it succeeds, or -1. + */ +int nghttp2_http_on_response_headers(nghttp2_stream *stream); + +/* + * This function is called trailer header (for both request and + * response) is received. This function performs validation and + * returns 0 if it succeeds, or -1. + */ +int nghttp2_http_on_trailer_headers(nghttp2_stream *stream, + nghttp2_frame *frame); + +/* + * This function is called when END_STREAM flag is seen in incoming + * frame. This function performs validation and returns 0 if it + * succeeds, or -1. + */ +int nghttp2_http_on_remote_end_stream(nghttp2_stream *stream); + +/* + * This function is called when chunk of data is received. This + * function performs validation and returns 0 if it succeeds, or -1. + */ +int nghttp2_http_on_data_chunk(nghttp2_stream *stream, size_t n); + +/* + * This function inspects header field in |frame| and records its + * method in stream->http_flags. If frame->hd.type is neither + * NGHTTP2_HEADERS nor NGHTTP2_PUSH_PROMISE, this function does + * nothing. + */ +void nghttp2_http_record_request_method(nghttp2_stream *stream, + nghttp2_frame *frame); + +#endif /* NGHTTP2_HTTP_H */ diff --git a/contrib/libs/nghttp2/lib/nghttp2_int.h b/contrib/libs/nghttp2/lib/nghttp2_int.h index ee195d1827..b23585ccb2 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_int.h +++ b/contrib/libs/nghttp2/lib/nghttp2_int.h @@ -1,58 +1,58 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * 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_INT_H -#define NGHTTP2_INT_H - -#ifdef HAVE_CONFIG_H +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * 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_INT_H +#define NGHTTP2_INT_H + +#ifdef HAVE_CONFIG_H # include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <nghttp2/nghttp2.h> - -/* Macros, types and constants for internal use */ - -/* "less" function, return nonzero if |lhs| is less than |rhs|. */ -typedef int (*nghttp2_less)(const void *lhs, const void *rhs); - -/* Internal error code. They must be in the range [-499, -100], - inclusive. */ -typedef enum { - NGHTTP2_ERR_CREDENTIAL_PENDING = -101, - NGHTTP2_ERR_IGN_HEADER_BLOCK = -103, - NGHTTP2_ERR_IGN_PAYLOAD = -104, - /* - * Invalid HTTP header field was received but it can be treated as - * if it was not received because of compatibility reasons. - */ - NGHTTP2_ERR_IGN_HTTP_HEADER = -105, - /* - * Invalid HTTP header field was received, and it is ignored. - * Unlike NGHTTP2_ERR_IGN_HTTP_HEADER, this does not invoke - * nghttp2_on_invalid_header_callback. - */ - NGHTTP2_ERR_REMOVE_HTTP_HEADER = -106 -} nghttp2_internal_error; - -#endif /* NGHTTP2_INT_H */ +#endif /* HAVE_CONFIG_H */ + +#include <nghttp2/nghttp2.h> + +/* Macros, types and constants for internal use */ + +/* "less" function, return nonzero if |lhs| is less than |rhs|. */ +typedef int (*nghttp2_less)(const void *lhs, const void *rhs); + +/* Internal error code. They must be in the range [-499, -100], + inclusive. */ +typedef enum { + NGHTTP2_ERR_CREDENTIAL_PENDING = -101, + NGHTTP2_ERR_IGN_HEADER_BLOCK = -103, + NGHTTP2_ERR_IGN_PAYLOAD = -104, + /* + * Invalid HTTP header field was received but it can be treated as + * if it was not received because of compatibility reasons. + */ + NGHTTP2_ERR_IGN_HTTP_HEADER = -105, + /* + * Invalid HTTP header field was received, and it is ignored. + * Unlike NGHTTP2_ERR_IGN_HTTP_HEADER, this does not invoke + * nghttp2_on_invalid_header_callback. + */ + NGHTTP2_ERR_REMOVE_HTTP_HEADER = -106 +} nghttp2_internal_error; + +#endif /* NGHTTP2_INT_H */ diff --git a/contrib/libs/nghttp2/lib/nghttp2_map.c b/contrib/libs/nghttp2/lib/nghttp2_map.c index 51d2bdb858..e5db168ca2 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_map.c +++ b/contrib/libs/nghttp2/lib/nghttp2_map.c @@ -1,107 +1,107 @@ -/* - * nghttp2 - HTTP/2 C Library - * +/* + * nghttp2 - HTTP/2 C Library + * * Copyright (c) 2017 ngtcp2 contributors * Copyright (c) 2012 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_map.h" - -#include <string.h> + * + * 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_map.h" + +#include <string.h> #include <assert.h> #include <stdio.h> - + #include "nghttp2_helper.h" - + #define NGHTTP2_INITIAL_TABLE_LENBITS 8 -int nghttp2_map_init(nghttp2_map *map, nghttp2_mem *mem) { - map->mem = mem; +int nghttp2_map_init(nghttp2_map *map, nghttp2_mem *mem) { + map->mem = mem; map->tablelen = 1 << NGHTTP2_INITIAL_TABLE_LENBITS; map->tablelenbits = NGHTTP2_INITIAL_TABLE_LENBITS; - map->table = + map->table = nghttp2_mem_calloc(mem, map->tablelen, sizeof(nghttp2_map_bucket)); - if (map->table == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - map->size = 0; - - return 0; -} - -void nghttp2_map_free(nghttp2_map *map) { + if (map->table == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + map->size = 0; + + return 0; +} + +void nghttp2_map_free(nghttp2_map *map) { if (!map) { return; } - nghttp2_mem_free(map->mem, map->table); -} - + nghttp2_mem_free(map->mem, map->table); +} + void nghttp2_map_each_free(nghttp2_map *map, int (*func)(void *data, void *ptr), - void *ptr) { - uint32_t i; + void *ptr) { + uint32_t i; nghttp2_map_bucket *bkt; - for (i = 0; i < map->tablelen; ++i) { + for (i = 0; i < map->tablelen; ++i) { bkt = &map->table[i]; if (bkt->data == NULL) { continue; - } + } func(bkt->data, ptr); - } -} - + } +} + int nghttp2_map_each(nghttp2_map *map, int (*func)(void *data, void *ptr), - void *ptr) { - int rv; - uint32_t i; + void *ptr) { + int rv; + uint32_t i; nghttp2_map_bucket *bkt; - for (i = 0; i < map->tablelen; ++i) { + for (i = 0; i < map->tablelen; ++i) { bkt = &map->table[i]; if (bkt->data == NULL) { continue; - } + } rv = func(bkt->data, ptr); if (rv != 0) { return rv; } - } + } + + return 0; +} - return 0; -} - static uint32_t hash(nghttp2_map_key_type key) { return (uint32_t)key * 2654435769u; -} - +} + static size_t h2idx(uint32_t hash, uint32_t bits) { return hash >> (32 - bits); -} - +} + static size_t distance(uint32_t tablelen, uint32_t tablelenbits, nghttp2_map_bucket *bkt, size_t idx) { return (idx - h2idx(bkt->hash, tablelenbits)) & (tablelen - 1); @@ -140,15 +140,15 @@ void nghttp2_map_print_distance(nghttp2_map *map) { if (bkt->data == NULL) { fprintf(stderr, "@%u <EMPTY>\n", i); continue; - } + } idx = h2idx(bkt->hash, map->tablelenbits); fprintf(stderr, "@%u hash=%08x key=%d base=%zu distance=%zu\n", i, bkt->hash, bkt->key, idx, distance(map->tablelen, map->tablelenbits, bkt, idx)); - } -} - + } +} + static int insert(nghttp2_map_bucket *table, uint32_t tablelen, uint32_t tablelenbits, uint32_t hash, nghttp2_map_key_type key, void *data) { @@ -185,59 +185,59 @@ static int insert(nghttp2_map_bucket *table, uint32_t tablelen, new_tablelenbits) must hold. */ static int map_resize(nghttp2_map *map, uint32_t new_tablelen, uint32_t new_tablelenbits) { - uint32_t i; + uint32_t i; nghttp2_map_bucket *new_table; nghttp2_map_bucket *bkt; int rv; (void)rv; - - new_table = + + new_table = nghttp2_mem_calloc(map->mem, new_tablelen, sizeof(nghttp2_map_bucket)); - if (new_table == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - for (i = 0; i < map->tablelen; ++i) { + if (new_table == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + for (i = 0; i < map->tablelen; ++i) { bkt = &map->table[i]; if (bkt->data == NULL) { continue; - } + } rv = insert(new_table, new_tablelen, new_tablelenbits, bkt->hash, bkt->key, bkt->data); assert(0 == rv); - } + } - nghttp2_mem_free(map->mem, map->table); - map->tablelen = new_tablelen; + nghttp2_mem_free(map->mem, map->table); + map->tablelen = new_tablelen; map->tablelenbits = new_tablelenbits; - map->table = new_table; - - return 0; -} - + map->table = new_table; + + return 0; +} + int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_key_type key, void *data) { - int rv; + int rv; assert(data); - /* Load factor is 0.75 */ - if ((map->size + 1) * 4 > map->tablelen * 3) { + /* Load factor is 0.75 */ + if ((map->size + 1) * 4 > map->tablelen * 3) { rv = map_resize(map, map->tablelen * 2, map->tablelenbits + 1); - if (rv != 0) { - return rv; - } - } + if (rv != 0) { + return rv; + } + } rv = insert(map->table, map->tablelen, map->tablelenbits, hash(key), key, data); - if (rv != 0) { - return rv; - } - ++map->size; - return 0; -} - + if (rv != 0) { + return rv; + } + ++map->size; + return 0; +} + void *nghttp2_map_find(nghttp2_map *map, nghttp2_map_key_type key) { uint32_t h = hash(key); size_t idx = h2idx(h, map->tablelenbits); @@ -250,7 +250,7 @@ void *nghttp2_map_find(nghttp2_map *map, nghttp2_map_key_type key) { if (bkt->data == NULL || d > distance(map->tablelen, map->tablelenbits, bkt, idx)) { return NULL; - } + } if (bkt->key == key) { return bkt->data; @@ -258,23 +258,23 @@ void *nghttp2_map_find(nghttp2_map *map, nghttp2_map_key_type key) { ++d; idx = (idx + 1) & (map->tablelen - 1); - } -} - + } +} + int nghttp2_map_remove(nghttp2_map *map, nghttp2_map_key_type key) { uint32_t h = hash(key); size_t idx = h2idx(h, map->tablelenbits), didx; nghttp2_map_bucket *bkt; size_t d = 0; - + for (;;) { bkt = &map->table[idx]; - + if (bkt->data == NULL || d > distance(map->tablelen, map->tablelenbits, bkt, idx)) { return NGHTTP2_ERR_INVALID_ARGUMENT; - } - + } + if (bkt->key == key) { map_bucket_set_data(bkt, 0, 0, NULL); @@ -302,12 +302,12 @@ int nghttp2_map_remove(nghttp2_map *map, nghttp2_map_key_type key) { ++d; idx = (idx + 1) & (map->tablelen - 1); - } -} - + } +} + void nghttp2_map_clear(nghttp2_map *map) { memset(map->table, 0, sizeof(*map->table) * map->tablelen); map->size = 0; } -size_t nghttp2_map_size(nghttp2_map *map) { return map->size; } +size_t nghttp2_map_size(nghttp2_map *map) { return map->size; } diff --git a/contrib/libs/nghttp2/lib/nghttp2_map.h b/contrib/libs/nghttp2/lib/nghttp2_map.h index e550188e3e..1419a09a35 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_map.h +++ b/contrib/libs/nghttp2/lib/nghttp2_map.h @@ -1,142 +1,142 @@ -/* - * nghttp2 - HTTP/2 C Library - * +/* + * nghttp2 - HTTP/2 C Library + * * Copyright (c) 2017 ngtcp2 contributors * Copyright (c) 2012 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_MAP_H -#define NGHTTP2_MAP_H - -#ifdef HAVE_CONFIG_H + * + * 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_MAP_H +#define NGHTTP2_MAP_H + +#ifdef HAVE_CONFIG_H # include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <nghttp2/nghttp2.h> - -#include "nghttp2_mem.h" - -/* Implementation of unordered map */ - +#endif /* HAVE_CONFIG_H */ + +#include <nghttp2/nghttp2.h> + +#include "nghttp2_mem.h" + +/* Implementation of unordered map */ + typedef int32_t nghttp2_map_key_type; - + typedef struct nghttp2_map_bucket { uint32_t hash; nghttp2_map_key_type key; void *data; } nghttp2_map_bucket; - + typedef struct nghttp2_map { nghttp2_map_bucket *table; - nghttp2_mem *mem; - size_t size; - uint32_t tablelen; + nghttp2_mem *mem; + size_t size; + uint32_t tablelen; uint32_t tablelenbits; -} nghttp2_map; - -/* - * Initializes the map |map|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory - */ -int nghttp2_map_init(nghttp2_map *map, nghttp2_mem *mem); - -/* - * Deallocates any resources allocated for |map|. The stored entries - * are not freed by this function. Use nghttp2_map_each_free() to free - * each entries. - */ -void nghttp2_map_free(nghttp2_map *map); - -/* - * Deallocates each entries using |func| function and any resources - * allocated for |map|. The |func| function is responsible for freeing +} nghttp2_map; + +/* + * Initializes the map |map|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_map_init(nghttp2_map *map, nghttp2_mem *mem); + +/* + * Deallocates any resources allocated for |map|. The stored entries + * are not freed by this function. Use nghttp2_map_each_free() to free + * each entries. + */ +void nghttp2_map_free(nghttp2_map *map); + +/* + * Deallocates each entries using |func| function and any resources + * allocated for |map|. The |func| function is responsible for freeing * given the |data| object. The |ptr| will be passed to the |func| as - * send argument. The return value of the |func| will be ignored. - */ + * send argument. The return value of the |func| will be ignored. + */ void nghttp2_map_each_free(nghttp2_map *map, int (*func)(void *data, void *ptr), - void *ptr); - -/* + void *ptr); + +/* * Inserts the new |data| with the |key| to the map |map|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_INVALID_ARGUMENT - * The item associated by |key| already exists. - * NGHTTP2_ERR_NOMEM - * Out of memory - */ + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_INVALID_ARGUMENT + * The item associated by |key| already exists. + * NGHTTP2_ERR_NOMEM + * Out of memory + */ int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_key_type key, void *data); - -/* + +/* * Returns the data associated by the key |key|. If there is no such * data, this function returns NULL. - */ + */ void *nghttp2_map_find(nghttp2_map *map, nghttp2_map_key_type key); - -/* + +/* * Removes the data associated by the key |key| from the |map|. The * removed data is not freed by this function. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_INVALID_ARGUMENT + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_INVALID_ARGUMENT * The data associated by |key| does not exist. - */ + */ int nghttp2_map_remove(nghttp2_map *map, nghttp2_map_key_type key); - -/* + +/* * Removes all entries from |map|. */ void nghttp2_map_clear(nghttp2_map *map); /* - * Returns the number of items stored in the map |map|. - */ -size_t nghttp2_map_size(nghttp2_map *map); - -/* + * Returns the number of items stored in the map |map|. + */ +size_t nghttp2_map_size(nghttp2_map *map); + +/* * Applies the function |func| to each data in the |map| with the - * optional user supplied pointer |ptr|. - * - * If the |func| returns 0, this function calls the |func| with the + * optional user supplied pointer |ptr|. + * + * If the |func| returns 0, this function calls the |func| with the * next data. If the |func| returns nonzero, it will not call the - * |func| for further entries and return the return value of the - * |func| immediately. Thus, this function returns 0 if all the - * invocations of the |func| return 0, or nonzero value which the last - * invocation of |func| returns. - * + * |func| for further entries and return the return value of the + * |func| immediately. Thus, this function returns 0 if all the + * invocations of the |func| return 0, or nonzero value which the last + * invocation of |func| returns. + * * Don't use this function to free each data. Use - * nghttp2_map_each_free() instead. - */ + * nghttp2_map_each_free() instead. + */ int nghttp2_map_each(nghttp2_map *map, int (*func)(void *data, void *ptr), - void *ptr); - + void *ptr); + void nghttp2_map_print_distance(nghttp2_map *map); -#endif /* NGHTTP2_MAP_H */ +#endif /* NGHTTP2_MAP_H */ diff --git a/contrib/libs/nghttp2/lib/nghttp2_mem.c b/contrib/libs/nghttp2/lib/nghttp2_mem.c index ef5b3e859d..6a449cffd7 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_mem.c +++ b/contrib/libs/nghttp2/lib/nghttp2_mem.c @@ -1,74 +1,74 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * 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_mem.h" - -static void *default_malloc(size_t size, void *mem_user_data) { - (void)mem_user_data; - - return malloc(size); -} - -static void default_free(void *ptr, void *mem_user_data) { - (void)mem_user_data; - - free(ptr); -} - -static void *default_calloc(size_t nmemb, size_t size, void *mem_user_data) { - (void)mem_user_data; - - return calloc(nmemb, size); -} - -static void *default_realloc(void *ptr, size_t size, void *mem_user_data) { - (void)mem_user_data; - - return realloc(ptr, size); -} - -static nghttp2_mem mem_default = {NULL, default_malloc, default_free, - default_calloc, default_realloc}; - -nghttp2_mem *nghttp2_mem_default(void) { return &mem_default; } - -void *nghttp2_mem_malloc(nghttp2_mem *mem, size_t size) { - return mem->malloc(size, mem->mem_user_data); -} - -void nghttp2_mem_free(nghttp2_mem *mem, void *ptr) { - mem->free(ptr, mem->mem_user_data); -} - -void nghttp2_mem_free2(nghttp2_free free_func, void *ptr, void *mem_user_data) { - free_func(ptr, mem_user_data); -} - -void *nghttp2_mem_calloc(nghttp2_mem *mem, size_t nmemb, size_t size) { - return mem->calloc(nmemb, size, mem->mem_user_data); -} - -void *nghttp2_mem_realloc(nghttp2_mem *mem, void *ptr, size_t size) { - return mem->realloc(ptr, size, mem->mem_user_data); -} +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * 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_mem.h" + +static void *default_malloc(size_t size, void *mem_user_data) { + (void)mem_user_data; + + return malloc(size); +} + +static void default_free(void *ptr, void *mem_user_data) { + (void)mem_user_data; + + free(ptr); +} + +static void *default_calloc(size_t nmemb, size_t size, void *mem_user_data) { + (void)mem_user_data; + + return calloc(nmemb, size); +} + +static void *default_realloc(void *ptr, size_t size, void *mem_user_data) { + (void)mem_user_data; + + return realloc(ptr, size); +} + +static nghttp2_mem mem_default = {NULL, default_malloc, default_free, + default_calloc, default_realloc}; + +nghttp2_mem *nghttp2_mem_default(void) { return &mem_default; } + +void *nghttp2_mem_malloc(nghttp2_mem *mem, size_t size) { + return mem->malloc(size, mem->mem_user_data); +} + +void nghttp2_mem_free(nghttp2_mem *mem, void *ptr) { + mem->free(ptr, mem->mem_user_data); +} + +void nghttp2_mem_free2(nghttp2_free free_func, void *ptr, void *mem_user_data) { + free_func(ptr, mem_user_data); +} + +void *nghttp2_mem_calloc(nghttp2_mem *mem, size_t nmemb, size_t size) { + return mem->calloc(nmemb, size, mem->mem_user_data); +} + +void *nghttp2_mem_realloc(nghttp2_mem *mem, void *ptr, size_t size) { + return mem->realloc(ptr, size, mem->mem_user_data); +} diff --git a/contrib/libs/nghttp2/lib/nghttp2_mem.h b/contrib/libs/nghttp2/lib/nghttp2_mem.h index 987aa7b27d..f83dbcb8f9 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_mem.h +++ b/contrib/libs/nghttp2/lib/nghttp2_mem.h @@ -1,45 +1,45 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * 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_MEM_H -#define NGHTTP2_MEM_H - -#ifdef HAVE_CONFIG_H +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * 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_MEM_H +#define NGHTTP2_MEM_H + +#ifdef HAVE_CONFIG_H # include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <nghttp2/nghttp2.h> - -/* The default, system standard memory allocator */ -nghttp2_mem *nghttp2_mem_default(void); - -/* Convenient wrapper functions to call allocator function in - |mem|. */ -void *nghttp2_mem_malloc(nghttp2_mem *mem, size_t size); -void nghttp2_mem_free(nghttp2_mem *mem, void *ptr); -void nghttp2_mem_free2(nghttp2_free free_func, void *ptr, void *mem_user_data); -void *nghttp2_mem_calloc(nghttp2_mem *mem, size_t nmemb, size_t size); -void *nghttp2_mem_realloc(nghttp2_mem *mem, void *ptr, size_t size); - -#endif /* NGHTTP2_MEM_H */ +#endif /* HAVE_CONFIG_H */ + +#include <nghttp2/nghttp2.h> + +/* The default, system standard memory allocator */ +nghttp2_mem *nghttp2_mem_default(void); + +/* Convenient wrapper functions to call allocator function in + |mem|. */ +void *nghttp2_mem_malloc(nghttp2_mem *mem, size_t size); +void nghttp2_mem_free(nghttp2_mem *mem, void *ptr); +void nghttp2_mem_free2(nghttp2_free free_func, void *ptr, void *mem_user_data); +void *nghttp2_mem_calloc(nghttp2_mem *mem, size_t nmemb, size_t size); +void *nghttp2_mem_realloc(nghttp2_mem *mem, void *ptr, size_t size); + +#endif /* NGHTTP2_MEM_H */ diff --git a/contrib/libs/nghttp2/lib/nghttp2_net.h b/contrib/libs/nghttp2/lib/nghttp2_net.h index a427fd889d..95ffee74a1 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_net.h +++ b/contrib/libs/nghttp2/lib/nghttp2_net.h @@ -1,91 +1,91 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * 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_NET_H -#define NGHTTP2_NET_H - -#ifdef HAVE_CONFIG_H +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * 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_NET_H +#define NGHTTP2_NET_H + +#ifdef HAVE_CONFIG_H # include <config.h> -#endif /* HAVE_CONFIG_H */ - -#ifdef HAVE_ARPA_INET_H +#endif /* HAVE_CONFIG_H */ + +#ifdef HAVE_ARPA_INET_H # include <arpa/inet.h> -#endif /* HAVE_ARPA_INET_H */ - -#ifdef HAVE_NETINET_IN_H +#endif /* HAVE_ARPA_INET_H */ + +#ifdef HAVE_NETINET_IN_H # include <netinet/in.h> -#endif /* HAVE_NETINET_IN_H */ - -#include <nghttp2/nghttp2.h> - -#if defined(WIN32) -/* Windows requires ws2_32 library for ntonl family functions. We - define inline functions for those function so that we don't have - dependeny on that lib. */ - +#endif /* HAVE_NETINET_IN_H */ + +#include <nghttp2/nghttp2.h> + +#if defined(WIN32) +/* Windows requires ws2_32 library for ntonl family functions. We + define inline functions for those function so that we don't have + dependeny on that lib. */ + # ifdef _MSC_VER # define STIN static __inline # else # define STIN static inline # endif - -STIN uint32_t htonl(uint32_t hostlong) { - uint32_t res; - unsigned char *p = (unsigned char *)&res; - *p++ = hostlong >> 24; - *p++ = (hostlong >> 16) & 0xffu; - *p++ = (hostlong >> 8) & 0xffu; - *p = hostlong & 0xffu; - return res; -} - -STIN uint16_t htons(uint16_t hostshort) { - uint16_t res; - unsigned char *p = (unsigned char *)&res; - *p++ = hostshort >> 8; - *p = hostshort & 0xffu; - return res; -} - -STIN uint32_t ntohl(uint32_t netlong) { - uint32_t res; - unsigned char *p = (unsigned char *)&netlong; - res = *p++ << 24; - res += *p++ << 16; - res += *p++ << 8; - res += *p; - return res; -} - -STIN uint16_t ntohs(uint16_t netshort) { - uint16_t res; - unsigned char *p = (unsigned char *)&netshort; - res = *p++ << 8; - res += *p; - return res; -} - -#endif /* WIN32 */ - -#endif /* NGHTTP2_NET_H */ + +STIN uint32_t htonl(uint32_t hostlong) { + uint32_t res; + unsigned char *p = (unsigned char *)&res; + *p++ = hostlong >> 24; + *p++ = (hostlong >> 16) & 0xffu; + *p++ = (hostlong >> 8) & 0xffu; + *p = hostlong & 0xffu; + return res; +} + +STIN uint16_t htons(uint16_t hostshort) { + uint16_t res; + unsigned char *p = (unsigned char *)&res; + *p++ = hostshort >> 8; + *p = hostshort & 0xffu; + return res; +} + +STIN uint32_t ntohl(uint32_t netlong) { + uint32_t res; + unsigned char *p = (unsigned char *)&netlong; + res = *p++ << 24; + res += *p++ << 16; + res += *p++ << 8; + res += *p; + return res; +} + +STIN uint16_t ntohs(uint16_t netshort) { + uint16_t res; + unsigned char *p = (unsigned char *)&netshort; + res = *p++ << 8; + res += *p; + return res; +} + +#endif /* WIN32 */ + +#endif /* NGHTTP2_NET_H */ diff --git a/contrib/libs/nghttp2/lib/nghttp2_npn.c b/contrib/libs/nghttp2/lib/nghttp2_npn.c index ce097ab446..d1384c8075 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_npn.c +++ b/contrib/libs/nghttp2/lib/nghttp2_npn.c @@ -1,57 +1,57 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * 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_npn.h" - -#include <string.h> - -static int select_next_protocol(unsigned char **out, unsigned char *outlen, - const unsigned char *in, unsigned int inlen, - const char *key, unsigned int keylen) { - unsigned int i; - for (i = 0; i + keylen <= inlen; i += (unsigned int)(in[i] + 1)) { - if (memcmp(&in[i], key, keylen) == 0) { - *out = (unsigned char *)&in[i + 1]; - *outlen = in[i]; - return 0; - } - } - return -1; -} - -#define NGHTTP2_HTTP_1_1_ALPN "\x8http/1.1" -#define NGHTTP2_HTTP_1_1_ALPN_LEN (sizeof(NGHTTP2_HTTP_1_1_ALPN) - 1) - -int nghttp2_select_next_protocol(unsigned char **out, unsigned char *outlen, - const unsigned char *in, unsigned int inlen) { - if (select_next_protocol(out, outlen, in, inlen, NGHTTP2_PROTO_ALPN, - NGHTTP2_PROTO_ALPN_LEN) == 0) { - return 1; - } - if (select_next_protocol(out, outlen, in, inlen, NGHTTP2_HTTP_1_1_ALPN, - NGHTTP2_HTTP_1_1_ALPN_LEN) == 0) { - return 0; - } - return -1; -} +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * 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_npn.h" + +#include <string.h> + +static int select_next_protocol(unsigned char **out, unsigned char *outlen, + const unsigned char *in, unsigned int inlen, + const char *key, unsigned int keylen) { + unsigned int i; + for (i = 0; i + keylen <= inlen; i += (unsigned int)(in[i] + 1)) { + if (memcmp(&in[i], key, keylen) == 0) { + *out = (unsigned char *)&in[i + 1]; + *outlen = in[i]; + return 0; + } + } + return -1; +} + +#define NGHTTP2_HTTP_1_1_ALPN "\x8http/1.1" +#define NGHTTP2_HTTP_1_1_ALPN_LEN (sizeof(NGHTTP2_HTTP_1_1_ALPN) - 1) + +int nghttp2_select_next_protocol(unsigned char **out, unsigned char *outlen, + const unsigned char *in, unsigned int inlen) { + if (select_next_protocol(out, outlen, in, inlen, NGHTTP2_PROTO_ALPN, + NGHTTP2_PROTO_ALPN_LEN) == 0) { + return 1; + } + if (select_next_protocol(out, outlen, in, inlen, NGHTTP2_HTTP_1_1_ALPN, + NGHTTP2_HTTP_1_1_ALPN_LEN) == 0) { + return 0; + } + return -1; +} diff --git a/contrib/libs/nghttp2/lib/nghttp2_npn.h b/contrib/libs/nghttp2/lib/nghttp2_npn.h index a8627f3e11..c6f1c04b68 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_npn.h +++ b/contrib/libs/nghttp2/lib/nghttp2_npn.h @@ -1,34 +1,34 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * 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_NPN_H -#define NGHTTP2_NPN_H - -#ifdef HAVE_CONFIG_H +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * 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_NPN_H +#define NGHTTP2_NPN_H + +#ifdef HAVE_CONFIG_H # include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <nghttp2/nghttp2.h> - -#endif /* NGHTTP2_NPN_H */ +#endif /* HAVE_CONFIG_H */ + +#include <nghttp2/nghttp2.h> + +#endif /* NGHTTP2_NPN_H */ diff --git a/contrib/libs/nghttp2/lib/nghttp2_option.c b/contrib/libs/nghttp2/lib/nghttp2_option.c index 28afc3d23a..34348e6606 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_option.c +++ b/contrib/libs/nghttp2/lib/nghttp2_option.c @@ -1,121 +1,121 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * 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_option.h" - -#include "nghttp2_session.h" - -int nghttp2_option_new(nghttp2_option **option_ptr) { - *option_ptr = calloc(1, sizeof(nghttp2_option)); - - if (*option_ptr == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - return 0; -} - -void nghttp2_option_del(nghttp2_option *option) { free(option); } - -void nghttp2_option_set_no_auto_window_update(nghttp2_option *option, int val) { - option->opt_set_mask |= NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE; - option->no_auto_window_update = val; -} - -void nghttp2_option_set_peer_max_concurrent_streams(nghttp2_option *option, - uint32_t val) { - option->opt_set_mask |= NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS; - option->peer_max_concurrent_streams = val; -} - -void nghttp2_option_set_no_recv_client_magic(nghttp2_option *option, int val) { - option->opt_set_mask |= NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC; - option->no_recv_client_magic = val; -} - -void nghttp2_option_set_no_http_messaging(nghttp2_option *option, int val) { - option->opt_set_mask |= NGHTTP2_OPT_NO_HTTP_MESSAGING; - option->no_http_messaging = val; -} - -void nghttp2_option_set_max_reserved_remote_streams(nghttp2_option *option, - uint32_t val) { - option->opt_set_mask |= NGHTTP2_OPT_MAX_RESERVED_REMOTE_STREAMS; - option->max_reserved_remote_streams = val; -} - -static void set_ext_type(uint8_t *ext_types, uint8_t type) { - ext_types[type / 8] = (uint8_t)(ext_types[type / 8] | (1 << (type & 0x7))); -} - -void nghttp2_option_set_user_recv_extension_type(nghttp2_option *option, - uint8_t type) { - if (type < 10) { - return; - } - - option->opt_set_mask |= NGHTTP2_OPT_USER_RECV_EXT_TYPES; - set_ext_type(option->user_recv_ext_types, type); -} - -void nghttp2_option_set_builtin_recv_extension_type(nghttp2_option *option, - uint8_t type) { - switch (type) { - case NGHTTP2_ALTSVC: - option->opt_set_mask |= NGHTTP2_OPT_BUILTIN_RECV_EXT_TYPES; - option->builtin_recv_ext_types |= NGHTTP2_TYPEMASK_ALTSVC; - return; +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * 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_option.h" + +#include "nghttp2_session.h" + +int nghttp2_option_new(nghttp2_option **option_ptr) { + *option_ptr = calloc(1, sizeof(nghttp2_option)); + + if (*option_ptr == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + return 0; +} + +void nghttp2_option_del(nghttp2_option *option) { free(option); } + +void nghttp2_option_set_no_auto_window_update(nghttp2_option *option, int val) { + option->opt_set_mask |= NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE; + option->no_auto_window_update = val; +} + +void nghttp2_option_set_peer_max_concurrent_streams(nghttp2_option *option, + uint32_t val) { + option->opt_set_mask |= NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS; + option->peer_max_concurrent_streams = val; +} + +void nghttp2_option_set_no_recv_client_magic(nghttp2_option *option, int val) { + option->opt_set_mask |= NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC; + option->no_recv_client_magic = val; +} + +void nghttp2_option_set_no_http_messaging(nghttp2_option *option, int val) { + option->opt_set_mask |= NGHTTP2_OPT_NO_HTTP_MESSAGING; + option->no_http_messaging = val; +} + +void nghttp2_option_set_max_reserved_remote_streams(nghttp2_option *option, + uint32_t val) { + option->opt_set_mask |= NGHTTP2_OPT_MAX_RESERVED_REMOTE_STREAMS; + option->max_reserved_remote_streams = val; +} + +static void set_ext_type(uint8_t *ext_types, uint8_t type) { + ext_types[type / 8] = (uint8_t)(ext_types[type / 8] | (1 << (type & 0x7))); +} + +void nghttp2_option_set_user_recv_extension_type(nghttp2_option *option, + uint8_t type) { + if (type < 10) { + return; + } + + option->opt_set_mask |= NGHTTP2_OPT_USER_RECV_EXT_TYPES; + set_ext_type(option->user_recv_ext_types, type); +} + +void nghttp2_option_set_builtin_recv_extension_type(nghttp2_option *option, + uint8_t type) { + switch (type) { + case NGHTTP2_ALTSVC: + option->opt_set_mask |= NGHTTP2_OPT_BUILTIN_RECV_EXT_TYPES; + option->builtin_recv_ext_types |= NGHTTP2_TYPEMASK_ALTSVC; + return; case NGHTTP2_ORIGIN: option->opt_set_mask |= NGHTTP2_OPT_BUILTIN_RECV_EXT_TYPES; option->builtin_recv_ext_types |= NGHTTP2_TYPEMASK_ORIGIN; return; - default: - return; - } -} - -void nghttp2_option_set_no_auto_ping_ack(nghttp2_option *option, int val) { - option->opt_set_mask |= NGHTTP2_OPT_NO_AUTO_PING_ACK; - option->no_auto_ping_ack = val; -} - -void nghttp2_option_set_max_send_header_block_length(nghttp2_option *option, - size_t val) { - option->opt_set_mask |= NGHTTP2_OPT_MAX_SEND_HEADER_BLOCK_LENGTH; - option->max_send_header_block_length = val; -} - -void nghttp2_option_set_max_deflate_dynamic_table_size(nghttp2_option *option, - size_t val) { - option->opt_set_mask |= NGHTTP2_OPT_MAX_DEFLATE_DYNAMIC_TABLE_SIZE; - option->max_deflate_dynamic_table_size = val; -} - -void nghttp2_option_set_no_closed_streams(nghttp2_option *option, int val) { - option->opt_set_mask |= NGHTTP2_OPT_NO_CLOSED_STREAMS; - option->no_closed_streams = val; -} + default: + return; + } +} + +void nghttp2_option_set_no_auto_ping_ack(nghttp2_option *option, int val) { + option->opt_set_mask |= NGHTTP2_OPT_NO_AUTO_PING_ACK; + option->no_auto_ping_ack = val; +} + +void nghttp2_option_set_max_send_header_block_length(nghttp2_option *option, + size_t val) { + option->opt_set_mask |= NGHTTP2_OPT_MAX_SEND_HEADER_BLOCK_LENGTH; + option->max_send_header_block_length = val; +} + +void nghttp2_option_set_max_deflate_dynamic_table_size(nghttp2_option *option, + size_t val) { + option->opt_set_mask |= NGHTTP2_OPT_MAX_DEFLATE_DYNAMIC_TABLE_SIZE; + option->max_deflate_dynamic_table_size = val; +} + +void nghttp2_option_set_no_closed_streams(nghttp2_option *option, int val) { + option->opt_set_mask |= NGHTTP2_OPT_NO_CLOSED_STREAMS; + option->no_closed_streams = val; +} void nghttp2_option_set_max_outbound_ack(nghttp2_option *option, size_t val) { option->opt_set_mask |= NGHTTP2_OPT_MAX_OUTBOUND_ACK; diff --git a/contrib/libs/nghttp2/lib/nghttp2_option.h b/contrib/libs/nghttp2/lib/nghttp2_option.h index 53ca7d5127..939729fdcd 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_option.h +++ b/contrib/libs/nghttp2/lib/nghttp2_option.h @@ -1,88 +1,88 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * 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_OPTION_H -#define NGHTTP2_OPTION_H - -#ifdef HAVE_CONFIG_H +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * 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_OPTION_H +#define NGHTTP2_OPTION_H + +#ifdef HAVE_CONFIG_H # include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <nghttp2/nghttp2.h> - -/** - * Configuration options - */ -typedef enum { - /** - * This option prevents the library from sending WINDOW_UPDATE for a - * connection automatically. If this option is set to nonzero, the - * library won't send WINDOW_UPDATE for DATA until application calls - * nghttp2_session_consume() to indicate the amount of consumed - * DATA. By default, this option is set to zero. - */ - NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE = 1, - /** - * This option sets the SETTINGS_MAX_CONCURRENT_STREAMS value of - * remote endpoint as if it is received in SETTINGS frame. Without - * specifying this option, before the local endpoint receives - * SETTINGS_MAX_CONCURRENT_STREAMS in SETTINGS frame from remote - * endpoint, SETTINGS_MAX_CONCURRENT_STREAMS is unlimited. This may - * cause problem if local endpoint submits lots of requests - * initially and sending them at once to the remote peer may lead to - * the rejection of some requests. Specifying this option to the - * sensible value, say 100, may avoid this kind of issue. This value - * will be overwritten if the local endpoint receives - * SETTINGS_MAX_CONCURRENT_STREAMS from the remote endpoint. - */ - NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS = 1 << 1, - NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC = 1 << 2, - NGHTTP2_OPT_NO_HTTP_MESSAGING = 1 << 3, - NGHTTP2_OPT_MAX_RESERVED_REMOTE_STREAMS = 1 << 4, - NGHTTP2_OPT_USER_RECV_EXT_TYPES = 1 << 5, - NGHTTP2_OPT_NO_AUTO_PING_ACK = 1 << 6, - NGHTTP2_OPT_BUILTIN_RECV_EXT_TYPES = 1 << 7, - NGHTTP2_OPT_MAX_SEND_HEADER_BLOCK_LENGTH = 1 << 8, - NGHTTP2_OPT_MAX_DEFLATE_DYNAMIC_TABLE_SIZE = 1 << 9, - NGHTTP2_OPT_NO_CLOSED_STREAMS = 1 << 10, +#endif /* HAVE_CONFIG_H */ + +#include <nghttp2/nghttp2.h> + +/** + * Configuration options + */ +typedef enum { + /** + * This option prevents the library from sending WINDOW_UPDATE for a + * connection automatically. If this option is set to nonzero, the + * library won't send WINDOW_UPDATE for DATA until application calls + * nghttp2_session_consume() to indicate the amount of consumed + * DATA. By default, this option is set to zero. + */ + NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE = 1, + /** + * This option sets the SETTINGS_MAX_CONCURRENT_STREAMS value of + * remote endpoint as if it is received in SETTINGS frame. Without + * specifying this option, before the local endpoint receives + * SETTINGS_MAX_CONCURRENT_STREAMS in SETTINGS frame from remote + * endpoint, SETTINGS_MAX_CONCURRENT_STREAMS is unlimited. This may + * cause problem if local endpoint submits lots of requests + * initially and sending them at once to the remote peer may lead to + * the rejection of some requests. Specifying this option to the + * sensible value, say 100, may avoid this kind of issue. This value + * will be overwritten if the local endpoint receives + * SETTINGS_MAX_CONCURRENT_STREAMS from the remote endpoint. + */ + NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS = 1 << 1, + NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC = 1 << 2, + NGHTTP2_OPT_NO_HTTP_MESSAGING = 1 << 3, + NGHTTP2_OPT_MAX_RESERVED_REMOTE_STREAMS = 1 << 4, + NGHTTP2_OPT_USER_RECV_EXT_TYPES = 1 << 5, + NGHTTP2_OPT_NO_AUTO_PING_ACK = 1 << 6, + NGHTTP2_OPT_BUILTIN_RECV_EXT_TYPES = 1 << 7, + NGHTTP2_OPT_MAX_SEND_HEADER_BLOCK_LENGTH = 1 << 8, + NGHTTP2_OPT_MAX_DEFLATE_DYNAMIC_TABLE_SIZE = 1 << 9, + NGHTTP2_OPT_NO_CLOSED_STREAMS = 1 << 10, NGHTTP2_OPT_MAX_OUTBOUND_ACK = 1 << 11, NGHTTP2_OPT_MAX_SETTINGS = 1 << 12, -} nghttp2_option_flag; - -/** - * Struct to store option values for nghttp2_session. - */ -struct nghttp2_option { - /** - * NGHTTP2_OPT_MAX_SEND_HEADER_BLOCK_LENGTH - */ - size_t max_send_header_block_length; - /** - * NGHTTP2_OPT_MAX_DEFLATE_DYNAMIC_TABLE_SIZE - */ - size_t max_deflate_dynamic_table_size; - /** +} nghttp2_option_flag; + +/** + * Struct to store option values for nghttp2_session. + */ +struct nghttp2_option { + /** + * NGHTTP2_OPT_MAX_SEND_HEADER_BLOCK_LENGTH + */ + size_t max_send_header_block_length; + /** + * NGHTTP2_OPT_MAX_DEFLATE_DYNAMIC_TABLE_SIZE + */ + size_t max_deflate_dynamic_table_size; + /** * NGHTTP2_OPT_MAX_OUTBOUND_ACK */ size_t max_outbound_ack; @@ -91,46 +91,46 @@ struct nghttp2_option { */ size_t max_settings; /** - * Bitwise OR of nghttp2_option_flag to determine that which fields - * are specified. - */ - uint32_t opt_set_mask; - /** - * NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS - */ - uint32_t peer_max_concurrent_streams; - /** - * NGHTTP2_OPT_MAX_RESERVED_REMOTE_STREAMS - */ - uint32_t max_reserved_remote_streams; - /** - * NGHTTP2_OPT_BUILTIN_RECV_EXT_TYPES - */ - uint32_t builtin_recv_ext_types; - /** - * NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE - */ - int no_auto_window_update; - /** - * NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC - */ - int no_recv_client_magic; - /** - * NGHTTP2_OPT_NO_HTTP_MESSAGING - */ - int no_http_messaging; - /** - * NGHTTP2_OPT_NO_AUTO_PING_ACK - */ - int no_auto_ping_ack; - /** - * NGHTTP2_OPT_NO_CLOSED_STREAMS - */ - int no_closed_streams; - /** - * NGHTTP2_OPT_USER_RECV_EXT_TYPES - */ - uint8_t user_recv_ext_types[32]; -}; - -#endif /* NGHTTP2_OPTION_H */ + * Bitwise OR of nghttp2_option_flag to determine that which fields + * are specified. + */ + uint32_t opt_set_mask; + /** + * NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS + */ + uint32_t peer_max_concurrent_streams; + /** + * NGHTTP2_OPT_MAX_RESERVED_REMOTE_STREAMS + */ + uint32_t max_reserved_remote_streams; + /** + * NGHTTP2_OPT_BUILTIN_RECV_EXT_TYPES + */ + uint32_t builtin_recv_ext_types; + /** + * NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE + */ + int no_auto_window_update; + /** + * NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC + */ + int no_recv_client_magic; + /** + * NGHTTP2_OPT_NO_HTTP_MESSAGING + */ + int no_http_messaging; + /** + * NGHTTP2_OPT_NO_AUTO_PING_ACK + */ + int no_auto_ping_ack; + /** + * NGHTTP2_OPT_NO_CLOSED_STREAMS + */ + int no_closed_streams; + /** + * NGHTTP2_OPT_USER_RECV_EXT_TYPES + */ + uint8_t user_recv_ext_types[32]; +}; + +#endif /* NGHTTP2_OPTION_H */ diff --git a/contrib/libs/nghttp2/lib/nghttp2_outbound_item.c b/contrib/libs/nghttp2/lib/nghttp2_outbound_item.c index e86e1709ac..f651c8029a 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_outbound_item.c +++ b/contrib/libs/nghttp2/lib/nghttp2_outbound_item.c @@ -1,127 +1,127 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * 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_outbound_item.h" - -#include <assert.h> -#include <string.h> - -void nghttp2_outbound_item_init(nghttp2_outbound_item *item) { - item->cycle = 0; - item->qnext = NULL; - item->queued = 0; - - memset(&item->aux_data, 0, sizeof(nghttp2_aux_data)); -} - -void nghttp2_outbound_item_free(nghttp2_outbound_item *item, nghttp2_mem *mem) { - nghttp2_frame *frame; - - if (item == NULL) { - return; - } - - frame = &item->frame; - - switch (frame->hd.type) { - case NGHTTP2_DATA: - nghttp2_frame_data_free(&frame->data); - break; - case NGHTTP2_HEADERS: - nghttp2_frame_headers_free(&frame->headers, mem); - break; - case NGHTTP2_PRIORITY: - nghttp2_frame_priority_free(&frame->priority); - break; - case NGHTTP2_RST_STREAM: - nghttp2_frame_rst_stream_free(&frame->rst_stream); - break; - case NGHTTP2_SETTINGS: - nghttp2_frame_settings_free(&frame->settings, mem); - break; - case NGHTTP2_PUSH_PROMISE: - nghttp2_frame_push_promise_free(&frame->push_promise, mem); - break; - case NGHTTP2_PING: - nghttp2_frame_ping_free(&frame->ping); - break; - case NGHTTP2_GOAWAY: - nghttp2_frame_goaway_free(&frame->goaway, mem); - break; - case NGHTTP2_WINDOW_UPDATE: - nghttp2_frame_window_update_free(&frame->window_update); - break; - default: { - nghttp2_ext_aux_data *aux_data; - - aux_data = &item->aux_data.ext; - - if (aux_data->builtin == 0) { - nghttp2_frame_extension_free(&frame->ext); - break; - } - - switch (frame->hd.type) { - case NGHTTP2_ALTSVC: - nghttp2_frame_altsvc_free(&frame->ext, mem); - break; +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * 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_outbound_item.h" + +#include <assert.h> +#include <string.h> + +void nghttp2_outbound_item_init(nghttp2_outbound_item *item) { + item->cycle = 0; + item->qnext = NULL; + item->queued = 0; + + memset(&item->aux_data, 0, sizeof(nghttp2_aux_data)); +} + +void nghttp2_outbound_item_free(nghttp2_outbound_item *item, nghttp2_mem *mem) { + nghttp2_frame *frame; + + if (item == NULL) { + return; + } + + frame = &item->frame; + + switch (frame->hd.type) { + case NGHTTP2_DATA: + nghttp2_frame_data_free(&frame->data); + break; + case NGHTTP2_HEADERS: + nghttp2_frame_headers_free(&frame->headers, mem); + break; + case NGHTTP2_PRIORITY: + nghttp2_frame_priority_free(&frame->priority); + break; + case NGHTTP2_RST_STREAM: + nghttp2_frame_rst_stream_free(&frame->rst_stream); + break; + case NGHTTP2_SETTINGS: + nghttp2_frame_settings_free(&frame->settings, mem); + break; + case NGHTTP2_PUSH_PROMISE: + nghttp2_frame_push_promise_free(&frame->push_promise, mem); + break; + case NGHTTP2_PING: + nghttp2_frame_ping_free(&frame->ping); + break; + case NGHTTP2_GOAWAY: + nghttp2_frame_goaway_free(&frame->goaway, mem); + break; + case NGHTTP2_WINDOW_UPDATE: + nghttp2_frame_window_update_free(&frame->window_update); + break; + default: { + nghttp2_ext_aux_data *aux_data; + + aux_data = &item->aux_data.ext; + + if (aux_data->builtin == 0) { + nghttp2_frame_extension_free(&frame->ext); + break; + } + + switch (frame->hd.type) { + case NGHTTP2_ALTSVC: + nghttp2_frame_altsvc_free(&frame->ext, mem); + break; case NGHTTP2_ORIGIN: nghttp2_frame_origin_free(&frame->ext, mem); break; - default: - assert(0); - break; - } - } - } -} - -void nghttp2_outbound_queue_init(nghttp2_outbound_queue *q) { - q->head = q->tail = NULL; - q->n = 0; -} - -void nghttp2_outbound_queue_push(nghttp2_outbound_queue *q, - nghttp2_outbound_item *item) { - if (q->tail) { - q->tail = q->tail->qnext = item; - } else { - q->head = q->tail = item; - } - ++q->n; -} - -void nghttp2_outbound_queue_pop(nghttp2_outbound_queue *q) { - nghttp2_outbound_item *item; - if (!q->head) { - return; - } - item = q->head; - q->head = q->head->qnext; - item->qnext = NULL; - if (!q->head) { - q->tail = NULL; - } - --q->n; -} + default: + assert(0); + break; + } + } + } +} + +void nghttp2_outbound_queue_init(nghttp2_outbound_queue *q) { + q->head = q->tail = NULL; + q->n = 0; +} + +void nghttp2_outbound_queue_push(nghttp2_outbound_queue *q, + nghttp2_outbound_item *item) { + if (q->tail) { + q->tail = q->tail->qnext = item; + } else { + q->head = q->tail = item; + } + ++q->n; +} + +void nghttp2_outbound_queue_pop(nghttp2_outbound_queue *q) { + nghttp2_outbound_item *item; + if (!q->head) { + return; + } + item = q->head; + q->head = q->head->qnext; + item->qnext = NULL; + if (!q->head) { + q->tail = NULL; + } + --q->n; +} diff --git a/contrib/libs/nghttp2/lib/nghttp2_outbound_item.h b/contrib/libs/nghttp2/lib/nghttp2_outbound_item.h index f8b7320ff5..b5f503a312 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_outbound_item.h +++ b/contrib/libs/nghttp2/lib/nghttp2_outbound_item.h @@ -1,166 +1,166 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * 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_OUTBOUND_ITEM_H -#define NGHTTP2_OUTBOUND_ITEM_H - -#ifdef HAVE_CONFIG_H +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * 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_OUTBOUND_ITEM_H +#define NGHTTP2_OUTBOUND_ITEM_H + +#ifdef HAVE_CONFIG_H # include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <nghttp2/nghttp2.h> -#include "nghttp2_frame.h" -#include "nghttp2_mem.h" - -/* struct used for HEADERS and PUSH_PROMISE frame */ -typedef struct { - nghttp2_data_provider data_prd; - void *stream_user_data; - /* error code when request HEADERS is canceled by RST_STREAM while - it is in queue. */ - uint32_t error_code; - /* nonzero if request HEADERS is canceled. The error code is stored - in |error_code|. */ - uint8_t canceled; -} nghttp2_headers_aux_data; - -/* struct used for DATA frame */ -typedef struct { - /** - * The data to be sent for this DATA frame. - */ - nghttp2_data_provider data_prd; - /** - * The flags of DATA frame. We use separate flags here and - * nghttp2_data frame. The latter contains flags actually sent to - * peer. This |flags| may contain NGHTTP2_FLAG_END_STREAM and only - * when |eof| becomes nonzero, flags in nghttp2_data has - * NGHTTP2_FLAG_END_STREAM set. - */ - uint8_t flags; - /** - * The flag to indicate whether EOF was reached or not. Initially - * |eof| is 0. It becomes 1 after all data were read. - */ - uint8_t eof; - /** - * The flag to indicate that NGHTTP2_DATA_FLAG_NO_COPY is used. - */ - uint8_t no_copy; -} nghttp2_data_aux_data; - -typedef enum { - NGHTTP2_GOAWAY_AUX_NONE = 0x0, - /* indicates that session should be terminated after the - transmission of this frame. */ - NGHTTP2_GOAWAY_AUX_TERM_ON_SEND = 0x1, - /* indicates that this GOAWAY is just a notification for graceful - shutdown. No nghttp2_session.goaway_flags should be updated on - the reaction to this frame. */ - NGHTTP2_GOAWAY_AUX_SHUTDOWN_NOTICE = 0x2 -} nghttp2_goaway_aux_flag; - -/* struct used for GOAWAY frame */ -typedef struct { - /* bitwise-OR of one or more of nghttp2_goaway_aux_flag. */ - uint8_t flags; -} nghttp2_goaway_aux_data; - -/* struct used for extension frame */ -typedef struct { - /* nonzero if this extension frame is serialized by library - function, instead of user-defined callbacks. */ - uint8_t builtin; -} nghttp2_ext_aux_data; - -/* Additional data which cannot be stored in nghttp2_frame struct */ -typedef union { - nghttp2_data_aux_data data; - nghttp2_headers_aux_data headers; - nghttp2_goaway_aux_data goaway; - nghttp2_ext_aux_data ext; -} nghttp2_aux_data; - -struct nghttp2_outbound_item; -typedef struct nghttp2_outbound_item nghttp2_outbound_item; - -struct nghttp2_outbound_item { - nghttp2_frame frame; - /* Storage for extension frame payload. frame->ext.payload points - to this structure to avoid frequent memory allocation. */ - nghttp2_ext_frame_payload ext_frame_payload; - nghttp2_aux_data aux_data; - /* The priority used in priority comparion. Smaller is served - earlier. For PING, SETTINGS and non-DATA frames (excluding - response HEADERS frame) have dedicated cycle value defined above. - For DATA frame, cycle is computed by taking into account of - effective weight and frame payload length previously sent, so - that the amount of transmission is distributed across streams - proportional to effective weight (inside a tree). */ - uint64_t cycle; - nghttp2_outbound_item *qnext; - /* nonzero if this object is queued, except for DATA or HEADERS - which are attached to stream as item. */ - uint8_t queued; -}; - -/* - * Initializes |item|. No memory allocation is done in this function. - * Don't call nghttp2_outbound_item_free() until frame member is - * initialized. - */ -void nghttp2_outbound_item_init(nghttp2_outbound_item *item); - -/* - * Deallocates resource for |item|. If |item| is NULL, this function - * does nothing. - */ -void nghttp2_outbound_item_free(nghttp2_outbound_item *item, nghttp2_mem *mem); - -/* - * queue for nghttp2_outbound_item. - */ -typedef struct { - nghttp2_outbound_item *head, *tail; - /* number of items in this queue. */ - size_t n; -} nghttp2_outbound_queue; - -void nghttp2_outbound_queue_init(nghttp2_outbound_queue *q); - -/* Pushes |item| into |q| */ -void nghttp2_outbound_queue_push(nghttp2_outbound_queue *q, - nghttp2_outbound_item *item); - -/* Pops |item| at the top from |q|. If |q| is empty, nothing - happens. */ -void nghttp2_outbound_queue_pop(nghttp2_outbound_queue *q); - -/* Returns the top item. */ -#define nghttp2_outbound_queue_top(Q) ((Q)->head) - -/* Returns the size of the queue */ -#define nghttp2_outbound_queue_size(Q) ((Q)->n) - -#endif /* NGHTTP2_OUTBOUND_ITEM_H */ +#endif /* HAVE_CONFIG_H */ + +#include <nghttp2/nghttp2.h> +#include "nghttp2_frame.h" +#include "nghttp2_mem.h" + +/* struct used for HEADERS and PUSH_PROMISE frame */ +typedef struct { + nghttp2_data_provider data_prd; + void *stream_user_data; + /* error code when request HEADERS is canceled by RST_STREAM while + it is in queue. */ + uint32_t error_code; + /* nonzero if request HEADERS is canceled. The error code is stored + in |error_code|. */ + uint8_t canceled; +} nghttp2_headers_aux_data; + +/* struct used for DATA frame */ +typedef struct { + /** + * The data to be sent for this DATA frame. + */ + nghttp2_data_provider data_prd; + /** + * The flags of DATA frame. We use separate flags here and + * nghttp2_data frame. The latter contains flags actually sent to + * peer. This |flags| may contain NGHTTP2_FLAG_END_STREAM and only + * when |eof| becomes nonzero, flags in nghttp2_data has + * NGHTTP2_FLAG_END_STREAM set. + */ + uint8_t flags; + /** + * The flag to indicate whether EOF was reached or not. Initially + * |eof| is 0. It becomes 1 after all data were read. + */ + uint8_t eof; + /** + * The flag to indicate that NGHTTP2_DATA_FLAG_NO_COPY is used. + */ + uint8_t no_copy; +} nghttp2_data_aux_data; + +typedef enum { + NGHTTP2_GOAWAY_AUX_NONE = 0x0, + /* indicates that session should be terminated after the + transmission of this frame. */ + NGHTTP2_GOAWAY_AUX_TERM_ON_SEND = 0x1, + /* indicates that this GOAWAY is just a notification for graceful + shutdown. No nghttp2_session.goaway_flags should be updated on + the reaction to this frame. */ + NGHTTP2_GOAWAY_AUX_SHUTDOWN_NOTICE = 0x2 +} nghttp2_goaway_aux_flag; + +/* struct used for GOAWAY frame */ +typedef struct { + /* bitwise-OR of one or more of nghttp2_goaway_aux_flag. */ + uint8_t flags; +} nghttp2_goaway_aux_data; + +/* struct used for extension frame */ +typedef struct { + /* nonzero if this extension frame is serialized by library + function, instead of user-defined callbacks. */ + uint8_t builtin; +} nghttp2_ext_aux_data; + +/* Additional data which cannot be stored in nghttp2_frame struct */ +typedef union { + nghttp2_data_aux_data data; + nghttp2_headers_aux_data headers; + nghttp2_goaway_aux_data goaway; + nghttp2_ext_aux_data ext; +} nghttp2_aux_data; + +struct nghttp2_outbound_item; +typedef struct nghttp2_outbound_item nghttp2_outbound_item; + +struct nghttp2_outbound_item { + nghttp2_frame frame; + /* Storage for extension frame payload. frame->ext.payload points + to this structure to avoid frequent memory allocation. */ + nghttp2_ext_frame_payload ext_frame_payload; + nghttp2_aux_data aux_data; + /* The priority used in priority comparion. Smaller is served + earlier. For PING, SETTINGS and non-DATA frames (excluding + response HEADERS frame) have dedicated cycle value defined above. + For DATA frame, cycle is computed by taking into account of + effective weight and frame payload length previously sent, so + that the amount of transmission is distributed across streams + proportional to effective weight (inside a tree). */ + uint64_t cycle; + nghttp2_outbound_item *qnext; + /* nonzero if this object is queued, except for DATA or HEADERS + which are attached to stream as item. */ + uint8_t queued; +}; + +/* + * Initializes |item|. No memory allocation is done in this function. + * Don't call nghttp2_outbound_item_free() until frame member is + * initialized. + */ +void nghttp2_outbound_item_init(nghttp2_outbound_item *item); + +/* + * Deallocates resource for |item|. If |item| is NULL, this function + * does nothing. + */ +void nghttp2_outbound_item_free(nghttp2_outbound_item *item, nghttp2_mem *mem); + +/* + * queue for nghttp2_outbound_item. + */ +typedef struct { + nghttp2_outbound_item *head, *tail; + /* number of items in this queue. */ + size_t n; +} nghttp2_outbound_queue; + +void nghttp2_outbound_queue_init(nghttp2_outbound_queue *q); + +/* Pushes |item| into |q| */ +void nghttp2_outbound_queue_push(nghttp2_outbound_queue *q, + nghttp2_outbound_item *item); + +/* Pops |item| at the top from |q|. If |q| is empty, nothing + happens. */ +void nghttp2_outbound_queue_pop(nghttp2_outbound_queue *q); + +/* Returns the top item. */ +#define nghttp2_outbound_queue_top(Q) ((Q)->head) + +/* Returns the size of the queue */ +#define nghttp2_outbound_queue_size(Q) ((Q)->n) + +#endif /* NGHTTP2_OUTBOUND_ITEM_H */ diff --git a/contrib/libs/nghttp2/lib/nghttp2_pq.c b/contrib/libs/nghttp2/lib/nghttp2_pq.c index 577d768513..bebccc7606 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_pq.c +++ b/contrib/libs/nghttp2/lib/nghttp2_pq.c @@ -1,184 +1,184 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * 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_pq.h" - -#include <stdio.h> -#include <assert.h> - -#include "nghttp2_helper.h" - -int 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) { - nghttp2_mem_free(pq->mem, pq->q); - pq->q = NULL; -} - -static void swap(nghttp2_pq *pq, size_t i, size_t j) { - nghttp2_pq_entry *a = pq->q[i]; - nghttp2_pq_entry *b = pq->q[j]; - - pq->q[i] = b; - b->index = i; - pq->q[j] = a; - a->index = j; -} - -static void bubble_up(nghttp2_pq *pq, size_t index) { - size_t parent; - while (index != 0) { - parent = (index - 1) / 2; - if (!pq->less(pq->q[index], pq->q[parent])) { - return; - } - swap(pq, parent, index); - index = parent; - } -} - -int nghttp2_pq_push(nghttp2_pq *pq, nghttp2_pq_entry *item) { - if (pq->capacity <= pq->length) { - void *nq; - size_t ncapacity; - - ncapacity = nghttp2_max(4, (pq->capacity * 2)); - - nq = nghttp2_mem_realloc(pq->mem, pq->q, - ncapacity * sizeof(nghttp2_pq_entry *)); - if (nq == NULL) { - return NGHTTP2_ERR_NOMEM; - } - pq->capacity = ncapacity; - pq->q = nq; - } - pq->q[pq->length] = item; - item->index = pq->length; - ++pq->length; - bubble_up(pq, pq->length - 1); - return 0; -} - -nghttp2_pq_entry *nghttp2_pq_top(nghttp2_pq *pq) { - if (pq->length == 0) { - return NULL; - } else { - return pq->q[0]; - } -} - -static void bubble_down(nghttp2_pq *pq, size_t index) { - size_t i, j, minindex; - for (;;) { - j = index * 2 + 1; - minindex = index; - for (i = 0; i < 2; ++i, ++j) { - if (j >= pq->length) { - break; - } - if (pq->less(pq->q[j], pq->q[minindex])) { - minindex = j; - } - } - if (minindex == index) { - return; - } - swap(pq, index, minindex); - index = minindex; - } -} - -void nghttp2_pq_pop(nghttp2_pq *pq) { - if (pq->length > 0) { - pq->q[0] = pq->q[pq->length - 1]; - pq->q[0]->index = 0; - --pq->length; - bubble_down(pq, 0); - } -} - -void nghttp2_pq_remove(nghttp2_pq *pq, nghttp2_pq_entry *item) { - assert(pq->q[item->index] == item); - - if (item->index == 0) { - nghttp2_pq_pop(pq); - return; - } - - if (item->index == pq->length - 1) { - --pq->length; - return; - } - - pq->q[item->index] = pq->q[pq->length - 1]; - pq->q[item->index]->index = item->index; - --pq->length; - - if (pq->less(item, pq->q[item->index])) { - bubble_down(pq, item->index); - } else { - bubble_up(pq, item->index); - } -} - -int nghttp2_pq_empty(nghttp2_pq *pq) { return pq->length == 0; } - -size_t nghttp2_pq_size(nghttp2_pq *pq) { return pq->length; } - -void nghttp2_pq_update(nghttp2_pq *pq, nghttp2_pq_item_cb fun, void *arg) { - size_t i; - int rv = 0; - if (pq->length == 0) { - return; - } - for (i = 0; i < pq->length; ++i) { - rv |= (*fun)(pq->q[i], arg); - } - if (rv) { - for (i = pq->length; i > 0; --i) { - bubble_down(pq, i - 1); - } - } -} - -int nghttp2_pq_each(nghttp2_pq *pq, nghttp2_pq_item_cb fun, void *arg) { - size_t i; - - if (pq->length == 0) { - return 0; - } - for (i = 0; i < pq->length; ++i) { - if ((*fun)(pq->q[i], arg)) { - return 1; - } - } - return 0; -} +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * 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_pq.h" + +#include <stdio.h> +#include <assert.h> + +#include "nghttp2_helper.h" + +int 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) { + nghttp2_mem_free(pq->mem, pq->q); + pq->q = NULL; +} + +static void swap(nghttp2_pq *pq, size_t i, size_t j) { + nghttp2_pq_entry *a = pq->q[i]; + nghttp2_pq_entry *b = pq->q[j]; + + pq->q[i] = b; + b->index = i; + pq->q[j] = a; + a->index = j; +} + +static void bubble_up(nghttp2_pq *pq, size_t index) { + size_t parent; + while (index != 0) { + parent = (index - 1) / 2; + if (!pq->less(pq->q[index], pq->q[parent])) { + return; + } + swap(pq, parent, index); + index = parent; + } +} + +int nghttp2_pq_push(nghttp2_pq *pq, nghttp2_pq_entry *item) { + if (pq->capacity <= pq->length) { + void *nq; + size_t ncapacity; + + ncapacity = nghttp2_max(4, (pq->capacity * 2)); + + nq = nghttp2_mem_realloc(pq->mem, pq->q, + ncapacity * sizeof(nghttp2_pq_entry *)); + if (nq == NULL) { + return NGHTTP2_ERR_NOMEM; + } + pq->capacity = ncapacity; + pq->q = nq; + } + pq->q[pq->length] = item; + item->index = pq->length; + ++pq->length; + bubble_up(pq, pq->length - 1); + return 0; +} + +nghttp2_pq_entry *nghttp2_pq_top(nghttp2_pq *pq) { + if (pq->length == 0) { + return NULL; + } else { + return pq->q[0]; + } +} + +static void bubble_down(nghttp2_pq *pq, size_t index) { + size_t i, j, minindex; + for (;;) { + j = index * 2 + 1; + minindex = index; + for (i = 0; i < 2; ++i, ++j) { + if (j >= pq->length) { + break; + } + if (pq->less(pq->q[j], pq->q[minindex])) { + minindex = j; + } + } + if (minindex == index) { + return; + } + swap(pq, index, minindex); + index = minindex; + } +} + +void nghttp2_pq_pop(nghttp2_pq *pq) { + if (pq->length > 0) { + pq->q[0] = pq->q[pq->length - 1]; + pq->q[0]->index = 0; + --pq->length; + bubble_down(pq, 0); + } +} + +void nghttp2_pq_remove(nghttp2_pq *pq, nghttp2_pq_entry *item) { + assert(pq->q[item->index] == item); + + if (item->index == 0) { + nghttp2_pq_pop(pq); + return; + } + + if (item->index == pq->length - 1) { + --pq->length; + return; + } + + pq->q[item->index] = pq->q[pq->length - 1]; + pq->q[item->index]->index = item->index; + --pq->length; + + if (pq->less(item, pq->q[item->index])) { + bubble_down(pq, item->index); + } else { + bubble_up(pq, item->index); + } +} + +int nghttp2_pq_empty(nghttp2_pq *pq) { return pq->length == 0; } + +size_t nghttp2_pq_size(nghttp2_pq *pq) { return pq->length; } + +void nghttp2_pq_update(nghttp2_pq *pq, nghttp2_pq_item_cb fun, void *arg) { + size_t i; + int rv = 0; + if (pq->length == 0) { + return; + } + for (i = 0; i < pq->length; ++i) { + rv |= (*fun)(pq->q[i], arg); + } + if (rv) { + for (i = pq->length; i > 0; --i) { + bubble_down(pq, i - 1); + } + } +} + +int nghttp2_pq_each(nghttp2_pq *pq, nghttp2_pq_item_cb fun, void *arg) { + size_t i; + + if (pq->length == 0) { + return 0; + } + for (i = 0; i < pq->length; ++i) { + if ((*fun)(pq->q[i], arg)) { + return 1; + } + } + return 0; +} diff --git a/contrib/libs/nghttp2/lib/nghttp2_pq.h b/contrib/libs/nghttp2/lib/nghttp2_pq.h index 65216f8bf5..2d7b702ac1 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_pq.h +++ b/contrib/libs/nghttp2/lib/nghttp2_pq.h @@ -1,130 +1,130 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * 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_PQ_H -#define NGHTTP2_PQ_H - -#ifdef HAVE_CONFIG_H +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * 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_PQ_H +#define NGHTTP2_PQ_H + +#ifdef HAVE_CONFIG_H # include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <nghttp2/nghttp2.h> -#include "nghttp2_int.h" -#include "nghttp2_mem.h" - -/* Implementation of priority queue */ - -typedef struct { - size_t index; -} nghttp2_pq_entry; - -typedef struct { - /* The pointer to the pointer to the item stored */ - nghttp2_pq_entry **q; - /* Memory allocator */ - nghttp2_mem *mem; - /* The number of items stored */ - size_t length; - /* The maximum number of items this pq can store. This is - automatically extended when length is reached to this value. */ - size_t capacity; - /* The less function between items */ - nghttp2_less less; -} nghttp2_pq; - -/* - * 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); - -/* - * Deallocates any resources allocated for |pq|. The stored items are - * not freed by this function. - */ -void nghttp2_pq_free(nghttp2_pq *pq); - -/* - * Adds |item| to the priority queue |pq|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -int nghttp2_pq_push(nghttp2_pq *pq, nghttp2_pq_entry *item); - -/* - * Returns item at the top of the queue |pq|. If the queue is empty, - * this function returns NULL. - */ -nghttp2_pq_entry *nghttp2_pq_top(nghttp2_pq *pq); - -/* - * Pops item at the top of the queue |pq|. The popped item is not - * freed by this function. - */ -void nghttp2_pq_pop(nghttp2_pq *pq); - -/* - * Returns nonzero if the queue |pq| is empty. - */ -int nghttp2_pq_empty(nghttp2_pq *pq); - -/* - * Returns the number of items in the queue |pq|. - */ -size_t nghttp2_pq_size(nghttp2_pq *pq); - -typedef int (*nghttp2_pq_item_cb)(nghttp2_pq_entry *item, void *arg); - -/* - * Updates each item in |pq| using function |fun| and re-construct - * priority queue. The |fun| must return non-zero if it modifies the - * item in a way that it affects ordering in the priority queue. The - * |arg| is passed to the 2nd parameter of |fun|. - */ -void nghttp2_pq_update(nghttp2_pq *pq, nghttp2_pq_item_cb fun, void *arg); - -/* - * Applys |fun| to each item in |pq|. The |arg| is passed as arg - * parameter to callback function. This function must not change the - * ordering key. If the return value from callback is nonzero, this - * function returns 1 immediately without iterating remaining items. - * Otherwise this function returns 0. - */ -int nghttp2_pq_each(nghttp2_pq *pq, nghttp2_pq_item_cb fun, void *arg); - -/* - * Removes |item| from priority queue. - */ -void nghttp2_pq_remove(nghttp2_pq *pq, nghttp2_pq_entry *item); - -#endif /* NGHTTP2_PQ_H */ +#endif /* HAVE_CONFIG_H */ + +#include <nghttp2/nghttp2.h> +#include "nghttp2_int.h" +#include "nghttp2_mem.h" + +/* Implementation of priority queue */ + +typedef struct { + size_t index; +} nghttp2_pq_entry; + +typedef struct { + /* The pointer to the pointer to the item stored */ + nghttp2_pq_entry **q; + /* Memory allocator */ + nghttp2_mem *mem; + /* The number of items stored */ + size_t length; + /* The maximum number of items this pq can store. This is + automatically extended when length is reached to this value. */ + size_t capacity; + /* The less function between items */ + nghttp2_less less; +} nghttp2_pq; + +/* + * 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); + +/* + * Deallocates any resources allocated for |pq|. The stored items are + * not freed by this function. + */ +void nghttp2_pq_free(nghttp2_pq *pq); + +/* + * Adds |item| to the priority queue |pq|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_pq_push(nghttp2_pq *pq, nghttp2_pq_entry *item); + +/* + * Returns item at the top of the queue |pq|. If the queue is empty, + * this function returns NULL. + */ +nghttp2_pq_entry *nghttp2_pq_top(nghttp2_pq *pq); + +/* + * Pops item at the top of the queue |pq|. The popped item is not + * freed by this function. + */ +void nghttp2_pq_pop(nghttp2_pq *pq); + +/* + * Returns nonzero if the queue |pq| is empty. + */ +int nghttp2_pq_empty(nghttp2_pq *pq); + +/* + * Returns the number of items in the queue |pq|. + */ +size_t nghttp2_pq_size(nghttp2_pq *pq); + +typedef int (*nghttp2_pq_item_cb)(nghttp2_pq_entry *item, void *arg); + +/* + * Updates each item in |pq| using function |fun| and re-construct + * priority queue. The |fun| must return non-zero if it modifies the + * item in a way that it affects ordering in the priority queue. The + * |arg| is passed to the 2nd parameter of |fun|. + */ +void nghttp2_pq_update(nghttp2_pq *pq, nghttp2_pq_item_cb fun, void *arg); + +/* + * Applys |fun| to each item in |pq|. The |arg| is passed as arg + * parameter to callback function. This function must not change the + * ordering key. If the return value from callback is nonzero, this + * function returns 1 immediately without iterating remaining items. + * Otherwise this function returns 0. + */ +int nghttp2_pq_each(nghttp2_pq *pq, nghttp2_pq_item_cb fun, void *arg); + +/* + * Removes |item| from priority queue. + */ +void nghttp2_pq_remove(nghttp2_pq *pq, nghttp2_pq_entry *item); + +#endif /* NGHTTP2_PQ_H */ diff --git a/contrib/libs/nghttp2/lib/nghttp2_priority_spec.c b/contrib/libs/nghttp2/lib/nghttp2_priority_spec.c index 7b74acfcbd..c2196e3063 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_priority_spec.c +++ b/contrib/libs/nghttp2/lib/nghttp2_priority_spec.c @@ -1,52 +1,52 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * 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_priority_spec.h" - -void nghttp2_priority_spec_init(nghttp2_priority_spec *pri_spec, - int32_t stream_id, int32_t weight, - int exclusive) { - pri_spec->stream_id = stream_id; - pri_spec->weight = weight; - pri_spec->exclusive = exclusive != 0; -} - -void nghttp2_priority_spec_default_init(nghttp2_priority_spec *pri_spec) { - pri_spec->stream_id = 0; - pri_spec->weight = NGHTTP2_DEFAULT_WEIGHT; - pri_spec->exclusive = 0; -} - -int nghttp2_priority_spec_check_default(const nghttp2_priority_spec *pri_spec) { - return pri_spec->stream_id == 0 && - pri_spec->weight == NGHTTP2_DEFAULT_WEIGHT && pri_spec->exclusive == 0; -} - -void nghttp2_priority_spec_normalize_weight(nghttp2_priority_spec *pri_spec) { - if (pri_spec->weight < NGHTTP2_MIN_WEIGHT) { - pri_spec->weight = NGHTTP2_MIN_WEIGHT; - } else if (pri_spec->weight > NGHTTP2_MAX_WEIGHT) { - pri_spec->weight = NGHTTP2_MAX_WEIGHT; - } -} +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * 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_priority_spec.h" + +void nghttp2_priority_spec_init(nghttp2_priority_spec *pri_spec, + int32_t stream_id, int32_t weight, + int exclusive) { + pri_spec->stream_id = stream_id; + pri_spec->weight = weight; + pri_spec->exclusive = exclusive != 0; +} + +void nghttp2_priority_spec_default_init(nghttp2_priority_spec *pri_spec) { + pri_spec->stream_id = 0; + pri_spec->weight = NGHTTP2_DEFAULT_WEIGHT; + pri_spec->exclusive = 0; +} + +int nghttp2_priority_spec_check_default(const nghttp2_priority_spec *pri_spec) { + return pri_spec->stream_id == 0 && + pri_spec->weight == NGHTTP2_DEFAULT_WEIGHT && pri_spec->exclusive == 0; +} + +void nghttp2_priority_spec_normalize_weight(nghttp2_priority_spec *pri_spec) { + if (pri_spec->weight < NGHTTP2_MIN_WEIGHT) { + pri_spec->weight = NGHTTP2_MIN_WEIGHT; + } else if (pri_spec->weight > NGHTTP2_MAX_WEIGHT) { + pri_spec->weight = NGHTTP2_MAX_WEIGHT; + } +} diff --git a/contrib/libs/nghttp2/lib/nghttp2_priority_spec.h b/contrib/libs/nghttp2/lib/nghttp2_priority_spec.h index d11c5f2163..92ece822a8 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_priority_spec.h +++ b/contrib/libs/nghttp2/lib/nghttp2_priority_spec.h @@ -1,42 +1,42 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * 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_PRIORITY_SPEC_H -#define NGHTTP2_PRIORITY_SPEC_H - -#ifdef HAVE_CONFIG_H +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * 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_PRIORITY_SPEC_H +#define NGHTTP2_PRIORITY_SPEC_H + +#ifdef HAVE_CONFIG_H # include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <nghttp2/nghttp2.h> - -/* - * This function normalizes pri_spec->weight if it is out of range. - * If pri_spec->weight is less than NGHTTP2_MIN_WEIGHT, it is set to - * NGHTTP2_MIN_WEIGHT. If pri_spec->weight is larger than - * NGHTTP2_MAX_WEIGHT, it is set to NGHTTP2_MAX_WEIGHT. - */ -void nghttp2_priority_spec_normalize_weight(nghttp2_priority_spec *pri_spec); - -#endif /* NGHTTP2_PRIORITY_SPEC_H */ +#endif /* HAVE_CONFIG_H */ + +#include <nghttp2/nghttp2.h> + +/* + * This function normalizes pri_spec->weight if it is out of range. + * If pri_spec->weight is less than NGHTTP2_MIN_WEIGHT, it is set to + * NGHTTP2_MIN_WEIGHT. If pri_spec->weight is larger than + * NGHTTP2_MAX_WEIGHT, it is set to NGHTTP2_MAX_WEIGHT. + */ +void nghttp2_priority_spec_normalize_weight(nghttp2_priority_spec *pri_spec); + +#endif /* NGHTTP2_PRIORITY_SPEC_H */ diff --git a/contrib/libs/nghttp2/lib/nghttp2_queue.c b/contrib/libs/nghttp2/lib/nghttp2_queue.c index 41fdddb722..055eb69c7e 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_queue.c +++ b/contrib/libs/nghttp2/lib/nghttp2_queue.c @@ -1,85 +1,85 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * 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_queue.h" - -#include <string.h> -#include <assert.h> - -void nghttp2_queue_init(nghttp2_queue *queue) { - queue->front = queue->back = NULL; -} - -void nghttp2_queue_free(nghttp2_queue *queue) { - if (!queue) { - return; - } else { - nghttp2_queue_cell *p = queue->front; - while (p) { - nghttp2_queue_cell *next = p->next; - free(p); - p = next; - } - } -} - -int nghttp2_queue_push(nghttp2_queue *queue, void *data) { - nghttp2_queue_cell *new_cell = - (nghttp2_queue_cell *)malloc(sizeof(nghttp2_queue_cell)); - if (!new_cell) { - return NGHTTP2_ERR_NOMEM; - } - new_cell->data = data; - new_cell->next = NULL; - if (queue->back) { - queue->back->next = new_cell; - queue->back = new_cell; - - } else { - queue->front = queue->back = new_cell; - } - return 0; -} - -void nghttp2_queue_pop(nghttp2_queue *queue) { - nghttp2_queue_cell *front = queue->front; - assert(front); - queue->front = front->next; - if (front == queue->back) { - queue->back = NULL; - } - free(front); -} - -void *nghttp2_queue_front(nghttp2_queue *queue) { - assert(queue->front); - return queue->front->data; -} - -void *nghttp2_queue_back(nghttp2_queue *queue) { - assert(queue->back); - return queue->back->data; -} - -int nghttp2_queue_empty(nghttp2_queue *queue) { return queue->front == NULL; } +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * 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_queue.h" + +#include <string.h> +#include <assert.h> + +void nghttp2_queue_init(nghttp2_queue *queue) { + queue->front = queue->back = NULL; +} + +void nghttp2_queue_free(nghttp2_queue *queue) { + if (!queue) { + return; + } else { + nghttp2_queue_cell *p = queue->front; + while (p) { + nghttp2_queue_cell *next = p->next; + free(p); + p = next; + } + } +} + +int nghttp2_queue_push(nghttp2_queue *queue, void *data) { + nghttp2_queue_cell *new_cell = + (nghttp2_queue_cell *)malloc(sizeof(nghttp2_queue_cell)); + if (!new_cell) { + return NGHTTP2_ERR_NOMEM; + } + new_cell->data = data; + new_cell->next = NULL; + if (queue->back) { + queue->back->next = new_cell; + queue->back = new_cell; + + } else { + queue->front = queue->back = new_cell; + } + return 0; +} + +void nghttp2_queue_pop(nghttp2_queue *queue) { + nghttp2_queue_cell *front = queue->front; + assert(front); + queue->front = front->next; + if (front == queue->back) { + queue->back = NULL; + } + free(front); +} + +void *nghttp2_queue_front(nghttp2_queue *queue) { + assert(queue->front); + return queue->front->data; +} + +void *nghttp2_queue_back(nghttp2_queue *queue) { + assert(queue->back); + return queue->back->data; +} + +int nghttp2_queue_empty(nghttp2_queue *queue) { return queue->front == NULL; } diff --git a/contrib/libs/nghttp2/lib/nghttp2_queue.h b/contrib/libs/nghttp2/lib/nghttp2_queue.h index 8249693899..a06fa6c7a4 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_queue.h +++ b/contrib/libs/nghttp2/lib/nghttp2_queue.h @@ -1,51 +1,51 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * 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_QUEUE_H -#define NGHTTP2_QUEUE_H - -#ifdef HAVE_CONFIG_H +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * 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_QUEUE_H +#define NGHTTP2_QUEUE_H + +#ifdef HAVE_CONFIG_H # include "config.h" -#endif /* HAVE_CONFIG_H */ - -#include <nghttp2/nghttp2.h> - -typedef struct nghttp2_queue_cell { - void *data; - struct nghttp2_queue_cell *next; -} nghttp2_queue_cell; - -typedef struct { - nghttp2_queue_cell *front, *back; -} nghttp2_queue; - -void nghttp2_queue_init(nghttp2_queue *queue); -void nghttp2_queue_free(nghttp2_queue *queue); -int nghttp2_queue_push(nghttp2_queue *queue, void *data); -void nghttp2_queue_pop(nghttp2_queue *queue); -void *nghttp2_queue_front(nghttp2_queue *queue); -void *nghttp2_queue_back(nghttp2_queue *queue); -int nghttp2_queue_empty(nghttp2_queue *queue); - -#endif /* NGHTTP2_QUEUE_H */ +#endif /* HAVE_CONFIG_H */ + +#include <nghttp2/nghttp2.h> + +typedef struct nghttp2_queue_cell { + void *data; + struct nghttp2_queue_cell *next; +} nghttp2_queue_cell; + +typedef struct { + nghttp2_queue_cell *front, *back; +} nghttp2_queue; + +void nghttp2_queue_init(nghttp2_queue *queue); +void nghttp2_queue_free(nghttp2_queue *queue); +int nghttp2_queue_push(nghttp2_queue *queue, void *data); +void nghttp2_queue_pop(nghttp2_queue *queue); +void *nghttp2_queue_front(nghttp2_queue *queue); +void *nghttp2_queue_back(nghttp2_queue *queue); +int nghttp2_queue_empty(nghttp2_queue *queue); + +#endif /* NGHTTP2_QUEUE_H */ diff --git a/contrib/libs/nghttp2/lib/nghttp2_rcbuf.c b/contrib/libs/nghttp2/lib/nghttp2_rcbuf.c index ee2900066a..7e7814d2d3 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_rcbuf.c +++ b/contrib/libs/nghttp2/lib/nghttp2_rcbuf.c @@ -1,102 +1,102 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2016 Tatsuhiro Tsujikawa - * - * 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_rcbuf.h" - -#include <string.h> -#include <assert.h> - -#include "nghttp2_mem.h" -#include "nghttp2_helper.h" - -int nghttp2_rcbuf_new(nghttp2_rcbuf **rcbuf_ptr, size_t size, - nghttp2_mem *mem) { - uint8_t *p; - - p = nghttp2_mem_malloc(mem, sizeof(nghttp2_rcbuf) + size); - if (p == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - *rcbuf_ptr = (void *)p; - - (*rcbuf_ptr)->mem_user_data = mem->mem_user_data; - (*rcbuf_ptr)->free = mem->free; - (*rcbuf_ptr)->base = p + sizeof(nghttp2_rcbuf); - (*rcbuf_ptr)->len = size; - (*rcbuf_ptr)->ref = 1; - - return 0; -} - -int nghttp2_rcbuf_new2(nghttp2_rcbuf **rcbuf_ptr, const uint8_t *src, - size_t srclen, nghttp2_mem *mem) { - int rv; - - rv = nghttp2_rcbuf_new(rcbuf_ptr, srclen + 1, mem); - if (rv != 0) { - return rv; - } - - (*rcbuf_ptr)->len = srclen; - *nghttp2_cpymem((*rcbuf_ptr)->base, src, srclen) = '\0'; - - return 0; -} - -/* - * Frees |rcbuf| itself, regardless of its reference cout. - */ -void nghttp2_rcbuf_del(nghttp2_rcbuf *rcbuf) { - nghttp2_mem_free2(rcbuf->free, rcbuf, rcbuf->mem_user_data); -} - -void nghttp2_rcbuf_incref(nghttp2_rcbuf *rcbuf) { - if (rcbuf->ref == -1) { - return; - } - - ++rcbuf->ref; -} - -void nghttp2_rcbuf_decref(nghttp2_rcbuf *rcbuf) { - if (rcbuf == NULL || rcbuf->ref == -1) { - return; - } - - assert(rcbuf->ref > 0); - - if (--rcbuf->ref == 0) { - nghttp2_rcbuf_del(rcbuf); - } -} - -nghttp2_vec nghttp2_rcbuf_get_buf(nghttp2_rcbuf *rcbuf) { - nghttp2_vec res = {rcbuf->base, rcbuf->len}; - return res; -} - -int nghttp2_rcbuf_is_static(const nghttp2_rcbuf *rcbuf) { - return rcbuf->ref == -1; -} +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2016 Tatsuhiro Tsujikawa + * + * 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_rcbuf.h" + +#include <string.h> +#include <assert.h> + +#include "nghttp2_mem.h" +#include "nghttp2_helper.h" + +int nghttp2_rcbuf_new(nghttp2_rcbuf **rcbuf_ptr, size_t size, + nghttp2_mem *mem) { + uint8_t *p; + + p = nghttp2_mem_malloc(mem, sizeof(nghttp2_rcbuf) + size); + if (p == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + *rcbuf_ptr = (void *)p; + + (*rcbuf_ptr)->mem_user_data = mem->mem_user_data; + (*rcbuf_ptr)->free = mem->free; + (*rcbuf_ptr)->base = p + sizeof(nghttp2_rcbuf); + (*rcbuf_ptr)->len = size; + (*rcbuf_ptr)->ref = 1; + + return 0; +} + +int nghttp2_rcbuf_new2(nghttp2_rcbuf **rcbuf_ptr, const uint8_t *src, + size_t srclen, nghttp2_mem *mem) { + int rv; + + rv = nghttp2_rcbuf_new(rcbuf_ptr, srclen + 1, mem); + if (rv != 0) { + return rv; + } + + (*rcbuf_ptr)->len = srclen; + *nghttp2_cpymem((*rcbuf_ptr)->base, src, srclen) = '\0'; + + return 0; +} + +/* + * Frees |rcbuf| itself, regardless of its reference cout. + */ +void nghttp2_rcbuf_del(nghttp2_rcbuf *rcbuf) { + nghttp2_mem_free2(rcbuf->free, rcbuf, rcbuf->mem_user_data); +} + +void nghttp2_rcbuf_incref(nghttp2_rcbuf *rcbuf) { + if (rcbuf->ref == -1) { + return; + } + + ++rcbuf->ref; +} + +void nghttp2_rcbuf_decref(nghttp2_rcbuf *rcbuf) { + if (rcbuf == NULL || rcbuf->ref == -1) { + return; + } + + assert(rcbuf->ref > 0); + + if (--rcbuf->ref == 0) { + nghttp2_rcbuf_del(rcbuf); + } +} + +nghttp2_vec nghttp2_rcbuf_get_buf(nghttp2_rcbuf *rcbuf) { + nghttp2_vec res = {rcbuf->base, rcbuf->len}; + return res; +} + +int nghttp2_rcbuf_is_static(const nghttp2_rcbuf *rcbuf) { + return rcbuf->ref == -1; +} diff --git a/contrib/libs/nghttp2/lib/nghttp2_rcbuf.h b/contrib/libs/nghttp2/lib/nghttp2_rcbuf.h index 98dedde3ac..6814e709fb 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_rcbuf.h +++ b/contrib/libs/nghttp2/lib/nghttp2_rcbuf.h @@ -1,80 +1,80 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2016 Tatsuhiro Tsujikawa - * - * 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_RCBUF_H -#define NGHTTP2_RCBUF_H - -#ifdef HAVE_CONFIG_H +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2016 Tatsuhiro Tsujikawa + * + * 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_RCBUF_H +#define NGHTTP2_RCBUF_H + +#ifdef HAVE_CONFIG_H # include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <nghttp2/nghttp2.h> - -struct nghttp2_rcbuf { - /* custom memory allocator belongs to the mem parameter when - creating this object. */ - void *mem_user_data; - nghttp2_free free; - /* The pointer to the underlying buffer */ - uint8_t *base; - /* Size of buffer pointed by |base|. */ - size_t len; - /* Reference count */ - int32_t ref; -}; - -/* - * Allocates nghttp2_rcbuf object with |size| as initial buffer size. - * When the function succeeds, the reference count becomes 1. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM: - * Out of memory. - */ -int nghttp2_rcbuf_new(nghttp2_rcbuf **rcbuf_ptr, size_t size, nghttp2_mem *mem); - -/* - * Like nghttp2_rcbuf_new(), but initializes the buffer with |src| of - * length |srclen|. This function allocates additional byte at the - * end and puts '\0' into it, so that the resulting buffer could be - * used as NULL-terminated string. Still (*rcbuf_ptr)->len equals to - * |srclen|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM: - * Out of memory. - */ -int nghttp2_rcbuf_new2(nghttp2_rcbuf **rcbuf_ptr, const uint8_t *src, - size_t srclen, nghttp2_mem *mem); - -/* - * Frees |rcbuf| itself, regardless of its reference cout. - */ -void nghttp2_rcbuf_del(nghttp2_rcbuf *rcbuf); - -#endif /* NGHTTP2_RCBUF_H */ +#endif /* HAVE_CONFIG_H */ + +#include <nghttp2/nghttp2.h> + +struct nghttp2_rcbuf { + /* custom memory allocator belongs to the mem parameter when + creating this object. */ + void *mem_user_data; + nghttp2_free free; + /* The pointer to the underlying buffer */ + uint8_t *base; + /* Size of buffer pointed by |base|. */ + size_t len; + /* Reference count */ + int32_t ref; +}; + +/* + * Allocates nghttp2_rcbuf object with |size| as initial buffer size. + * When the function succeeds, the reference count becomes 1. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM: + * Out of memory. + */ +int nghttp2_rcbuf_new(nghttp2_rcbuf **rcbuf_ptr, size_t size, nghttp2_mem *mem); + +/* + * Like nghttp2_rcbuf_new(), but initializes the buffer with |src| of + * length |srclen|. This function allocates additional byte at the + * end and puts '\0' into it, so that the resulting buffer could be + * used as NULL-terminated string. Still (*rcbuf_ptr)->len equals to + * |srclen|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM: + * Out of memory. + */ +int nghttp2_rcbuf_new2(nghttp2_rcbuf **rcbuf_ptr, const uint8_t *src, + size_t srclen, nghttp2_mem *mem); + +/* + * Frees |rcbuf| itself, regardless of its reference cout. + */ +void nghttp2_rcbuf_del(nghttp2_rcbuf *rcbuf); + +#endif /* NGHTTP2_RCBUF_H */ diff --git a/contrib/libs/nghttp2/lib/nghttp2_session.c b/contrib/libs/nghttp2/lib/nghttp2_session.c index c34596ee88..36f1179f72 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_session.c +++ b/contrib/libs/nghttp2/lib/nghttp2_session.c @@ -1,523 +1,523 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * 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_session.h" - -#include <string.h> -#include <stddef.h> -#include <stdio.h> -#include <assert.h> -#include <stdarg.h> - -#include "nghttp2_helper.h" -#include "nghttp2_net.h" -#include "nghttp2_priority_spec.h" -#include "nghttp2_option.h" -#include "nghttp2_http.h" -#include "nghttp2_pq.h" -#include "nghttp2_debug.h" - -/* - * Returns non-zero if the number of outgoing opened streams is larger - * than or equal to - * remote_settings.max_concurrent_streams. - */ -static int -session_is_outgoing_concurrent_streams_max(nghttp2_session *session) { - return session->remote_settings.max_concurrent_streams <= - session->num_outgoing_streams; -} - -/* - * Returns non-zero if the number of incoming opened streams is larger - * than or equal to - * local_settings.max_concurrent_streams. - */ -static int -session_is_incoming_concurrent_streams_max(nghttp2_session *session) { - return session->local_settings.max_concurrent_streams <= - session->num_incoming_streams; -} - -/* - * Returns non-zero if the number of incoming opened streams is larger - * than or equal to - * session->pending_local_max_concurrent_stream. - */ -static int -session_is_incoming_concurrent_streams_pending_max(nghttp2_session *session) { - return session->pending_local_max_concurrent_stream <= - session->num_incoming_streams; -} - -/* - * Returns non-zero if |lib_error| is non-fatal error. - */ -static int is_non_fatal(int lib_error_code) { - return lib_error_code < 0 && lib_error_code > NGHTTP2_ERR_FATAL; -} - -int nghttp2_is_fatal(int lib_error_code) { - return lib_error_code < NGHTTP2_ERR_FATAL; -} - -static int session_enforce_http_messaging(nghttp2_session *session) { - return (session->opt_flags & NGHTTP2_OPTMASK_NO_HTTP_MESSAGING) == 0; -} - -/* - * Returns nonzero if |frame| is trailer headers. - */ -static int session_trailer_headers(nghttp2_session *session, - nghttp2_stream *stream, - nghttp2_frame *frame) { - if (!stream || frame->hd.type != NGHTTP2_HEADERS) { - return 0; - } - if (session->server) { - return frame->headers.cat == NGHTTP2_HCAT_HEADERS; - } - - return frame->headers.cat == NGHTTP2_HCAT_HEADERS && - (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) == 0; -} - -/* Returns nonzero if the |stream| is in reserved(remote) state */ -static int state_reserved_remote(nghttp2_session *session, - nghttp2_stream *stream) { - return stream->state == NGHTTP2_STREAM_RESERVED && - !nghttp2_session_is_my_stream_id(session, stream->stream_id); -} - -/* Returns nonzero if the |stream| is in reserved(local) state */ -static int state_reserved_local(nghttp2_session *session, - nghttp2_stream *stream) { - return stream->state == NGHTTP2_STREAM_RESERVED && - nghttp2_session_is_my_stream_id(session, stream->stream_id); -} - -/* - * Checks whether received stream_id is valid. This function returns - * 1 if it succeeds, or 0. - */ -static int session_is_new_peer_stream_id(nghttp2_session *session, - int32_t stream_id) { - return stream_id != 0 && - !nghttp2_session_is_my_stream_id(session, stream_id) && - session->last_recv_stream_id < stream_id; -} - -static int session_detect_idle_stream(nghttp2_session *session, - int32_t stream_id) { - /* Assume that stream object with stream_id does not exist */ - if (nghttp2_session_is_my_stream_id(session, stream_id)) { - if (session->last_sent_stream_id < stream_id) { - return 1; - } - return 0; - } - if (session_is_new_peer_stream_id(session, stream_id)) { - return 1; - } - return 0; -} - -static int check_ext_type_set(const uint8_t *ext_types, uint8_t type) { - return (ext_types[type / 8] & (1 << (type & 0x7))) > 0; -} - -static int session_call_error_callback(nghttp2_session *session, - int lib_error_code, const char *fmt, - ...) { - size_t bufsize; - va_list ap; - char *buf; - int rv; - nghttp2_mem *mem; - - if (!session->callbacks.error_callback && - !session->callbacks.error_callback2) { - return 0; - } - - mem = &session->mem; - - va_start(ap, fmt); - rv = vsnprintf(NULL, 0, fmt, ap); - va_end(ap); - - if (rv < 0) { - return NGHTTP2_ERR_NOMEM; - } - - bufsize = (size_t)(rv + 1); - - buf = nghttp2_mem_malloc(mem, bufsize); - if (buf == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - va_start(ap, fmt); - rv = vsnprintf(buf, bufsize, fmt, ap); - va_end(ap); - - if (rv < 0) { - nghttp2_mem_free(mem, buf); - /* vsnprintf may return error because of various things we can - imagine, but typically we don't want to drop session just for - debug callback. */ - DEBUGF("error_callback: vsnprintf failed. The template was %s\n", fmt); - return 0; - } - - if (session->callbacks.error_callback2) { - rv = session->callbacks.error_callback2(session, lib_error_code, buf, - (size_t)rv, session->user_data); - } else { - rv = session->callbacks.error_callback(session, buf, (size_t)rv, - session->user_data); - } - - nghttp2_mem_free(mem, buf); - - if (rv != 0) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static int session_terminate_session(nghttp2_session *session, - int32_t last_stream_id, - uint32_t error_code, const char *reason) { - int rv; - const uint8_t *debug_data; - size_t debug_datalen; - - if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) { - return 0; - } - - /* Ignore all incoming frames because we are going to tear down the - session. */ - session->iframe.state = NGHTTP2_IB_IGN_ALL; - - if (reason == NULL) { - debug_data = NULL; - debug_datalen = 0; - } else { - debug_data = (const uint8_t *)reason; - debug_datalen = strlen(reason); - } - - rv = nghttp2_session_add_goaway(session, last_stream_id, error_code, - debug_data, debug_datalen, - NGHTTP2_GOAWAY_AUX_TERM_ON_SEND); - - if (rv != 0) { - return rv; - } - - session->goaway_flags |= NGHTTP2_GOAWAY_TERM_ON_SEND; - - return 0; -} - -int nghttp2_session_terminate_session(nghttp2_session *session, - uint32_t error_code) { - return session_terminate_session(session, session->last_proc_stream_id, - error_code, NULL); -} - -int nghttp2_session_terminate_session2(nghttp2_session *session, - int32_t last_stream_id, - uint32_t error_code) { - return session_terminate_session(session, last_stream_id, error_code, NULL); -} - -int nghttp2_session_terminate_session_with_reason(nghttp2_session *session, - uint32_t error_code, - const char *reason) { - return session_terminate_session(session, session->last_proc_stream_id, - error_code, reason); -} - -int nghttp2_session_is_my_stream_id(nghttp2_session *session, - int32_t stream_id) { - int rem; - if (stream_id == 0) { - return 0; - } - rem = stream_id & 0x1; - if (session->server) { - return rem == 0; - } - return rem == 1; -} - -nghttp2_stream *nghttp2_session_get_stream(nghttp2_session *session, - int32_t stream_id) { - nghttp2_stream *stream; - - stream = (nghttp2_stream *)nghttp2_map_find(&session->streams, stream_id); - - if (stream == NULL || (stream->flags & NGHTTP2_STREAM_FLAG_CLOSED) || - stream->state == NGHTTP2_STREAM_IDLE) { - return NULL; - } - - return stream; -} - -nghttp2_stream *nghttp2_session_get_stream_raw(nghttp2_session *session, - int32_t stream_id) { - return (nghttp2_stream *)nghttp2_map_find(&session->streams, stream_id); -} - -static void session_inbound_frame_reset(nghttp2_session *session) { - nghttp2_inbound_frame *iframe = &session->iframe; - nghttp2_mem *mem = &session->mem; - /* A bit risky code, since if this function is called from - nghttp2_session_new(), we rely on the fact that - iframe->frame.hd.type is 0, so that no free is performed. */ - switch (iframe->frame.hd.type) { - case NGHTTP2_DATA: - break; - case NGHTTP2_HEADERS: - nghttp2_frame_headers_free(&iframe->frame.headers, mem); - break; - case NGHTTP2_PRIORITY: - nghttp2_frame_priority_free(&iframe->frame.priority); - break; - case NGHTTP2_RST_STREAM: - nghttp2_frame_rst_stream_free(&iframe->frame.rst_stream); - break; - case NGHTTP2_SETTINGS: - nghttp2_frame_settings_free(&iframe->frame.settings, mem); - - nghttp2_mem_free(mem, iframe->iv); - - iframe->iv = NULL; - iframe->niv = 0; - iframe->max_niv = 0; - - break; - case NGHTTP2_PUSH_PROMISE: - nghttp2_frame_push_promise_free(&iframe->frame.push_promise, mem); - break; - case NGHTTP2_PING: - nghttp2_frame_ping_free(&iframe->frame.ping); - break; - case NGHTTP2_GOAWAY: - nghttp2_frame_goaway_free(&iframe->frame.goaway, mem); - break; - case NGHTTP2_WINDOW_UPDATE: - nghttp2_frame_window_update_free(&iframe->frame.window_update); - break; - default: - /* extension frame */ - if (check_ext_type_set(session->user_recv_ext_types, - iframe->frame.hd.type)) { - nghttp2_frame_extension_free(&iframe->frame.ext); - } else { - switch (iframe->frame.hd.type) { - case NGHTTP2_ALTSVC: - if ((session->builtin_recv_ext_types & NGHTTP2_TYPEMASK_ALTSVC) == 0) { - break; - } - nghttp2_frame_altsvc_free(&iframe->frame.ext, mem); - break; +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * 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_session.h" + +#include <string.h> +#include <stddef.h> +#include <stdio.h> +#include <assert.h> +#include <stdarg.h> + +#include "nghttp2_helper.h" +#include "nghttp2_net.h" +#include "nghttp2_priority_spec.h" +#include "nghttp2_option.h" +#include "nghttp2_http.h" +#include "nghttp2_pq.h" +#include "nghttp2_debug.h" + +/* + * Returns non-zero if the number of outgoing opened streams is larger + * than or equal to + * remote_settings.max_concurrent_streams. + */ +static int +session_is_outgoing_concurrent_streams_max(nghttp2_session *session) { + return session->remote_settings.max_concurrent_streams <= + session->num_outgoing_streams; +} + +/* + * Returns non-zero if the number of incoming opened streams is larger + * than or equal to + * local_settings.max_concurrent_streams. + */ +static int +session_is_incoming_concurrent_streams_max(nghttp2_session *session) { + return session->local_settings.max_concurrent_streams <= + session->num_incoming_streams; +} + +/* + * Returns non-zero if the number of incoming opened streams is larger + * than or equal to + * session->pending_local_max_concurrent_stream. + */ +static int +session_is_incoming_concurrent_streams_pending_max(nghttp2_session *session) { + return session->pending_local_max_concurrent_stream <= + session->num_incoming_streams; +} + +/* + * Returns non-zero if |lib_error| is non-fatal error. + */ +static int is_non_fatal(int lib_error_code) { + return lib_error_code < 0 && lib_error_code > NGHTTP2_ERR_FATAL; +} + +int nghttp2_is_fatal(int lib_error_code) { + return lib_error_code < NGHTTP2_ERR_FATAL; +} + +static int session_enforce_http_messaging(nghttp2_session *session) { + return (session->opt_flags & NGHTTP2_OPTMASK_NO_HTTP_MESSAGING) == 0; +} + +/* + * Returns nonzero if |frame| is trailer headers. + */ +static int session_trailer_headers(nghttp2_session *session, + nghttp2_stream *stream, + nghttp2_frame *frame) { + if (!stream || frame->hd.type != NGHTTP2_HEADERS) { + return 0; + } + if (session->server) { + return frame->headers.cat == NGHTTP2_HCAT_HEADERS; + } + + return frame->headers.cat == NGHTTP2_HCAT_HEADERS && + (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) == 0; +} + +/* Returns nonzero if the |stream| is in reserved(remote) state */ +static int state_reserved_remote(nghttp2_session *session, + nghttp2_stream *stream) { + return stream->state == NGHTTP2_STREAM_RESERVED && + !nghttp2_session_is_my_stream_id(session, stream->stream_id); +} + +/* Returns nonzero if the |stream| is in reserved(local) state */ +static int state_reserved_local(nghttp2_session *session, + nghttp2_stream *stream) { + return stream->state == NGHTTP2_STREAM_RESERVED && + nghttp2_session_is_my_stream_id(session, stream->stream_id); +} + +/* + * Checks whether received stream_id is valid. This function returns + * 1 if it succeeds, or 0. + */ +static int session_is_new_peer_stream_id(nghttp2_session *session, + int32_t stream_id) { + return stream_id != 0 && + !nghttp2_session_is_my_stream_id(session, stream_id) && + session->last_recv_stream_id < stream_id; +} + +static int session_detect_idle_stream(nghttp2_session *session, + int32_t stream_id) { + /* Assume that stream object with stream_id does not exist */ + if (nghttp2_session_is_my_stream_id(session, stream_id)) { + if (session->last_sent_stream_id < stream_id) { + return 1; + } + return 0; + } + if (session_is_new_peer_stream_id(session, stream_id)) { + return 1; + } + return 0; +} + +static int check_ext_type_set(const uint8_t *ext_types, uint8_t type) { + return (ext_types[type / 8] & (1 << (type & 0x7))) > 0; +} + +static int session_call_error_callback(nghttp2_session *session, + int lib_error_code, const char *fmt, + ...) { + size_t bufsize; + va_list ap; + char *buf; + int rv; + nghttp2_mem *mem; + + if (!session->callbacks.error_callback && + !session->callbacks.error_callback2) { + return 0; + } + + mem = &session->mem; + + va_start(ap, fmt); + rv = vsnprintf(NULL, 0, fmt, ap); + va_end(ap); + + if (rv < 0) { + return NGHTTP2_ERR_NOMEM; + } + + bufsize = (size_t)(rv + 1); + + buf = nghttp2_mem_malloc(mem, bufsize); + if (buf == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + va_start(ap, fmt); + rv = vsnprintf(buf, bufsize, fmt, ap); + va_end(ap); + + if (rv < 0) { + nghttp2_mem_free(mem, buf); + /* vsnprintf may return error because of various things we can + imagine, but typically we don't want to drop session just for + debug callback. */ + DEBUGF("error_callback: vsnprintf failed. The template was %s\n", fmt); + return 0; + } + + if (session->callbacks.error_callback2) { + rv = session->callbacks.error_callback2(session, lib_error_code, buf, + (size_t)rv, session->user_data); + } else { + rv = session->callbacks.error_callback(session, buf, (size_t)rv, + session->user_data); + } + + nghttp2_mem_free(mem, buf); + + if (rv != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int session_terminate_session(nghttp2_session *session, + int32_t last_stream_id, + uint32_t error_code, const char *reason) { + int rv; + const uint8_t *debug_data; + size_t debug_datalen; + + if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) { + return 0; + } + + /* Ignore all incoming frames because we are going to tear down the + session. */ + session->iframe.state = NGHTTP2_IB_IGN_ALL; + + if (reason == NULL) { + debug_data = NULL; + debug_datalen = 0; + } else { + debug_data = (const uint8_t *)reason; + debug_datalen = strlen(reason); + } + + rv = nghttp2_session_add_goaway(session, last_stream_id, error_code, + debug_data, debug_datalen, + NGHTTP2_GOAWAY_AUX_TERM_ON_SEND); + + if (rv != 0) { + return rv; + } + + session->goaway_flags |= NGHTTP2_GOAWAY_TERM_ON_SEND; + + return 0; +} + +int nghttp2_session_terminate_session(nghttp2_session *session, + uint32_t error_code) { + return session_terminate_session(session, session->last_proc_stream_id, + error_code, NULL); +} + +int nghttp2_session_terminate_session2(nghttp2_session *session, + int32_t last_stream_id, + uint32_t error_code) { + return session_terminate_session(session, last_stream_id, error_code, NULL); +} + +int nghttp2_session_terminate_session_with_reason(nghttp2_session *session, + uint32_t error_code, + const char *reason) { + return session_terminate_session(session, session->last_proc_stream_id, + error_code, reason); +} + +int nghttp2_session_is_my_stream_id(nghttp2_session *session, + int32_t stream_id) { + int rem; + if (stream_id == 0) { + return 0; + } + rem = stream_id & 0x1; + if (session->server) { + return rem == 0; + } + return rem == 1; +} + +nghttp2_stream *nghttp2_session_get_stream(nghttp2_session *session, + int32_t stream_id) { + nghttp2_stream *stream; + + stream = (nghttp2_stream *)nghttp2_map_find(&session->streams, stream_id); + + if (stream == NULL || (stream->flags & NGHTTP2_STREAM_FLAG_CLOSED) || + stream->state == NGHTTP2_STREAM_IDLE) { + return NULL; + } + + return stream; +} + +nghttp2_stream *nghttp2_session_get_stream_raw(nghttp2_session *session, + int32_t stream_id) { + return (nghttp2_stream *)nghttp2_map_find(&session->streams, stream_id); +} + +static void session_inbound_frame_reset(nghttp2_session *session) { + nghttp2_inbound_frame *iframe = &session->iframe; + nghttp2_mem *mem = &session->mem; + /* A bit risky code, since if this function is called from + nghttp2_session_new(), we rely on the fact that + iframe->frame.hd.type is 0, so that no free is performed. */ + switch (iframe->frame.hd.type) { + case NGHTTP2_DATA: + break; + case NGHTTP2_HEADERS: + nghttp2_frame_headers_free(&iframe->frame.headers, mem); + break; + case NGHTTP2_PRIORITY: + nghttp2_frame_priority_free(&iframe->frame.priority); + break; + case NGHTTP2_RST_STREAM: + nghttp2_frame_rst_stream_free(&iframe->frame.rst_stream); + break; + case NGHTTP2_SETTINGS: + nghttp2_frame_settings_free(&iframe->frame.settings, mem); + + nghttp2_mem_free(mem, iframe->iv); + + iframe->iv = NULL; + iframe->niv = 0; + iframe->max_niv = 0; + + break; + case NGHTTP2_PUSH_PROMISE: + nghttp2_frame_push_promise_free(&iframe->frame.push_promise, mem); + break; + case NGHTTP2_PING: + nghttp2_frame_ping_free(&iframe->frame.ping); + break; + case NGHTTP2_GOAWAY: + nghttp2_frame_goaway_free(&iframe->frame.goaway, mem); + break; + case NGHTTP2_WINDOW_UPDATE: + nghttp2_frame_window_update_free(&iframe->frame.window_update); + break; + default: + /* extension frame */ + if (check_ext_type_set(session->user_recv_ext_types, + iframe->frame.hd.type)) { + nghttp2_frame_extension_free(&iframe->frame.ext); + } else { + switch (iframe->frame.hd.type) { + case NGHTTP2_ALTSVC: + if ((session->builtin_recv_ext_types & NGHTTP2_TYPEMASK_ALTSVC) == 0) { + break; + } + nghttp2_frame_altsvc_free(&iframe->frame.ext, mem); + break; case NGHTTP2_ORIGIN: if ((session->builtin_recv_ext_types & NGHTTP2_TYPEMASK_ORIGIN) == 0) { break; } nghttp2_frame_origin_free(&iframe->frame.ext, mem); break; - } - } - - break; - } - - memset(&iframe->frame, 0, sizeof(nghttp2_frame)); - memset(&iframe->ext_frame_payload, 0, sizeof(nghttp2_ext_frame_payload)); - - iframe->state = NGHTTP2_IB_READ_HEAD; - - nghttp2_buf_wrap_init(&iframe->sbuf, iframe->raw_sbuf, - sizeof(iframe->raw_sbuf)); - iframe->sbuf.mark += NGHTTP2_FRAME_HDLEN; - - nghttp2_buf_free(&iframe->lbuf, mem); - nghttp2_buf_wrap_init(&iframe->lbuf, NULL, 0); - - iframe->raw_lbuf = NULL; - - iframe->payloadleft = 0; - iframe->padlen = 0; -} - -static void init_settings(nghttp2_settings_storage *settings) { - settings->header_table_size = NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE; - settings->enable_push = 1; - settings->max_concurrent_streams = NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS; - settings->initial_window_size = NGHTTP2_INITIAL_WINDOW_SIZE; - settings->max_frame_size = NGHTTP2_MAX_FRAME_SIZE_MIN; - settings->max_header_list_size = UINT32_MAX; -} - -static void active_outbound_item_reset(nghttp2_active_outbound_item *aob, - nghttp2_mem *mem) { - DEBUGF("send: reset nghttp2_active_outbound_item\n"); - DEBUGF("send: aob->item = %p\n", aob->item); - nghttp2_outbound_item_free(aob->item, mem); - nghttp2_mem_free(mem, aob->item); - aob->item = NULL; - nghttp2_bufs_reset(&aob->framebufs); - aob->state = NGHTTP2_OB_POP_ITEM; -} - -int nghttp2_enable_strict_preface = 1; - -static int session_new(nghttp2_session **session_ptr, - const nghttp2_session_callbacks *callbacks, - void *user_data, int server, - const nghttp2_option *option, nghttp2_mem *mem) { - int rv; - size_t nbuffer; - size_t max_deflate_dynamic_table_size = - NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE; - - if (mem == NULL) { - mem = nghttp2_mem_default(); - } - - *session_ptr = nghttp2_mem_calloc(mem, 1, sizeof(nghttp2_session)); - if (*session_ptr == NULL) { - rv = NGHTTP2_ERR_NOMEM; - goto fail_session; - } - - (*session_ptr)->mem = *mem; - mem = &(*session_ptr)->mem; - - /* next_stream_id is initialized in either - nghttp2_session_client_new2 or nghttp2_session_server_new2 */ - - nghttp2_stream_init(&(*session_ptr)->root, 0, NGHTTP2_STREAM_FLAG_NONE, - NGHTTP2_STREAM_IDLE, NGHTTP2_DEFAULT_WEIGHT, 0, 0, NULL, - mem); - - (*session_ptr)->remote_window_size = NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE; - (*session_ptr)->recv_window_size = 0; - (*session_ptr)->consumed_size = 0; - (*session_ptr)->recv_reduction = 0; - (*session_ptr)->local_window_size = NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE; - - (*session_ptr)->goaway_flags = NGHTTP2_GOAWAY_NONE; - (*session_ptr)->local_last_stream_id = (1u << 31) - 1; - (*session_ptr)->remote_last_stream_id = (1u << 31) - 1; - - (*session_ptr)->pending_local_max_concurrent_stream = - NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS; - (*session_ptr)->pending_enable_push = 1; - - if (server) { - (*session_ptr)->server = 1; - } - - init_settings(&(*session_ptr)->remote_settings); - init_settings(&(*session_ptr)->local_settings); - - (*session_ptr)->max_incoming_reserved_streams = - NGHTTP2_MAX_INCOMING_RESERVED_STREAMS; - - /* Limit max outgoing concurrent streams to sensible value */ - (*session_ptr)->remote_settings.max_concurrent_streams = 100; - - (*session_ptr)->max_send_header_block_length = NGHTTP2_MAX_HEADERSLEN; + } + } + + break; + } + + memset(&iframe->frame, 0, sizeof(nghttp2_frame)); + memset(&iframe->ext_frame_payload, 0, sizeof(nghttp2_ext_frame_payload)); + + iframe->state = NGHTTP2_IB_READ_HEAD; + + nghttp2_buf_wrap_init(&iframe->sbuf, iframe->raw_sbuf, + sizeof(iframe->raw_sbuf)); + iframe->sbuf.mark += NGHTTP2_FRAME_HDLEN; + + nghttp2_buf_free(&iframe->lbuf, mem); + nghttp2_buf_wrap_init(&iframe->lbuf, NULL, 0); + + iframe->raw_lbuf = NULL; + + iframe->payloadleft = 0; + iframe->padlen = 0; +} + +static void init_settings(nghttp2_settings_storage *settings) { + settings->header_table_size = NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE; + settings->enable_push = 1; + settings->max_concurrent_streams = NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS; + settings->initial_window_size = NGHTTP2_INITIAL_WINDOW_SIZE; + settings->max_frame_size = NGHTTP2_MAX_FRAME_SIZE_MIN; + settings->max_header_list_size = UINT32_MAX; +} + +static void active_outbound_item_reset(nghttp2_active_outbound_item *aob, + nghttp2_mem *mem) { + DEBUGF("send: reset nghttp2_active_outbound_item\n"); + DEBUGF("send: aob->item = %p\n", aob->item); + nghttp2_outbound_item_free(aob->item, mem); + nghttp2_mem_free(mem, aob->item); + aob->item = NULL; + nghttp2_bufs_reset(&aob->framebufs); + aob->state = NGHTTP2_OB_POP_ITEM; +} + +int nghttp2_enable_strict_preface = 1; + +static int session_new(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data, int server, + const nghttp2_option *option, nghttp2_mem *mem) { + int rv; + size_t nbuffer; + size_t max_deflate_dynamic_table_size = + NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE; + + if (mem == NULL) { + mem = nghttp2_mem_default(); + } + + *session_ptr = nghttp2_mem_calloc(mem, 1, sizeof(nghttp2_session)); + if (*session_ptr == NULL) { + rv = NGHTTP2_ERR_NOMEM; + goto fail_session; + } + + (*session_ptr)->mem = *mem; + mem = &(*session_ptr)->mem; + + /* next_stream_id is initialized in either + nghttp2_session_client_new2 or nghttp2_session_server_new2 */ + + nghttp2_stream_init(&(*session_ptr)->root, 0, NGHTTP2_STREAM_FLAG_NONE, + NGHTTP2_STREAM_IDLE, NGHTTP2_DEFAULT_WEIGHT, 0, 0, NULL, + mem); + + (*session_ptr)->remote_window_size = NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE; + (*session_ptr)->recv_window_size = 0; + (*session_ptr)->consumed_size = 0; + (*session_ptr)->recv_reduction = 0; + (*session_ptr)->local_window_size = NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE; + + (*session_ptr)->goaway_flags = NGHTTP2_GOAWAY_NONE; + (*session_ptr)->local_last_stream_id = (1u << 31) - 1; + (*session_ptr)->remote_last_stream_id = (1u << 31) - 1; + + (*session_ptr)->pending_local_max_concurrent_stream = + NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS; + (*session_ptr)->pending_enable_push = 1; + + if (server) { + (*session_ptr)->server = 1; + } + + init_settings(&(*session_ptr)->remote_settings); + init_settings(&(*session_ptr)->local_settings); + + (*session_ptr)->max_incoming_reserved_streams = + NGHTTP2_MAX_INCOMING_RESERVED_STREAMS; + + /* Limit max outgoing concurrent streams to sensible value */ + (*session_ptr)->remote_settings.max_concurrent_streams = 100; + + (*session_ptr)->max_send_header_block_length = NGHTTP2_MAX_HEADERSLEN; (*session_ptr)->max_outbound_ack = NGHTTP2_DEFAULT_MAX_OBQ_FLOOD_ITEM; (*session_ptr)->max_settings = NGHTTP2_DEFAULT_MAX_SETTINGS; - - if (option) { - if ((option->opt_set_mask & NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE) && - option->no_auto_window_update) { - - (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE; - } - - if (option->opt_set_mask & NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS) { - - (*session_ptr)->remote_settings.max_concurrent_streams = - option->peer_max_concurrent_streams; - } - - if (option->opt_set_mask & NGHTTP2_OPT_MAX_RESERVED_REMOTE_STREAMS) { - - (*session_ptr)->max_incoming_reserved_streams = - option->max_reserved_remote_streams; - } - - if ((option->opt_set_mask & NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC) && - option->no_recv_client_magic) { - - (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_RECV_CLIENT_MAGIC; - } - - if ((option->opt_set_mask & NGHTTP2_OPT_NO_HTTP_MESSAGING) && - option->no_http_messaging) { - - (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_HTTP_MESSAGING; - } - - if (option->opt_set_mask & NGHTTP2_OPT_USER_RECV_EXT_TYPES) { - memcpy((*session_ptr)->user_recv_ext_types, option->user_recv_ext_types, - sizeof((*session_ptr)->user_recv_ext_types)); - } - - if (option->opt_set_mask & NGHTTP2_OPT_BUILTIN_RECV_EXT_TYPES) { - (*session_ptr)->builtin_recv_ext_types = option->builtin_recv_ext_types; - } - - if ((option->opt_set_mask & NGHTTP2_OPT_NO_AUTO_PING_ACK) && - option->no_auto_ping_ack) { - (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_AUTO_PING_ACK; - } - - if (option->opt_set_mask & NGHTTP2_OPT_MAX_SEND_HEADER_BLOCK_LENGTH) { - (*session_ptr)->max_send_header_block_length = - option->max_send_header_block_length; - } - - if (option->opt_set_mask & NGHTTP2_OPT_MAX_DEFLATE_DYNAMIC_TABLE_SIZE) { - max_deflate_dynamic_table_size = option->max_deflate_dynamic_table_size; - } - - if ((option->opt_set_mask & NGHTTP2_OPT_NO_CLOSED_STREAMS) && - option->no_closed_streams) { - (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_CLOSED_STREAMS; - } + + if (option) { + if ((option->opt_set_mask & NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE) && + option->no_auto_window_update) { + + (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE; + } + + if (option->opt_set_mask & NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS) { + + (*session_ptr)->remote_settings.max_concurrent_streams = + option->peer_max_concurrent_streams; + } + + if (option->opt_set_mask & NGHTTP2_OPT_MAX_RESERVED_REMOTE_STREAMS) { + + (*session_ptr)->max_incoming_reserved_streams = + option->max_reserved_remote_streams; + } + + if ((option->opt_set_mask & NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC) && + option->no_recv_client_magic) { + + (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_RECV_CLIENT_MAGIC; + } + + if ((option->opt_set_mask & NGHTTP2_OPT_NO_HTTP_MESSAGING) && + option->no_http_messaging) { + + (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_HTTP_MESSAGING; + } + + if (option->opt_set_mask & NGHTTP2_OPT_USER_RECV_EXT_TYPES) { + memcpy((*session_ptr)->user_recv_ext_types, option->user_recv_ext_types, + sizeof((*session_ptr)->user_recv_ext_types)); + } + + if (option->opt_set_mask & NGHTTP2_OPT_BUILTIN_RECV_EXT_TYPES) { + (*session_ptr)->builtin_recv_ext_types = option->builtin_recv_ext_types; + } + + if ((option->opt_set_mask & NGHTTP2_OPT_NO_AUTO_PING_ACK) && + option->no_auto_ping_ack) { + (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_AUTO_PING_ACK; + } + + if (option->opt_set_mask & NGHTTP2_OPT_MAX_SEND_HEADER_BLOCK_LENGTH) { + (*session_ptr)->max_send_header_block_length = + option->max_send_header_block_length; + } + + if (option->opt_set_mask & NGHTTP2_OPT_MAX_DEFLATE_DYNAMIC_TABLE_SIZE) { + max_deflate_dynamic_table_size = option->max_deflate_dynamic_table_size; + } + + if ((option->opt_set_mask & NGHTTP2_OPT_NO_CLOSED_STREAMS) && + option->no_closed_streams) { + (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_CLOSED_STREAMS; + } if (option->opt_set_mask & NGHTTP2_OPT_MAX_OUTBOUND_ACK) { (*session_ptr)->max_outbound_ack = option->max_outbound_ack; @@ -527,438 +527,438 @@ static int session_new(nghttp2_session **session_ptr, option->max_settings) { (*session_ptr)->max_settings = option->max_settings; } - } - - rv = nghttp2_hd_deflate_init2(&(*session_ptr)->hd_deflater, - max_deflate_dynamic_table_size, mem); - if (rv != 0) { - goto fail_hd_deflater; - } - rv = nghttp2_hd_inflate_init(&(*session_ptr)->hd_inflater, mem); - if (rv != 0) { - goto fail_hd_inflater; - } - rv = nghttp2_map_init(&(*session_ptr)->streams, mem); - if (rv != 0) { - goto fail_map; - } - - nbuffer = ((*session_ptr)->max_send_header_block_length + - NGHTTP2_FRAMEBUF_CHUNKLEN - 1) / - NGHTTP2_FRAMEBUF_CHUNKLEN; - - if (nbuffer == 0) { - nbuffer = 1; - } - - /* 1 for Pad Field. */ - rv = nghttp2_bufs_init3(&(*session_ptr)->aob.framebufs, - NGHTTP2_FRAMEBUF_CHUNKLEN, nbuffer, 1, - NGHTTP2_FRAME_HDLEN + 1, mem); - if (rv != 0) { - goto fail_aob_framebuf; - } - - active_outbound_item_reset(&(*session_ptr)->aob, mem); - - (*session_ptr)->callbacks = *callbacks; - (*session_ptr)->user_data = user_data; - - session_inbound_frame_reset(*session_ptr); - - if (nghttp2_enable_strict_preface) { - nghttp2_inbound_frame *iframe = &(*session_ptr)->iframe; - - if (server && ((*session_ptr)->opt_flags & - NGHTTP2_OPTMASK_NO_RECV_CLIENT_MAGIC) == 0) { - iframe->state = NGHTTP2_IB_READ_CLIENT_MAGIC; - iframe->payloadleft = NGHTTP2_CLIENT_MAGIC_LEN; - } else { - iframe->state = NGHTTP2_IB_READ_FIRST_SETTINGS; - } - - if (!server) { - (*session_ptr)->aob.state = NGHTTP2_OB_SEND_CLIENT_MAGIC; - nghttp2_bufs_add(&(*session_ptr)->aob.framebufs, NGHTTP2_CLIENT_MAGIC, - NGHTTP2_CLIENT_MAGIC_LEN); - } - } - - return 0; - -fail_aob_framebuf: - nghttp2_map_free(&(*session_ptr)->streams); -fail_map: - nghttp2_hd_inflate_free(&(*session_ptr)->hd_inflater); -fail_hd_inflater: - nghttp2_hd_deflate_free(&(*session_ptr)->hd_deflater); -fail_hd_deflater: - nghttp2_mem_free(mem, *session_ptr); -fail_session: - return rv; -} - -int nghttp2_session_client_new(nghttp2_session **session_ptr, - const nghttp2_session_callbacks *callbacks, - void *user_data) { - return nghttp2_session_client_new3(session_ptr, callbacks, user_data, NULL, - NULL); -} - -int nghttp2_session_client_new2(nghttp2_session **session_ptr, - const nghttp2_session_callbacks *callbacks, - void *user_data, const nghttp2_option *option) { - return nghttp2_session_client_new3(session_ptr, callbacks, user_data, option, - NULL); -} - -int nghttp2_session_client_new3(nghttp2_session **session_ptr, - const nghttp2_session_callbacks *callbacks, - void *user_data, const nghttp2_option *option, - nghttp2_mem *mem) { - int rv; - nghttp2_session *session; - - rv = session_new(&session, callbacks, user_data, 0, option, mem); - - if (rv != 0) { - return rv; - } - /* IDs for use in client */ - session->next_stream_id = 1; - - *session_ptr = session; - - return 0; -} - -int nghttp2_session_server_new(nghttp2_session **session_ptr, - const nghttp2_session_callbacks *callbacks, - void *user_data) { - return nghttp2_session_server_new3(session_ptr, callbacks, user_data, NULL, - NULL); -} - -int nghttp2_session_server_new2(nghttp2_session **session_ptr, - const nghttp2_session_callbacks *callbacks, - void *user_data, const nghttp2_option *option) { - return nghttp2_session_server_new3(session_ptr, callbacks, user_data, option, - NULL); -} - -int nghttp2_session_server_new3(nghttp2_session **session_ptr, - const nghttp2_session_callbacks *callbacks, - void *user_data, const nghttp2_option *option, - nghttp2_mem *mem) { - int rv; - nghttp2_session *session; - - rv = session_new(&session, callbacks, user_data, 1, option, mem); - - if (rv != 0) { - return rv; - } - /* IDs for use in client */ - session->next_stream_id = 2; - - *session_ptr = session; - - return 0; -} - + } + + rv = nghttp2_hd_deflate_init2(&(*session_ptr)->hd_deflater, + max_deflate_dynamic_table_size, mem); + if (rv != 0) { + goto fail_hd_deflater; + } + rv = nghttp2_hd_inflate_init(&(*session_ptr)->hd_inflater, mem); + if (rv != 0) { + goto fail_hd_inflater; + } + rv = nghttp2_map_init(&(*session_ptr)->streams, mem); + if (rv != 0) { + goto fail_map; + } + + nbuffer = ((*session_ptr)->max_send_header_block_length + + NGHTTP2_FRAMEBUF_CHUNKLEN - 1) / + NGHTTP2_FRAMEBUF_CHUNKLEN; + + if (nbuffer == 0) { + nbuffer = 1; + } + + /* 1 for Pad Field. */ + rv = nghttp2_bufs_init3(&(*session_ptr)->aob.framebufs, + NGHTTP2_FRAMEBUF_CHUNKLEN, nbuffer, 1, + NGHTTP2_FRAME_HDLEN + 1, mem); + if (rv != 0) { + goto fail_aob_framebuf; + } + + active_outbound_item_reset(&(*session_ptr)->aob, mem); + + (*session_ptr)->callbacks = *callbacks; + (*session_ptr)->user_data = user_data; + + session_inbound_frame_reset(*session_ptr); + + if (nghttp2_enable_strict_preface) { + nghttp2_inbound_frame *iframe = &(*session_ptr)->iframe; + + if (server && ((*session_ptr)->opt_flags & + NGHTTP2_OPTMASK_NO_RECV_CLIENT_MAGIC) == 0) { + iframe->state = NGHTTP2_IB_READ_CLIENT_MAGIC; + iframe->payloadleft = NGHTTP2_CLIENT_MAGIC_LEN; + } else { + iframe->state = NGHTTP2_IB_READ_FIRST_SETTINGS; + } + + if (!server) { + (*session_ptr)->aob.state = NGHTTP2_OB_SEND_CLIENT_MAGIC; + nghttp2_bufs_add(&(*session_ptr)->aob.framebufs, NGHTTP2_CLIENT_MAGIC, + NGHTTP2_CLIENT_MAGIC_LEN); + } + } + + return 0; + +fail_aob_framebuf: + nghttp2_map_free(&(*session_ptr)->streams); +fail_map: + nghttp2_hd_inflate_free(&(*session_ptr)->hd_inflater); +fail_hd_inflater: + nghttp2_hd_deflate_free(&(*session_ptr)->hd_deflater); +fail_hd_deflater: + nghttp2_mem_free(mem, *session_ptr); +fail_session: + return rv; +} + +int nghttp2_session_client_new(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data) { + return nghttp2_session_client_new3(session_ptr, callbacks, user_data, NULL, + NULL); +} + +int nghttp2_session_client_new2(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data, const nghttp2_option *option) { + return nghttp2_session_client_new3(session_ptr, callbacks, user_data, option, + NULL); +} + +int nghttp2_session_client_new3(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data, const nghttp2_option *option, + nghttp2_mem *mem) { + int rv; + nghttp2_session *session; + + rv = session_new(&session, callbacks, user_data, 0, option, mem); + + if (rv != 0) { + return rv; + } + /* IDs for use in client */ + session->next_stream_id = 1; + + *session_ptr = session; + + return 0; +} + +int nghttp2_session_server_new(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data) { + return nghttp2_session_server_new3(session_ptr, callbacks, user_data, NULL, + NULL); +} + +int nghttp2_session_server_new2(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data, const nghttp2_option *option) { + return nghttp2_session_server_new3(session_ptr, callbacks, user_data, option, + NULL); +} + +int nghttp2_session_server_new3(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data, const nghttp2_option *option, + nghttp2_mem *mem) { + int rv; + nghttp2_session *session; + + rv = session_new(&session, callbacks, user_data, 1, option, mem); + + if (rv != 0) { + return rv; + } + /* IDs for use in client */ + session->next_stream_id = 2; + + *session_ptr = session; + + return 0; +} + static int free_streams(void *entry, void *ptr) { - nghttp2_session *session; - nghttp2_stream *stream; - nghttp2_outbound_item *item; - nghttp2_mem *mem; - - session = (nghttp2_session *)ptr; - mem = &session->mem; - stream = (nghttp2_stream *)entry; - item = stream->item; - - if (item && !item->queued && item != session->aob.item) { - nghttp2_outbound_item_free(item, mem); - nghttp2_mem_free(mem, item); - } - - nghttp2_stream_free(stream); - nghttp2_mem_free(mem, stream); - - return 0; -} - -static void ob_q_free(nghttp2_outbound_queue *q, nghttp2_mem *mem) { - nghttp2_outbound_item *item, *next; - for (item = q->head; item;) { - next = item->qnext; - nghttp2_outbound_item_free(item, mem); - nghttp2_mem_free(mem, item); - item = next; - } -} - -static int inflight_settings_new(nghttp2_inflight_settings **settings_ptr, - const nghttp2_settings_entry *iv, size_t niv, - nghttp2_mem *mem) { - *settings_ptr = nghttp2_mem_malloc(mem, sizeof(nghttp2_inflight_settings)); - if (!*settings_ptr) { - return NGHTTP2_ERR_NOMEM; - } - - if (niv > 0) { - (*settings_ptr)->iv = nghttp2_frame_iv_copy(iv, niv, mem); - if (!(*settings_ptr)->iv) { - nghttp2_mem_free(mem, *settings_ptr); - return NGHTTP2_ERR_NOMEM; - } - } else { - (*settings_ptr)->iv = NULL; - } - - (*settings_ptr)->niv = niv; - (*settings_ptr)->next = NULL; - - return 0; -} - -static void inflight_settings_del(nghttp2_inflight_settings *settings, - nghttp2_mem *mem) { - if (!settings) { - return; - } - - nghttp2_mem_free(mem, settings->iv); - nghttp2_mem_free(mem, settings); -} - -void nghttp2_session_del(nghttp2_session *session) { - nghttp2_mem *mem; - nghttp2_inflight_settings *settings; - - if (session == NULL) { - return; - } - - mem = &session->mem; - - for (settings = session->inflight_settings_head; settings;) { - nghttp2_inflight_settings *next = settings->next; - inflight_settings_del(settings, mem); - settings = next; - } - - nghttp2_stream_free(&session->root); - - /* Have to free streams first, so that we can check - stream->item->queued */ - nghttp2_map_each_free(&session->streams, free_streams, session); - nghttp2_map_free(&session->streams); - - ob_q_free(&session->ob_urgent, mem); - ob_q_free(&session->ob_reg, mem); - ob_q_free(&session->ob_syn, mem); - - active_outbound_item_reset(&session->aob, mem); - session_inbound_frame_reset(session); - nghttp2_hd_deflate_free(&session->hd_deflater); - nghttp2_hd_inflate_free(&session->hd_inflater); - nghttp2_bufs_free(&session->aob.framebufs); - nghttp2_mem_free(mem, session); -} - -int nghttp2_session_reprioritize_stream( - nghttp2_session *session, nghttp2_stream *stream, - const nghttp2_priority_spec *pri_spec_in) { - int rv; - nghttp2_stream *dep_stream = NULL; - nghttp2_priority_spec pri_spec_default; - const nghttp2_priority_spec *pri_spec = pri_spec_in; - - assert(pri_spec->stream_id != stream->stream_id); - - if (!nghttp2_stream_in_dep_tree(stream)) { - return 0; - } - - if (pri_spec->stream_id != 0) { - dep_stream = nghttp2_session_get_stream_raw(session, pri_spec->stream_id); - - if (!dep_stream && - session_detect_idle_stream(session, pri_spec->stream_id)) { - - nghttp2_priority_spec_default_init(&pri_spec_default); - - dep_stream = nghttp2_session_open_stream( - session, pri_spec->stream_id, NGHTTP2_FLAG_NONE, &pri_spec_default, - NGHTTP2_STREAM_IDLE, NULL); - - if (dep_stream == NULL) { - return NGHTTP2_ERR_NOMEM; - } - } else if (!dep_stream || !nghttp2_stream_in_dep_tree(dep_stream)) { - nghttp2_priority_spec_default_init(&pri_spec_default); - pri_spec = &pri_spec_default; - } - } - - if (pri_spec->stream_id == 0) { - dep_stream = &session->root; - } else if (nghttp2_stream_dep_find_ancestor(dep_stream, stream)) { - DEBUGF("stream: cycle detected, dep_stream(%p)=%d stream(%p)=%d\n", - dep_stream, dep_stream->stream_id, stream, stream->stream_id); - - nghttp2_stream_dep_remove_subtree(dep_stream); - rv = nghttp2_stream_dep_add_subtree(stream->dep_prev, dep_stream); - if (rv != 0) { - return rv; - } - } - - assert(dep_stream); - - if (dep_stream == stream->dep_prev && !pri_spec->exclusive) { - /* This is minor optimization when just weight is changed. */ - nghttp2_stream_change_weight(stream, pri_spec->weight); - - return 0; - } - - nghttp2_stream_dep_remove_subtree(stream); - - /* We have to update weight after removing stream from tree */ - stream->weight = pri_spec->weight; - - if (pri_spec->exclusive) { - rv = nghttp2_stream_dep_insert_subtree(dep_stream, stream); - } else { - rv = nghttp2_stream_dep_add_subtree(dep_stream, stream); - } - - if (rv != 0) { - return rv; - } - - return 0; -} - -int nghttp2_session_add_item(nghttp2_session *session, - nghttp2_outbound_item *item) { - /* TODO Return error if stream is not found for the frame requiring - stream presence. */ - int rv = 0; - nghttp2_stream *stream; - nghttp2_frame *frame; - - frame = &item->frame; - stream = nghttp2_session_get_stream(session, frame->hd.stream_id); - - switch (frame->hd.type) { - case NGHTTP2_DATA: - if (!stream) { - return NGHTTP2_ERR_STREAM_CLOSED; - } - - if (stream->item) { - return NGHTTP2_ERR_DATA_EXIST; - } - - rv = nghttp2_stream_attach_item(stream, item); - - if (rv != 0) { - return rv; - } - - return 0; - case NGHTTP2_HEADERS: - /* We push request HEADERS and push response HEADERS to - dedicated queue because their transmission is affected by - SETTINGS_MAX_CONCURRENT_STREAMS */ - /* TODO If 2 HEADERS are submitted for reserved stream, then - both of them are queued into ob_syn, which is not - desirable. */ - if (frame->headers.cat == NGHTTP2_HCAT_REQUEST || - (stream && stream->state == NGHTTP2_STREAM_RESERVED)) { - nghttp2_outbound_queue_push(&session->ob_syn, item); - item->queued = 1; - return 0; - ; - } - - nghttp2_outbound_queue_push(&session->ob_reg, item); - item->queued = 1; - return 0; - case NGHTTP2_SETTINGS: - case NGHTTP2_PING: - nghttp2_outbound_queue_push(&session->ob_urgent, item); - item->queued = 1; - return 0; - case NGHTTP2_RST_STREAM: - if (stream) { - stream->state = NGHTTP2_STREAM_CLOSING; - } - nghttp2_outbound_queue_push(&session->ob_reg, item); - item->queued = 1; - return 0; - case NGHTTP2_PUSH_PROMISE: { - nghttp2_headers_aux_data *aux_data; - nghttp2_priority_spec pri_spec; - - aux_data = &item->aux_data.headers; - - if (!stream) { - return NGHTTP2_ERR_STREAM_CLOSED; - } - - nghttp2_priority_spec_init(&pri_spec, stream->stream_id, - NGHTTP2_DEFAULT_WEIGHT, 0); - - if (!nghttp2_session_open_stream( - session, frame->push_promise.promised_stream_id, - NGHTTP2_STREAM_FLAG_NONE, &pri_spec, NGHTTP2_STREAM_RESERVED, - aux_data->stream_user_data)) { - return NGHTTP2_ERR_NOMEM; - } - - /* We don't have to call nghttp2_session_adjust_closed_stream() - here, since stream->stream_id is local stream_id, and it does - not affect closed stream count. */ - - nghttp2_outbound_queue_push(&session->ob_reg, item); - item->queued = 1; - - return 0; - } - case NGHTTP2_WINDOW_UPDATE: - if (stream) { - stream->window_update_queued = 1; - } else if (frame->hd.stream_id == 0) { - session->window_update_queued = 1; - } - nghttp2_outbound_queue_push(&session->ob_reg, item); - item->queued = 1; - return 0; - default: - nghttp2_outbound_queue_push(&session->ob_reg, item); - item->queued = 1; - return 0; - } -} - -int nghttp2_session_add_rst_stream(nghttp2_session *session, int32_t stream_id, - uint32_t error_code) { - int rv; - nghttp2_outbound_item *item; - nghttp2_frame *frame; - nghttp2_stream *stream; - nghttp2_mem *mem; - - mem = &session->mem; - stream = nghttp2_session_get_stream(session, stream_id); - if (stream && stream->state == NGHTTP2_STREAM_CLOSING) { - return 0; - } - + nghttp2_session *session; + nghttp2_stream *stream; + nghttp2_outbound_item *item; + nghttp2_mem *mem; + + session = (nghttp2_session *)ptr; + mem = &session->mem; + stream = (nghttp2_stream *)entry; + item = stream->item; + + if (item && !item->queued && item != session->aob.item) { + nghttp2_outbound_item_free(item, mem); + nghttp2_mem_free(mem, item); + } + + nghttp2_stream_free(stream); + nghttp2_mem_free(mem, stream); + + return 0; +} + +static void ob_q_free(nghttp2_outbound_queue *q, nghttp2_mem *mem) { + nghttp2_outbound_item *item, *next; + for (item = q->head; item;) { + next = item->qnext; + nghttp2_outbound_item_free(item, mem); + nghttp2_mem_free(mem, item); + item = next; + } +} + +static int inflight_settings_new(nghttp2_inflight_settings **settings_ptr, + const nghttp2_settings_entry *iv, size_t niv, + nghttp2_mem *mem) { + *settings_ptr = nghttp2_mem_malloc(mem, sizeof(nghttp2_inflight_settings)); + if (!*settings_ptr) { + return NGHTTP2_ERR_NOMEM; + } + + if (niv > 0) { + (*settings_ptr)->iv = nghttp2_frame_iv_copy(iv, niv, mem); + if (!(*settings_ptr)->iv) { + nghttp2_mem_free(mem, *settings_ptr); + return NGHTTP2_ERR_NOMEM; + } + } else { + (*settings_ptr)->iv = NULL; + } + + (*settings_ptr)->niv = niv; + (*settings_ptr)->next = NULL; + + return 0; +} + +static void inflight_settings_del(nghttp2_inflight_settings *settings, + nghttp2_mem *mem) { + if (!settings) { + return; + } + + nghttp2_mem_free(mem, settings->iv); + nghttp2_mem_free(mem, settings); +} + +void nghttp2_session_del(nghttp2_session *session) { + nghttp2_mem *mem; + nghttp2_inflight_settings *settings; + + if (session == NULL) { + return; + } + + mem = &session->mem; + + for (settings = session->inflight_settings_head; settings;) { + nghttp2_inflight_settings *next = settings->next; + inflight_settings_del(settings, mem); + settings = next; + } + + nghttp2_stream_free(&session->root); + + /* Have to free streams first, so that we can check + stream->item->queued */ + nghttp2_map_each_free(&session->streams, free_streams, session); + nghttp2_map_free(&session->streams); + + ob_q_free(&session->ob_urgent, mem); + ob_q_free(&session->ob_reg, mem); + ob_q_free(&session->ob_syn, mem); + + active_outbound_item_reset(&session->aob, mem); + session_inbound_frame_reset(session); + nghttp2_hd_deflate_free(&session->hd_deflater); + nghttp2_hd_inflate_free(&session->hd_inflater); + nghttp2_bufs_free(&session->aob.framebufs); + nghttp2_mem_free(mem, session); +} + +int nghttp2_session_reprioritize_stream( + nghttp2_session *session, nghttp2_stream *stream, + const nghttp2_priority_spec *pri_spec_in) { + int rv; + nghttp2_stream *dep_stream = NULL; + nghttp2_priority_spec pri_spec_default; + const nghttp2_priority_spec *pri_spec = pri_spec_in; + + assert(pri_spec->stream_id != stream->stream_id); + + if (!nghttp2_stream_in_dep_tree(stream)) { + return 0; + } + + if (pri_spec->stream_id != 0) { + dep_stream = nghttp2_session_get_stream_raw(session, pri_spec->stream_id); + + if (!dep_stream && + session_detect_idle_stream(session, pri_spec->stream_id)) { + + nghttp2_priority_spec_default_init(&pri_spec_default); + + dep_stream = nghttp2_session_open_stream( + session, pri_spec->stream_id, NGHTTP2_FLAG_NONE, &pri_spec_default, + NGHTTP2_STREAM_IDLE, NULL); + + if (dep_stream == NULL) { + return NGHTTP2_ERR_NOMEM; + } + } else if (!dep_stream || !nghttp2_stream_in_dep_tree(dep_stream)) { + nghttp2_priority_spec_default_init(&pri_spec_default); + pri_spec = &pri_spec_default; + } + } + + if (pri_spec->stream_id == 0) { + dep_stream = &session->root; + } else if (nghttp2_stream_dep_find_ancestor(dep_stream, stream)) { + DEBUGF("stream: cycle detected, dep_stream(%p)=%d stream(%p)=%d\n", + dep_stream, dep_stream->stream_id, stream, stream->stream_id); + + nghttp2_stream_dep_remove_subtree(dep_stream); + rv = nghttp2_stream_dep_add_subtree(stream->dep_prev, dep_stream); + if (rv != 0) { + return rv; + } + } + + assert(dep_stream); + + if (dep_stream == stream->dep_prev && !pri_spec->exclusive) { + /* This is minor optimization when just weight is changed. */ + nghttp2_stream_change_weight(stream, pri_spec->weight); + + return 0; + } + + nghttp2_stream_dep_remove_subtree(stream); + + /* We have to update weight after removing stream from tree */ + stream->weight = pri_spec->weight; + + if (pri_spec->exclusive) { + rv = nghttp2_stream_dep_insert_subtree(dep_stream, stream); + } else { + rv = nghttp2_stream_dep_add_subtree(dep_stream, stream); + } + + if (rv != 0) { + return rv; + } + + return 0; +} + +int nghttp2_session_add_item(nghttp2_session *session, + nghttp2_outbound_item *item) { + /* TODO Return error if stream is not found for the frame requiring + stream presence. */ + int rv = 0; + nghttp2_stream *stream; + nghttp2_frame *frame; + + frame = &item->frame; + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + + switch (frame->hd.type) { + case NGHTTP2_DATA: + if (!stream) { + return NGHTTP2_ERR_STREAM_CLOSED; + } + + if (stream->item) { + return NGHTTP2_ERR_DATA_EXIST; + } + + rv = nghttp2_stream_attach_item(stream, item); + + if (rv != 0) { + return rv; + } + + return 0; + case NGHTTP2_HEADERS: + /* We push request HEADERS and push response HEADERS to + dedicated queue because their transmission is affected by + SETTINGS_MAX_CONCURRENT_STREAMS */ + /* TODO If 2 HEADERS are submitted for reserved stream, then + both of them are queued into ob_syn, which is not + desirable. */ + if (frame->headers.cat == NGHTTP2_HCAT_REQUEST || + (stream && stream->state == NGHTTP2_STREAM_RESERVED)) { + nghttp2_outbound_queue_push(&session->ob_syn, item); + item->queued = 1; + return 0; + ; + } + + nghttp2_outbound_queue_push(&session->ob_reg, item); + item->queued = 1; + return 0; + case NGHTTP2_SETTINGS: + case NGHTTP2_PING: + nghttp2_outbound_queue_push(&session->ob_urgent, item); + item->queued = 1; + return 0; + case NGHTTP2_RST_STREAM: + if (stream) { + stream->state = NGHTTP2_STREAM_CLOSING; + } + nghttp2_outbound_queue_push(&session->ob_reg, item); + item->queued = 1; + return 0; + case NGHTTP2_PUSH_PROMISE: { + nghttp2_headers_aux_data *aux_data; + nghttp2_priority_spec pri_spec; + + aux_data = &item->aux_data.headers; + + if (!stream) { + return NGHTTP2_ERR_STREAM_CLOSED; + } + + nghttp2_priority_spec_init(&pri_spec, stream->stream_id, + NGHTTP2_DEFAULT_WEIGHT, 0); + + if (!nghttp2_session_open_stream( + session, frame->push_promise.promised_stream_id, + NGHTTP2_STREAM_FLAG_NONE, &pri_spec, NGHTTP2_STREAM_RESERVED, + aux_data->stream_user_data)) { + return NGHTTP2_ERR_NOMEM; + } + + /* We don't have to call nghttp2_session_adjust_closed_stream() + here, since stream->stream_id is local stream_id, and it does + not affect closed stream count. */ + + nghttp2_outbound_queue_push(&session->ob_reg, item); + item->queued = 1; + + return 0; + } + case NGHTTP2_WINDOW_UPDATE: + if (stream) { + stream->window_update_queued = 1; + } else if (frame->hd.stream_id == 0) { + session->window_update_queued = 1; + } + nghttp2_outbound_queue_push(&session->ob_reg, item); + item->queued = 1; + return 0; + default: + nghttp2_outbound_queue_push(&session->ob_reg, item); + item->queued = 1; + return 0; + } +} + +int nghttp2_session_add_rst_stream(nghttp2_session *session, int32_t stream_id, + uint32_t error_code) { + int rv; + nghttp2_outbound_item *item; + nghttp2_frame *frame; + nghttp2_stream *stream; + nghttp2_mem *mem; + + mem = &session->mem; + stream = nghttp2_session_get_stream(session, stream_id); + if (stream && stream->state == NGHTTP2_STREAM_CLOSING) { + return 0; + } + /* Sending RST_STREAM to an idle stream is subject to protocol violation. Historically, nghttp2 allows this. In order not to disrupt the existing applications, we don't error out this case @@ -971,812 +971,812 @@ int nghttp2_session_add_rst_stream(nghttp2_session *session, int32_t stream_id, return 0; } - /* Cancel pending request HEADERS in ob_syn if this RST_STREAM - refers to that stream. */ - if (!session->server && nghttp2_session_is_my_stream_id(session, stream_id) && - nghttp2_outbound_queue_top(&session->ob_syn)) { - nghttp2_headers_aux_data *aux_data; - nghttp2_frame *headers_frame; - - headers_frame = &nghttp2_outbound_queue_top(&session->ob_syn)->frame; - assert(headers_frame->hd.type == NGHTTP2_HEADERS); - + /* Cancel pending request HEADERS in ob_syn if this RST_STREAM + refers to that stream. */ + if (!session->server && nghttp2_session_is_my_stream_id(session, stream_id) && + nghttp2_outbound_queue_top(&session->ob_syn)) { + nghttp2_headers_aux_data *aux_data; + nghttp2_frame *headers_frame; + + headers_frame = &nghttp2_outbound_queue_top(&session->ob_syn)->frame; + assert(headers_frame->hd.type == NGHTTP2_HEADERS); + if (headers_frame->hd.stream_id <= stream_id) { - - for (item = session->ob_syn.head; item; item = item->qnext) { - aux_data = &item->aux_data.headers; - - if (item->frame.hd.stream_id < stream_id) { - continue; - } - - /* stream_id in ob_syn queue must be strictly increasing. If - we found larger ID, then we can break here. */ - if (item->frame.hd.stream_id > stream_id || aux_data->canceled) { - break; - } - - aux_data->error_code = error_code; - aux_data->canceled = 1; - - return 0; - } - } - } - - item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); - if (item == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - nghttp2_outbound_item_init(item); - - frame = &item->frame; - - nghttp2_frame_rst_stream_init(&frame->rst_stream, stream_id, error_code); - rv = nghttp2_session_add_item(session, item); - if (rv != 0) { - nghttp2_frame_rst_stream_free(&frame->rst_stream); - nghttp2_mem_free(mem, item); - return rv; - } - return 0; -} - -nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session, - int32_t stream_id, uint8_t flags, - nghttp2_priority_spec *pri_spec_in, - nghttp2_stream_state initial_state, - void *stream_user_data) { - int rv; - nghttp2_stream *stream; - nghttp2_stream *dep_stream = NULL; - int stream_alloc = 0; - nghttp2_priority_spec pri_spec_default; - nghttp2_priority_spec *pri_spec = pri_spec_in; - nghttp2_mem *mem; - - mem = &session->mem; - stream = nghttp2_session_get_stream_raw(session, stream_id); - - 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; - } - } else { - stream = nghttp2_mem_malloc(mem, sizeof(nghttp2_stream)); - if (stream == NULL) { - return NULL; - } - - stream_alloc = 1; - } - - if (pri_spec->stream_id != 0) { - dep_stream = nghttp2_session_get_stream_raw(session, pri_spec->stream_id); - - if (!dep_stream && - session_detect_idle_stream(session, pri_spec->stream_id)) { - /* Depends on idle stream, which does not exist in memory. - Assign default priority for it. */ - nghttp2_priority_spec_default_init(&pri_spec_default); - - dep_stream = nghttp2_session_open_stream( - session, pri_spec->stream_id, NGHTTP2_FLAG_NONE, &pri_spec_default, - NGHTTP2_STREAM_IDLE, NULL); - - if (dep_stream == NULL) { - if (stream_alloc) { - nghttp2_mem_free(mem, stream); - } - - return NULL; - } - } else if (!dep_stream || !nghttp2_stream_in_dep_tree(dep_stream)) { - /* If dep_stream is not part of dependency tree, stream will get - default priority. This handles the case when - pri_spec->stream_id == stream_id. This happens because we - don't check pri_spec->stream_id against new stream ID in - nghttp2_submit_request. This also handles the case when idle - stream created by PRIORITY frame was opened. Somehow we - first remove the idle stream from dependency tree. This is - done to simplify code base, but ideally we should retain old - dependency. But I'm not sure this adds values. */ - nghttp2_priority_spec_default_init(&pri_spec_default); - pri_spec = &pri_spec_default; - } - } - - if (initial_state == NGHTTP2_STREAM_RESERVED) { - flags |= NGHTTP2_STREAM_FLAG_PUSH; - } - - if (stream_alloc) { - nghttp2_stream_init(stream, stream_id, flags, initial_state, - pri_spec->weight, - (int32_t)session->remote_settings.initial_window_size, - (int32_t)session->local_settings.initial_window_size, - stream_user_data, mem); - + + for (item = session->ob_syn.head; item; item = item->qnext) { + aux_data = &item->aux_data.headers; + + if (item->frame.hd.stream_id < stream_id) { + continue; + } + + /* stream_id in ob_syn queue must be strictly increasing. If + we found larger ID, then we can break here. */ + if (item->frame.hd.stream_id > stream_id || aux_data->canceled) { + break; + } + + aux_data->error_code = error_code; + aux_data->canceled = 1; + + return 0; + } + } + } + + item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); + if (item == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + nghttp2_outbound_item_init(item); + + frame = &item->frame; + + nghttp2_frame_rst_stream_init(&frame->rst_stream, stream_id, error_code); + rv = nghttp2_session_add_item(session, item); + if (rv != 0) { + nghttp2_frame_rst_stream_free(&frame->rst_stream); + nghttp2_mem_free(mem, item); + return rv; + } + return 0; +} + +nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session, + int32_t stream_id, uint8_t flags, + nghttp2_priority_spec *pri_spec_in, + nghttp2_stream_state initial_state, + void *stream_user_data) { + int rv; + nghttp2_stream *stream; + nghttp2_stream *dep_stream = NULL; + int stream_alloc = 0; + nghttp2_priority_spec pri_spec_default; + nghttp2_priority_spec *pri_spec = pri_spec_in; + nghttp2_mem *mem; + + mem = &session->mem; + stream = nghttp2_session_get_stream_raw(session, stream_id); + + 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; + } + } else { + stream = nghttp2_mem_malloc(mem, sizeof(nghttp2_stream)); + if (stream == NULL) { + return NULL; + } + + stream_alloc = 1; + } + + if (pri_spec->stream_id != 0) { + dep_stream = nghttp2_session_get_stream_raw(session, pri_spec->stream_id); + + if (!dep_stream && + session_detect_idle_stream(session, pri_spec->stream_id)) { + /* Depends on idle stream, which does not exist in memory. + Assign default priority for it. */ + nghttp2_priority_spec_default_init(&pri_spec_default); + + dep_stream = nghttp2_session_open_stream( + session, pri_spec->stream_id, NGHTTP2_FLAG_NONE, &pri_spec_default, + NGHTTP2_STREAM_IDLE, NULL); + + if (dep_stream == NULL) { + if (stream_alloc) { + nghttp2_mem_free(mem, stream); + } + + return NULL; + } + } else if (!dep_stream || !nghttp2_stream_in_dep_tree(dep_stream)) { + /* If dep_stream is not part of dependency tree, stream will get + default priority. This handles the case when + pri_spec->stream_id == stream_id. This happens because we + don't check pri_spec->stream_id against new stream ID in + nghttp2_submit_request. This also handles the case when idle + stream created by PRIORITY frame was opened. Somehow we + first remove the idle stream from dependency tree. This is + done to simplify code base, but ideally we should retain old + dependency. But I'm not sure this adds values. */ + nghttp2_priority_spec_default_init(&pri_spec_default); + pri_spec = &pri_spec_default; + } + } + + if (initial_state == NGHTTP2_STREAM_RESERVED) { + flags |= NGHTTP2_STREAM_FLAG_PUSH; + } + + if (stream_alloc) { + nghttp2_stream_init(stream, stream_id, flags, initial_state, + pri_spec->weight, + (int32_t)session->remote_settings.initial_window_size, + (int32_t)session->local_settings.initial_window_size, + stream_user_data, mem); + rv = nghttp2_map_insert(&session->streams, stream_id, stream); - if (rv != 0) { - nghttp2_stream_free(stream); - nghttp2_mem_free(mem, stream); - return NULL; - } - } else { - stream->flags = flags; - stream->state = initial_state; - stream->weight = pri_spec->weight; - stream->stream_user_data = stream_user_data; - } - - switch (initial_state) { - case NGHTTP2_STREAM_RESERVED: - if (nghttp2_session_is_my_stream_id(session, stream_id)) { - /* reserved (local) */ - nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); - } else { - /* reserved (remote) */ - nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR); - ++session->num_incoming_reserved_streams; - } - /* Reserved stream does not count in the concurrent streams - limit. That is one of the DOS vector. */ - break; - case NGHTTP2_STREAM_IDLE: - /* Idle stream does not count toward the concurrent streams limit. - This is used as anchor node in dependency tree. */ - nghttp2_session_keep_idle_stream(session, stream); - break; - default: - if (nghttp2_session_is_my_stream_id(session, stream_id)) { - ++session->num_outgoing_streams; - } else { - ++session->num_incoming_streams; - } - } - - if (pri_spec->stream_id == 0) { - dep_stream = &session->root; - } - - assert(dep_stream); - - if (pri_spec->exclusive) { - rv = nghttp2_stream_dep_insert(dep_stream, stream); - if (rv != 0) { - return NULL; - } - } else { - nghttp2_stream_dep_add(dep_stream, stream); - } - - return stream; -} - -int nghttp2_session_close_stream(nghttp2_session *session, int32_t stream_id, - uint32_t error_code) { - int rv; - nghttp2_stream *stream; - nghttp2_mem *mem; - int is_my_stream_id; - - mem = &session->mem; - stream = nghttp2_session_get_stream(session, stream_id); - - if (!stream) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - - DEBUGF("stream: stream(%p)=%d close\n", stream, stream->stream_id); - - if (stream->item) { - nghttp2_outbound_item *item; - - item = stream->item; - - rv = nghttp2_stream_detach_item(stream); - - if (rv != 0) { - return rv; - } - - /* If item is queued, it will be deleted when it is popped - (nghttp2_session_prep_frame() will fail). If session->aob.item - points to this item, let active_outbound_item_reset() - free the item. */ - if (!item->queued && item != session->aob.item) { - nghttp2_outbound_item_free(item, mem); - nghttp2_mem_free(mem, item); - } - } - - /* We call on_stream_close_callback even if stream->state is - NGHTTP2_STREAM_INITIAL. This will happen while sending request - HEADERS, a local endpoint receives RST_STREAM for that stream. It - may be PROTOCOL_ERROR, but without notifying stream closure will - hang the stream in a local endpoint. - */ - - if (session->callbacks.on_stream_close_callback) { - if (session->callbacks.on_stream_close_callback( - session, stream_id, error_code, session->user_data) != 0) { - - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - } - - is_my_stream_id = nghttp2_session_is_my_stream_id(session, stream_id); - - /* pushed streams which is not opened yet is not counted toward max - concurrent limits */ - if ((stream->flags & NGHTTP2_STREAM_FLAG_PUSH)) { - if (!is_my_stream_id) { - --session->num_incoming_reserved_streams; - } - } else { - if (is_my_stream_id) { - --session->num_outgoing_streams; - } else { - --session->num_incoming_streams; - } - } - - /* Closes both directions just in case they are not closed yet */ - stream->flags |= NGHTTP2_STREAM_FLAG_CLOSED; - - if ((session->opt_flags & NGHTTP2_OPTMASK_NO_CLOSED_STREAMS) == 0 && - session->server && !is_my_stream_id && - nghttp2_stream_in_dep_tree(stream)) { - /* On server side, retain stream at most MAX_CONCURRENT_STREAMS - combined with the current active incoming streams to make - dependency tree work better. */ - nghttp2_session_keep_closed_stream(session, stream); - } else { - rv = nghttp2_session_destroy_stream(session, stream); - if (rv != 0) { - return rv; - } - } - - return 0; -} - -int nghttp2_session_destroy_stream(nghttp2_session *session, - nghttp2_stream *stream) { - nghttp2_mem *mem; - int rv; - - DEBUGF("stream: destroy closed stream(%p)=%d\n", stream, stream->stream_id); - - mem = &session->mem; - - if (nghttp2_stream_in_dep_tree(stream)) { - rv = nghttp2_stream_dep_remove(stream); - if (rv != 0) { - return rv; - } - } - - nghttp2_map_remove(&session->streams, stream->stream_id); - nghttp2_stream_free(stream); - nghttp2_mem_free(mem, stream); - - return 0; -} - -void nghttp2_session_keep_closed_stream(nghttp2_session *session, - nghttp2_stream *stream) { - DEBUGF("stream: keep closed stream(%p)=%d, state=%d\n", stream, - stream->stream_id, stream->state); - - if (session->closed_stream_tail) { - session->closed_stream_tail->closed_next = stream; - stream->closed_prev = session->closed_stream_tail; - } else { - session->closed_stream_head = stream; - } - session->closed_stream_tail = stream; - - ++session->num_closed_streams; -} - -void nghttp2_session_keep_idle_stream(nghttp2_session *session, - nghttp2_stream *stream) { - DEBUGF("stream: keep idle stream(%p)=%d, state=%d\n", stream, - stream->stream_id, stream->state); - - if (session->idle_stream_tail) { - session->idle_stream_tail->closed_next = stream; - stream->closed_prev = session->idle_stream_tail; - } else { - session->idle_stream_head = stream; - } - session->idle_stream_tail = stream; - - ++session->num_idle_streams; -} - -void nghttp2_session_detach_idle_stream(nghttp2_session *session, - nghttp2_stream *stream) { - nghttp2_stream *prev_stream, *next_stream; - - DEBUGF("stream: detach idle stream(%p)=%d, state=%d\n", stream, - stream->stream_id, stream->state); - - prev_stream = stream->closed_prev; - next_stream = stream->closed_next; - - if (prev_stream) { - prev_stream->closed_next = next_stream; - } else { - session->idle_stream_head = next_stream; - } - - if (next_stream) { - next_stream->closed_prev = prev_stream; - } else { - session->idle_stream_tail = prev_stream; - } - - stream->closed_prev = NULL; - stream->closed_next = NULL; - - --session->num_idle_streams; -} - -int nghttp2_session_adjust_closed_stream(nghttp2_session *session) { - size_t num_stream_max; - int rv; - - if (session->local_settings.max_concurrent_streams == - NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS) { - num_stream_max = session->pending_local_max_concurrent_stream; - } else { - num_stream_max = session->local_settings.max_concurrent_streams; - } - - DEBUGF("stream: adjusting kept closed streams num_closed_streams=%zu, " - "num_incoming_streams=%zu, max_concurrent_streams=%zu\n", - session->num_closed_streams, session->num_incoming_streams, - num_stream_max); - - while (session->num_closed_streams > 0 && - session->num_closed_streams + session->num_incoming_streams > - num_stream_max) { - nghttp2_stream *head_stream; - nghttp2_stream *next; - - head_stream = session->closed_stream_head; - - assert(head_stream); - - next = head_stream->closed_next; - - rv = nghttp2_session_destroy_stream(session, head_stream); - if (rv != 0) { - return rv; - } - - /* head_stream is now freed */ - - session->closed_stream_head = next; - - if (session->closed_stream_head) { - session->closed_stream_head->closed_prev = NULL; - } else { - session->closed_stream_tail = NULL; - } - - --session->num_closed_streams; - } - - return 0; -} - -int nghttp2_session_adjust_idle_stream(nghttp2_session *session) { - size_t max; - int rv; - - /* Make minimum number of idle streams 16, and maximum 100, which - are arbitrary chosen numbers. */ - max = nghttp2_min( - 100, nghttp2_max( - 16, nghttp2_min(session->local_settings.max_concurrent_streams, - session->pending_local_max_concurrent_stream))); - - DEBUGF("stream: adjusting kept idle streams num_idle_streams=%zu, max=%zu\n", - session->num_idle_streams, max); - - while (session->num_idle_streams > max) { - nghttp2_stream *head; - nghttp2_stream *next; - - head = session->idle_stream_head; - assert(head); - - next = head->closed_next; - - rv = nghttp2_session_destroy_stream(session, head); - if (rv != 0) { - return rv; - } - - /* head is now destroyed */ - - session->idle_stream_head = next; - - if (session->idle_stream_head) { - session->idle_stream_head->closed_prev = NULL; - } else { - session->idle_stream_tail = NULL; - } - - --session->num_idle_streams; - } - - return 0; -} - -/* - * Closes stream with stream ID |stream_id| if both transmission and - * reception of the stream were disallowed. The |error_code| indicates - * the reason of the closure. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_INVALID_ARGUMENT - * The stream is not found. - * NGHTTP2_ERR_CALLBACK_FAILURE - * The callback function failed. - */ -int nghttp2_session_close_stream_if_shut_rdwr(nghttp2_session *session, - nghttp2_stream *stream) { - if ((stream->shut_flags & NGHTTP2_SHUT_RDWR) == NGHTTP2_SHUT_RDWR) { - return nghttp2_session_close_stream(session, stream->stream_id, - NGHTTP2_NO_ERROR); - } - return 0; -} - -/* - * Returns nonzero if local endpoint allows reception of new stream - * from remote. - */ -static int session_allow_incoming_new_stream(nghttp2_session *session) { - return (session->goaway_flags & - (NGHTTP2_GOAWAY_TERM_ON_SEND | NGHTTP2_GOAWAY_SENT)) == 0; -} - -/* - * This function returns nonzero if session is closing. - */ -static int session_is_closing(nghttp2_session *session) { - return (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) != 0 || - (nghttp2_session_want_read(session) == 0 && - nghttp2_session_want_write(session) == 0); -} - -/* - * Check that we can send a frame to the |stream|. This function - * returns 0 if we can send a frame to the |frame|, or one of the - * following negative error codes: - * - * NGHTTP2_ERR_STREAM_CLOSED - * The stream is already closed. - * NGHTTP2_ERR_STREAM_SHUT_WR - * The stream is half-closed for transmission. - * NGHTTP2_ERR_SESSION_CLOSING - * This session is closing. - */ -static int session_predicate_for_stream_send(nghttp2_session *session, - nghttp2_stream *stream) { - if (stream == NULL) { - return NGHTTP2_ERR_STREAM_CLOSED; - } - if (session_is_closing(session)) { - return NGHTTP2_ERR_SESSION_CLOSING; - } - if (stream->shut_flags & NGHTTP2_SHUT_WR) { - return NGHTTP2_ERR_STREAM_SHUT_WR; - } - return 0; -} - -int nghttp2_session_check_request_allowed(nghttp2_session *session) { - return !session->server && session->next_stream_id <= INT32_MAX && - (session->goaway_flags & NGHTTP2_GOAWAY_RECV) == 0 && - !session_is_closing(session); -} - -/* - * This function checks request HEADERS frame, which opens stream, can - * be sent at this time. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_START_STREAM_NOT_ALLOWED - * New stream cannot be created because of GOAWAY: session is - * going down or received last_stream_id is strictly less than - * frame->hd.stream_id. - * NGHTTP2_ERR_STREAM_CLOSING - * request HEADERS was canceled by RST_STREAM while it is in queue. - */ -static int session_predicate_request_headers_send(nghttp2_session *session, - nghttp2_outbound_item *item) { - if (item->aux_data.headers.canceled) { - return NGHTTP2_ERR_STREAM_CLOSING; - } - /* If we are terminating session (NGHTTP2_GOAWAY_TERM_ON_SEND), - GOAWAY was received from peer, or session is about to close, new - request is not allowed. */ - if ((session->goaway_flags & NGHTTP2_GOAWAY_RECV) || - session_is_closing(session)) { - return NGHTTP2_ERR_START_STREAM_NOT_ALLOWED; - } - return 0; -} - -/* - * This function checks HEADERS, which is the first frame from the - * server, with the |stream| can be sent at this time. The |stream| - * can be NULL. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_STREAM_CLOSED - * The stream is already closed or does not exist. - * NGHTTP2_ERR_STREAM_SHUT_WR - * The transmission is not allowed for this stream (e.g., a frame - * with END_STREAM flag set has already sent) - * NGHTTP2_ERR_INVALID_STREAM_ID - * The stream ID is invalid. - * NGHTTP2_ERR_STREAM_CLOSING - * RST_STREAM was queued for this stream. - * NGHTTP2_ERR_INVALID_STREAM_STATE - * The state of the stream is not valid. - * NGHTTP2_ERR_SESSION_CLOSING - * This session is closing. - * NGHTTP2_ERR_PROTO - * Client side attempted to send response. - */ -static int session_predicate_response_headers_send(nghttp2_session *session, - nghttp2_stream *stream) { - int rv; - rv = session_predicate_for_stream_send(session, stream); - if (rv != 0) { - return rv; - } - assert(stream); - if (!session->server) { - return NGHTTP2_ERR_PROTO; - } - if (nghttp2_session_is_my_stream_id(session, stream->stream_id)) { - return NGHTTP2_ERR_INVALID_STREAM_ID; - } - switch (stream->state) { - case NGHTTP2_STREAM_OPENING: - return 0; - case NGHTTP2_STREAM_CLOSING: - return NGHTTP2_ERR_STREAM_CLOSING; - default: - return NGHTTP2_ERR_INVALID_STREAM_STATE; - } -} - -/* - * This function checks HEADERS for reserved stream can be sent. The - * |stream| must be reserved state and the |session| is server side. - * The |stream| can be NULL. - * - * This function returns 0 if it succeeds, or one of the following - * error codes: - * - * NGHTTP2_ERR_STREAM_CLOSED - * The stream is already closed. - * NGHTTP2_ERR_STREAM_SHUT_WR - * The stream is half-closed for transmission. - * NGHTTP2_ERR_PROTO - * The stream is not reserved state - * NGHTTP2_ERR_STREAM_CLOSED - * RST_STREAM was queued for this stream. - * NGHTTP2_ERR_SESSION_CLOSING - * This session is closing. - * NGHTTP2_ERR_START_STREAM_NOT_ALLOWED - * New stream cannot be created because GOAWAY is already sent or - * received. - * NGHTTP2_ERR_PROTO - * Client side attempted to send push response. - */ -static int -session_predicate_push_response_headers_send(nghttp2_session *session, - nghttp2_stream *stream) { - int rv; - /* TODO Should disallow HEADERS if GOAWAY has already been issued? */ - rv = session_predicate_for_stream_send(session, stream); - if (rv != 0) { - return rv; - } - assert(stream); - if (!session->server) { - return NGHTTP2_ERR_PROTO; - } - if (stream->state != NGHTTP2_STREAM_RESERVED) { - return NGHTTP2_ERR_PROTO; - } - if (session->goaway_flags & NGHTTP2_GOAWAY_RECV) { - return NGHTTP2_ERR_START_STREAM_NOT_ALLOWED; - } - return 0; -} - -/* - * This function checks HEADERS, which is neither stream-opening nor - * first response header, with the |stream| can be sent at this time. - * The |stream| can be NULL. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_STREAM_CLOSED - * The stream is already closed or does not exist. - * NGHTTP2_ERR_STREAM_SHUT_WR - * The transmission is not allowed for this stream (e.g., a frame - * with END_STREAM flag set has already sent) - * NGHTTP2_ERR_STREAM_CLOSING - * RST_STREAM was queued for this stream. - * NGHTTP2_ERR_INVALID_STREAM_STATE - * The state of the stream is not valid. - * NGHTTP2_ERR_SESSION_CLOSING - * This session is closing. - */ -static int session_predicate_headers_send(nghttp2_session *session, - nghttp2_stream *stream) { - int rv; - rv = session_predicate_for_stream_send(session, stream); - if (rv != 0) { - return rv; - } - assert(stream); - - switch (stream->state) { - case NGHTTP2_STREAM_OPENED: - return 0; - case NGHTTP2_STREAM_CLOSING: - return NGHTTP2_ERR_STREAM_CLOSING; - default: - if (nghttp2_session_is_my_stream_id(session, stream->stream_id)) { - return 0; - } - return NGHTTP2_ERR_INVALID_STREAM_STATE; - } -} - -/* - * This function checks PUSH_PROMISE frame |frame| with the |stream| - * can be sent at this time. The |stream| can be NULL. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_START_STREAM_NOT_ALLOWED - * New stream cannot be created because GOAWAY is already sent or - * received. - * NGHTTP2_ERR_PROTO - * The client side attempts to send PUSH_PROMISE, or the server - * sends PUSH_PROMISE for the stream not initiated by the client. - * NGHTTP2_ERR_STREAM_CLOSED - * The stream is already closed or does not exist. - * NGHTTP2_ERR_STREAM_CLOSING - * RST_STREAM was queued for this stream. - * NGHTTP2_ERR_STREAM_SHUT_WR - * The transmission is not allowed for this stream (e.g., a frame - * with END_STREAM flag set has already sent) - * NGHTTP2_ERR_PUSH_DISABLED - * The remote peer disabled reception of PUSH_PROMISE. - * NGHTTP2_ERR_SESSION_CLOSING - * This session is closing. - */ -static int session_predicate_push_promise_send(nghttp2_session *session, - nghttp2_stream *stream) { - int rv; - - if (!session->server) { - return NGHTTP2_ERR_PROTO; - } - - rv = session_predicate_for_stream_send(session, stream); - if (rv != 0) { - return rv; - } - - assert(stream); - - if (session->remote_settings.enable_push == 0) { - return NGHTTP2_ERR_PUSH_DISABLED; - } - if (stream->state == NGHTTP2_STREAM_CLOSING) { - return NGHTTP2_ERR_STREAM_CLOSING; - } - if (session->goaway_flags & NGHTTP2_GOAWAY_RECV) { - return NGHTTP2_ERR_START_STREAM_NOT_ALLOWED; - } - return 0; -} - -/* - * This function checks WINDOW_UPDATE with the stream ID |stream_id| - * can be sent at this time. Note that END_STREAM flag of the previous - * frame does not affect the transmission of the WINDOW_UPDATE frame. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_STREAM_CLOSED - * The stream is already closed or does not exist. - * NGHTTP2_ERR_STREAM_CLOSING - * RST_STREAM was queued for this stream. - * NGHTTP2_ERR_INVALID_STREAM_STATE - * The state of the stream is not valid. - * NGHTTP2_ERR_SESSION_CLOSING - * This session is closing. - */ -static int session_predicate_window_update_send(nghttp2_session *session, - int32_t stream_id) { - nghttp2_stream *stream; - - if (session_is_closing(session)) { - return NGHTTP2_ERR_SESSION_CLOSING; - } - - if (stream_id == 0) { - /* Connection-level window update */ - return 0; - } - stream = nghttp2_session_get_stream(session, stream_id); - if (stream == NULL) { - return NGHTTP2_ERR_STREAM_CLOSED; - } - if (stream->state == NGHTTP2_STREAM_CLOSING) { - return NGHTTP2_ERR_STREAM_CLOSING; - } - if (state_reserved_local(session, stream)) { - return NGHTTP2_ERR_INVALID_STREAM_STATE; - } - return 0; -} - -static int session_predicate_altsvc_send(nghttp2_session *session, - int32_t stream_id) { - nghttp2_stream *stream; - - if (session_is_closing(session)) { - return NGHTTP2_ERR_SESSION_CLOSING; - } - - if (stream_id == 0) { - return 0; - } - - stream = nghttp2_session_get_stream(session, stream_id); - if (stream == NULL) { - return NGHTTP2_ERR_STREAM_CLOSED; - } - if (stream->state == NGHTTP2_STREAM_CLOSING) { - return NGHTTP2_ERR_STREAM_CLOSING; - } - - return 0; -} - + if (rv != 0) { + nghttp2_stream_free(stream); + nghttp2_mem_free(mem, stream); + return NULL; + } + } else { + stream->flags = flags; + stream->state = initial_state; + stream->weight = pri_spec->weight; + stream->stream_user_data = stream_user_data; + } + + switch (initial_state) { + case NGHTTP2_STREAM_RESERVED: + if (nghttp2_session_is_my_stream_id(session, stream_id)) { + /* reserved (local) */ + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); + } else { + /* reserved (remote) */ + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR); + ++session->num_incoming_reserved_streams; + } + /* Reserved stream does not count in the concurrent streams + limit. That is one of the DOS vector. */ + break; + case NGHTTP2_STREAM_IDLE: + /* Idle stream does not count toward the concurrent streams limit. + This is used as anchor node in dependency tree. */ + nghttp2_session_keep_idle_stream(session, stream); + break; + default: + if (nghttp2_session_is_my_stream_id(session, stream_id)) { + ++session->num_outgoing_streams; + } else { + ++session->num_incoming_streams; + } + } + + if (pri_spec->stream_id == 0) { + dep_stream = &session->root; + } + + assert(dep_stream); + + if (pri_spec->exclusive) { + rv = nghttp2_stream_dep_insert(dep_stream, stream); + if (rv != 0) { + return NULL; + } + } else { + nghttp2_stream_dep_add(dep_stream, stream); + } + + return stream; +} + +int nghttp2_session_close_stream(nghttp2_session *session, int32_t stream_id, + uint32_t error_code) { + int rv; + nghttp2_stream *stream; + nghttp2_mem *mem; + int is_my_stream_id; + + mem = &session->mem; + stream = nghttp2_session_get_stream(session, stream_id); + + if (!stream) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + DEBUGF("stream: stream(%p)=%d close\n", stream, stream->stream_id); + + if (stream->item) { + nghttp2_outbound_item *item; + + item = stream->item; + + rv = nghttp2_stream_detach_item(stream); + + if (rv != 0) { + return rv; + } + + /* If item is queued, it will be deleted when it is popped + (nghttp2_session_prep_frame() will fail). If session->aob.item + points to this item, let active_outbound_item_reset() + free the item. */ + if (!item->queued && item != session->aob.item) { + nghttp2_outbound_item_free(item, mem); + nghttp2_mem_free(mem, item); + } + } + + /* We call on_stream_close_callback even if stream->state is + NGHTTP2_STREAM_INITIAL. This will happen while sending request + HEADERS, a local endpoint receives RST_STREAM for that stream. It + may be PROTOCOL_ERROR, but without notifying stream closure will + hang the stream in a local endpoint. + */ + + if (session->callbacks.on_stream_close_callback) { + if (session->callbacks.on_stream_close_callback( + session, stream_id, error_code, session->user_data) != 0) { + + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + + is_my_stream_id = nghttp2_session_is_my_stream_id(session, stream_id); + + /* pushed streams which is not opened yet is not counted toward max + concurrent limits */ + if ((stream->flags & NGHTTP2_STREAM_FLAG_PUSH)) { + if (!is_my_stream_id) { + --session->num_incoming_reserved_streams; + } + } else { + if (is_my_stream_id) { + --session->num_outgoing_streams; + } else { + --session->num_incoming_streams; + } + } + + /* Closes both directions just in case they are not closed yet */ + stream->flags |= NGHTTP2_STREAM_FLAG_CLOSED; + + if ((session->opt_flags & NGHTTP2_OPTMASK_NO_CLOSED_STREAMS) == 0 && + session->server && !is_my_stream_id && + nghttp2_stream_in_dep_tree(stream)) { + /* On server side, retain stream at most MAX_CONCURRENT_STREAMS + combined with the current active incoming streams to make + dependency tree work better. */ + nghttp2_session_keep_closed_stream(session, stream); + } else { + rv = nghttp2_session_destroy_stream(session, stream); + if (rv != 0) { + return rv; + } + } + + return 0; +} + +int nghttp2_session_destroy_stream(nghttp2_session *session, + nghttp2_stream *stream) { + nghttp2_mem *mem; + int rv; + + DEBUGF("stream: destroy closed stream(%p)=%d\n", stream, stream->stream_id); + + mem = &session->mem; + + if (nghttp2_stream_in_dep_tree(stream)) { + rv = nghttp2_stream_dep_remove(stream); + if (rv != 0) { + return rv; + } + } + + nghttp2_map_remove(&session->streams, stream->stream_id); + nghttp2_stream_free(stream); + nghttp2_mem_free(mem, stream); + + return 0; +} + +void nghttp2_session_keep_closed_stream(nghttp2_session *session, + nghttp2_stream *stream) { + DEBUGF("stream: keep closed stream(%p)=%d, state=%d\n", stream, + stream->stream_id, stream->state); + + if (session->closed_stream_tail) { + session->closed_stream_tail->closed_next = stream; + stream->closed_prev = session->closed_stream_tail; + } else { + session->closed_stream_head = stream; + } + session->closed_stream_tail = stream; + + ++session->num_closed_streams; +} + +void nghttp2_session_keep_idle_stream(nghttp2_session *session, + nghttp2_stream *stream) { + DEBUGF("stream: keep idle stream(%p)=%d, state=%d\n", stream, + stream->stream_id, stream->state); + + if (session->idle_stream_tail) { + session->idle_stream_tail->closed_next = stream; + stream->closed_prev = session->idle_stream_tail; + } else { + session->idle_stream_head = stream; + } + session->idle_stream_tail = stream; + + ++session->num_idle_streams; +} + +void nghttp2_session_detach_idle_stream(nghttp2_session *session, + nghttp2_stream *stream) { + nghttp2_stream *prev_stream, *next_stream; + + DEBUGF("stream: detach idle stream(%p)=%d, state=%d\n", stream, + stream->stream_id, stream->state); + + prev_stream = stream->closed_prev; + next_stream = stream->closed_next; + + if (prev_stream) { + prev_stream->closed_next = next_stream; + } else { + session->idle_stream_head = next_stream; + } + + if (next_stream) { + next_stream->closed_prev = prev_stream; + } else { + session->idle_stream_tail = prev_stream; + } + + stream->closed_prev = NULL; + stream->closed_next = NULL; + + --session->num_idle_streams; +} + +int nghttp2_session_adjust_closed_stream(nghttp2_session *session) { + size_t num_stream_max; + int rv; + + if (session->local_settings.max_concurrent_streams == + NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS) { + num_stream_max = session->pending_local_max_concurrent_stream; + } else { + num_stream_max = session->local_settings.max_concurrent_streams; + } + + DEBUGF("stream: adjusting kept closed streams num_closed_streams=%zu, " + "num_incoming_streams=%zu, max_concurrent_streams=%zu\n", + session->num_closed_streams, session->num_incoming_streams, + num_stream_max); + + while (session->num_closed_streams > 0 && + session->num_closed_streams + session->num_incoming_streams > + num_stream_max) { + nghttp2_stream *head_stream; + nghttp2_stream *next; + + head_stream = session->closed_stream_head; + + assert(head_stream); + + next = head_stream->closed_next; + + rv = nghttp2_session_destroy_stream(session, head_stream); + if (rv != 0) { + return rv; + } + + /* head_stream is now freed */ + + session->closed_stream_head = next; + + if (session->closed_stream_head) { + session->closed_stream_head->closed_prev = NULL; + } else { + session->closed_stream_tail = NULL; + } + + --session->num_closed_streams; + } + + return 0; +} + +int nghttp2_session_adjust_idle_stream(nghttp2_session *session) { + size_t max; + int rv; + + /* Make minimum number of idle streams 16, and maximum 100, which + are arbitrary chosen numbers. */ + max = nghttp2_min( + 100, nghttp2_max( + 16, nghttp2_min(session->local_settings.max_concurrent_streams, + session->pending_local_max_concurrent_stream))); + + DEBUGF("stream: adjusting kept idle streams num_idle_streams=%zu, max=%zu\n", + session->num_idle_streams, max); + + while (session->num_idle_streams > max) { + nghttp2_stream *head; + nghttp2_stream *next; + + head = session->idle_stream_head; + assert(head); + + next = head->closed_next; + + rv = nghttp2_session_destroy_stream(session, head); + if (rv != 0) { + return rv; + } + + /* head is now destroyed */ + + session->idle_stream_head = next; + + if (session->idle_stream_head) { + session->idle_stream_head->closed_prev = NULL; + } else { + session->idle_stream_tail = NULL; + } + + --session->num_idle_streams; + } + + return 0; +} + +/* + * Closes stream with stream ID |stream_id| if both transmission and + * reception of the stream were disallowed. The |error_code| indicates + * the reason of the closure. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_INVALID_ARGUMENT + * The stream is not found. + * NGHTTP2_ERR_CALLBACK_FAILURE + * The callback function failed. + */ +int nghttp2_session_close_stream_if_shut_rdwr(nghttp2_session *session, + nghttp2_stream *stream) { + if ((stream->shut_flags & NGHTTP2_SHUT_RDWR) == NGHTTP2_SHUT_RDWR) { + return nghttp2_session_close_stream(session, stream->stream_id, + NGHTTP2_NO_ERROR); + } + return 0; +} + +/* + * Returns nonzero if local endpoint allows reception of new stream + * from remote. + */ +static int session_allow_incoming_new_stream(nghttp2_session *session) { + return (session->goaway_flags & + (NGHTTP2_GOAWAY_TERM_ON_SEND | NGHTTP2_GOAWAY_SENT)) == 0; +} + +/* + * This function returns nonzero if session is closing. + */ +static int session_is_closing(nghttp2_session *session) { + return (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) != 0 || + (nghttp2_session_want_read(session) == 0 && + nghttp2_session_want_write(session) == 0); +} + +/* + * Check that we can send a frame to the |stream|. This function + * returns 0 if we can send a frame to the |frame|, or one of the + * following negative error codes: + * + * NGHTTP2_ERR_STREAM_CLOSED + * The stream is already closed. + * NGHTTP2_ERR_STREAM_SHUT_WR + * The stream is half-closed for transmission. + * NGHTTP2_ERR_SESSION_CLOSING + * This session is closing. + */ +static int session_predicate_for_stream_send(nghttp2_session *session, + nghttp2_stream *stream) { + if (stream == NULL) { + return NGHTTP2_ERR_STREAM_CLOSED; + } + if (session_is_closing(session)) { + return NGHTTP2_ERR_SESSION_CLOSING; + } + if (stream->shut_flags & NGHTTP2_SHUT_WR) { + return NGHTTP2_ERR_STREAM_SHUT_WR; + } + return 0; +} + +int nghttp2_session_check_request_allowed(nghttp2_session *session) { + return !session->server && session->next_stream_id <= INT32_MAX && + (session->goaway_flags & NGHTTP2_GOAWAY_RECV) == 0 && + !session_is_closing(session); +} + +/* + * This function checks request HEADERS frame, which opens stream, can + * be sent at this time. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_START_STREAM_NOT_ALLOWED + * New stream cannot be created because of GOAWAY: session is + * going down or received last_stream_id is strictly less than + * frame->hd.stream_id. + * NGHTTP2_ERR_STREAM_CLOSING + * request HEADERS was canceled by RST_STREAM while it is in queue. + */ +static int session_predicate_request_headers_send(nghttp2_session *session, + nghttp2_outbound_item *item) { + if (item->aux_data.headers.canceled) { + return NGHTTP2_ERR_STREAM_CLOSING; + } + /* If we are terminating session (NGHTTP2_GOAWAY_TERM_ON_SEND), + GOAWAY was received from peer, or session is about to close, new + request is not allowed. */ + if ((session->goaway_flags & NGHTTP2_GOAWAY_RECV) || + session_is_closing(session)) { + return NGHTTP2_ERR_START_STREAM_NOT_ALLOWED; + } + return 0; +} + +/* + * This function checks HEADERS, which is the first frame from the + * server, with the |stream| can be sent at this time. The |stream| + * can be NULL. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_STREAM_CLOSED + * The stream is already closed or does not exist. + * NGHTTP2_ERR_STREAM_SHUT_WR + * The transmission is not allowed for this stream (e.g., a frame + * with END_STREAM flag set has already sent) + * NGHTTP2_ERR_INVALID_STREAM_ID + * The stream ID is invalid. + * NGHTTP2_ERR_STREAM_CLOSING + * RST_STREAM was queued for this stream. + * NGHTTP2_ERR_INVALID_STREAM_STATE + * The state of the stream is not valid. + * NGHTTP2_ERR_SESSION_CLOSING + * This session is closing. + * NGHTTP2_ERR_PROTO + * Client side attempted to send response. + */ +static int session_predicate_response_headers_send(nghttp2_session *session, + nghttp2_stream *stream) { + int rv; + rv = session_predicate_for_stream_send(session, stream); + if (rv != 0) { + return rv; + } + assert(stream); + if (!session->server) { + return NGHTTP2_ERR_PROTO; + } + if (nghttp2_session_is_my_stream_id(session, stream->stream_id)) { + return NGHTTP2_ERR_INVALID_STREAM_ID; + } + switch (stream->state) { + case NGHTTP2_STREAM_OPENING: + return 0; + case NGHTTP2_STREAM_CLOSING: + return NGHTTP2_ERR_STREAM_CLOSING; + default: + return NGHTTP2_ERR_INVALID_STREAM_STATE; + } +} + +/* + * This function checks HEADERS for reserved stream can be sent. The + * |stream| must be reserved state and the |session| is server side. + * The |stream| can be NULL. + * + * This function returns 0 if it succeeds, or one of the following + * error codes: + * + * NGHTTP2_ERR_STREAM_CLOSED + * The stream is already closed. + * NGHTTP2_ERR_STREAM_SHUT_WR + * The stream is half-closed for transmission. + * NGHTTP2_ERR_PROTO + * The stream is not reserved state + * NGHTTP2_ERR_STREAM_CLOSED + * RST_STREAM was queued for this stream. + * NGHTTP2_ERR_SESSION_CLOSING + * This session is closing. + * NGHTTP2_ERR_START_STREAM_NOT_ALLOWED + * New stream cannot be created because GOAWAY is already sent or + * received. + * NGHTTP2_ERR_PROTO + * Client side attempted to send push response. + */ +static int +session_predicate_push_response_headers_send(nghttp2_session *session, + nghttp2_stream *stream) { + int rv; + /* TODO Should disallow HEADERS if GOAWAY has already been issued? */ + rv = session_predicate_for_stream_send(session, stream); + if (rv != 0) { + return rv; + } + assert(stream); + if (!session->server) { + return NGHTTP2_ERR_PROTO; + } + if (stream->state != NGHTTP2_STREAM_RESERVED) { + return NGHTTP2_ERR_PROTO; + } + if (session->goaway_flags & NGHTTP2_GOAWAY_RECV) { + return NGHTTP2_ERR_START_STREAM_NOT_ALLOWED; + } + return 0; +} + +/* + * This function checks HEADERS, which is neither stream-opening nor + * first response header, with the |stream| can be sent at this time. + * The |stream| can be NULL. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_STREAM_CLOSED + * The stream is already closed or does not exist. + * NGHTTP2_ERR_STREAM_SHUT_WR + * The transmission is not allowed for this stream (e.g., a frame + * with END_STREAM flag set has already sent) + * NGHTTP2_ERR_STREAM_CLOSING + * RST_STREAM was queued for this stream. + * NGHTTP2_ERR_INVALID_STREAM_STATE + * The state of the stream is not valid. + * NGHTTP2_ERR_SESSION_CLOSING + * This session is closing. + */ +static int session_predicate_headers_send(nghttp2_session *session, + nghttp2_stream *stream) { + int rv; + rv = session_predicate_for_stream_send(session, stream); + if (rv != 0) { + return rv; + } + assert(stream); + + switch (stream->state) { + case NGHTTP2_STREAM_OPENED: + return 0; + case NGHTTP2_STREAM_CLOSING: + return NGHTTP2_ERR_STREAM_CLOSING; + default: + if (nghttp2_session_is_my_stream_id(session, stream->stream_id)) { + return 0; + } + return NGHTTP2_ERR_INVALID_STREAM_STATE; + } +} + +/* + * This function checks PUSH_PROMISE frame |frame| with the |stream| + * can be sent at this time. The |stream| can be NULL. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_START_STREAM_NOT_ALLOWED + * New stream cannot be created because GOAWAY is already sent or + * received. + * NGHTTP2_ERR_PROTO + * The client side attempts to send PUSH_PROMISE, or the server + * sends PUSH_PROMISE for the stream not initiated by the client. + * NGHTTP2_ERR_STREAM_CLOSED + * The stream is already closed or does not exist. + * NGHTTP2_ERR_STREAM_CLOSING + * RST_STREAM was queued for this stream. + * NGHTTP2_ERR_STREAM_SHUT_WR + * The transmission is not allowed for this stream (e.g., a frame + * with END_STREAM flag set has already sent) + * NGHTTP2_ERR_PUSH_DISABLED + * The remote peer disabled reception of PUSH_PROMISE. + * NGHTTP2_ERR_SESSION_CLOSING + * This session is closing. + */ +static int session_predicate_push_promise_send(nghttp2_session *session, + nghttp2_stream *stream) { + int rv; + + if (!session->server) { + return NGHTTP2_ERR_PROTO; + } + + rv = session_predicate_for_stream_send(session, stream); + if (rv != 0) { + return rv; + } + + assert(stream); + + if (session->remote_settings.enable_push == 0) { + return NGHTTP2_ERR_PUSH_DISABLED; + } + if (stream->state == NGHTTP2_STREAM_CLOSING) { + return NGHTTP2_ERR_STREAM_CLOSING; + } + if (session->goaway_flags & NGHTTP2_GOAWAY_RECV) { + return NGHTTP2_ERR_START_STREAM_NOT_ALLOWED; + } + return 0; +} + +/* + * This function checks WINDOW_UPDATE with the stream ID |stream_id| + * can be sent at this time. Note that END_STREAM flag of the previous + * frame does not affect the transmission of the WINDOW_UPDATE frame. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_STREAM_CLOSED + * The stream is already closed or does not exist. + * NGHTTP2_ERR_STREAM_CLOSING + * RST_STREAM was queued for this stream. + * NGHTTP2_ERR_INVALID_STREAM_STATE + * The state of the stream is not valid. + * NGHTTP2_ERR_SESSION_CLOSING + * This session is closing. + */ +static int session_predicate_window_update_send(nghttp2_session *session, + int32_t stream_id) { + nghttp2_stream *stream; + + if (session_is_closing(session)) { + return NGHTTP2_ERR_SESSION_CLOSING; + } + + if (stream_id == 0) { + /* Connection-level window update */ + return 0; + } + stream = nghttp2_session_get_stream(session, stream_id); + if (stream == NULL) { + return NGHTTP2_ERR_STREAM_CLOSED; + } + if (stream->state == NGHTTP2_STREAM_CLOSING) { + return NGHTTP2_ERR_STREAM_CLOSING; + } + if (state_reserved_local(session, stream)) { + return NGHTTP2_ERR_INVALID_STREAM_STATE; + } + return 0; +} + +static int session_predicate_altsvc_send(nghttp2_session *session, + int32_t stream_id) { + nghttp2_stream *stream; + + if (session_is_closing(session)) { + return NGHTTP2_ERR_SESSION_CLOSING; + } + + if (stream_id == 0) { + return 0; + } + + stream = nghttp2_session_get_stream(session, stream_id); + if (stream == NULL) { + return NGHTTP2_ERR_STREAM_CLOSED; + } + if (stream->state == NGHTTP2_STREAM_CLOSING) { + return NGHTTP2_ERR_STREAM_CLOSING; + } + + return 0; +} + static int session_predicate_origin_send(nghttp2_session *session) { if (session_is_closing(session)) { return NGHTTP2_ERR_SESSION_CLOSING; @@ -1784,538 +1784,538 @@ static int session_predicate_origin_send(nghttp2_session *session) { return 0; } -/* Take into account settings max frame size and both connection-level - flow control here */ -static ssize_t -nghttp2_session_enforce_flow_control_limits(nghttp2_session *session, - nghttp2_stream *stream, - ssize_t requested_window_size) { - DEBUGF("send: remote windowsize connection=%d, remote maxframsize=%u, " - "stream(id %d)=%d\n", - session->remote_window_size, session->remote_settings.max_frame_size, - stream->stream_id, stream->remote_window_size); - - return nghttp2_min(nghttp2_min(nghttp2_min(requested_window_size, - stream->remote_window_size), - session->remote_window_size), - (int32_t)session->remote_settings.max_frame_size); -} - -/* - * Returns the maximum length of next data read. If the - * connection-level and/or stream-wise flow control are enabled, the - * return value takes into account those current window sizes. The remote - * settings for max frame size is also taken into account. - */ -static size_t nghttp2_session_next_data_read(nghttp2_session *session, - nghttp2_stream *stream) { - ssize_t window_size; - - window_size = nghttp2_session_enforce_flow_control_limits( - session, stream, NGHTTP2_DATA_PAYLOADLEN); - - DEBUGF("send: available window=%zd\n", window_size); - - return window_size > 0 ? (size_t)window_size : 0; -} - -/* - * This function checks DATA with the |stream| can be sent at this - * time. The |stream| can be NULL. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_STREAM_CLOSED - * The stream is already closed or does not exist. - * NGHTTP2_ERR_STREAM_SHUT_WR - * The transmission is not allowed for this stream (e.g., a frame - * with END_STREAM flag set has already sent) - * NGHTTP2_ERR_STREAM_CLOSING - * RST_STREAM was queued for this stream. - * NGHTTP2_ERR_INVALID_STREAM_STATE - * The state of the stream is not valid. - * NGHTTP2_ERR_SESSION_CLOSING - * This session is closing. - */ -static int nghttp2_session_predicate_data_send(nghttp2_session *session, - nghttp2_stream *stream) { - int rv; - rv = session_predicate_for_stream_send(session, stream); - if (rv != 0) { - return rv; - } - assert(stream); - if (nghttp2_session_is_my_stream_id(session, stream->stream_id)) { - /* Request body data */ - /* If stream->state is NGHTTP2_STREAM_CLOSING, RST_STREAM was - queued but not yet sent. In this case, we won't send DATA - frames. */ - if (stream->state == NGHTTP2_STREAM_CLOSING) { - return NGHTTP2_ERR_STREAM_CLOSING; - } - if (stream->state == NGHTTP2_STREAM_RESERVED) { - return NGHTTP2_ERR_INVALID_STREAM_STATE; - } - return 0; - } - /* Response body data */ - if (stream->state == NGHTTP2_STREAM_OPENED) { - return 0; - } - if (stream->state == NGHTTP2_STREAM_CLOSING) { - return NGHTTP2_ERR_STREAM_CLOSING; - } - return NGHTTP2_ERR_INVALID_STREAM_STATE; -} - -static ssize_t session_call_select_padding(nghttp2_session *session, - const nghttp2_frame *frame, - size_t max_payloadlen) { - ssize_t rv; - - if (frame->hd.length >= max_payloadlen) { - return (ssize_t)frame->hd.length; - } - - if (session->callbacks.select_padding_callback) { - size_t max_paddedlen; - - max_paddedlen = - nghttp2_min(frame->hd.length + NGHTTP2_MAX_PADLEN, max_payloadlen); - - rv = session->callbacks.select_padding_callback( - session, frame, max_paddedlen, session->user_data); - if (rv < (ssize_t)frame->hd.length || rv > (ssize_t)max_paddedlen) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - return rv; - } - return (ssize_t)frame->hd.length; -} - -/* Add padding to HEADERS or PUSH_PROMISE. We use - frame->headers.padlen in this function to use the fact that - frame->push_promise has also padlen in the same position. */ -static int session_headers_add_pad(nghttp2_session *session, - nghttp2_frame *frame) { - int rv; - ssize_t padded_payloadlen; - nghttp2_active_outbound_item *aob; - nghttp2_bufs *framebufs; - size_t padlen; - size_t max_payloadlen; - - aob = &session->aob; - framebufs = &aob->framebufs; - - max_payloadlen = nghttp2_min(NGHTTP2_MAX_PAYLOADLEN, - frame->hd.length + NGHTTP2_MAX_PADLEN); - - padded_payloadlen = - session_call_select_padding(session, frame, max_payloadlen); - - if (nghttp2_is_fatal((int)padded_payloadlen)) { - return (int)padded_payloadlen; - } - - padlen = (size_t)padded_payloadlen - frame->hd.length; - - DEBUGF("send: padding selected: payloadlen=%zd, padlen=%zu\n", - padded_payloadlen, padlen); - - rv = nghttp2_frame_add_pad(framebufs, &frame->hd, padlen, 0); - - if (rv != 0) { - return rv; - } - - frame->headers.padlen = padlen; - - return 0; -} - -static size_t session_estimate_headers_payload(nghttp2_session *session, - const nghttp2_nv *nva, - size_t nvlen, - size_t additional) { - return nghttp2_hd_deflate_bound(&session->hd_deflater, nva, nvlen) + - additional; -} - -static int session_pack_extension(nghttp2_session *session, nghttp2_bufs *bufs, - nghttp2_frame *frame) { - ssize_t rv; - nghttp2_buf *buf; - size_t buflen; - size_t framelen; - - assert(session->callbacks.pack_extension_callback); - - buf = &bufs->head->buf; - buflen = nghttp2_min(nghttp2_buf_avail(buf), NGHTTP2_MAX_PAYLOADLEN); - - rv = session->callbacks.pack_extension_callback(session, buf->last, buflen, - frame, session->user_data); - if (rv == NGHTTP2_ERR_CANCEL) { - return (int)rv; - } - - if (rv < 0 || (size_t)rv > buflen) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - - framelen = (size_t)rv; - - frame->hd.length = framelen; - - assert(buf->pos == buf->last); - buf->last += framelen; - buf->pos -= NGHTTP2_FRAME_HDLEN; - - nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); - - return 0; -} - -/* - * This function serializes frame for transmission. - * - * This function returns 0 if it succeeds, or one of negative error - * codes, including both fatal and non-fatal ones. - */ -static int session_prep_frame(nghttp2_session *session, - nghttp2_outbound_item *item) { - int rv; - nghttp2_frame *frame; - nghttp2_mem *mem; - - mem = &session->mem; - frame = &item->frame; - - switch (frame->hd.type) { - case NGHTTP2_DATA: { - size_t next_readmax; - nghttp2_stream *stream; - - stream = nghttp2_session_get_stream(session, frame->hd.stream_id); - - if (stream) { - assert(stream->item == item); - } - - rv = nghttp2_session_predicate_data_send(session, stream); - if (rv != 0) { - // If stream was already closed, nghttp2_session_get_stream() - // returns NULL, but item is still attached to the stream. - // Search stream including closed again. - stream = nghttp2_session_get_stream_raw(session, frame->hd.stream_id); - if (stream) { - int rv2; - - rv2 = nghttp2_stream_detach_item(stream); - - if (nghttp2_is_fatal(rv2)) { - return rv2; - } - } - - return rv; - } - /* Assuming stream is not NULL */ - assert(stream); - next_readmax = nghttp2_session_next_data_read(session, stream); - - if (next_readmax == 0) { - - /* This must be true since we only pop DATA frame item from - queue when session->remote_window_size > 0 */ - assert(session->remote_window_size > 0); - - rv = nghttp2_stream_defer_item(stream, - NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - - session->aob.item = NULL; - active_outbound_item_reset(&session->aob, mem); - return NGHTTP2_ERR_DEFERRED; - } - - rv = nghttp2_session_pack_data(session, &session->aob.framebufs, - next_readmax, frame, &item->aux_data.data, - stream); - if (rv == NGHTTP2_ERR_PAUSE) { - return rv; - } - if (rv == NGHTTP2_ERR_DEFERRED) { - rv = nghttp2_stream_defer_item(stream, NGHTTP2_STREAM_FLAG_DEFERRED_USER); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - - session->aob.item = NULL; - active_outbound_item_reset(&session->aob, mem); - return NGHTTP2_ERR_DEFERRED; - } - if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { - rv = nghttp2_stream_detach_item(stream); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - - rv = nghttp2_session_add_rst_stream(session, frame->hd.stream_id, - NGHTTP2_INTERNAL_ERROR); - if (nghttp2_is_fatal(rv)) { - return rv; - } - return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; - } - if (rv != 0) { - int rv2; - - rv2 = nghttp2_stream_detach_item(stream); - - if (nghttp2_is_fatal(rv2)) { - return rv2; - } - - return rv; - } - return 0; - } - case NGHTTP2_HEADERS: { - nghttp2_headers_aux_data *aux_data; - size_t estimated_payloadlen; - - aux_data = &item->aux_data.headers; - - if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) { - /* initial HEADERS, which opens stream */ - nghttp2_stream *stream; - - stream = nghttp2_session_open_stream( - session, frame->hd.stream_id, NGHTTP2_STREAM_FLAG_NONE, - &frame->headers.pri_spec, NGHTTP2_STREAM_INITIAL, - aux_data->stream_user_data); - - if (stream == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - /* We don't call nghttp2_session_adjust_closed_stream() here, - since we don't keep closed stream in client side */ - - rv = session_predicate_request_headers_send(session, item); - if (rv != 0) { - return rv; - } - - if (session_enforce_http_messaging(session)) { - nghttp2_http_record_request_method(stream, frame); - } - } else { - nghttp2_stream *stream; - - stream = nghttp2_session_get_stream(session, frame->hd.stream_id); - - if (stream && stream->state == NGHTTP2_STREAM_RESERVED) { - rv = session_predicate_push_response_headers_send(session, stream); - if (rv == 0) { - frame->headers.cat = NGHTTP2_HCAT_PUSH_RESPONSE; - - if (aux_data->stream_user_data) { - stream->stream_user_data = aux_data->stream_user_data; - } - } - } else if (session_predicate_response_headers_send(session, stream) == - 0) { - frame->headers.cat = NGHTTP2_HCAT_RESPONSE; - rv = 0; - } else { - frame->headers.cat = NGHTTP2_HCAT_HEADERS; - - rv = session_predicate_headers_send(session, stream); - } - - if (rv != 0) { - return rv; - } - } - - estimated_payloadlen = session_estimate_headers_payload( - session, frame->headers.nva, frame->headers.nvlen, - NGHTTP2_PRIORITY_SPECLEN); - - if (estimated_payloadlen > session->max_send_header_block_length) { - return NGHTTP2_ERR_FRAME_SIZE_ERROR; - } - - rv = nghttp2_frame_pack_headers(&session->aob.framebufs, &frame->headers, - &session->hd_deflater); - - if (rv != 0) { - return rv; - } - - DEBUGF("send: before padding, HEADERS serialized in %zd bytes\n", - nghttp2_bufs_len(&session->aob.framebufs)); - - rv = session_headers_add_pad(session, frame); - - if (rv != 0) { - return rv; - } - - DEBUGF("send: HEADERS finally serialized in %zd bytes\n", - nghttp2_bufs_len(&session->aob.framebufs)); - - if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) { - assert(session->last_sent_stream_id < frame->hd.stream_id); - session->last_sent_stream_id = frame->hd.stream_id; - } - - return 0; - } - case NGHTTP2_PRIORITY: { - if (session_is_closing(session)) { - return NGHTTP2_ERR_SESSION_CLOSING; - } - /* PRIORITY frame can be sent at any time and to any stream - ID. */ - nghttp2_frame_pack_priority(&session->aob.framebufs, &frame->priority); - - /* Peer can send PRIORITY frame against idle stream to create - "anchor" in dependency tree. Only client can do this in - nghttp2. In nghttp2, only server retains non-active (closed - or idle) streams in memory, so we don't open stream here. */ - return 0; - } - case NGHTTP2_RST_STREAM: - if (session_is_closing(session)) { - return NGHTTP2_ERR_SESSION_CLOSING; - } - nghttp2_frame_pack_rst_stream(&session->aob.framebufs, &frame->rst_stream); - return 0; - case NGHTTP2_SETTINGS: { - if (frame->hd.flags & NGHTTP2_FLAG_ACK) { - assert(session->obq_flood_counter_ > 0); - --session->obq_flood_counter_; - /* When session is about to close, don't send SETTINGS ACK. - We are required to send SETTINGS without ACK though; for - example, we have to send SETTINGS as a part of connection - preface. */ - if (session_is_closing(session)) { - return NGHTTP2_ERR_SESSION_CLOSING; - } - } - - rv = nghttp2_frame_pack_settings(&session->aob.framebufs, &frame->settings); - if (rv != 0) { - return rv; - } - return 0; - } - case NGHTTP2_PUSH_PROMISE: { - nghttp2_stream *stream; - size_t estimated_payloadlen; - - /* stream could be NULL if associated stream was already - closed. */ - stream = nghttp2_session_get_stream(session, frame->hd.stream_id); - - /* predicate should fail if stream is NULL. */ - rv = session_predicate_push_promise_send(session, stream); - if (rv != 0) { - return rv; - } - - assert(stream); - - estimated_payloadlen = session_estimate_headers_payload( - session, frame->push_promise.nva, frame->push_promise.nvlen, 0); - - if (estimated_payloadlen > session->max_send_header_block_length) { - return NGHTTP2_ERR_FRAME_SIZE_ERROR; - } - - rv = nghttp2_frame_pack_push_promise( - &session->aob.framebufs, &frame->push_promise, &session->hd_deflater); - if (rv != 0) { - return rv; - } - rv = session_headers_add_pad(session, frame); - if (rv != 0) { - return rv; - } - - assert(session->last_sent_stream_id + 2 <= - frame->push_promise.promised_stream_id); - session->last_sent_stream_id = frame->push_promise.promised_stream_id; - - return 0; - } - case NGHTTP2_PING: - if (frame->hd.flags & NGHTTP2_FLAG_ACK) { - assert(session->obq_flood_counter_ > 0); - --session->obq_flood_counter_; - } - /* PING frame is allowed to be sent unless termination GOAWAY is - sent */ - if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) { - return NGHTTP2_ERR_SESSION_CLOSING; - } - nghttp2_frame_pack_ping(&session->aob.framebufs, &frame->ping); - return 0; - case NGHTTP2_GOAWAY: - rv = nghttp2_frame_pack_goaway(&session->aob.framebufs, &frame->goaway); - if (rv != 0) { - return rv; - } - session->local_last_stream_id = frame->goaway.last_stream_id; - - return 0; - case NGHTTP2_WINDOW_UPDATE: - rv = session_predicate_window_update_send(session, frame->hd.stream_id); - if (rv != 0) { - return rv; - } - nghttp2_frame_pack_window_update(&session->aob.framebufs, - &frame->window_update); - return 0; - case NGHTTP2_CONTINUATION: - /* We never handle CONTINUATION here. */ - assert(0); - return 0; - default: { - nghttp2_ext_aux_data *aux_data; - - /* extension frame */ - - aux_data = &item->aux_data.ext; - - if (aux_data->builtin == 0) { - if (session_is_closing(session)) { - return NGHTTP2_ERR_SESSION_CLOSING; - } - - return session_pack_extension(session, &session->aob.framebufs, frame); - } - - switch (frame->hd.type) { - case NGHTTP2_ALTSVC: - rv = session_predicate_altsvc_send(session, frame->hd.stream_id); - if (rv != 0) { - return rv; - } - - nghttp2_frame_pack_altsvc(&session->aob.framebufs, &frame->ext); - - return 0; +/* Take into account settings max frame size and both connection-level + flow control here */ +static ssize_t +nghttp2_session_enforce_flow_control_limits(nghttp2_session *session, + nghttp2_stream *stream, + ssize_t requested_window_size) { + DEBUGF("send: remote windowsize connection=%d, remote maxframsize=%u, " + "stream(id %d)=%d\n", + session->remote_window_size, session->remote_settings.max_frame_size, + stream->stream_id, stream->remote_window_size); + + return nghttp2_min(nghttp2_min(nghttp2_min(requested_window_size, + stream->remote_window_size), + session->remote_window_size), + (int32_t)session->remote_settings.max_frame_size); +} + +/* + * Returns the maximum length of next data read. If the + * connection-level and/or stream-wise flow control are enabled, the + * return value takes into account those current window sizes. The remote + * settings for max frame size is also taken into account. + */ +static size_t nghttp2_session_next_data_read(nghttp2_session *session, + nghttp2_stream *stream) { + ssize_t window_size; + + window_size = nghttp2_session_enforce_flow_control_limits( + session, stream, NGHTTP2_DATA_PAYLOADLEN); + + DEBUGF("send: available window=%zd\n", window_size); + + return window_size > 0 ? (size_t)window_size : 0; +} + +/* + * This function checks DATA with the |stream| can be sent at this + * time. The |stream| can be NULL. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_STREAM_CLOSED + * The stream is already closed or does not exist. + * NGHTTP2_ERR_STREAM_SHUT_WR + * The transmission is not allowed for this stream (e.g., a frame + * with END_STREAM flag set has already sent) + * NGHTTP2_ERR_STREAM_CLOSING + * RST_STREAM was queued for this stream. + * NGHTTP2_ERR_INVALID_STREAM_STATE + * The state of the stream is not valid. + * NGHTTP2_ERR_SESSION_CLOSING + * This session is closing. + */ +static int nghttp2_session_predicate_data_send(nghttp2_session *session, + nghttp2_stream *stream) { + int rv; + rv = session_predicate_for_stream_send(session, stream); + if (rv != 0) { + return rv; + } + assert(stream); + if (nghttp2_session_is_my_stream_id(session, stream->stream_id)) { + /* Request body data */ + /* If stream->state is NGHTTP2_STREAM_CLOSING, RST_STREAM was + queued but not yet sent. In this case, we won't send DATA + frames. */ + if (stream->state == NGHTTP2_STREAM_CLOSING) { + return NGHTTP2_ERR_STREAM_CLOSING; + } + if (stream->state == NGHTTP2_STREAM_RESERVED) { + return NGHTTP2_ERR_INVALID_STREAM_STATE; + } + return 0; + } + /* Response body data */ + if (stream->state == NGHTTP2_STREAM_OPENED) { + return 0; + } + if (stream->state == NGHTTP2_STREAM_CLOSING) { + return NGHTTP2_ERR_STREAM_CLOSING; + } + return NGHTTP2_ERR_INVALID_STREAM_STATE; +} + +static ssize_t session_call_select_padding(nghttp2_session *session, + const nghttp2_frame *frame, + size_t max_payloadlen) { + ssize_t rv; + + if (frame->hd.length >= max_payloadlen) { + return (ssize_t)frame->hd.length; + } + + if (session->callbacks.select_padding_callback) { + size_t max_paddedlen; + + max_paddedlen = + nghttp2_min(frame->hd.length + NGHTTP2_MAX_PADLEN, max_payloadlen); + + rv = session->callbacks.select_padding_callback( + session, frame, max_paddedlen, session->user_data); + if (rv < (ssize_t)frame->hd.length || rv > (ssize_t)max_paddedlen) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + return rv; + } + return (ssize_t)frame->hd.length; +} + +/* Add padding to HEADERS or PUSH_PROMISE. We use + frame->headers.padlen in this function to use the fact that + frame->push_promise has also padlen in the same position. */ +static int session_headers_add_pad(nghttp2_session *session, + nghttp2_frame *frame) { + int rv; + ssize_t padded_payloadlen; + nghttp2_active_outbound_item *aob; + nghttp2_bufs *framebufs; + size_t padlen; + size_t max_payloadlen; + + aob = &session->aob; + framebufs = &aob->framebufs; + + max_payloadlen = nghttp2_min(NGHTTP2_MAX_PAYLOADLEN, + frame->hd.length + NGHTTP2_MAX_PADLEN); + + padded_payloadlen = + session_call_select_padding(session, frame, max_payloadlen); + + if (nghttp2_is_fatal((int)padded_payloadlen)) { + return (int)padded_payloadlen; + } + + padlen = (size_t)padded_payloadlen - frame->hd.length; + + DEBUGF("send: padding selected: payloadlen=%zd, padlen=%zu\n", + padded_payloadlen, padlen); + + rv = nghttp2_frame_add_pad(framebufs, &frame->hd, padlen, 0); + + if (rv != 0) { + return rv; + } + + frame->headers.padlen = padlen; + + return 0; +} + +static size_t session_estimate_headers_payload(nghttp2_session *session, + const nghttp2_nv *nva, + size_t nvlen, + size_t additional) { + return nghttp2_hd_deflate_bound(&session->hd_deflater, nva, nvlen) + + additional; +} + +static int session_pack_extension(nghttp2_session *session, nghttp2_bufs *bufs, + nghttp2_frame *frame) { + ssize_t rv; + nghttp2_buf *buf; + size_t buflen; + size_t framelen; + + assert(session->callbacks.pack_extension_callback); + + buf = &bufs->head->buf; + buflen = nghttp2_min(nghttp2_buf_avail(buf), NGHTTP2_MAX_PAYLOADLEN); + + rv = session->callbacks.pack_extension_callback(session, buf->last, buflen, + frame, session->user_data); + if (rv == NGHTTP2_ERR_CANCEL) { + return (int)rv; + } + + if (rv < 0 || (size_t)rv > buflen) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + + framelen = (size_t)rv; + + frame->hd.length = framelen; + + assert(buf->pos == buf->last); + buf->last += framelen; + buf->pos -= NGHTTP2_FRAME_HDLEN; + + nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); + + return 0; +} + +/* + * This function serializes frame for transmission. + * + * This function returns 0 if it succeeds, or one of negative error + * codes, including both fatal and non-fatal ones. + */ +static int session_prep_frame(nghttp2_session *session, + nghttp2_outbound_item *item) { + int rv; + nghttp2_frame *frame; + nghttp2_mem *mem; + + mem = &session->mem; + frame = &item->frame; + + switch (frame->hd.type) { + case NGHTTP2_DATA: { + size_t next_readmax; + nghttp2_stream *stream; + + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + + if (stream) { + assert(stream->item == item); + } + + rv = nghttp2_session_predicate_data_send(session, stream); + if (rv != 0) { + // If stream was already closed, nghttp2_session_get_stream() + // returns NULL, but item is still attached to the stream. + // Search stream including closed again. + stream = nghttp2_session_get_stream_raw(session, frame->hd.stream_id); + if (stream) { + int rv2; + + rv2 = nghttp2_stream_detach_item(stream); + + if (nghttp2_is_fatal(rv2)) { + return rv2; + } + } + + return rv; + } + /* Assuming stream is not NULL */ + assert(stream); + next_readmax = nghttp2_session_next_data_read(session, stream); + + if (next_readmax == 0) { + + /* This must be true since we only pop DATA frame item from + queue when session->remote_window_size > 0 */ + assert(session->remote_window_size > 0); + + rv = nghttp2_stream_defer_item(stream, + NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + session->aob.item = NULL; + active_outbound_item_reset(&session->aob, mem); + return NGHTTP2_ERR_DEFERRED; + } + + rv = nghttp2_session_pack_data(session, &session->aob.framebufs, + next_readmax, frame, &item->aux_data.data, + stream); + if (rv == NGHTTP2_ERR_PAUSE) { + return rv; + } + if (rv == NGHTTP2_ERR_DEFERRED) { + rv = nghttp2_stream_defer_item(stream, NGHTTP2_STREAM_FLAG_DEFERRED_USER); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + session->aob.item = NULL; + active_outbound_item_reset(&session->aob, mem); + return NGHTTP2_ERR_DEFERRED; + } + if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { + rv = nghttp2_stream_detach_item(stream); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + rv = nghttp2_session_add_rst_stream(session, frame->hd.stream_id, + NGHTTP2_INTERNAL_ERROR); + if (nghttp2_is_fatal(rv)) { + return rv; + } + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + } + if (rv != 0) { + int rv2; + + rv2 = nghttp2_stream_detach_item(stream); + + if (nghttp2_is_fatal(rv2)) { + return rv2; + } + + return rv; + } + return 0; + } + case NGHTTP2_HEADERS: { + nghttp2_headers_aux_data *aux_data; + size_t estimated_payloadlen; + + aux_data = &item->aux_data.headers; + + if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) { + /* initial HEADERS, which opens stream */ + nghttp2_stream *stream; + + stream = nghttp2_session_open_stream( + session, frame->hd.stream_id, NGHTTP2_STREAM_FLAG_NONE, + &frame->headers.pri_spec, NGHTTP2_STREAM_INITIAL, + aux_data->stream_user_data); + + if (stream == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + /* We don't call nghttp2_session_adjust_closed_stream() here, + since we don't keep closed stream in client side */ + + rv = session_predicate_request_headers_send(session, item); + if (rv != 0) { + return rv; + } + + if (session_enforce_http_messaging(session)) { + nghttp2_http_record_request_method(stream, frame); + } + } else { + nghttp2_stream *stream; + + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + + if (stream && stream->state == NGHTTP2_STREAM_RESERVED) { + rv = session_predicate_push_response_headers_send(session, stream); + if (rv == 0) { + frame->headers.cat = NGHTTP2_HCAT_PUSH_RESPONSE; + + if (aux_data->stream_user_data) { + stream->stream_user_data = aux_data->stream_user_data; + } + } + } else if (session_predicate_response_headers_send(session, stream) == + 0) { + frame->headers.cat = NGHTTP2_HCAT_RESPONSE; + rv = 0; + } else { + frame->headers.cat = NGHTTP2_HCAT_HEADERS; + + rv = session_predicate_headers_send(session, stream); + } + + if (rv != 0) { + return rv; + } + } + + estimated_payloadlen = session_estimate_headers_payload( + session, frame->headers.nva, frame->headers.nvlen, + NGHTTP2_PRIORITY_SPECLEN); + + if (estimated_payloadlen > session->max_send_header_block_length) { + return NGHTTP2_ERR_FRAME_SIZE_ERROR; + } + + rv = nghttp2_frame_pack_headers(&session->aob.framebufs, &frame->headers, + &session->hd_deflater); + + if (rv != 0) { + return rv; + } + + DEBUGF("send: before padding, HEADERS serialized in %zd bytes\n", + nghttp2_bufs_len(&session->aob.framebufs)); + + rv = session_headers_add_pad(session, frame); + + if (rv != 0) { + return rv; + } + + DEBUGF("send: HEADERS finally serialized in %zd bytes\n", + nghttp2_bufs_len(&session->aob.framebufs)); + + if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) { + assert(session->last_sent_stream_id < frame->hd.stream_id); + session->last_sent_stream_id = frame->hd.stream_id; + } + + return 0; + } + case NGHTTP2_PRIORITY: { + if (session_is_closing(session)) { + return NGHTTP2_ERR_SESSION_CLOSING; + } + /* PRIORITY frame can be sent at any time and to any stream + ID. */ + nghttp2_frame_pack_priority(&session->aob.framebufs, &frame->priority); + + /* Peer can send PRIORITY frame against idle stream to create + "anchor" in dependency tree. Only client can do this in + nghttp2. In nghttp2, only server retains non-active (closed + or idle) streams in memory, so we don't open stream here. */ + return 0; + } + case NGHTTP2_RST_STREAM: + if (session_is_closing(session)) { + return NGHTTP2_ERR_SESSION_CLOSING; + } + nghttp2_frame_pack_rst_stream(&session->aob.framebufs, &frame->rst_stream); + return 0; + case NGHTTP2_SETTINGS: { + if (frame->hd.flags & NGHTTP2_FLAG_ACK) { + assert(session->obq_flood_counter_ > 0); + --session->obq_flood_counter_; + /* When session is about to close, don't send SETTINGS ACK. + We are required to send SETTINGS without ACK though; for + example, we have to send SETTINGS as a part of connection + preface. */ + if (session_is_closing(session)) { + return NGHTTP2_ERR_SESSION_CLOSING; + } + } + + rv = nghttp2_frame_pack_settings(&session->aob.framebufs, &frame->settings); + if (rv != 0) { + return rv; + } + return 0; + } + case NGHTTP2_PUSH_PROMISE: { + nghttp2_stream *stream; + size_t estimated_payloadlen; + + /* stream could be NULL if associated stream was already + closed. */ + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + + /* predicate should fail if stream is NULL. */ + rv = session_predicate_push_promise_send(session, stream); + if (rv != 0) { + return rv; + } + + assert(stream); + + estimated_payloadlen = session_estimate_headers_payload( + session, frame->push_promise.nva, frame->push_promise.nvlen, 0); + + if (estimated_payloadlen > session->max_send_header_block_length) { + return NGHTTP2_ERR_FRAME_SIZE_ERROR; + } + + rv = nghttp2_frame_pack_push_promise( + &session->aob.framebufs, &frame->push_promise, &session->hd_deflater); + if (rv != 0) { + return rv; + } + rv = session_headers_add_pad(session, frame); + if (rv != 0) { + return rv; + } + + assert(session->last_sent_stream_id + 2 <= + frame->push_promise.promised_stream_id); + session->last_sent_stream_id = frame->push_promise.promised_stream_id; + + return 0; + } + case NGHTTP2_PING: + if (frame->hd.flags & NGHTTP2_FLAG_ACK) { + assert(session->obq_flood_counter_ > 0); + --session->obq_flood_counter_; + } + /* PING frame is allowed to be sent unless termination GOAWAY is + sent */ + if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) { + return NGHTTP2_ERR_SESSION_CLOSING; + } + nghttp2_frame_pack_ping(&session->aob.framebufs, &frame->ping); + return 0; + case NGHTTP2_GOAWAY: + rv = nghttp2_frame_pack_goaway(&session->aob.framebufs, &frame->goaway); + if (rv != 0) { + return rv; + } + session->local_last_stream_id = frame->goaway.last_stream_id; + + return 0; + case NGHTTP2_WINDOW_UPDATE: + rv = session_predicate_window_update_send(session, frame->hd.stream_id); + if (rv != 0) { + return rv; + } + nghttp2_frame_pack_window_update(&session->aob.framebufs, + &frame->window_update); + return 0; + case NGHTTP2_CONTINUATION: + /* We never handle CONTINUATION here. */ + assert(0); + return 0; + default: { + nghttp2_ext_aux_data *aux_data; + + /* extension frame */ + + aux_data = &item->aux_data.ext; + + if (aux_data->builtin == 0) { + if (session_is_closing(session)) { + return NGHTTP2_ERR_SESSION_CLOSING; + } + + return session_pack_extension(session, &session->aob.framebufs, frame); + } + + switch (frame->hd.type) { + case NGHTTP2_ALTSVC: + rv = session_predicate_altsvc_send(session, frame->hd.stream_id); + if (rv != 0) { + return rv; + } + + nghttp2_frame_pack_altsvc(&session->aob.framebufs, &frame->ext); + + return 0; case NGHTTP2_ORIGIN: rv = session_predicate_origin_send(session); if (rv != 0) { @@ -2328,1321 +2328,1321 @@ static int session_prep_frame(nghttp2_session *session, } return 0; - default: - /* Unreachable here */ - assert(0); - return 0; - } - } - } -} - -nghttp2_outbound_item * -nghttp2_session_get_next_ob_item(nghttp2_session *session) { - if (nghttp2_outbound_queue_top(&session->ob_urgent)) { - return nghttp2_outbound_queue_top(&session->ob_urgent); - } - - if (nghttp2_outbound_queue_top(&session->ob_reg)) { - return nghttp2_outbound_queue_top(&session->ob_reg); - } - - if (!session_is_outgoing_concurrent_streams_max(session)) { - if (nghttp2_outbound_queue_top(&session->ob_syn)) { - return nghttp2_outbound_queue_top(&session->ob_syn); - } - } - - if (session->remote_window_size > 0) { - return nghttp2_stream_next_outbound_item(&session->root); - } - - return NULL; -} - -nghttp2_outbound_item * -nghttp2_session_pop_next_ob_item(nghttp2_session *session) { - nghttp2_outbound_item *item; - - item = nghttp2_outbound_queue_top(&session->ob_urgent); - if (item) { - nghttp2_outbound_queue_pop(&session->ob_urgent); - item->queued = 0; - return item; - } - - item = nghttp2_outbound_queue_top(&session->ob_reg); - if (item) { - nghttp2_outbound_queue_pop(&session->ob_reg); - item->queued = 0; - return item; - } - - if (!session_is_outgoing_concurrent_streams_max(session)) { - item = nghttp2_outbound_queue_top(&session->ob_syn); - if (item) { - nghttp2_outbound_queue_pop(&session->ob_syn); - item->queued = 0; - return item; - } - } - - if (session->remote_window_size > 0) { - return nghttp2_stream_next_outbound_item(&session->root); - } - - return NULL; -} - -static int session_call_before_frame_send(nghttp2_session *session, - nghttp2_frame *frame) { - int rv; - if (session->callbacks.before_frame_send_callback) { - rv = session->callbacks.before_frame_send_callback(session, frame, - session->user_data); - if (rv == NGHTTP2_ERR_CANCEL) { - return rv; - } - - if (rv != 0) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - } - return 0; -} - -static int session_call_on_frame_send(nghttp2_session *session, - nghttp2_frame *frame) { - int rv; - if (session->callbacks.on_frame_send_callback) { - rv = session->callbacks.on_frame_send_callback(session, frame, - session->user_data); - if (rv != 0) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - } - return 0; -} - + default: + /* Unreachable here */ + assert(0); + return 0; + } + } + } +} + +nghttp2_outbound_item * +nghttp2_session_get_next_ob_item(nghttp2_session *session) { + if (nghttp2_outbound_queue_top(&session->ob_urgent)) { + return nghttp2_outbound_queue_top(&session->ob_urgent); + } + + if (nghttp2_outbound_queue_top(&session->ob_reg)) { + return nghttp2_outbound_queue_top(&session->ob_reg); + } + + if (!session_is_outgoing_concurrent_streams_max(session)) { + if (nghttp2_outbound_queue_top(&session->ob_syn)) { + return nghttp2_outbound_queue_top(&session->ob_syn); + } + } + + if (session->remote_window_size > 0) { + return nghttp2_stream_next_outbound_item(&session->root); + } + + return NULL; +} + +nghttp2_outbound_item * +nghttp2_session_pop_next_ob_item(nghttp2_session *session) { + nghttp2_outbound_item *item; + + item = nghttp2_outbound_queue_top(&session->ob_urgent); + if (item) { + nghttp2_outbound_queue_pop(&session->ob_urgent); + item->queued = 0; + return item; + } + + item = nghttp2_outbound_queue_top(&session->ob_reg); + if (item) { + nghttp2_outbound_queue_pop(&session->ob_reg); + item->queued = 0; + return item; + } + + if (!session_is_outgoing_concurrent_streams_max(session)) { + item = nghttp2_outbound_queue_top(&session->ob_syn); + if (item) { + nghttp2_outbound_queue_pop(&session->ob_syn); + item->queued = 0; + return item; + } + } + + if (session->remote_window_size > 0) { + return nghttp2_stream_next_outbound_item(&session->root); + } + + return NULL; +} + +static int session_call_before_frame_send(nghttp2_session *session, + nghttp2_frame *frame) { + int rv; + if (session->callbacks.before_frame_send_callback) { + rv = session->callbacks.before_frame_send_callback(session, frame, + session->user_data); + if (rv == NGHTTP2_ERR_CANCEL) { + return rv; + } + + if (rv != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + return 0; +} + +static int session_call_on_frame_send(nghttp2_session *session, + nghttp2_frame *frame) { + int rv; + if (session->callbacks.on_frame_send_callback) { + rv = session->callbacks.on_frame_send_callback(session, frame, + session->user_data); + if (rv != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + return 0; +} + static int find_stream_on_goaway_func(void *entry, void *ptr) { - nghttp2_close_stream_on_goaway_arg *arg; - nghttp2_stream *stream; - - arg = (nghttp2_close_stream_on_goaway_arg *)ptr; - stream = (nghttp2_stream *)entry; - - if (nghttp2_session_is_my_stream_id(arg->session, stream->stream_id)) { - if (arg->incoming) { - return 0; - } - } else if (!arg->incoming) { - return 0; - } - - if (stream->state != NGHTTP2_STREAM_IDLE && - (stream->flags & NGHTTP2_STREAM_FLAG_CLOSED) == 0 && - stream->stream_id > arg->last_stream_id) { - /* We are collecting streams to close because we cannot call - nghttp2_session_close_stream() inside nghttp2_map_each(). - Reuse closed_next member.. bad choice? */ - assert(stream->closed_next == NULL); - assert(stream->closed_prev == NULL); - - if (arg->head) { - stream->closed_next = arg->head; - arg->head = stream; - } else { - arg->head = stream; - } - } - - return 0; -} - -/* Closes non-idle and non-closed streams whose stream ID > - last_stream_id. If incoming is nonzero, we are going to close - incoming streams. Otherwise, close outgoing streams. */ -static int session_close_stream_on_goaway(nghttp2_session *session, - int32_t last_stream_id, - int incoming) { - int rv; - nghttp2_stream *stream, *next_stream; - nghttp2_close_stream_on_goaway_arg arg = {session, NULL, last_stream_id, - incoming}; - - rv = nghttp2_map_each(&session->streams, find_stream_on_goaway_func, &arg); - assert(rv == 0); - - stream = arg.head; - while (stream) { - next_stream = stream->closed_next; - stream->closed_next = NULL; - rv = nghttp2_session_close_stream(session, stream->stream_id, - NGHTTP2_REFUSED_STREAM); - - /* stream may be deleted here */ - - stream = next_stream; - - if (nghttp2_is_fatal(rv)) { - /* Clean up closed_next member just in case */ - while (stream) { - next_stream = stream->closed_next; - stream->closed_next = NULL; - stream = next_stream; - } - return rv; - } - } - - return 0; -} - -static void reschedule_stream(nghttp2_stream *stream) { - stream->last_writelen = stream->item->frame.hd.length; - - nghttp2_stream_reschedule(stream); -} - -static int session_update_stream_consumed_size(nghttp2_session *session, - nghttp2_stream *stream, - size_t delta_size); - -static int session_update_connection_consumed_size(nghttp2_session *session, - size_t delta_size); - -/* - * Called after a frame is sent. This function runs - * on_frame_send_callback and handles stream closure upon END_STREAM - * or RST_STREAM. This function does not reset session->aob. It is a - * responsibility of session_after_frame_sent2. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - * NGHTTP2_ERR_CALLBACK_FAILURE - * The callback function failed. - */ -static int session_after_frame_sent1(nghttp2_session *session) { - int rv; - nghttp2_active_outbound_item *aob = &session->aob; - nghttp2_outbound_item *item = aob->item; - nghttp2_bufs *framebufs = &aob->framebufs; - nghttp2_frame *frame; - nghttp2_stream *stream; - - frame = &item->frame; - - if (frame->hd.type == NGHTTP2_DATA) { - nghttp2_data_aux_data *aux_data; - - aux_data = &item->aux_data.data; - - stream = nghttp2_session_get_stream(session, frame->hd.stream_id); - /* We update flow control window after a frame was completely - sent. This is possible because we choose payload length not to - exceed the window */ - session->remote_window_size -= (int32_t)frame->hd.length; - if (stream) { - stream->remote_window_size -= (int32_t)frame->hd.length; - } - - if (stream && aux_data->eof) { - rv = nghttp2_stream_detach_item(stream); - if (nghttp2_is_fatal(rv)) { - return rv; - } - - /* Call on_frame_send_callback after - nghttp2_stream_detach_item(), so that application can issue - nghttp2_submit_data() in the callback. */ - if (session->callbacks.on_frame_send_callback) { - rv = session_call_on_frame_send(session, frame); - if (nghttp2_is_fatal(rv)) { - return rv; - } - } - - if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { - int stream_closed; - - stream_closed = - (stream->shut_flags & NGHTTP2_SHUT_RDWR) == NGHTTP2_SHUT_RDWR; - - nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR); - - rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream); - if (nghttp2_is_fatal(rv)) { - return rv; - } - /* stream may be NULL if it was closed */ - if (stream_closed) { - stream = NULL; - } - } - return 0; - } - - if (session->callbacks.on_frame_send_callback) { - rv = session_call_on_frame_send(session, frame); - if (nghttp2_is_fatal(rv)) { - return rv; - } - } - - return 0; - } - - /* non-DATA frame */ - - if (frame->hd.type == NGHTTP2_HEADERS || - frame->hd.type == NGHTTP2_PUSH_PROMISE) { - if (nghttp2_bufs_next_present(framebufs)) { - DEBUGF("send: CONTINUATION exists, just return\n"); - return 0; - } - } - rv = session_call_on_frame_send(session, frame); - if (nghttp2_is_fatal(rv)) { - return rv; - } - switch (frame->hd.type) { - case NGHTTP2_HEADERS: { - nghttp2_headers_aux_data *aux_data; - - stream = nghttp2_session_get_stream(session, frame->hd.stream_id); - if (!stream) { - return 0; - } - - switch (frame->headers.cat) { - case NGHTTP2_HCAT_REQUEST: { - stream->state = NGHTTP2_STREAM_OPENING; - if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { - nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR); - } - rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream); - if (nghttp2_is_fatal(rv)) { - return rv; - } - /* We assume aux_data is a pointer to nghttp2_headers_aux_data */ - aux_data = &item->aux_data.headers; - if (aux_data->data_prd.read_callback) { - /* nghttp2_submit_data() makes a copy of aux_data->data_prd */ - rv = nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM, - frame->hd.stream_id, &aux_data->data_prd); - if (nghttp2_is_fatal(rv)) { - return rv; - } - /* TODO nghttp2_submit_data() may fail if stream has already - DATA frame item. We might have to handle it here. */ - } - return 0; - } - case NGHTTP2_HCAT_PUSH_RESPONSE: - stream->flags = (uint8_t)(stream->flags & ~NGHTTP2_STREAM_FLAG_PUSH); - ++session->num_outgoing_streams; - /* Fall through */ - case NGHTTP2_HCAT_RESPONSE: - stream->state = NGHTTP2_STREAM_OPENED; - /* Fall through */ - case NGHTTP2_HCAT_HEADERS: - if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { - nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR); - } - rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream); - if (nghttp2_is_fatal(rv)) { - return rv; - } - /* We assume aux_data is a pointer to nghttp2_headers_aux_data */ - aux_data = &item->aux_data.headers; - if (aux_data->data_prd.read_callback) { - rv = nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM, - frame->hd.stream_id, &aux_data->data_prd); - if (nghttp2_is_fatal(rv)) { - return rv; - } - /* TODO nghttp2_submit_data() may fail if stream has already - DATA frame item. We might have to handle it here. */ - } - return 0; - default: - /* Unreachable */ - assert(0); - return 0; - } - } - case NGHTTP2_PRIORITY: - if (session->server) { - return 0; - ; - } - - stream = nghttp2_session_get_stream_raw(session, frame->hd.stream_id); - - if (!stream) { - if (!session_detect_idle_stream(session, frame->hd.stream_id)) { - return 0; - } - - stream = nghttp2_session_open_stream( - session, frame->hd.stream_id, NGHTTP2_FLAG_NONE, - &frame->priority.pri_spec, NGHTTP2_STREAM_IDLE, NULL); - if (!stream) { - return NGHTTP2_ERR_NOMEM; - } - } else { - rv = nghttp2_session_reprioritize_stream(session, stream, - &frame->priority.pri_spec); - if (nghttp2_is_fatal(rv)) { - return rv; - } - } - - rv = nghttp2_session_adjust_idle_stream(session); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - - return 0; - case NGHTTP2_RST_STREAM: - rv = nghttp2_session_close_stream(session, frame->hd.stream_id, - frame->rst_stream.error_code); - if (nghttp2_is_fatal(rv)) { - return rv; - } - return 0; - case NGHTTP2_GOAWAY: { - nghttp2_goaway_aux_data *aux_data; - - aux_data = &item->aux_data.goaway; - - if ((aux_data->flags & NGHTTP2_GOAWAY_AUX_SHUTDOWN_NOTICE) == 0) { - - if (aux_data->flags & NGHTTP2_GOAWAY_AUX_TERM_ON_SEND) { - session->goaway_flags |= NGHTTP2_GOAWAY_TERM_SENT; - } - - session->goaway_flags |= NGHTTP2_GOAWAY_SENT; - - rv = session_close_stream_on_goaway(session, frame->goaway.last_stream_id, - 1); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - } - - return 0; - } - case NGHTTP2_WINDOW_UPDATE: - if (frame->hd.stream_id == 0) { - session->window_update_queued = 0; - if (session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) { - rv = session_update_connection_consumed_size(session, 0); - } else { + nghttp2_close_stream_on_goaway_arg *arg; + nghttp2_stream *stream; + + arg = (nghttp2_close_stream_on_goaway_arg *)ptr; + stream = (nghttp2_stream *)entry; + + if (nghttp2_session_is_my_stream_id(arg->session, stream->stream_id)) { + if (arg->incoming) { + return 0; + } + } else if (!arg->incoming) { + return 0; + } + + if (stream->state != NGHTTP2_STREAM_IDLE && + (stream->flags & NGHTTP2_STREAM_FLAG_CLOSED) == 0 && + stream->stream_id > arg->last_stream_id) { + /* We are collecting streams to close because we cannot call + nghttp2_session_close_stream() inside nghttp2_map_each(). + Reuse closed_next member.. bad choice? */ + assert(stream->closed_next == NULL); + assert(stream->closed_prev == NULL); + + if (arg->head) { + stream->closed_next = arg->head; + arg->head = stream; + } else { + arg->head = stream; + } + } + + return 0; +} + +/* Closes non-idle and non-closed streams whose stream ID > + last_stream_id. If incoming is nonzero, we are going to close + incoming streams. Otherwise, close outgoing streams. */ +static int session_close_stream_on_goaway(nghttp2_session *session, + int32_t last_stream_id, + int incoming) { + int rv; + nghttp2_stream *stream, *next_stream; + nghttp2_close_stream_on_goaway_arg arg = {session, NULL, last_stream_id, + incoming}; + + rv = nghttp2_map_each(&session->streams, find_stream_on_goaway_func, &arg); + assert(rv == 0); + + stream = arg.head; + while (stream) { + next_stream = stream->closed_next; + stream->closed_next = NULL; + rv = nghttp2_session_close_stream(session, stream->stream_id, + NGHTTP2_REFUSED_STREAM); + + /* stream may be deleted here */ + + stream = next_stream; + + if (nghttp2_is_fatal(rv)) { + /* Clean up closed_next member just in case */ + while (stream) { + next_stream = stream->closed_next; + stream->closed_next = NULL; + stream = next_stream; + } + return rv; + } + } + + return 0; +} + +static void reschedule_stream(nghttp2_stream *stream) { + stream->last_writelen = stream->item->frame.hd.length; + + nghttp2_stream_reschedule(stream); +} + +static int session_update_stream_consumed_size(nghttp2_session *session, + nghttp2_stream *stream, + size_t delta_size); + +static int session_update_connection_consumed_size(nghttp2_session *session, + size_t delta_size); + +/* + * Called after a frame is sent. This function runs + * on_frame_send_callback and handles stream closure upon END_STREAM + * or RST_STREAM. This function does not reset session->aob. It is a + * responsibility of session_after_frame_sent2. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_CALLBACK_FAILURE + * The callback function failed. + */ +static int session_after_frame_sent1(nghttp2_session *session) { + int rv; + nghttp2_active_outbound_item *aob = &session->aob; + nghttp2_outbound_item *item = aob->item; + nghttp2_bufs *framebufs = &aob->framebufs; + nghttp2_frame *frame; + nghttp2_stream *stream; + + frame = &item->frame; + + if (frame->hd.type == NGHTTP2_DATA) { + nghttp2_data_aux_data *aux_data; + + aux_data = &item->aux_data.data; + + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + /* We update flow control window after a frame was completely + sent. This is possible because we choose payload length not to + exceed the window */ + session->remote_window_size -= (int32_t)frame->hd.length; + if (stream) { + stream->remote_window_size -= (int32_t)frame->hd.length; + } + + if (stream && aux_data->eof) { + rv = nghttp2_stream_detach_item(stream); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + /* Call on_frame_send_callback after + nghttp2_stream_detach_item(), so that application can issue + nghttp2_submit_data() in the callback. */ + if (session->callbacks.on_frame_send_callback) { + rv = session_call_on_frame_send(session, frame); + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + int stream_closed; + + stream_closed = + (stream->shut_flags & NGHTTP2_SHUT_RDWR) == NGHTTP2_SHUT_RDWR; + + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR); + + rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream); + if (nghttp2_is_fatal(rv)) { + return rv; + } + /* stream may be NULL if it was closed */ + if (stream_closed) { + stream = NULL; + } + } + return 0; + } + + if (session->callbacks.on_frame_send_callback) { + rv = session_call_on_frame_send(session, frame); + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + + return 0; + } + + /* non-DATA frame */ + + if (frame->hd.type == NGHTTP2_HEADERS || + frame->hd.type == NGHTTP2_PUSH_PROMISE) { + if (nghttp2_bufs_next_present(framebufs)) { + DEBUGF("send: CONTINUATION exists, just return\n"); + return 0; + } + } + rv = session_call_on_frame_send(session, frame); + if (nghttp2_is_fatal(rv)) { + return rv; + } + switch (frame->hd.type) { + case NGHTTP2_HEADERS: { + nghttp2_headers_aux_data *aux_data; + + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + if (!stream) { + return 0; + } + + switch (frame->headers.cat) { + case NGHTTP2_HCAT_REQUEST: { + stream->state = NGHTTP2_STREAM_OPENING; + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR); + } + rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream); + if (nghttp2_is_fatal(rv)) { + return rv; + } + /* We assume aux_data is a pointer to nghttp2_headers_aux_data */ + aux_data = &item->aux_data.headers; + if (aux_data->data_prd.read_callback) { + /* nghttp2_submit_data() makes a copy of aux_data->data_prd */ + rv = nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM, + frame->hd.stream_id, &aux_data->data_prd); + if (nghttp2_is_fatal(rv)) { + return rv; + } + /* TODO nghttp2_submit_data() may fail if stream has already + DATA frame item. We might have to handle it here. */ + } + return 0; + } + case NGHTTP2_HCAT_PUSH_RESPONSE: + stream->flags = (uint8_t)(stream->flags & ~NGHTTP2_STREAM_FLAG_PUSH); + ++session->num_outgoing_streams; + /* Fall through */ + case NGHTTP2_HCAT_RESPONSE: + stream->state = NGHTTP2_STREAM_OPENED; + /* Fall through */ + case NGHTTP2_HCAT_HEADERS: + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR); + } + rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream); + if (nghttp2_is_fatal(rv)) { + return rv; + } + /* We assume aux_data is a pointer to nghttp2_headers_aux_data */ + aux_data = &item->aux_data.headers; + if (aux_data->data_prd.read_callback) { + rv = nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM, + frame->hd.stream_id, &aux_data->data_prd); + if (nghttp2_is_fatal(rv)) { + return rv; + } + /* TODO nghttp2_submit_data() may fail if stream has already + DATA frame item. We might have to handle it here. */ + } + return 0; + default: + /* Unreachable */ + assert(0); + return 0; + } + } + case NGHTTP2_PRIORITY: + if (session->server) { + return 0; + ; + } + + stream = nghttp2_session_get_stream_raw(session, frame->hd.stream_id); + + if (!stream) { + if (!session_detect_idle_stream(session, frame->hd.stream_id)) { + return 0; + } + + stream = nghttp2_session_open_stream( + session, frame->hd.stream_id, NGHTTP2_FLAG_NONE, + &frame->priority.pri_spec, NGHTTP2_STREAM_IDLE, NULL); + if (!stream) { + return NGHTTP2_ERR_NOMEM; + } + } else { + rv = nghttp2_session_reprioritize_stream(session, stream, + &frame->priority.pri_spec); + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + + rv = nghttp2_session_adjust_idle_stream(session); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + return 0; + case NGHTTP2_RST_STREAM: + rv = nghttp2_session_close_stream(session, frame->hd.stream_id, + frame->rst_stream.error_code); + if (nghttp2_is_fatal(rv)) { + return rv; + } + return 0; + case NGHTTP2_GOAWAY: { + nghttp2_goaway_aux_data *aux_data; + + aux_data = &item->aux_data.goaway; + + if ((aux_data->flags & NGHTTP2_GOAWAY_AUX_SHUTDOWN_NOTICE) == 0) { + + if (aux_data->flags & NGHTTP2_GOAWAY_AUX_TERM_ON_SEND) { + session->goaway_flags |= NGHTTP2_GOAWAY_TERM_SENT; + } + + session->goaway_flags |= NGHTTP2_GOAWAY_SENT; + + rv = session_close_stream_on_goaway(session, frame->goaway.last_stream_id, + 1); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + + return 0; + } + case NGHTTP2_WINDOW_UPDATE: + if (frame->hd.stream_id == 0) { + session->window_update_queued = 0; + if (session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) { + rv = session_update_connection_consumed_size(session, 0); + } else { rv = nghttp2_session_update_recv_connection_window_size(session, 0); - } - - if (nghttp2_is_fatal(rv)) { - return rv; - } - - return 0; - } - - stream = nghttp2_session_get_stream(session, frame->hd.stream_id); - if (!stream) { - return 0; - } - - stream->window_update_queued = 0; - - /* We don't have to send WINDOW_UPDATE if END_STREAM from peer - is seen. */ - if (stream->shut_flags & NGHTTP2_SHUT_RD) { - return 0; - } - - if (session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) { - rv = session_update_stream_consumed_size(session, stream, 0); - } else { + } + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + return 0; + } + + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + if (!stream) { + return 0; + } + + stream->window_update_queued = 0; + + /* We don't have to send WINDOW_UPDATE if END_STREAM from peer + is seen. */ + if (stream->shut_flags & NGHTTP2_SHUT_RD) { + return 0; + } + + if (session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) { + rv = session_update_stream_consumed_size(session, stream, 0); + } else { rv = nghttp2_session_update_recv_stream_window_size(session, stream, 0, 1); - } - - if (nghttp2_is_fatal(rv)) { - return rv; - } - - return 0; - default: - return 0; - } -} - -/* - * Called after a frame is sent and session_after_frame_sent1. This - * function is responsible to reset session->aob. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - * NGHTTP2_ERR_CALLBACK_FAILURE - * The callback function failed. - */ -static int session_after_frame_sent2(nghttp2_session *session) { - int rv; - nghttp2_active_outbound_item *aob = &session->aob; - nghttp2_outbound_item *item = aob->item; - nghttp2_bufs *framebufs = &aob->framebufs; - nghttp2_frame *frame; - nghttp2_mem *mem; - nghttp2_stream *stream; - nghttp2_data_aux_data *aux_data; - - mem = &session->mem; - frame = &item->frame; - - if (frame->hd.type != NGHTTP2_DATA) { - - if (frame->hd.type == NGHTTP2_HEADERS || - frame->hd.type == NGHTTP2_PUSH_PROMISE) { - - if (nghttp2_bufs_next_present(framebufs)) { - framebufs->cur = framebufs->cur->next; - - DEBUGF("send: next CONTINUATION frame, %zu bytes\n", - nghttp2_buf_len(&framebufs->cur->buf)); - - return 0; - } - } - - active_outbound_item_reset(&session->aob, mem); - - return 0; - } - - /* DATA frame */ - - aux_data = &item->aux_data.data; - - /* On EOF, we have already detached data. Please note that - application may issue nghttp2_submit_data() in - on_frame_send_callback (call from session_after_frame_sent1), - which attach data to stream. We don't want to detach it. */ - if (aux_data->eof) { - active_outbound_item_reset(aob, mem); - - return 0; - } - - /* Reset no_copy here because next write may not use this. */ - aux_data->no_copy = 0; - - stream = nghttp2_session_get_stream(session, frame->hd.stream_id); - - /* If session is closed or RST_STREAM was queued, we won't send - further data. */ - if (nghttp2_session_predicate_data_send(session, stream) != 0) { - if (stream) { - rv = nghttp2_stream_detach_item(stream); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - } - - active_outbound_item_reset(aob, mem); - - return 0; - } - - aob->item = NULL; - active_outbound_item_reset(&session->aob, mem); - - return 0; -} - -static int session_call_send_data(nghttp2_session *session, - nghttp2_outbound_item *item, - nghttp2_bufs *framebufs) { - int rv; - nghttp2_buf *buf; - size_t length; - nghttp2_frame *frame; - nghttp2_data_aux_data *aux_data; - - buf = &framebufs->cur->buf; - frame = &item->frame; - length = frame->hd.length - frame->data.padlen; - aux_data = &item->aux_data.data; - - rv = session->callbacks.send_data_callback(session, frame, buf->pos, length, - &aux_data->data_prd.source, - session->user_data); - - switch (rv) { - case 0: - case NGHTTP2_ERR_WOULDBLOCK: - case NGHTTP2_ERR_PAUSE: - case NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE: - return rv; - default: - return NGHTTP2_ERR_CALLBACK_FAILURE; - } -} - -static ssize_t nghttp2_session_mem_send_internal(nghttp2_session *session, - const uint8_t **data_ptr, - int fast_cb) { - int rv; - nghttp2_active_outbound_item *aob; - nghttp2_bufs *framebufs; - nghttp2_mem *mem; - - mem = &session->mem; - aob = &session->aob; - framebufs = &aob->framebufs; - - /* We may have idle streams more than we expect (e.g., - nghttp2_session_change_stream_priority() or - nghttp2_session_create_idle_stream()). Adjust them here. */ - rv = nghttp2_session_adjust_idle_stream(session); - if (nghttp2_is_fatal(rv)) { - return rv; - } - - for (;;) { - switch (aob->state) { - case NGHTTP2_OB_POP_ITEM: { - nghttp2_outbound_item *item; - - item = nghttp2_session_pop_next_ob_item(session); - if (item == NULL) { - return 0; - } - - rv = session_prep_frame(session, item); - if (rv == NGHTTP2_ERR_PAUSE) { - return 0; - } - if (rv == NGHTTP2_ERR_DEFERRED) { - DEBUGF("send: frame transmission deferred\n"); - break; - } - if (rv < 0) { - int32_t opened_stream_id = 0; - uint32_t error_code = NGHTTP2_INTERNAL_ERROR; - - DEBUGF("send: frame preparation failed with %s\n", - nghttp2_strerror(rv)); - /* TODO If the error comes from compressor, the connection - must be closed. */ - if (item->frame.hd.type != NGHTTP2_DATA && - session->callbacks.on_frame_not_send_callback && is_non_fatal(rv)) { - nghttp2_frame *frame = &item->frame; - /* The library is responsible for the transmission of - WINDOW_UPDATE frame, so we don't call error callback for - it. */ - if (frame->hd.type != NGHTTP2_WINDOW_UPDATE && - session->callbacks.on_frame_not_send_callback( - session, frame, rv, session->user_data) != 0) { - - nghttp2_outbound_item_free(item, mem); - nghttp2_mem_free(mem, item); - - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - } - /* We have to close stream opened by failed request HEADERS - or PUSH_PROMISE. */ - switch (item->frame.hd.type) { - case NGHTTP2_HEADERS: - if (item->frame.headers.cat == NGHTTP2_HCAT_REQUEST) { - opened_stream_id = item->frame.hd.stream_id; - if (item->aux_data.headers.canceled) { - error_code = item->aux_data.headers.error_code; - } else { - /* Set error_code to REFUSED_STREAM so that application - can send request again. */ - error_code = NGHTTP2_REFUSED_STREAM; - } - } - break; - case NGHTTP2_PUSH_PROMISE: - opened_stream_id = item->frame.push_promise.promised_stream_id; - break; - } - if (opened_stream_id) { - /* careful not to override rv */ - int rv2; - rv2 = nghttp2_session_close_stream(session, opened_stream_id, - error_code); - - if (nghttp2_is_fatal(rv2)) { - return rv2; - } - } - - nghttp2_outbound_item_free(item, mem); - nghttp2_mem_free(mem, item); - active_outbound_item_reset(aob, mem); - - if (rv == NGHTTP2_ERR_HEADER_COMP) { - /* If header compression error occurred, should terminiate - connection. */ - rv = nghttp2_session_terminate_session(session, - NGHTTP2_INTERNAL_ERROR); - } - if (nghttp2_is_fatal(rv)) { - return rv; - } - break; - } - - aob->item = item; - - nghttp2_bufs_rewind(framebufs); - - if (item->frame.hd.type != NGHTTP2_DATA) { - nghttp2_frame *frame; - - frame = &item->frame; - - DEBUGF("send: next frame: payloadlen=%zu, type=%u, flags=0x%02x, " - "stream_id=%d\n", - frame->hd.length, frame->hd.type, frame->hd.flags, - frame->hd.stream_id); - - rv = session_call_before_frame_send(session, frame); - if (nghttp2_is_fatal(rv)) { - return rv; - } - - if (rv == NGHTTP2_ERR_CANCEL) { - int32_t opened_stream_id = 0; - uint32_t error_code = NGHTTP2_INTERNAL_ERROR; - - if (session->callbacks.on_frame_not_send_callback) { - if (session->callbacks.on_frame_not_send_callback( - session, frame, rv, session->user_data) != 0) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - } - - /* We have to close stream opened by canceled request - HEADERS or PUSH_PROMISE. */ - switch (item->frame.hd.type) { - case NGHTTP2_HEADERS: - if (item->frame.headers.cat == NGHTTP2_HCAT_REQUEST) { - opened_stream_id = item->frame.hd.stream_id; - /* We don't have to check - item->aux_data.headers.canceled since it has already - been checked. */ - /* Set error_code to REFUSED_STREAM so that application - can send request again. */ - error_code = NGHTTP2_REFUSED_STREAM; - } - break; - case NGHTTP2_PUSH_PROMISE: - opened_stream_id = item->frame.push_promise.promised_stream_id; - break; - } - if (opened_stream_id) { - /* careful not to override rv */ - int rv2; - rv2 = nghttp2_session_close_stream(session, opened_stream_id, - error_code); - - if (nghttp2_is_fatal(rv2)) { - return rv2; - } - } - - active_outbound_item_reset(aob, mem); - - break; - } - } else { - DEBUGF("send: next frame: DATA\n"); - - if (item->aux_data.data.no_copy) { - aob->state = NGHTTP2_OB_SEND_NO_COPY; - break; - } - } - - DEBUGF("send: start transmitting frame type=%u, length=%zd\n", - framebufs->cur->buf.pos[3], - framebufs->cur->buf.last - framebufs->cur->buf.pos); - - aob->state = NGHTTP2_OB_SEND_DATA; - - break; - } - case NGHTTP2_OB_SEND_DATA: { - size_t datalen; - nghttp2_buf *buf; - - buf = &framebufs->cur->buf; - - if (buf->pos == buf->last) { - DEBUGF("send: end transmission of a frame\n"); - - /* Frame has completely sent */ - if (fast_cb) { - rv = session_after_frame_sent2(session); - } else { - rv = session_after_frame_sent1(session); - if (rv < 0) { - /* FATAL */ - assert(nghttp2_is_fatal(rv)); - return rv; - } - rv = session_after_frame_sent2(session); - } - if (rv < 0) { - /* FATAL */ - assert(nghttp2_is_fatal(rv)); - return rv; - } - /* We have already adjusted the next state */ - break; - } - - *data_ptr = buf->pos; - datalen = nghttp2_buf_len(buf); - - /* We increment the offset here. If send_callback does not send - everything, we will adjust it. */ - buf->pos += datalen; - - return (ssize_t)datalen; - } - case NGHTTP2_OB_SEND_NO_COPY: { - nghttp2_stream *stream; - nghttp2_frame *frame; - int pause; - - DEBUGF("send: no copy DATA\n"); - - frame = &aob->item->frame; - - stream = nghttp2_session_get_stream(session, frame->hd.stream_id); - if (stream == NULL) { - DEBUGF("send: no copy DATA cancelled because stream was closed\n"); - - active_outbound_item_reset(aob, mem); - - break; - } - - rv = session_call_send_data(session, aob->item, framebufs); - if (nghttp2_is_fatal(rv)) { - return rv; - } - - if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { - rv = nghttp2_stream_detach_item(stream); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - - rv = nghttp2_session_add_rst_stream(session, frame->hd.stream_id, - NGHTTP2_INTERNAL_ERROR); - if (nghttp2_is_fatal(rv)) { - return rv; - } - - active_outbound_item_reset(aob, mem); - - break; - } - - if (rv == NGHTTP2_ERR_WOULDBLOCK) { - return 0; - } - - pause = (rv == NGHTTP2_ERR_PAUSE); - - rv = session_after_frame_sent1(session); - if (rv < 0) { - assert(nghttp2_is_fatal(rv)); - return rv; - } - rv = session_after_frame_sent2(session); - if (rv < 0) { - assert(nghttp2_is_fatal(rv)); - return rv; - } - - /* We have already adjusted the next state */ - - if (pause) { - return 0; - } - - break; - } - case NGHTTP2_OB_SEND_CLIENT_MAGIC: { - size_t datalen; - nghttp2_buf *buf; - - buf = &framebufs->cur->buf; - - if (buf->pos == buf->last) { - DEBUGF("send: end transmission of client magic\n"); - active_outbound_item_reset(aob, mem); - break; - } - - *data_ptr = buf->pos; - datalen = nghttp2_buf_len(buf); - - buf->pos += datalen; - - return (ssize_t)datalen; - } - } - } -} - -ssize_t nghttp2_session_mem_send(nghttp2_session *session, - const uint8_t **data_ptr) { - int rv; - ssize_t len; - - *data_ptr = NULL; - - len = nghttp2_session_mem_send_internal(session, data_ptr, 1); - if (len <= 0) { - return len; - } - - if (session->aob.item) { - /* We have to call session_after_frame_sent1 here to handle stream - closure upon transmission of frames. Otherwise, END_STREAM may - be reached to client before we call nghttp2_session_mem_send - again and we may get exceeding number of incoming streams. */ - rv = session_after_frame_sent1(session); - if (rv < 0) { - assert(nghttp2_is_fatal(rv)); - return (ssize_t)rv; - } - } - - return len; -} - -int nghttp2_session_send(nghttp2_session *session) { - const uint8_t *data = NULL; - ssize_t datalen; - ssize_t sentlen; - nghttp2_bufs *framebufs; - - framebufs = &session->aob.framebufs; - - for (;;) { - datalen = nghttp2_session_mem_send_internal(session, &data, 0); - if (datalen <= 0) { - return (int)datalen; - } - sentlen = session->callbacks.send_callback(session, data, (size_t)datalen, - 0, session->user_data); - if (sentlen < 0) { - if (sentlen == NGHTTP2_ERR_WOULDBLOCK) { - /* Transmission canceled. Rewind the offset */ - framebufs->cur->buf.pos -= datalen; - - return 0; - } - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - /* Rewind the offset to the amount of unsent bytes */ - framebufs->cur->buf.pos -= datalen - sentlen; - } -} - -static ssize_t session_recv(nghttp2_session *session, uint8_t *buf, - size_t len) { - ssize_t rv; - rv = session->callbacks.recv_callback(session, buf, len, 0, - session->user_data); - if (rv > 0) { - if ((size_t)rv > len) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - } else if (rv < 0 && rv != NGHTTP2_ERR_WOULDBLOCK && rv != NGHTTP2_ERR_EOF) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - return rv; -} - -static int session_call_on_begin_frame(nghttp2_session *session, - const nghttp2_frame_hd *hd) { - int rv; - - if (session->callbacks.on_begin_frame_callback) { - - rv = session->callbacks.on_begin_frame_callback(session, hd, - session->user_data); - - if (rv != 0) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - } - - return 0; -} - -static int session_call_on_frame_received(nghttp2_session *session, - nghttp2_frame *frame) { - int rv; - if (session->callbacks.on_frame_recv_callback) { - rv = session->callbacks.on_frame_recv_callback(session, frame, - session->user_data); - if (rv != 0) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - } - return 0; -} - -static int session_call_on_begin_headers(nghttp2_session *session, - nghttp2_frame *frame) { - int rv; - DEBUGF("recv: call on_begin_headers callback stream_id=%d\n", - frame->hd.stream_id); - if (session->callbacks.on_begin_headers_callback) { - rv = session->callbacks.on_begin_headers_callback(session, frame, - session->user_data); - if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { - return rv; - } - if (rv != 0) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - } - return 0; -} - -static int session_call_on_header(nghttp2_session *session, - const nghttp2_frame *frame, - const nghttp2_hd_nv *nv) { - int rv = 0; - if (session->callbacks.on_header_callback2) { - rv = session->callbacks.on_header_callback2( - session, frame, nv->name, nv->value, nv->flags, session->user_data); - } else if (session->callbacks.on_header_callback) { - rv = session->callbacks.on_header_callback( - session, frame, nv->name->base, nv->name->len, nv->value->base, - nv->value->len, nv->flags, session->user_data); - } - - if (rv == NGHTTP2_ERR_PAUSE || rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { - return rv; - } - if (rv != 0) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static int session_call_on_invalid_header(nghttp2_session *session, - const nghttp2_frame *frame, - const nghttp2_hd_nv *nv) { - int rv; - if (session->callbacks.on_invalid_header_callback2) { - rv = session->callbacks.on_invalid_header_callback2( - session, frame, nv->name, nv->value, nv->flags, session->user_data); - } else if (session->callbacks.on_invalid_header_callback) { - rv = session->callbacks.on_invalid_header_callback( - session, frame, nv->name->base, nv->name->len, nv->value->base, - nv->value->len, nv->flags, session->user_data); - } else { - return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; - } - - if (rv == NGHTTP2_ERR_PAUSE || rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { - return rv; - } - if (rv != 0) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - - return 0; -} - -static int -session_call_on_extension_chunk_recv_callback(nghttp2_session *session, - const uint8_t *data, size_t len) { - int rv; - nghttp2_inbound_frame *iframe = &session->iframe; - nghttp2_frame *frame = &iframe->frame; - - if (session->callbacks.on_extension_chunk_recv_callback) { - rv = session->callbacks.on_extension_chunk_recv_callback( - session, &frame->hd, data, len, session->user_data); - if (rv == NGHTTP2_ERR_CANCEL) { - return rv; - } - if (rv != 0) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - } - - return 0; -} - -static int session_call_unpack_extension_callback(nghttp2_session *session) { - int rv; - nghttp2_inbound_frame *iframe = &session->iframe; - nghttp2_frame *frame = &iframe->frame; - void *payload = NULL; - - rv = session->callbacks.unpack_extension_callback( - session, &payload, &frame->hd, session->user_data); - if (rv == NGHTTP2_ERR_CANCEL) { - return rv; - } - if (rv != 0) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - - frame->ext.payload = payload; - - return 0; -} - -/* - * Handles frame size error. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -static int session_handle_frame_size_error(nghttp2_session *session) { - /* TODO Currently no callback is called for this error, because we - call this callback before reading any payload */ - return nghttp2_session_terminate_session(session, NGHTTP2_FRAME_SIZE_ERROR); -} - -static uint32_t get_error_code_from_lib_error_code(int lib_error_code) { - switch (lib_error_code) { - case NGHTTP2_ERR_STREAM_CLOSED: - return NGHTTP2_STREAM_CLOSED; - case NGHTTP2_ERR_HEADER_COMP: - return NGHTTP2_COMPRESSION_ERROR; - case NGHTTP2_ERR_FRAME_SIZE_ERROR: - return NGHTTP2_FRAME_SIZE_ERROR; - case NGHTTP2_ERR_FLOW_CONTROL: - return NGHTTP2_FLOW_CONTROL_ERROR; - case NGHTTP2_ERR_REFUSED_STREAM: - return NGHTTP2_REFUSED_STREAM; - case NGHTTP2_ERR_PROTO: - case NGHTTP2_ERR_HTTP_HEADER: - case NGHTTP2_ERR_HTTP_MESSAGING: - return NGHTTP2_PROTOCOL_ERROR; - default: - return NGHTTP2_INTERNAL_ERROR; - } -} - -/* - * Calls on_invalid_frame_recv_callback if it is set to |session|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_CALLBACK_FAILURE - * User defined callback function fails. - */ -static int session_call_on_invalid_frame_recv_callback(nghttp2_session *session, - nghttp2_frame *frame, - int lib_error_code) { - if (session->callbacks.on_invalid_frame_recv_callback) { - if (session->callbacks.on_invalid_frame_recv_callback( - session, frame, lib_error_code, session->user_data) != 0) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - } - return 0; -} - -static int session_handle_invalid_stream2(nghttp2_session *session, - int32_t stream_id, - nghttp2_frame *frame, - int lib_error_code) { - int rv; - rv = nghttp2_session_add_rst_stream( - session, stream_id, get_error_code_from_lib_error_code(lib_error_code)); - if (rv != 0) { - return rv; - } - if (session->callbacks.on_invalid_frame_recv_callback) { - if (session->callbacks.on_invalid_frame_recv_callback( - session, frame, lib_error_code, session->user_data) != 0) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - } - return 0; -} - -static int session_handle_invalid_stream(nghttp2_session *session, - nghttp2_frame *frame, - int lib_error_code) { - return session_handle_invalid_stream2(session, frame->hd.stream_id, frame, - lib_error_code); -} - -static int session_inflate_handle_invalid_stream(nghttp2_session *session, - nghttp2_frame *frame, - int lib_error_code) { - int rv; - rv = session_handle_invalid_stream(session, frame, lib_error_code); - if (nghttp2_is_fatal(rv)) { - return rv; - } - return NGHTTP2_ERR_IGN_HEADER_BLOCK; -} - -/* - * Handles invalid frame which causes connection error. - */ -static int session_handle_invalid_connection(nghttp2_session *session, - nghttp2_frame *frame, - int lib_error_code, - const char *reason) { - if (session->callbacks.on_invalid_frame_recv_callback) { - if (session->callbacks.on_invalid_frame_recv_callback( - session, frame, lib_error_code, session->user_data) != 0) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - } - return nghttp2_session_terminate_session_with_reason( - session, get_error_code_from_lib_error_code(lib_error_code), reason); -} - -static int session_inflate_handle_invalid_connection(nghttp2_session *session, - nghttp2_frame *frame, - int lib_error_code, - const char *reason) { - int rv; - rv = - session_handle_invalid_connection(session, frame, lib_error_code, reason); - if (nghttp2_is_fatal(rv)) { - return rv; - } - return NGHTTP2_ERR_IGN_HEADER_BLOCK; -} - -/* - * Inflates header block in the memory pointed by |in| with |inlen| - * bytes. If this function returns NGHTTP2_ERR_PAUSE, the caller must - * call this function again, until it returns 0 or one of negative - * error code. If |call_header_cb| is zero, the on_header_callback - * are not invoked and the function never return NGHTTP2_ERR_PAUSE. If - * the given |in| is the last chunk of header block, the |final| must - * be nonzero. If header block is successfully processed (which is - * indicated by the return value 0, NGHTTP2_ERR_PAUSE or - * NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE), the number of processed - * input bytes is assigned to the |*readlen_ptr|. - * - * This function return 0 if it succeeds, or one of the negative error - * codes: - * - * NGHTTP2_ERR_CALLBACK_FAILURE - * The callback function failed. - * NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE - * The callback returns this error code, indicating that this - * stream should be RST_STREAMed. - * NGHTTP2_ERR_NOMEM - * Out of memory. - * NGHTTP2_ERR_PAUSE - * The callback function returned NGHTTP2_ERR_PAUSE - * NGHTTP2_ERR_HEADER_COMP - * Header decompression failed - */ -static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame, - size_t *readlen_ptr, uint8_t *in, size_t inlen, - int final, int call_header_cb) { - ssize_t proclen; - int rv; - int inflate_flags; - nghttp2_hd_nv nv; - nghttp2_stream *stream; - nghttp2_stream *subject_stream; - int trailer = 0; - - *readlen_ptr = 0; - stream = nghttp2_session_get_stream(session, frame->hd.stream_id); - - if (frame->hd.type == NGHTTP2_PUSH_PROMISE) { - subject_stream = nghttp2_session_get_stream( - session, frame->push_promise.promised_stream_id); - } else { - subject_stream = stream; - trailer = session_trailer_headers(session, stream, frame); - } - - DEBUGF("recv: decoding header block %zu bytes\n", inlen); - for (;;) { - inflate_flags = 0; - proclen = nghttp2_hd_inflate_hd_nv(&session->hd_inflater, &nv, - &inflate_flags, in, inlen, final); - if (nghttp2_is_fatal((int)proclen)) { - return (int)proclen; - } - if (proclen < 0) { - if (session->iframe.state == NGHTTP2_IB_READ_HEADER_BLOCK) { - if (subject_stream && subject_stream->state != NGHTTP2_STREAM_CLOSING) { - /* Adding RST_STREAM here is very important. It prevents - from invoking subsequent callbacks for the same stream - ID. */ - rv = nghttp2_session_add_rst_stream( - session, subject_stream->stream_id, NGHTTP2_COMPRESSION_ERROR); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - } - } - rv = - nghttp2_session_terminate_session(session, NGHTTP2_COMPRESSION_ERROR); - if (nghttp2_is_fatal(rv)) { - return rv; - } - - return NGHTTP2_ERR_HEADER_COMP; - } - in += proclen; - inlen -= (size_t)proclen; - *readlen_ptr += (size_t)proclen; - - DEBUGF("recv: proclen=%zd\n", proclen); - - if (call_header_cb && (inflate_flags & NGHTTP2_HD_INFLATE_EMIT)) { - rv = 0; + } + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + return 0; + default: + return 0; + } +} + +/* + * Called after a frame is sent and session_after_frame_sent1. This + * function is responsible to reset session->aob. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_CALLBACK_FAILURE + * The callback function failed. + */ +static int session_after_frame_sent2(nghttp2_session *session) { + int rv; + nghttp2_active_outbound_item *aob = &session->aob; + nghttp2_outbound_item *item = aob->item; + nghttp2_bufs *framebufs = &aob->framebufs; + nghttp2_frame *frame; + nghttp2_mem *mem; + nghttp2_stream *stream; + nghttp2_data_aux_data *aux_data; + + mem = &session->mem; + frame = &item->frame; + + if (frame->hd.type != NGHTTP2_DATA) { + + if (frame->hd.type == NGHTTP2_HEADERS || + frame->hd.type == NGHTTP2_PUSH_PROMISE) { + + if (nghttp2_bufs_next_present(framebufs)) { + framebufs->cur = framebufs->cur->next; + + DEBUGF("send: next CONTINUATION frame, %zu bytes\n", + nghttp2_buf_len(&framebufs->cur->buf)); + + return 0; + } + } + + active_outbound_item_reset(&session->aob, mem); + + return 0; + } + + /* DATA frame */ + + aux_data = &item->aux_data.data; + + /* On EOF, we have already detached data. Please note that + application may issue nghttp2_submit_data() in + on_frame_send_callback (call from session_after_frame_sent1), + which attach data to stream. We don't want to detach it. */ + if (aux_data->eof) { + active_outbound_item_reset(aob, mem); + + return 0; + } + + /* Reset no_copy here because next write may not use this. */ + aux_data->no_copy = 0; + + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + + /* If session is closed or RST_STREAM was queued, we won't send + further data. */ + if (nghttp2_session_predicate_data_send(session, stream) != 0) { + if (stream) { + rv = nghttp2_stream_detach_item(stream); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + + active_outbound_item_reset(aob, mem); + + return 0; + } + + aob->item = NULL; + active_outbound_item_reset(&session->aob, mem); + + return 0; +} + +static int session_call_send_data(nghttp2_session *session, + nghttp2_outbound_item *item, + nghttp2_bufs *framebufs) { + int rv; + nghttp2_buf *buf; + size_t length; + nghttp2_frame *frame; + nghttp2_data_aux_data *aux_data; + + buf = &framebufs->cur->buf; + frame = &item->frame; + length = frame->hd.length - frame->data.padlen; + aux_data = &item->aux_data.data; + + rv = session->callbacks.send_data_callback(session, frame, buf->pos, length, + &aux_data->data_prd.source, + session->user_data); + + switch (rv) { + case 0: + case NGHTTP2_ERR_WOULDBLOCK: + case NGHTTP2_ERR_PAUSE: + case NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE: + return rv; + default: + return NGHTTP2_ERR_CALLBACK_FAILURE; + } +} + +static ssize_t nghttp2_session_mem_send_internal(nghttp2_session *session, + const uint8_t **data_ptr, + int fast_cb) { + int rv; + nghttp2_active_outbound_item *aob; + nghttp2_bufs *framebufs; + nghttp2_mem *mem; + + mem = &session->mem; + aob = &session->aob; + framebufs = &aob->framebufs; + + /* We may have idle streams more than we expect (e.g., + nghttp2_session_change_stream_priority() or + nghttp2_session_create_idle_stream()). Adjust them here. */ + rv = nghttp2_session_adjust_idle_stream(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + for (;;) { + switch (aob->state) { + case NGHTTP2_OB_POP_ITEM: { + nghttp2_outbound_item *item; + + item = nghttp2_session_pop_next_ob_item(session); + if (item == NULL) { + return 0; + } + + rv = session_prep_frame(session, item); + if (rv == NGHTTP2_ERR_PAUSE) { + return 0; + } + if (rv == NGHTTP2_ERR_DEFERRED) { + DEBUGF("send: frame transmission deferred\n"); + break; + } + if (rv < 0) { + int32_t opened_stream_id = 0; + uint32_t error_code = NGHTTP2_INTERNAL_ERROR; + + DEBUGF("send: frame preparation failed with %s\n", + nghttp2_strerror(rv)); + /* TODO If the error comes from compressor, the connection + must be closed. */ + if (item->frame.hd.type != NGHTTP2_DATA && + session->callbacks.on_frame_not_send_callback && is_non_fatal(rv)) { + nghttp2_frame *frame = &item->frame; + /* The library is responsible for the transmission of + WINDOW_UPDATE frame, so we don't call error callback for + it. */ + if (frame->hd.type != NGHTTP2_WINDOW_UPDATE && + session->callbacks.on_frame_not_send_callback( + session, frame, rv, session->user_data) != 0) { + + nghttp2_outbound_item_free(item, mem); + nghttp2_mem_free(mem, item); + + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + /* We have to close stream opened by failed request HEADERS + or PUSH_PROMISE. */ + switch (item->frame.hd.type) { + case NGHTTP2_HEADERS: + if (item->frame.headers.cat == NGHTTP2_HCAT_REQUEST) { + opened_stream_id = item->frame.hd.stream_id; + if (item->aux_data.headers.canceled) { + error_code = item->aux_data.headers.error_code; + } else { + /* Set error_code to REFUSED_STREAM so that application + can send request again. */ + error_code = NGHTTP2_REFUSED_STREAM; + } + } + break; + case NGHTTP2_PUSH_PROMISE: + opened_stream_id = item->frame.push_promise.promised_stream_id; + break; + } + if (opened_stream_id) { + /* careful not to override rv */ + int rv2; + rv2 = nghttp2_session_close_stream(session, opened_stream_id, + error_code); + + if (nghttp2_is_fatal(rv2)) { + return rv2; + } + } + + nghttp2_outbound_item_free(item, mem); + nghttp2_mem_free(mem, item); + active_outbound_item_reset(aob, mem); + + if (rv == NGHTTP2_ERR_HEADER_COMP) { + /* If header compression error occurred, should terminiate + connection. */ + rv = nghttp2_session_terminate_session(session, + NGHTTP2_INTERNAL_ERROR); + } + if (nghttp2_is_fatal(rv)) { + return rv; + } + break; + } + + aob->item = item; + + nghttp2_bufs_rewind(framebufs); + + if (item->frame.hd.type != NGHTTP2_DATA) { + nghttp2_frame *frame; + + frame = &item->frame; + + DEBUGF("send: next frame: payloadlen=%zu, type=%u, flags=0x%02x, " + "stream_id=%d\n", + frame->hd.length, frame->hd.type, frame->hd.flags, + frame->hd.stream_id); + + rv = session_call_before_frame_send(session, frame); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + if (rv == NGHTTP2_ERR_CANCEL) { + int32_t opened_stream_id = 0; + uint32_t error_code = NGHTTP2_INTERNAL_ERROR; + + if (session->callbacks.on_frame_not_send_callback) { + if (session->callbacks.on_frame_not_send_callback( + session, frame, rv, session->user_data) != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + + /* We have to close stream opened by canceled request + HEADERS or PUSH_PROMISE. */ + switch (item->frame.hd.type) { + case NGHTTP2_HEADERS: + if (item->frame.headers.cat == NGHTTP2_HCAT_REQUEST) { + opened_stream_id = item->frame.hd.stream_id; + /* We don't have to check + item->aux_data.headers.canceled since it has already + been checked. */ + /* Set error_code to REFUSED_STREAM so that application + can send request again. */ + error_code = NGHTTP2_REFUSED_STREAM; + } + break; + case NGHTTP2_PUSH_PROMISE: + opened_stream_id = item->frame.push_promise.promised_stream_id; + break; + } + if (opened_stream_id) { + /* careful not to override rv */ + int rv2; + rv2 = nghttp2_session_close_stream(session, opened_stream_id, + error_code); + + if (nghttp2_is_fatal(rv2)) { + return rv2; + } + } + + active_outbound_item_reset(aob, mem); + + break; + } + } else { + DEBUGF("send: next frame: DATA\n"); + + if (item->aux_data.data.no_copy) { + aob->state = NGHTTP2_OB_SEND_NO_COPY; + break; + } + } + + DEBUGF("send: start transmitting frame type=%u, length=%zd\n", + framebufs->cur->buf.pos[3], + framebufs->cur->buf.last - framebufs->cur->buf.pos); + + aob->state = NGHTTP2_OB_SEND_DATA; + + break; + } + case NGHTTP2_OB_SEND_DATA: { + size_t datalen; + nghttp2_buf *buf; + + buf = &framebufs->cur->buf; + + if (buf->pos == buf->last) { + DEBUGF("send: end transmission of a frame\n"); + + /* Frame has completely sent */ + if (fast_cb) { + rv = session_after_frame_sent2(session); + } else { + rv = session_after_frame_sent1(session); + if (rv < 0) { + /* FATAL */ + assert(nghttp2_is_fatal(rv)); + return rv; + } + rv = session_after_frame_sent2(session); + } + if (rv < 0) { + /* FATAL */ + assert(nghttp2_is_fatal(rv)); + return rv; + } + /* We have already adjusted the next state */ + break; + } + + *data_ptr = buf->pos; + datalen = nghttp2_buf_len(buf); + + /* We increment the offset here. If send_callback does not send + everything, we will adjust it. */ + buf->pos += datalen; + + return (ssize_t)datalen; + } + case NGHTTP2_OB_SEND_NO_COPY: { + nghttp2_stream *stream; + nghttp2_frame *frame; + int pause; + + DEBUGF("send: no copy DATA\n"); + + frame = &aob->item->frame; + + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + if (stream == NULL) { + DEBUGF("send: no copy DATA cancelled because stream was closed\n"); + + active_outbound_item_reset(aob, mem); + + break; + } + + rv = session_call_send_data(session, aob->item, framebufs); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { + rv = nghttp2_stream_detach_item(stream); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + rv = nghttp2_session_add_rst_stream(session, frame->hd.stream_id, + NGHTTP2_INTERNAL_ERROR); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + active_outbound_item_reset(aob, mem); + + break; + } + + if (rv == NGHTTP2_ERR_WOULDBLOCK) { + return 0; + } + + pause = (rv == NGHTTP2_ERR_PAUSE); + + rv = session_after_frame_sent1(session); + if (rv < 0) { + assert(nghttp2_is_fatal(rv)); + return rv; + } + rv = session_after_frame_sent2(session); + if (rv < 0) { + assert(nghttp2_is_fatal(rv)); + return rv; + } + + /* We have already adjusted the next state */ + + if (pause) { + return 0; + } + + break; + } + case NGHTTP2_OB_SEND_CLIENT_MAGIC: { + size_t datalen; + nghttp2_buf *buf; + + buf = &framebufs->cur->buf; + + if (buf->pos == buf->last) { + DEBUGF("send: end transmission of client magic\n"); + active_outbound_item_reset(aob, mem); + break; + } + + *data_ptr = buf->pos; + datalen = nghttp2_buf_len(buf); + + buf->pos += datalen; + + return (ssize_t)datalen; + } + } + } +} + +ssize_t nghttp2_session_mem_send(nghttp2_session *session, + const uint8_t **data_ptr) { + int rv; + ssize_t len; + + *data_ptr = NULL; + + len = nghttp2_session_mem_send_internal(session, data_ptr, 1); + if (len <= 0) { + return len; + } + + if (session->aob.item) { + /* We have to call session_after_frame_sent1 here to handle stream + closure upon transmission of frames. Otherwise, END_STREAM may + be reached to client before we call nghttp2_session_mem_send + again and we may get exceeding number of incoming streams. */ + rv = session_after_frame_sent1(session); + if (rv < 0) { + assert(nghttp2_is_fatal(rv)); + return (ssize_t)rv; + } + } + + return len; +} + +int nghttp2_session_send(nghttp2_session *session) { + const uint8_t *data = NULL; + ssize_t datalen; + ssize_t sentlen; + nghttp2_bufs *framebufs; + + framebufs = &session->aob.framebufs; + + for (;;) { + datalen = nghttp2_session_mem_send_internal(session, &data, 0); + if (datalen <= 0) { + return (int)datalen; + } + sentlen = session->callbacks.send_callback(session, data, (size_t)datalen, + 0, session->user_data); + if (sentlen < 0) { + if (sentlen == NGHTTP2_ERR_WOULDBLOCK) { + /* Transmission canceled. Rewind the offset */ + framebufs->cur->buf.pos -= datalen; + + return 0; + } + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + /* Rewind the offset to the amount of unsent bytes */ + framebufs->cur->buf.pos -= datalen - sentlen; + } +} + +static ssize_t session_recv(nghttp2_session *session, uint8_t *buf, + size_t len) { + ssize_t rv; + rv = session->callbacks.recv_callback(session, buf, len, 0, + session->user_data); + if (rv > 0) { + if ((size_t)rv > len) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } else if (rv < 0 && rv != NGHTTP2_ERR_WOULDBLOCK && rv != NGHTTP2_ERR_EOF) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + return rv; +} + +static int session_call_on_begin_frame(nghttp2_session *session, + const nghttp2_frame_hd *hd) { + int rv; + + if (session->callbacks.on_begin_frame_callback) { + + rv = session->callbacks.on_begin_frame_callback(session, hd, + session->user_data); + + if (rv != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + + return 0; +} + +static int session_call_on_frame_received(nghttp2_session *session, + nghttp2_frame *frame) { + int rv; + if (session->callbacks.on_frame_recv_callback) { + rv = session->callbacks.on_frame_recv_callback(session, frame, + session->user_data); + if (rv != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + return 0; +} + +static int session_call_on_begin_headers(nghttp2_session *session, + nghttp2_frame *frame) { + int rv; + DEBUGF("recv: call on_begin_headers callback stream_id=%d\n", + frame->hd.stream_id); + if (session->callbacks.on_begin_headers_callback) { + rv = session->callbacks.on_begin_headers_callback(session, frame, + session->user_data); + if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { + return rv; + } + if (rv != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + return 0; +} + +static int session_call_on_header(nghttp2_session *session, + const nghttp2_frame *frame, + const nghttp2_hd_nv *nv) { + int rv = 0; + if (session->callbacks.on_header_callback2) { + rv = session->callbacks.on_header_callback2( + session, frame, nv->name, nv->value, nv->flags, session->user_data); + } else if (session->callbacks.on_header_callback) { + rv = session->callbacks.on_header_callback( + session, frame, nv->name->base, nv->name->len, nv->value->base, + nv->value->len, nv->flags, session->user_data); + } + + if (rv == NGHTTP2_ERR_PAUSE || rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { + return rv; + } + if (rv != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int session_call_on_invalid_header(nghttp2_session *session, + const nghttp2_frame *frame, + const nghttp2_hd_nv *nv) { + int rv; + if (session->callbacks.on_invalid_header_callback2) { + rv = session->callbacks.on_invalid_header_callback2( + session, frame, nv->name, nv->value, nv->flags, session->user_data); + } else if (session->callbacks.on_invalid_header_callback) { + rv = session->callbacks.on_invalid_header_callback( + session, frame, nv->name->base, nv->name->len, nv->value->base, + nv->value->len, nv->flags, session->user_data); + } else { + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + } + + if (rv == NGHTTP2_ERR_PAUSE || rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { + return rv; + } + if (rv != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int +session_call_on_extension_chunk_recv_callback(nghttp2_session *session, + const uint8_t *data, size_t len) { + int rv; + nghttp2_inbound_frame *iframe = &session->iframe; + nghttp2_frame *frame = &iframe->frame; + + if (session->callbacks.on_extension_chunk_recv_callback) { + rv = session->callbacks.on_extension_chunk_recv_callback( + session, &frame->hd, data, len, session->user_data); + if (rv == NGHTTP2_ERR_CANCEL) { + return rv; + } + if (rv != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + + return 0; +} + +static int session_call_unpack_extension_callback(nghttp2_session *session) { + int rv; + nghttp2_inbound_frame *iframe = &session->iframe; + nghttp2_frame *frame = &iframe->frame; + void *payload = NULL; + + rv = session->callbacks.unpack_extension_callback( + session, &payload, &frame->hd, session->user_data); + if (rv == NGHTTP2_ERR_CANCEL) { + return rv; + } + if (rv != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + + frame->ext.payload = payload; + + return 0; +} + +/* + * Handles frame size error. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +static int session_handle_frame_size_error(nghttp2_session *session) { + /* TODO Currently no callback is called for this error, because we + call this callback before reading any payload */ + return nghttp2_session_terminate_session(session, NGHTTP2_FRAME_SIZE_ERROR); +} + +static uint32_t get_error_code_from_lib_error_code(int lib_error_code) { + switch (lib_error_code) { + case NGHTTP2_ERR_STREAM_CLOSED: + return NGHTTP2_STREAM_CLOSED; + case NGHTTP2_ERR_HEADER_COMP: + return NGHTTP2_COMPRESSION_ERROR; + case NGHTTP2_ERR_FRAME_SIZE_ERROR: + return NGHTTP2_FRAME_SIZE_ERROR; + case NGHTTP2_ERR_FLOW_CONTROL: + return NGHTTP2_FLOW_CONTROL_ERROR; + case NGHTTP2_ERR_REFUSED_STREAM: + return NGHTTP2_REFUSED_STREAM; + case NGHTTP2_ERR_PROTO: + case NGHTTP2_ERR_HTTP_HEADER: + case NGHTTP2_ERR_HTTP_MESSAGING: + return NGHTTP2_PROTOCOL_ERROR; + default: + return NGHTTP2_INTERNAL_ERROR; + } +} + +/* + * Calls on_invalid_frame_recv_callback if it is set to |session|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_CALLBACK_FAILURE + * User defined callback function fails. + */ +static int session_call_on_invalid_frame_recv_callback(nghttp2_session *session, + nghttp2_frame *frame, + int lib_error_code) { + if (session->callbacks.on_invalid_frame_recv_callback) { + if (session->callbacks.on_invalid_frame_recv_callback( + session, frame, lib_error_code, session->user_data) != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + return 0; +} + +static int session_handle_invalid_stream2(nghttp2_session *session, + int32_t stream_id, + nghttp2_frame *frame, + int lib_error_code) { + int rv; + rv = nghttp2_session_add_rst_stream( + session, stream_id, get_error_code_from_lib_error_code(lib_error_code)); + if (rv != 0) { + return rv; + } + if (session->callbacks.on_invalid_frame_recv_callback) { + if (session->callbacks.on_invalid_frame_recv_callback( + session, frame, lib_error_code, session->user_data) != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + return 0; +} + +static int session_handle_invalid_stream(nghttp2_session *session, + nghttp2_frame *frame, + int lib_error_code) { + return session_handle_invalid_stream2(session, frame->hd.stream_id, frame, + lib_error_code); +} + +static int session_inflate_handle_invalid_stream(nghttp2_session *session, + nghttp2_frame *frame, + int lib_error_code) { + int rv; + rv = session_handle_invalid_stream(session, frame, lib_error_code); + if (nghttp2_is_fatal(rv)) { + return rv; + } + return NGHTTP2_ERR_IGN_HEADER_BLOCK; +} + +/* + * Handles invalid frame which causes connection error. + */ +static int session_handle_invalid_connection(nghttp2_session *session, + nghttp2_frame *frame, + int lib_error_code, + const char *reason) { + if (session->callbacks.on_invalid_frame_recv_callback) { + if (session->callbacks.on_invalid_frame_recv_callback( + session, frame, lib_error_code, session->user_data) != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + return nghttp2_session_terminate_session_with_reason( + session, get_error_code_from_lib_error_code(lib_error_code), reason); +} + +static int session_inflate_handle_invalid_connection(nghttp2_session *session, + nghttp2_frame *frame, + int lib_error_code, + const char *reason) { + int rv; + rv = + session_handle_invalid_connection(session, frame, lib_error_code, reason); + if (nghttp2_is_fatal(rv)) { + return rv; + } + return NGHTTP2_ERR_IGN_HEADER_BLOCK; +} + +/* + * Inflates header block in the memory pointed by |in| with |inlen| + * bytes. If this function returns NGHTTP2_ERR_PAUSE, the caller must + * call this function again, until it returns 0 or one of negative + * error code. If |call_header_cb| is zero, the on_header_callback + * are not invoked and the function never return NGHTTP2_ERR_PAUSE. If + * the given |in| is the last chunk of header block, the |final| must + * be nonzero. If header block is successfully processed (which is + * indicated by the return value 0, NGHTTP2_ERR_PAUSE or + * NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE), the number of processed + * input bytes is assigned to the |*readlen_ptr|. + * + * This function return 0 if it succeeds, or one of the negative error + * codes: + * + * NGHTTP2_ERR_CALLBACK_FAILURE + * The callback function failed. + * NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE + * The callback returns this error code, indicating that this + * stream should be RST_STREAMed. + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_PAUSE + * The callback function returned NGHTTP2_ERR_PAUSE + * NGHTTP2_ERR_HEADER_COMP + * Header decompression failed + */ +static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame, + size_t *readlen_ptr, uint8_t *in, size_t inlen, + int final, int call_header_cb) { + ssize_t proclen; + int rv; + int inflate_flags; + nghttp2_hd_nv nv; + nghttp2_stream *stream; + nghttp2_stream *subject_stream; + int trailer = 0; + + *readlen_ptr = 0; + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + + if (frame->hd.type == NGHTTP2_PUSH_PROMISE) { + subject_stream = nghttp2_session_get_stream( + session, frame->push_promise.promised_stream_id); + } else { + subject_stream = stream; + trailer = session_trailer_headers(session, stream, frame); + } + + DEBUGF("recv: decoding header block %zu bytes\n", inlen); + for (;;) { + inflate_flags = 0; + proclen = nghttp2_hd_inflate_hd_nv(&session->hd_inflater, &nv, + &inflate_flags, in, inlen, final); + if (nghttp2_is_fatal((int)proclen)) { + return (int)proclen; + } + if (proclen < 0) { + if (session->iframe.state == NGHTTP2_IB_READ_HEADER_BLOCK) { + if (subject_stream && subject_stream->state != NGHTTP2_STREAM_CLOSING) { + /* Adding RST_STREAM here is very important. It prevents + from invoking subsequent callbacks for the same stream + ID. */ + rv = nghttp2_session_add_rst_stream( + session, subject_stream->stream_id, NGHTTP2_COMPRESSION_ERROR); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + } + rv = + nghttp2_session_terminate_session(session, NGHTTP2_COMPRESSION_ERROR); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + return NGHTTP2_ERR_HEADER_COMP; + } + in += proclen; + inlen -= (size_t)proclen; + *readlen_ptr += (size_t)proclen; + + DEBUGF("recv: proclen=%zd\n", proclen); + + if (call_header_cb && (inflate_flags & NGHTTP2_HD_INFLATE_EMIT)) { + rv = 0; if (subject_stream) { if (session_enforce_http_messaging(session)) { rv = nghttp2_http_on_header(session, subject_stream, frame, &nv, trailer); - + if (rv == NGHTTP2_ERR_IGN_HTTP_HEADER) { /* Don't overwrite rv here */ int rv2; - + rv2 = session_call_on_invalid_header(session, frame, &nv); if (rv2 == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { rv = NGHTTP2_ERR_HTTP_HEADER; @@ -3666,24 +3666,24 @@ static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame, if (nghttp2_is_fatal(rv2)) { return rv2; } - } + } } - + if (rv == NGHTTP2_ERR_HTTP_HEADER) { DEBUGF("recv: HTTP error: type=%u, id=%d, header %.*s: %.*s\n", - frame->hd.type, frame->hd.stream_id, (int)nv.name->len, - nv.name->base, (int)nv.value->len, nv.value->base); - + frame->hd.type, frame->hd.stream_id, (int)nv.name->len, + nv.name->base, (int)nv.value->len, nv.value->base); + rv = session_call_error_callback( - session, NGHTTP2_ERR_HTTP_HEADER, + session, NGHTTP2_ERR_HTTP_HEADER, "Invalid HTTP header field was received: frame type: " - "%u, stream: %d, name: [%.*s], value: [%.*s]", - frame->hd.type, frame->hd.stream_id, (int)nv.name->len, - nv.name->base, (int)nv.value->len, nv.value->base); - + "%u, stream: %d, name: [%.*s], value: [%.*s]", + frame->hd.type, frame->hd.stream_id, (int)nv.name->len, + nv.name->base, (int)nv.value->len, nv.value->base); + if (nghttp2_is_fatal(rv)) { return rv; - } + } rv = session_handle_invalid_stream2(session, subject_stream->stream_id, @@ -3692,117 +3692,117 @@ static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame, return rv; } return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; - } - } + } + } if (rv == 0) { rv = session_call_on_header(session, frame, &nv); /* This handles NGHTTP2_ERR_PAUSE and NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE as well */ if (rv != 0) { - return rv; - } - } - } - } - if (inflate_flags & NGHTTP2_HD_INFLATE_FINAL) { - nghttp2_hd_inflate_end_headers(&session->hd_inflater); - break; - } - if ((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 && inlen == 0) { - break; - } - } - return 0; -} - -/* - * Call this function when HEADERS frame was completely received. - * - * This function returns 0 if it succeeds, or one of negative error - * codes: - * - * NGHTTP2_ERR_CALLBACK_FAILURE - * The callback function failed. - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -static int session_end_stream_headers_received(nghttp2_session *session, - nghttp2_frame *frame, - nghttp2_stream *stream) { - int rv; - if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) { - return 0; - } - - nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); - rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream); - if (nghttp2_is_fatal(rv)) { - return rv; - } - - return 0; -} - -static int session_after_header_block_received(nghttp2_session *session) { - int rv = 0; - nghttp2_frame *frame = &session->iframe.frame; - nghttp2_stream *stream; - - /* We don't call on_frame_recv_callback if stream has been closed - already or being closed. */ - stream = nghttp2_session_get_stream(session, frame->hd.stream_id); - if (!stream || stream->state == NGHTTP2_STREAM_CLOSING) { - return 0; - } - - if (session_enforce_http_messaging(session)) { - if (frame->hd.type == NGHTTP2_PUSH_PROMISE) { - nghttp2_stream *subject_stream; - - subject_stream = nghttp2_session_get_stream( - session, frame->push_promise.promised_stream_id); - if (subject_stream) { - rv = nghttp2_http_on_request_headers(subject_stream, frame); - } - } else { - assert(frame->hd.type == NGHTTP2_HEADERS); - switch (frame->headers.cat) { - case NGHTTP2_HCAT_REQUEST: - rv = nghttp2_http_on_request_headers(stream, frame); - break; - case NGHTTP2_HCAT_RESPONSE: - case NGHTTP2_HCAT_PUSH_RESPONSE: - rv = nghttp2_http_on_response_headers(stream); - break; - case NGHTTP2_HCAT_HEADERS: - if (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) { - assert(!session->server); - rv = nghttp2_http_on_response_headers(stream); - } else { - rv = nghttp2_http_on_trailer_headers(stream, frame); - } - break; - default: - assert(0); - } - if (rv == 0 && (frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) { - rv = nghttp2_http_on_remote_end_stream(stream); - } - } - if (rv != 0) { - int32_t stream_id; - - if (frame->hd.type == NGHTTP2_PUSH_PROMISE) { - stream_id = frame->push_promise.promised_stream_id; - } else { - stream_id = frame->hd.stream_id; - } - - rv = session_handle_invalid_stream2(session, stream_id, frame, - NGHTTP2_ERR_HTTP_MESSAGING); - if (nghttp2_is_fatal(rv)) { - return rv; - } + return rv; + } + } + } + } + if (inflate_flags & NGHTTP2_HD_INFLATE_FINAL) { + nghttp2_hd_inflate_end_headers(&session->hd_inflater); + break; + } + if ((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 && inlen == 0) { + break; + } + } + return 0; +} + +/* + * Call this function when HEADERS frame was completely received. + * + * This function returns 0 if it succeeds, or one of negative error + * codes: + * + * NGHTTP2_ERR_CALLBACK_FAILURE + * The callback function failed. + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +static int session_end_stream_headers_received(nghttp2_session *session, + nghttp2_frame *frame, + nghttp2_stream *stream) { + int rv; + if ((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) { + return 0; + } + + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); + rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + return 0; +} + +static int session_after_header_block_received(nghttp2_session *session) { + int rv = 0; + nghttp2_frame *frame = &session->iframe.frame; + nghttp2_stream *stream; + + /* We don't call on_frame_recv_callback if stream has been closed + already or being closed. */ + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + if (!stream || stream->state == NGHTTP2_STREAM_CLOSING) { + return 0; + } + + if (session_enforce_http_messaging(session)) { + if (frame->hd.type == NGHTTP2_PUSH_PROMISE) { + nghttp2_stream *subject_stream; + + subject_stream = nghttp2_session_get_stream( + session, frame->push_promise.promised_stream_id); + if (subject_stream) { + rv = nghttp2_http_on_request_headers(subject_stream, frame); + } + } else { + assert(frame->hd.type == NGHTTP2_HEADERS); + switch (frame->headers.cat) { + case NGHTTP2_HCAT_REQUEST: + rv = nghttp2_http_on_request_headers(stream, frame); + break; + case NGHTTP2_HCAT_RESPONSE: + case NGHTTP2_HCAT_PUSH_RESPONSE: + rv = nghttp2_http_on_response_headers(stream); + break; + case NGHTTP2_HCAT_HEADERS: + if (stream->http_flags & NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE) { + assert(!session->server); + rv = nghttp2_http_on_response_headers(stream); + } else { + rv = nghttp2_http_on_trailer_headers(stream, frame); + } + break; + default: + assert(0); + } + if (rv == 0 && (frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) { + rv = nghttp2_http_on_remote_end_stream(stream); + } + } + if (rv != 0) { + int32_t stream_id; + + if (frame->hd.type == NGHTTP2_PUSH_PROMISE) { + stream_id = frame->push_promise.promised_stream_id; + } else { + stream_id = frame->hd.stream_id; + } + + rv = session_handle_invalid_stream2(session, stream_id, frame, + NGHTTP2_ERR_HTTP_MESSAGING); + if (nghttp2_is_fatal(rv)) { + return rv; + } if (frame->hd.type == NGHTTP2_HEADERS && (frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) { @@ -3811,716 +3811,716 @@ static int session_after_header_block_received(nghttp2_session *session) { because RST_STREAM has been submitted. */ } return 0; - } - } - + } + } + + rv = session_call_on_frame_received(session, frame); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + if (frame->hd.type != NGHTTP2_HEADERS) { + return 0; + } + + return session_end_stream_headers_received(session, frame, stream); +} + +int nghttp2_session_on_request_headers_received(nghttp2_session *session, + nghttp2_frame *frame) { + int rv = 0; + nghttp2_stream *stream; + if (frame->hd.stream_id == 0) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, "request HEADERS: stream_id == 0"); + } + + /* If client receives idle stream from server, it is invalid + regardless stream ID is even or odd. This is because client is + not expected to receive request from server. */ + if (!session->server) { + if (session_detect_idle_stream(session, frame->hd.stream_id)) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, + "request HEADERS: client received request"); + } + + return NGHTTP2_ERR_IGN_HEADER_BLOCK; + } + + assert(session->server); + + if (!session_is_new_peer_stream_id(session, frame->hd.stream_id)) { + if (frame->hd.stream_id == 0 || + nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, + "request HEADERS: invalid stream_id"); + } + + /* RFC 7540 says if an endpoint receives a HEADERS with invalid + * stream ID (e.g, numerically smaller than previous), it MUST + * issue connection error with error code PROTOCOL_ERROR. It is a + * bit hard to detect this, since we cannot remember all streams + * we observed so far. + * + * You might imagine this is really easy. But no. HTTP/2 is + * asynchronous protocol, and usually client and server do not + * share the complete picture of open/closed stream status. For + * example, after server sends RST_STREAM for a stream, client may + * send trailer HEADERS for that stream. If naive server detects + * that, and issued connection error, then it is a bug of server + * implementation since client is not wrong if it did not get + * RST_STREAM when it issued trailer HEADERS. + * + * At the moment, we are very conservative here. We only use + * connection error if stream ID refers idle stream, or we are + * sure that stream is half-closed(remote) or closed. Otherwise + * we just ignore HEADERS for now. + */ + stream = nghttp2_session_get_stream_raw(session, frame->hd.stream_id); + if (stream && (stream->shut_flags & NGHTTP2_SHUT_RD)) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_STREAM_CLOSED, "HEADERS: stream closed"); + } + + return NGHTTP2_ERR_IGN_HEADER_BLOCK; + } + session->last_recv_stream_id = frame->hd.stream_id; + + if (session_is_incoming_concurrent_streams_max(session)) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, + "request HEADERS: max concurrent streams exceeded"); + } + + if (!session_allow_incoming_new_stream(session)) { + /* We just ignore stream after GOAWAY was sent */ + return NGHTTP2_ERR_IGN_HEADER_BLOCK; + } + + if (frame->headers.pri_spec.stream_id == frame->hd.stream_id) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, "request HEADERS: depend on itself"); + } + + if (session_is_incoming_concurrent_streams_pending_max(session)) { + return session_inflate_handle_invalid_stream(session, frame, + NGHTTP2_ERR_REFUSED_STREAM); + } + + stream = nghttp2_session_open_stream( + session, frame->hd.stream_id, NGHTTP2_STREAM_FLAG_NONE, + &frame->headers.pri_spec, NGHTTP2_STREAM_OPENING, NULL); + if (!stream) { + return NGHTTP2_ERR_NOMEM; + } + + rv = nghttp2_session_adjust_closed_stream(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + session->last_proc_stream_id = session->last_recv_stream_id; + + rv = session_call_on_begin_headers(session, frame); + if (rv != 0) { + return rv; + } + return 0; +} + +int nghttp2_session_on_response_headers_received(nghttp2_session *session, + nghttp2_frame *frame, + nghttp2_stream *stream) { + int rv; + /* This function is only called if stream->state == + NGHTTP2_STREAM_OPENING and stream_id is local side initiated. */ + assert(stream->state == NGHTTP2_STREAM_OPENING && + nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)); + if (frame->hd.stream_id == 0) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, "response HEADERS: stream_id == 0"); + } + if (stream->shut_flags & NGHTTP2_SHUT_RD) { + /* half closed (remote): from the spec: + + If an endpoint receives additional frames for a stream that is + in this state it MUST respond with a stream error (Section + 5.4.2) of type STREAM_CLOSED. + + We go further, and make it connection error. + */ + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_STREAM_CLOSED, "HEADERS: stream closed"); + } + stream->state = NGHTTP2_STREAM_OPENED; + rv = session_call_on_begin_headers(session, frame); + if (rv != 0) { + return rv; + } + return 0; +} + +int nghttp2_session_on_push_response_headers_received(nghttp2_session *session, + nghttp2_frame *frame, + nghttp2_stream *stream) { + int rv = 0; + assert(stream->state == NGHTTP2_STREAM_RESERVED); + if (frame->hd.stream_id == 0) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, + "push response HEADERS: stream_id == 0"); + } + + if (session->server) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, + "HEADERS: no HEADERS allowed from client in reserved state"); + } + + if (session_is_incoming_concurrent_streams_max(session)) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, + "push response HEADERS: max concurrent streams exceeded"); + } + + if (!session_allow_incoming_new_stream(session)) { + /* We don't accept new stream after GOAWAY was sent. */ + return NGHTTP2_ERR_IGN_HEADER_BLOCK; + } + + if (session_is_incoming_concurrent_streams_pending_max(session)) { + return session_inflate_handle_invalid_stream(session, frame, + NGHTTP2_ERR_REFUSED_STREAM); + } + + nghttp2_stream_promise_fulfilled(stream); + if (!nghttp2_session_is_my_stream_id(session, stream->stream_id)) { + --session->num_incoming_reserved_streams; + } + ++session->num_incoming_streams; + rv = session_call_on_begin_headers(session, frame); + if (rv != 0) { + return rv; + } + return 0; +} + +int nghttp2_session_on_headers_received(nghttp2_session *session, + nghttp2_frame *frame, + nghttp2_stream *stream) { + int rv = 0; + if (frame->hd.stream_id == 0) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, "HEADERS: stream_id == 0"); + } + if ((stream->shut_flags & NGHTTP2_SHUT_RD)) { + /* half closed (remote): from the spec: + + If an endpoint receives additional frames for a stream that is + in this state it MUST respond with a stream error (Section + 5.4.2) of type STREAM_CLOSED. + + we go further, and make it connection error. + */ + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_STREAM_CLOSED, "HEADERS: stream closed"); + } + if (nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) { + if (stream->state == NGHTTP2_STREAM_OPENED) { + rv = session_call_on_begin_headers(session, frame); + if (rv != 0) { + return rv; + } + return 0; + } + + return NGHTTP2_ERR_IGN_HEADER_BLOCK; + } + /* If this is remote peer initiated stream, it is OK unless it + has sent END_STREAM frame already. But if stream is in + NGHTTP2_STREAM_CLOSING, we discard the frame. This is a race + condition. */ + if (stream->state != NGHTTP2_STREAM_CLOSING) { + rv = session_call_on_begin_headers(session, frame); + if (rv != 0) { + return rv; + } + return 0; + } + return NGHTTP2_ERR_IGN_HEADER_BLOCK; +} + +static int session_process_headers_frame(nghttp2_session *session) { + int rv; + nghttp2_inbound_frame *iframe = &session->iframe; + nghttp2_frame *frame = &iframe->frame; + nghttp2_stream *stream; + + rv = nghttp2_frame_unpack_headers_payload(&frame->headers, iframe->sbuf.pos); + + if (rv != 0) { + return nghttp2_session_terminate_session_with_reason( + session, NGHTTP2_PROTOCOL_ERROR, "HEADERS: could not unpack"); + } + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + if (!stream) { + frame->headers.cat = NGHTTP2_HCAT_REQUEST; + return nghttp2_session_on_request_headers_received(session, frame); + } + + if (stream->state == NGHTTP2_STREAM_RESERVED) { + frame->headers.cat = NGHTTP2_HCAT_PUSH_RESPONSE; + return nghttp2_session_on_push_response_headers_received(session, frame, + stream); + } + + if (stream->state == NGHTTP2_STREAM_OPENING && + nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) { + frame->headers.cat = NGHTTP2_HCAT_RESPONSE; + return nghttp2_session_on_response_headers_received(session, frame, stream); + } + + frame->headers.cat = NGHTTP2_HCAT_HEADERS; + return nghttp2_session_on_headers_received(session, frame, stream); +} + +int nghttp2_session_on_priority_received(nghttp2_session *session, + nghttp2_frame *frame) { + int rv; + nghttp2_stream *stream; + + if (frame->hd.stream_id == 0) { + return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, + "PRIORITY: stream_id == 0"); + } + + if (frame->priority.pri_spec.stream_id == frame->hd.stream_id) { + return nghttp2_session_terminate_session_with_reason( + session, NGHTTP2_PROTOCOL_ERROR, "depend on itself"); + } + + if (!session->server) { + /* Re-prioritization works only in server */ + return session_call_on_frame_received(session, frame); + } + + stream = nghttp2_session_get_stream_raw(session, frame->hd.stream_id); + + if (!stream) { + /* PRIORITY against idle stream can create anchor node in + dependency tree. */ + if (!session_detect_idle_stream(session, frame->hd.stream_id)) { + return 0; + } + + stream = nghttp2_session_open_stream( + session, frame->hd.stream_id, NGHTTP2_STREAM_FLAG_NONE, + &frame->priority.pri_spec, NGHTTP2_STREAM_IDLE, NULL); + + if (stream == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + rv = nghttp2_session_adjust_idle_stream(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + } else { + rv = nghttp2_session_reprioritize_stream(session, stream, + &frame->priority.pri_spec); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + rv = nghttp2_session_adjust_idle_stream(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + + return session_call_on_frame_received(session, frame); +} + +static int session_process_priority_frame(nghttp2_session *session) { + nghttp2_inbound_frame *iframe = &session->iframe; + nghttp2_frame *frame = &iframe->frame; + + nghttp2_frame_unpack_priority_payload(&frame->priority, iframe->sbuf.pos); + + return nghttp2_session_on_priority_received(session, frame); +} + +int nghttp2_session_on_rst_stream_received(nghttp2_session *session, + nghttp2_frame *frame) { + int rv; + nghttp2_stream *stream; + if (frame->hd.stream_id == 0) { + return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, + "RST_STREAM: stream_id == 0"); + } + + if (session_detect_idle_stream(session, frame->hd.stream_id)) { + return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, + "RST_STREAM: stream in idle"); + } + + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + if (stream) { + /* We may use stream->shut_flags for strict error checking. */ + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); + } + rv = session_call_on_frame_received(session, frame); + if (rv != 0) { + return rv; + } + rv = nghttp2_session_close_stream(session, frame->hd.stream_id, + frame->rst_stream.error_code); if (nghttp2_is_fatal(rv)) { return rv; - } - - if (frame->hd.type != NGHTTP2_HEADERS) { - return 0; - } - - return session_end_stream_headers_received(session, frame, stream); -} - -int nghttp2_session_on_request_headers_received(nghttp2_session *session, - nghttp2_frame *frame) { - int rv = 0; - nghttp2_stream *stream; - if (frame->hd.stream_id == 0) { - return session_inflate_handle_invalid_connection( - session, frame, NGHTTP2_ERR_PROTO, "request HEADERS: stream_id == 0"); - } - - /* If client receives idle stream from server, it is invalid - regardless stream ID is even or odd. This is because client is - not expected to receive request from server. */ - if (!session->server) { - if (session_detect_idle_stream(session, frame->hd.stream_id)) { - return session_inflate_handle_invalid_connection( - session, frame, NGHTTP2_ERR_PROTO, - "request HEADERS: client received request"); - } - - return NGHTTP2_ERR_IGN_HEADER_BLOCK; - } - - assert(session->server); - - if (!session_is_new_peer_stream_id(session, frame->hd.stream_id)) { - if (frame->hd.stream_id == 0 || - nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) { - return session_inflate_handle_invalid_connection( - session, frame, NGHTTP2_ERR_PROTO, - "request HEADERS: invalid stream_id"); - } - - /* RFC 7540 says if an endpoint receives a HEADERS with invalid - * stream ID (e.g, numerically smaller than previous), it MUST - * issue connection error with error code PROTOCOL_ERROR. It is a - * bit hard to detect this, since we cannot remember all streams - * we observed so far. - * - * You might imagine this is really easy. But no. HTTP/2 is - * asynchronous protocol, and usually client and server do not - * share the complete picture of open/closed stream status. For - * example, after server sends RST_STREAM for a stream, client may - * send trailer HEADERS for that stream. If naive server detects - * that, and issued connection error, then it is a bug of server - * implementation since client is not wrong if it did not get - * RST_STREAM when it issued trailer HEADERS. - * - * At the moment, we are very conservative here. We only use - * connection error if stream ID refers idle stream, or we are - * sure that stream is half-closed(remote) or closed. Otherwise - * we just ignore HEADERS for now. - */ - stream = nghttp2_session_get_stream_raw(session, frame->hd.stream_id); - if (stream && (stream->shut_flags & NGHTTP2_SHUT_RD)) { - return session_inflate_handle_invalid_connection( - session, frame, NGHTTP2_ERR_STREAM_CLOSED, "HEADERS: stream closed"); - } - - return NGHTTP2_ERR_IGN_HEADER_BLOCK; - } - session->last_recv_stream_id = frame->hd.stream_id; - - if (session_is_incoming_concurrent_streams_max(session)) { - return session_inflate_handle_invalid_connection( - session, frame, NGHTTP2_ERR_PROTO, - "request HEADERS: max concurrent streams exceeded"); - } - - if (!session_allow_incoming_new_stream(session)) { - /* We just ignore stream after GOAWAY was sent */ - return NGHTTP2_ERR_IGN_HEADER_BLOCK; - } - - if (frame->headers.pri_spec.stream_id == frame->hd.stream_id) { - return session_inflate_handle_invalid_connection( - session, frame, NGHTTP2_ERR_PROTO, "request HEADERS: depend on itself"); - } - - if (session_is_incoming_concurrent_streams_pending_max(session)) { - return session_inflate_handle_invalid_stream(session, frame, - NGHTTP2_ERR_REFUSED_STREAM); - } - - stream = nghttp2_session_open_stream( - session, frame->hd.stream_id, NGHTTP2_STREAM_FLAG_NONE, - &frame->headers.pri_spec, NGHTTP2_STREAM_OPENING, NULL); - if (!stream) { - return NGHTTP2_ERR_NOMEM; - } - - rv = nghttp2_session_adjust_closed_stream(session); - if (nghttp2_is_fatal(rv)) { - return rv; - } - - session->last_proc_stream_id = session->last_recv_stream_id; - - rv = session_call_on_begin_headers(session, frame); - if (rv != 0) { - return rv; - } - return 0; -} - -int nghttp2_session_on_response_headers_received(nghttp2_session *session, - nghttp2_frame *frame, - nghttp2_stream *stream) { - int rv; - /* This function is only called if stream->state == - NGHTTP2_STREAM_OPENING and stream_id is local side initiated. */ - assert(stream->state == NGHTTP2_STREAM_OPENING && - nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)); - if (frame->hd.stream_id == 0) { - return session_inflate_handle_invalid_connection( - session, frame, NGHTTP2_ERR_PROTO, "response HEADERS: stream_id == 0"); - } - if (stream->shut_flags & NGHTTP2_SHUT_RD) { - /* half closed (remote): from the spec: - - If an endpoint receives additional frames for a stream that is - in this state it MUST respond with a stream error (Section - 5.4.2) of type STREAM_CLOSED. - - We go further, and make it connection error. - */ - return session_inflate_handle_invalid_connection( - session, frame, NGHTTP2_ERR_STREAM_CLOSED, "HEADERS: stream closed"); - } - stream->state = NGHTTP2_STREAM_OPENED; - rv = session_call_on_begin_headers(session, frame); - if (rv != 0) { - return rv; - } - return 0; -} - -int nghttp2_session_on_push_response_headers_received(nghttp2_session *session, - nghttp2_frame *frame, - nghttp2_stream *stream) { - int rv = 0; - assert(stream->state == NGHTTP2_STREAM_RESERVED); - if (frame->hd.stream_id == 0) { - return session_inflate_handle_invalid_connection( - session, frame, NGHTTP2_ERR_PROTO, - "push response HEADERS: stream_id == 0"); - } - - if (session->server) { - return session_inflate_handle_invalid_connection( - session, frame, NGHTTP2_ERR_PROTO, - "HEADERS: no HEADERS allowed from client in reserved state"); - } - - if (session_is_incoming_concurrent_streams_max(session)) { - return session_inflate_handle_invalid_connection( - session, frame, NGHTTP2_ERR_PROTO, - "push response HEADERS: max concurrent streams exceeded"); - } - - if (!session_allow_incoming_new_stream(session)) { - /* We don't accept new stream after GOAWAY was sent. */ - return NGHTTP2_ERR_IGN_HEADER_BLOCK; - } - - if (session_is_incoming_concurrent_streams_pending_max(session)) { - return session_inflate_handle_invalid_stream(session, frame, - NGHTTP2_ERR_REFUSED_STREAM); - } - - nghttp2_stream_promise_fulfilled(stream); - if (!nghttp2_session_is_my_stream_id(session, stream->stream_id)) { - --session->num_incoming_reserved_streams; - } - ++session->num_incoming_streams; - rv = session_call_on_begin_headers(session, frame); - if (rv != 0) { - return rv; - } - return 0; -} - -int nghttp2_session_on_headers_received(nghttp2_session *session, - nghttp2_frame *frame, - nghttp2_stream *stream) { - int rv = 0; - if (frame->hd.stream_id == 0) { - return session_inflate_handle_invalid_connection( - session, frame, NGHTTP2_ERR_PROTO, "HEADERS: stream_id == 0"); - } - if ((stream->shut_flags & NGHTTP2_SHUT_RD)) { - /* half closed (remote): from the spec: - - If an endpoint receives additional frames for a stream that is - in this state it MUST respond with a stream error (Section - 5.4.2) of type STREAM_CLOSED. - - we go further, and make it connection error. - */ - return session_inflate_handle_invalid_connection( - session, frame, NGHTTP2_ERR_STREAM_CLOSED, "HEADERS: stream closed"); - } - if (nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) { - if (stream->state == NGHTTP2_STREAM_OPENED) { - rv = session_call_on_begin_headers(session, frame); - if (rv != 0) { - return rv; - } - return 0; - } - - return NGHTTP2_ERR_IGN_HEADER_BLOCK; - } - /* If this is remote peer initiated stream, it is OK unless it - has sent END_STREAM frame already. But if stream is in - NGHTTP2_STREAM_CLOSING, we discard the frame. This is a race - condition. */ - if (stream->state != NGHTTP2_STREAM_CLOSING) { - rv = session_call_on_begin_headers(session, frame); - if (rv != 0) { - return rv; - } - return 0; - } - return NGHTTP2_ERR_IGN_HEADER_BLOCK; -} - -static int session_process_headers_frame(nghttp2_session *session) { - int rv; - nghttp2_inbound_frame *iframe = &session->iframe; - nghttp2_frame *frame = &iframe->frame; - nghttp2_stream *stream; - - rv = nghttp2_frame_unpack_headers_payload(&frame->headers, iframe->sbuf.pos); - - if (rv != 0) { - return nghttp2_session_terminate_session_with_reason( - session, NGHTTP2_PROTOCOL_ERROR, "HEADERS: could not unpack"); - } - stream = nghttp2_session_get_stream(session, frame->hd.stream_id); - if (!stream) { - frame->headers.cat = NGHTTP2_HCAT_REQUEST; - return nghttp2_session_on_request_headers_received(session, frame); - } - - if (stream->state == NGHTTP2_STREAM_RESERVED) { - frame->headers.cat = NGHTTP2_HCAT_PUSH_RESPONSE; - return nghttp2_session_on_push_response_headers_received(session, frame, - stream); - } - - if (stream->state == NGHTTP2_STREAM_OPENING && - nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) { - frame->headers.cat = NGHTTP2_HCAT_RESPONSE; - return nghttp2_session_on_response_headers_received(session, frame, stream); - } - - frame->headers.cat = NGHTTP2_HCAT_HEADERS; - return nghttp2_session_on_headers_received(session, frame, stream); -} - -int nghttp2_session_on_priority_received(nghttp2_session *session, - nghttp2_frame *frame) { - int rv; - nghttp2_stream *stream; - - if (frame->hd.stream_id == 0) { - return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, - "PRIORITY: stream_id == 0"); - } - - if (frame->priority.pri_spec.stream_id == frame->hd.stream_id) { - return nghttp2_session_terminate_session_with_reason( - session, NGHTTP2_PROTOCOL_ERROR, "depend on itself"); - } - - if (!session->server) { - /* Re-prioritization works only in server */ - return session_call_on_frame_received(session, frame); - } - - stream = nghttp2_session_get_stream_raw(session, frame->hd.stream_id); - - if (!stream) { - /* PRIORITY against idle stream can create anchor node in - dependency tree. */ - if (!session_detect_idle_stream(session, frame->hd.stream_id)) { - return 0; - } - - stream = nghttp2_session_open_stream( - session, frame->hd.stream_id, NGHTTP2_STREAM_FLAG_NONE, - &frame->priority.pri_spec, NGHTTP2_STREAM_IDLE, NULL); - - if (stream == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - rv = nghttp2_session_adjust_idle_stream(session); - if (nghttp2_is_fatal(rv)) { - return rv; - } - } else { - rv = nghttp2_session_reprioritize_stream(session, stream, - &frame->priority.pri_spec); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - - rv = nghttp2_session_adjust_idle_stream(session); - if (nghttp2_is_fatal(rv)) { - return rv; - } - } - - return session_call_on_frame_received(session, frame); -} - -static int session_process_priority_frame(nghttp2_session *session) { - nghttp2_inbound_frame *iframe = &session->iframe; - nghttp2_frame *frame = &iframe->frame; - - nghttp2_frame_unpack_priority_payload(&frame->priority, iframe->sbuf.pos); - - return nghttp2_session_on_priority_received(session, frame); -} - -int nghttp2_session_on_rst_stream_received(nghttp2_session *session, - nghttp2_frame *frame) { - int rv; - nghttp2_stream *stream; - if (frame->hd.stream_id == 0) { - return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, - "RST_STREAM: stream_id == 0"); - } - - if (session_detect_idle_stream(session, frame->hd.stream_id)) { - return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, - "RST_STREAM: stream in idle"); - } - - stream = nghttp2_session_get_stream(session, frame->hd.stream_id); - if (stream) { - /* We may use stream->shut_flags for strict error checking. */ - nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); - } - - rv = session_call_on_frame_received(session, frame); - if (rv != 0) { - return rv; - } - rv = nghttp2_session_close_stream(session, frame->hd.stream_id, - frame->rst_stream.error_code); - if (nghttp2_is_fatal(rv)) { - return rv; - } - return 0; -} - -static int session_process_rst_stream_frame(nghttp2_session *session) { - nghttp2_inbound_frame *iframe = &session->iframe; - nghttp2_frame *frame = &iframe->frame; - - nghttp2_frame_unpack_rst_stream_payload(&frame->rst_stream, iframe->sbuf.pos); - - return nghttp2_session_on_rst_stream_received(session, frame); -} - + } + return 0; +} + +static int session_process_rst_stream_frame(nghttp2_session *session) { + nghttp2_inbound_frame *iframe = &session->iframe; + nghttp2_frame *frame = &iframe->frame; + + nghttp2_frame_unpack_rst_stream_payload(&frame->rst_stream, iframe->sbuf.pos); + + return nghttp2_session_on_rst_stream_received(session, frame); +} + static int update_remote_initial_window_size_func(void *entry, void *ptr) { - int rv; - nghttp2_update_window_size_arg *arg; - nghttp2_stream *stream; - - arg = (nghttp2_update_window_size_arg *)ptr; - stream = (nghttp2_stream *)entry; - - rv = nghttp2_stream_update_remote_initial_window_size( - stream, arg->new_window_size, arg->old_window_size); - if (rv != 0) { - return nghttp2_session_add_rst_stream(arg->session, stream->stream_id, - NGHTTP2_FLOW_CONTROL_ERROR); - } - - /* If window size gets positive, push deferred DATA frame to - outbound queue. */ - 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); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - } - return 0; -} - -/* - * Updates the remote initial window size of all active streams. If - * error occurs, all streams may not be updated. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -static int -session_update_remote_initial_window_size(nghttp2_session *session, - int32_t new_initial_window_size) { - nghttp2_update_window_size_arg arg; - - arg.session = session; - arg.new_window_size = new_initial_window_size; - arg.old_window_size = (int32_t)session->remote_settings.initial_window_size; - - return nghttp2_map_each(&session->streams, - update_remote_initial_window_size_func, &arg); -} - + int rv; + nghttp2_update_window_size_arg *arg; + nghttp2_stream *stream; + + arg = (nghttp2_update_window_size_arg *)ptr; + stream = (nghttp2_stream *)entry; + + rv = nghttp2_stream_update_remote_initial_window_size( + stream, arg->new_window_size, arg->old_window_size); + if (rv != 0) { + return nghttp2_session_add_rst_stream(arg->session, stream->stream_id, + NGHTTP2_FLOW_CONTROL_ERROR); + } + + /* If window size gets positive, push deferred DATA frame to + outbound queue. */ + 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); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + return 0; +} + +/* + * Updates the remote initial window size of all active streams. If + * error occurs, all streams may not be updated. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +static int +session_update_remote_initial_window_size(nghttp2_session *session, + int32_t new_initial_window_size) { + nghttp2_update_window_size_arg arg; + + arg.session = session; + arg.new_window_size = new_initial_window_size; + arg.old_window_size = (int32_t)session->remote_settings.initial_window_size; + + return nghttp2_map_each(&session->streams, + update_remote_initial_window_size_func, &arg); +} + static int update_local_initial_window_size_func(void *entry, void *ptr) { - int rv; - nghttp2_update_window_size_arg *arg; - nghttp2_stream *stream; - arg = (nghttp2_update_window_size_arg *)ptr; - stream = (nghttp2_stream *)entry; - rv = nghttp2_stream_update_local_initial_window_size( - stream, arg->new_window_size, arg->old_window_size); - if (rv != 0) { - return nghttp2_session_add_rst_stream(arg->session, stream->stream_id, - NGHTTP2_FLOW_CONTROL_ERROR); - } - 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, - stream->recv_window_size)) { - - rv = nghttp2_session_add_window_update(arg->session, NGHTTP2_FLAG_NONE, - stream->stream_id, - stream->recv_window_size); - if (rv != 0) { - return rv; - } - - stream->recv_window_size = 0; - } - return 0; -} - -/* - * Updates the local initial window size of all active streams. If - * error occurs, all streams may not be updated. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -static int -session_update_local_initial_window_size(nghttp2_session *session, - int32_t new_initial_window_size, - int32_t old_initial_window_size) { - nghttp2_update_window_size_arg arg; - arg.session = session; - arg.new_window_size = new_initial_window_size; - arg.old_window_size = old_initial_window_size; - return nghttp2_map_each(&session->streams, - update_local_initial_window_size_func, &arg); -} - -/* - * Apply SETTINGS values |iv| having |niv| elements to the local - * settings. We assumes that all values in |iv| is correct, since we - * validated them in nghttp2_session_add_settings() already. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_HEADER_COMP - * The header table size is out of range - * NGHTTP2_ERR_NOMEM - * Out of memory - */ -int nghttp2_session_update_local_settings(nghttp2_session *session, - nghttp2_settings_entry *iv, - size_t niv) { - int rv; - size_t i; - int32_t new_initial_window_size = -1; - uint32_t header_table_size = 0; - uint32_t min_header_table_size = UINT32_MAX; - uint8_t header_table_size_seen = 0; - /* For NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, use the value last - seen. For NGHTTP2_SETTINGS_HEADER_TABLE_SIZE, use both minimum - value and last seen value. */ - for (i = 0; i < niv; ++i) { - switch (iv[i].settings_id) { - case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: - header_table_size_seen = 1; - header_table_size = iv[i].value; - min_header_table_size = nghttp2_min(min_header_table_size, iv[i].value); - break; - case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: - new_initial_window_size = (int32_t)iv[i].value; - break; - } - } - if (header_table_size_seen) { - if (min_header_table_size < header_table_size) { - rv = nghttp2_hd_inflate_change_table_size(&session->hd_inflater, - min_header_table_size); - if (rv != 0) { - return rv; - } - } - - rv = nghttp2_hd_inflate_change_table_size(&session->hd_inflater, - header_table_size); - if (rv != 0) { - return rv; - } - } - if (new_initial_window_size != -1) { - rv = session_update_local_initial_window_size( - session, new_initial_window_size, - (int32_t)session->local_settings.initial_window_size); - if (rv != 0) { - return rv; - } - } - - for (i = 0; i < niv; ++i) { - switch (iv[i].settings_id) { - case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: - session->local_settings.header_table_size = iv[i].value; - break; - case NGHTTP2_SETTINGS_ENABLE_PUSH: - session->local_settings.enable_push = iv[i].value; - break; - case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: - session->local_settings.max_concurrent_streams = iv[i].value; - break; - case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: - session->local_settings.initial_window_size = iv[i].value; - break; - case NGHTTP2_SETTINGS_MAX_FRAME_SIZE: - session->local_settings.max_frame_size = iv[i].value; - break; - case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: - session->local_settings.max_header_list_size = iv[i].value; - break; + int rv; + nghttp2_update_window_size_arg *arg; + nghttp2_stream *stream; + arg = (nghttp2_update_window_size_arg *)ptr; + stream = (nghttp2_stream *)entry; + rv = nghttp2_stream_update_local_initial_window_size( + stream, arg->new_window_size, arg->old_window_size); + if (rv != 0) { + return nghttp2_session_add_rst_stream(arg->session, stream->stream_id, + NGHTTP2_FLOW_CONTROL_ERROR); + } + 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, + stream->recv_window_size)) { + + rv = nghttp2_session_add_window_update(arg->session, NGHTTP2_FLAG_NONE, + stream->stream_id, + stream->recv_window_size); + if (rv != 0) { + return rv; + } + + stream->recv_window_size = 0; + } + return 0; +} + +/* + * Updates the local initial window size of all active streams. If + * error occurs, all streams may not be updated. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +static int +session_update_local_initial_window_size(nghttp2_session *session, + int32_t new_initial_window_size, + int32_t old_initial_window_size) { + nghttp2_update_window_size_arg arg; + arg.session = session; + arg.new_window_size = new_initial_window_size; + arg.old_window_size = old_initial_window_size; + return nghttp2_map_each(&session->streams, + update_local_initial_window_size_func, &arg); +} + +/* + * Apply SETTINGS values |iv| having |niv| elements to the local + * settings. We assumes that all values in |iv| is correct, since we + * validated them in nghttp2_session_add_settings() already. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_HEADER_COMP + * The header table size is out of range + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_session_update_local_settings(nghttp2_session *session, + nghttp2_settings_entry *iv, + size_t niv) { + int rv; + size_t i; + int32_t new_initial_window_size = -1; + uint32_t header_table_size = 0; + uint32_t min_header_table_size = UINT32_MAX; + uint8_t header_table_size_seen = 0; + /* For NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, use the value last + seen. For NGHTTP2_SETTINGS_HEADER_TABLE_SIZE, use both minimum + value and last seen value. */ + for (i = 0; i < niv; ++i) { + switch (iv[i].settings_id) { + case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: + header_table_size_seen = 1; + header_table_size = iv[i].value; + min_header_table_size = nghttp2_min(min_header_table_size, iv[i].value); + break; + case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: + new_initial_window_size = (int32_t)iv[i].value; + break; + } + } + if (header_table_size_seen) { + if (min_header_table_size < header_table_size) { + rv = nghttp2_hd_inflate_change_table_size(&session->hd_inflater, + min_header_table_size); + if (rv != 0) { + return rv; + } + } + + rv = nghttp2_hd_inflate_change_table_size(&session->hd_inflater, + header_table_size); + if (rv != 0) { + return rv; + } + } + if (new_initial_window_size != -1) { + rv = session_update_local_initial_window_size( + session, new_initial_window_size, + (int32_t)session->local_settings.initial_window_size); + if (rv != 0) { + return rv; + } + } + + for (i = 0; i < niv; ++i) { + switch (iv[i].settings_id) { + case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: + session->local_settings.header_table_size = iv[i].value; + break; + case NGHTTP2_SETTINGS_ENABLE_PUSH: + session->local_settings.enable_push = iv[i].value; + break; + case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: + session->local_settings.max_concurrent_streams = iv[i].value; + break; + case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: + session->local_settings.initial_window_size = iv[i].value; + break; + case NGHTTP2_SETTINGS_MAX_FRAME_SIZE: + session->local_settings.max_frame_size = iv[i].value; + break; + case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: + session->local_settings.max_header_list_size = iv[i].value; + break; case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL: session->local_settings.enable_connect_protocol = iv[i].value; break; - } - } - - return 0; -} - -int nghttp2_session_on_settings_received(nghttp2_session *session, - nghttp2_frame *frame, int noack) { - int rv; - size_t i; - nghttp2_mem *mem; - nghttp2_inflight_settings *settings; - - mem = &session->mem; - - if (frame->hd.stream_id != 0) { - return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, - "SETTINGS: stream_id != 0"); - } - if (frame->hd.flags & NGHTTP2_FLAG_ACK) { - if (frame->settings.niv != 0) { - return session_handle_invalid_connection( - session, frame, NGHTTP2_ERR_FRAME_SIZE_ERROR, - "SETTINGS: ACK and payload != 0"); - } - - settings = session->inflight_settings_head; - - if (!settings) { - return session_handle_invalid_connection( - session, frame, NGHTTP2_ERR_PROTO, "SETTINGS: unexpected ACK"); - } - - rv = nghttp2_session_update_local_settings(session, settings->iv, - settings->niv); - - session->inflight_settings_head = settings->next; - - inflight_settings_del(settings, mem); - - if (rv != 0) { - if (nghttp2_is_fatal(rv)) { - return rv; - } - return session_handle_invalid_connection(session, frame, rv, NULL); - } - return session_call_on_frame_received(session, frame); - } - + } + } + + return 0; +} + +int nghttp2_session_on_settings_received(nghttp2_session *session, + nghttp2_frame *frame, int noack) { + int rv; + size_t i; + nghttp2_mem *mem; + nghttp2_inflight_settings *settings; + + mem = &session->mem; + + if (frame->hd.stream_id != 0) { + return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, + "SETTINGS: stream_id != 0"); + } + if (frame->hd.flags & NGHTTP2_FLAG_ACK) { + if (frame->settings.niv != 0) { + return session_handle_invalid_connection( + session, frame, NGHTTP2_ERR_FRAME_SIZE_ERROR, + "SETTINGS: ACK and payload != 0"); + } + + settings = session->inflight_settings_head; + + if (!settings) { + return session_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, "SETTINGS: unexpected ACK"); + } + + rv = nghttp2_session_update_local_settings(session, settings->iv, + settings->niv); + + session->inflight_settings_head = settings->next; + + inflight_settings_del(settings, mem); + + if (rv != 0) { + if (nghttp2_is_fatal(rv)) { + return rv; + } + return session_handle_invalid_connection(session, frame, rv, NULL); + } + return session_call_on_frame_received(session, frame); + } + if (!session->remote_settings_received) { session->remote_settings.max_concurrent_streams = NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS; session->remote_settings_received = 1; } - for (i = 0; i < frame->settings.niv; ++i) { - nghttp2_settings_entry *entry = &frame->settings.iv[i]; - - switch (entry->settings_id) { - case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: - - rv = nghttp2_hd_deflate_change_table_size(&session->hd_deflater, - entry->value); - if (rv != 0) { - if (nghttp2_is_fatal(rv)) { - return rv; - } else { - return session_handle_invalid_connection( - session, frame, NGHTTP2_ERR_HEADER_COMP, NULL); - } - } - - session->remote_settings.header_table_size = entry->value; - - break; - case NGHTTP2_SETTINGS_ENABLE_PUSH: - - if (entry->value != 0 && entry->value != 1) { - return session_handle_invalid_connection( - session, frame, NGHTTP2_ERR_PROTO, - "SETTINGS: invalid SETTINGS_ENBLE_PUSH"); - } - - if (!session->server && entry->value != 0) { - return session_handle_invalid_connection( - session, frame, NGHTTP2_ERR_PROTO, - "SETTINGS: server attempted to enable push"); - } - - session->remote_settings.enable_push = entry->value; - - break; - case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: - - session->remote_settings.max_concurrent_streams = entry->value; - - break; - case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: - - /* Update the initial window size of the all active streams */ - /* Check that initial_window_size < (1u << 31) */ - if (entry->value > NGHTTP2_MAX_WINDOW_SIZE) { - return session_handle_invalid_connection( - session, frame, NGHTTP2_ERR_FLOW_CONTROL, - "SETTINGS: too large SETTINGS_INITIAL_WINDOW_SIZE"); - } - - rv = session_update_remote_initial_window_size(session, - (int32_t)entry->value); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - - if (rv != 0) { - return session_handle_invalid_connection( - session, frame, NGHTTP2_ERR_FLOW_CONTROL, NULL); - } - - session->remote_settings.initial_window_size = entry->value; - - break; - case NGHTTP2_SETTINGS_MAX_FRAME_SIZE: - - if (entry->value < NGHTTP2_MAX_FRAME_SIZE_MIN || - entry->value > NGHTTP2_MAX_FRAME_SIZE_MAX) { - return session_handle_invalid_connection( - session, frame, NGHTTP2_ERR_PROTO, - "SETTINGS: invalid SETTINGS_MAX_FRAME_SIZE"); - } - - session->remote_settings.max_frame_size = entry->value; - - break; - case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: - - session->remote_settings.max_header_list_size = entry->value; - - break; + for (i = 0; i < frame->settings.niv; ++i) { + nghttp2_settings_entry *entry = &frame->settings.iv[i]; + + switch (entry->settings_id) { + case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: + + rv = nghttp2_hd_deflate_change_table_size(&session->hd_deflater, + entry->value); + if (rv != 0) { + if (nghttp2_is_fatal(rv)) { + return rv; + } else { + return session_handle_invalid_connection( + session, frame, NGHTTP2_ERR_HEADER_COMP, NULL); + } + } + + session->remote_settings.header_table_size = entry->value; + + break; + case NGHTTP2_SETTINGS_ENABLE_PUSH: + + if (entry->value != 0 && entry->value != 1) { + return session_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, + "SETTINGS: invalid SETTINGS_ENBLE_PUSH"); + } + + if (!session->server && entry->value != 0) { + return session_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, + "SETTINGS: server attempted to enable push"); + } + + session->remote_settings.enable_push = entry->value; + + break; + case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: + + session->remote_settings.max_concurrent_streams = entry->value; + + break; + case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: + + /* Update the initial window size of the all active streams */ + /* Check that initial_window_size < (1u << 31) */ + if (entry->value > NGHTTP2_MAX_WINDOW_SIZE) { + return session_handle_invalid_connection( + session, frame, NGHTTP2_ERR_FLOW_CONTROL, + "SETTINGS: too large SETTINGS_INITIAL_WINDOW_SIZE"); + } + + rv = session_update_remote_initial_window_size(session, + (int32_t)entry->value); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + if (rv != 0) { + return session_handle_invalid_connection( + session, frame, NGHTTP2_ERR_FLOW_CONTROL, NULL); + } + + session->remote_settings.initial_window_size = entry->value; + + break; + case NGHTTP2_SETTINGS_MAX_FRAME_SIZE: + + if (entry->value < NGHTTP2_MAX_FRAME_SIZE_MIN || + entry->value > NGHTTP2_MAX_FRAME_SIZE_MAX) { + return session_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, + "SETTINGS: invalid SETTINGS_MAX_FRAME_SIZE"); + } + + session->remote_settings.max_frame_size = entry->value; + + break; + case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: + + session->remote_settings.max_header_list_size = entry->value; + + break; case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL: if (entry->value != 0 && entry->value != 1) { @@ -4541,378 +4541,378 @@ int nghttp2_session_on_settings_received(nghttp2_session *session, session->remote_settings.enable_connect_protocol = entry->value; break; - } - } - - if (!noack && !session_is_closing(session)) { - rv = nghttp2_session_add_settings(session, NGHTTP2_FLAG_ACK, NULL, 0); - - if (rv != 0) { - if (nghttp2_is_fatal(rv)) { - return rv; - } - - return session_handle_invalid_connection(session, frame, - NGHTTP2_ERR_INTERNAL, NULL); - } - } - - return session_call_on_frame_received(session, frame); -} - -static int session_process_settings_frame(nghttp2_session *session) { - nghttp2_inbound_frame *iframe = &session->iframe; - nghttp2_frame *frame = &iframe->frame; - size_t i; - nghttp2_settings_entry min_header_size_entry; - - if (iframe->max_niv) { - min_header_size_entry = iframe->iv[iframe->max_niv - 1]; - - if (min_header_size_entry.value < UINT32_MAX) { - /* If we have less value, then we must have - SETTINGS_HEADER_TABLE_SIZE in i < iframe->niv */ - for (i = 0; i < iframe->niv; ++i) { - if (iframe->iv[i].settings_id == NGHTTP2_SETTINGS_HEADER_TABLE_SIZE) { - break; - } - } - - assert(i < iframe->niv); - - if (min_header_size_entry.value != iframe->iv[i].value) { - iframe->iv[iframe->niv++] = iframe->iv[i]; - iframe->iv[i] = min_header_size_entry; - } - } - } - - nghttp2_frame_unpack_settings_payload(&frame->settings, iframe->iv, - iframe->niv); - - iframe->iv = NULL; - iframe->niv = 0; - iframe->max_niv = 0; - - return nghttp2_session_on_settings_received(session, frame, 0 /* ACK */); -} - -int nghttp2_session_on_push_promise_received(nghttp2_session *session, - nghttp2_frame *frame) { - int rv; - nghttp2_stream *stream; - nghttp2_stream *promised_stream; - nghttp2_priority_spec pri_spec; - - if (frame->hd.stream_id == 0) { - return session_inflate_handle_invalid_connection( - session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: stream_id == 0"); - } - if (session->server || session->local_settings.enable_push == 0) { - return session_inflate_handle_invalid_connection( - session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: push disabled"); - } - - if (!nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) { - return session_inflate_handle_invalid_connection( - session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: invalid stream_id"); - } - - if (!session_allow_incoming_new_stream(session)) { - /* We just discard PUSH_PROMISE after GOAWAY was sent */ - return NGHTTP2_ERR_IGN_HEADER_BLOCK; - } - - if (!session_is_new_peer_stream_id(session, - frame->push_promise.promised_stream_id)) { - /* The spec says if an endpoint receives a PUSH_PROMISE with - illegal stream ID is subject to a connection error of type - PROTOCOL_ERROR. */ - return session_inflate_handle_invalid_connection( - session, frame, NGHTTP2_ERR_PROTO, - "PUSH_PROMISE: invalid promised_stream_id"); - } - - if (session_detect_idle_stream(session, frame->hd.stream_id)) { - return session_inflate_handle_invalid_connection( - session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: stream in idle"); - } - - session->last_recv_stream_id = frame->push_promise.promised_stream_id; - stream = nghttp2_session_get_stream(session, frame->hd.stream_id); - if (!stream || stream->state == NGHTTP2_STREAM_CLOSING || - !session->pending_enable_push || - session->num_incoming_reserved_streams >= - session->max_incoming_reserved_streams) { - /* Currently, client does not retain closed stream, so we don't - check NGHTTP2_SHUT_RD condition here. */ - - rv = nghttp2_session_add_rst_stream( - session, frame->push_promise.promised_stream_id, NGHTTP2_CANCEL); - if (rv != 0) { - return rv; - } - return NGHTTP2_ERR_IGN_HEADER_BLOCK; - } - - if (stream->shut_flags & NGHTTP2_SHUT_RD) { - return session_inflate_handle_invalid_connection( - session, frame, NGHTTP2_ERR_STREAM_CLOSED, - "PUSH_PROMISE: stream closed"); - } - - nghttp2_priority_spec_init(&pri_spec, stream->stream_id, - NGHTTP2_DEFAULT_WEIGHT, 0); - - promised_stream = nghttp2_session_open_stream( - session, frame->push_promise.promised_stream_id, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec, NGHTTP2_STREAM_RESERVED, NULL); - - if (!promised_stream) { - return NGHTTP2_ERR_NOMEM; - } - - /* We don't call nghttp2_session_adjust_closed_stream(), since we - don't keep closed stream in client side */ - - session->last_proc_stream_id = session->last_recv_stream_id; - rv = session_call_on_begin_headers(session, frame); - if (rv != 0) { - return rv; - } - return 0; -} - -static int session_process_push_promise_frame(nghttp2_session *session) { - int rv; - nghttp2_inbound_frame *iframe = &session->iframe; - nghttp2_frame *frame = &iframe->frame; - - rv = nghttp2_frame_unpack_push_promise_payload(&frame->push_promise, - iframe->sbuf.pos); - - if (rv != 0) { - return nghttp2_session_terminate_session_with_reason( - session, NGHTTP2_PROTOCOL_ERROR, "PUSH_PROMISE: could not unpack"); - } - - return nghttp2_session_on_push_promise_received(session, frame); -} - -int nghttp2_session_on_ping_received(nghttp2_session *session, - nghttp2_frame *frame) { - int rv = 0; - if (frame->hd.stream_id != 0) { - return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, - "PING: stream_id != 0"); - } - if ((session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_PING_ACK) == 0 && - (frame->hd.flags & NGHTTP2_FLAG_ACK) == 0 && - !session_is_closing(session)) { - /* Peer sent ping, so ping it back */ - rv = nghttp2_session_add_ping(session, NGHTTP2_FLAG_ACK, - frame->ping.opaque_data); - if (rv != 0) { - return rv; - } - } - return session_call_on_frame_received(session, frame); -} - -static int session_process_ping_frame(nghttp2_session *session) { - nghttp2_inbound_frame *iframe = &session->iframe; - nghttp2_frame *frame = &iframe->frame; - - nghttp2_frame_unpack_ping_payload(&frame->ping, iframe->sbuf.pos); - - return nghttp2_session_on_ping_received(session, frame); -} - -int nghttp2_session_on_goaway_received(nghttp2_session *session, - nghttp2_frame *frame) { - int rv; - - if (frame->hd.stream_id != 0) { - return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, - "GOAWAY: stream_id != 0"); - } - /* Spec says Endpoints MUST NOT increase the value they send in the - last stream identifier. */ - if ((frame->goaway.last_stream_id > 0 && - !nghttp2_session_is_my_stream_id(session, - frame->goaway.last_stream_id)) || - session->remote_last_stream_id < frame->goaway.last_stream_id) { - return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, - "GOAWAY: invalid last_stream_id"); - } - - session->goaway_flags |= NGHTTP2_GOAWAY_RECV; - - session->remote_last_stream_id = frame->goaway.last_stream_id; - - rv = session_call_on_frame_received(session, frame); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - - return session_close_stream_on_goaway(session, frame->goaway.last_stream_id, - 0); -} - -static int session_process_goaway_frame(nghttp2_session *session) { - nghttp2_inbound_frame *iframe = &session->iframe; - nghttp2_frame *frame = &iframe->frame; - - nghttp2_frame_unpack_goaway_payload(&frame->goaway, iframe->sbuf.pos, - iframe->lbuf.pos, - nghttp2_buf_len(&iframe->lbuf)); - - nghttp2_buf_wrap_init(&iframe->lbuf, NULL, 0); - - return nghttp2_session_on_goaway_received(session, frame); -} - -static int -session_on_connection_window_update_received(nghttp2_session *session, - nghttp2_frame *frame) { - /* Handle connection-level flow control */ - if (frame->window_update.window_size_increment == 0) { - return session_handle_invalid_connection( - session, frame, NGHTTP2_ERR_PROTO, - "WINDOW_UPDATE: window_size_increment == 0"); - } - - if (NGHTTP2_MAX_WINDOW_SIZE - frame->window_update.window_size_increment < - session->remote_window_size) { - return session_handle_invalid_connection(session, frame, - NGHTTP2_ERR_FLOW_CONTROL, NULL); - } - session->remote_window_size += frame->window_update.window_size_increment; - - return session_call_on_frame_received(session, frame); -} - -static int session_on_stream_window_update_received(nghttp2_session *session, - nghttp2_frame *frame) { - int rv; - nghttp2_stream *stream; - - if (session_detect_idle_stream(session, frame->hd.stream_id)) { - return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, - "WINDOW_UPDATE to idle stream"); - } - - stream = nghttp2_session_get_stream(session, frame->hd.stream_id); - if (!stream) { - return 0; - } - if (state_reserved_remote(session, stream)) { - return session_handle_invalid_connection( - session, frame, NGHTTP2_ERR_PROTO, "WINDOW_UPADATE to reserved stream"); - } - if (frame->window_update.window_size_increment == 0) { - return session_handle_invalid_connection( - session, frame, NGHTTP2_ERR_PROTO, - "WINDOW_UPDATE: window_size_increment == 0"); - } - if (NGHTTP2_MAX_WINDOW_SIZE - frame->window_update.window_size_increment < - stream->remote_window_size) { - return session_handle_invalid_stream(session, frame, - NGHTTP2_ERR_FLOW_CONTROL); - } - stream->remote_window_size += frame->window_update.window_size_increment; - - 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); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - } - return session_call_on_frame_received(session, frame); -} - -int nghttp2_session_on_window_update_received(nghttp2_session *session, - nghttp2_frame *frame) { - if (frame->hd.stream_id == 0) { - return session_on_connection_window_update_received(session, frame); - } else { - return session_on_stream_window_update_received(session, frame); - } -} - -static int session_process_window_update_frame(nghttp2_session *session) { - nghttp2_inbound_frame *iframe = &session->iframe; - nghttp2_frame *frame = &iframe->frame; - - nghttp2_frame_unpack_window_update_payload(&frame->window_update, - iframe->sbuf.pos); - - return nghttp2_session_on_window_update_received(session, frame); -} - -int nghttp2_session_on_altsvc_received(nghttp2_session *session, - nghttp2_frame *frame) { - nghttp2_ext_altsvc *altsvc; - nghttp2_stream *stream; - - altsvc = frame->ext.payload; - - /* session->server case has been excluded */ - - if (frame->hd.stream_id == 0) { - if (altsvc->origin_len == 0) { - return session_call_on_invalid_frame_recv_callback(session, frame, - NGHTTP2_ERR_PROTO); - } - } else { - if (altsvc->origin_len > 0) { - return session_call_on_invalid_frame_recv_callback(session, frame, - NGHTTP2_ERR_PROTO); - } - - stream = nghttp2_session_get_stream(session, frame->hd.stream_id); - if (!stream) { - return 0; - } - - if (stream->state == NGHTTP2_STREAM_CLOSING) { - return 0; - } - } - - if (altsvc->field_value_len == 0) { - return session_call_on_invalid_frame_recv_callback(session, frame, - NGHTTP2_ERR_PROTO); - } - - return session_call_on_frame_received(session, frame); -} - + } + } + + if (!noack && !session_is_closing(session)) { + rv = nghttp2_session_add_settings(session, NGHTTP2_FLAG_ACK, NULL, 0); + + if (rv != 0) { + if (nghttp2_is_fatal(rv)) { + return rv; + } + + return session_handle_invalid_connection(session, frame, + NGHTTP2_ERR_INTERNAL, NULL); + } + } + + return session_call_on_frame_received(session, frame); +} + +static int session_process_settings_frame(nghttp2_session *session) { + nghttp2_inbound_frame *iframe = &session->iframe; + nghttp2_frame *frame = &iframe->frame; + size_t i; + nghttp2_settings_entry min_header_size_entry; + + if (iframe->max_niv) { + min_header_size_entry = iframe->iv[iframe->max_niv - 1]; + + if (min_header_size_entry.value < UINT32_MAX) { + /* If we have less value, then we must have + SETTINGS_HEADER_TABLE_SIZE in i < iframe->niv */ + for (i = 0; i < iframe->niv; ++i) { + if (iframe->iv[i].settings_id == NGHTTP2_SETTINGS_HEADER_TABLE_SIZE) { + break; + } + } + + assert(i < iframe->niv); + + if (min_header_size_entry.value != iframe->iv[i].value) { + iframe->iv[iframe->niv++] = iframe->iv[i]; + iframe->iv[i] = min_header_size_entry; + } + } + } + + nghttp2_frame_unpack_settings_payload(&frame->settings, iframe->iv, + iframe->niv); + + iframe->iv = NULL; + iframe->niv = 0; + iframe->max_niv = 0; + + return nghttp2_session_on_settings_received(session, frame, 0 /* ACK */); +} + +int nghttp2_session_on_push_promise_received(nghttp2_session *session, + nghttp2_frame *frame) { + int rv; + nghttp2_stream *stream; + nghttp2_stream *promised_stream; + nghttp2_priority_spec pri_spec; + + if (frame->hd.stream_id == 0) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: stream_id == 0"); + } + if (session->server || session->local_settings.enable_push == 0) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: push disabled"); + } + + if (!nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: invalid stream_id"); + } + + if (!session_allow_incoming_new_stream(session)) { + /* We just discard PUSH_PROMISE after GOAWAY was sent */ + return NGHTTP2_ERR_IGN_HEADER_BLOCK; + } + + if (!session_is_new_peer_stream_id(session, + frame->push_promise.promised_stream_id)) { + /* The spec says if an endpoint receives a PUSH_PROMISE with + illegal stream ID is subject to a connection error of type + PROTOCOL_ERROR. */ + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, + "PUSH_PROMISE: invalid promised_stream_id"); + } + + if (session_detect_idle_stream(session, frame->hd.stream_id)) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, "PUSH_PROMISE: stream in idle"); + } + + session->last_recv_stream_id = frame->push_promise.promised_stream_id; + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + if (!stream || stream->state == NGHTTP2_STREAM_CLOSING || + !session->pending_enable_push || + session->num_incoming_reserved_streams >= + session->max_incoming_reserved_streams) { + /* Currently, client does not retain closed stream, so we don't + check NGHTTP2_SHUT_RD condition here. */ + + rv = nghttp2_session_add_rst_stream( + session, frame->push_promise.promised_stream_id, NGHTTP2_CANCEL); + if (rv != 0) { + return rv; + } + return NGHTTP2_ERR_IGN_HEADER_BLOCK; + } + + if (stream->shut_flags & NGHTTP2_SHUT_RD) { + return session_inflate_handle_invalid_connection( + session, frame, NGHTTP2_ERR_STREAM_CLOSED, + "PUSH_PROMISE: stream closed"); + } + + nghttp2_priority_spec_init(&pri_spec, stream->stream_id, + NGHTTP2_DEFAULT_WEIGHT, 0); + + promised_stream = nghttp2_session_open_stream( + session, frame->push_promise.promised_stream_id, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec, NGHTTP2_STREAM_RESERVED, NULL); + + if (!promised_stream) { + return NGHTTP2_ERR_NOMEM; + } + + /* We don't call nghttp2_session_adjust_closed_stream(), since we + don't keep closed stream in client side */ + + session->last_proc_stream_id = session->last_recv_stream_id; + rv = session_call_on_begin_headers(session, frame); + if (rv != 0) { + return rv; + } + return 0; +} + +static int session_process_push_promise_frame(nghttp2_session *session) { + int rv; + nghttp2_inbound_frame *iframe = &session->iframe; + nghttp2_frame *frame = &iframe->frame; + + rv = nghttp2_frame_unpack_push_promise_payload(&frame->push_promise, + iframe->sbuf.pos); + + if (rv != 0) { + return nghttp2_session_terminate_session_with_reason( + session, NGHTTP2_PROTOCOL_ERROR, "PUSH_PROMISE: could not unpack"); + } + + return nghttp2_session_on_push_promise_received(session, frame); +} + +int nghttp2_session_on_ping_received(nghttp2_session *session, + nghttp2_frame *frame) { + int rv = 0; + if (frame->hd.stream_id != 0) { + return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, + "PING: stream_id != 0"); + } + if ((session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_PING_ACK) == 0 && + (frame->hd.flags & NGHTTP2_FLAG_ACK) == 0 && + !session_is_closing(session)) { + /* Peer sent ping, so ping it back */ + rv = nghttp2_session_add_ping(session, NGHTTP2_FLAG_ACK, + frame->ping.opaque_data); + if (rv != 0) { + return rv; + } + } + return session_call_on_frame_received(session, frame); +} + +static int session_process_ping_frame(nghttp2_session *session) { + nghttp2_inbound_frame *iframe = &session->iframe; + nghttp2_frame *frame = &iframe->frame; + + nghttp2_frame_unpack_ping_payload(&frame->ping, iframe->sbuf.pos); + + return nghttp2_session_on_ping_received(session, frame); +} + +int nghttp2_session_on_goaway_received(nghttp2_session *session, + nghttp2_frame *frame) { + int rv; + + if (frame->hd.stream_id != 0) { + return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, + "GOAWAY: stream_id != 0"); + } + /* Spec says Endpoints MUST NOT increase the value they send in the + last stream identifier. */ + if ((frame->goaway.last_stream_id > 0 && + !nghttp2_session_is_my_stream_id(session, + frame->goaway.last_stream_id)) || + session->remote_last_stream_id < frame->goaway.last_stream_id) { + return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, + "GOAWAY: invalid last_stream_id"); + } + + session->goaway_flags |= NGHTTP2_GOAWAY_RECV; + + session->remote_last_stream_id = frame->goaway.last_stream_id; + + rv = session_call_on_frame_received(session, frame); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + return session_close_stream_on_goaway(session, frame->goaway.last_stream_id, + 0); +} + +static int session_process_goaway_frame(nghttp2_session *session) { + nghttp2_inbound_frame *iframe = &session->iframe; + nghttp2_frame *frame = &iframe->frame; + + nghttp2_frame_unpack_goaway_payload(&frame->goaway, iframe->sbuf.pos, + iframe->lbuf.pos, + nghttp2_buf_len(&iframe->lbuf)); + + nghttp2_buf_wrap_init(&iframe->lbuf, NULL, 0); + + return nghttp2_session_on_goaway_received(session, frame); +} + +static int +session_on_connection_window_update_received(nghttp2_session *session, + nghttp2_frame *frame) { + /* Handle connection-level flow control */ + if (frame->window_update.window_size_increment == 0) { + return session_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, + "WINDOW_UPDATE: window_size_increment == 0"); + } + + if (NGHTTP2_MAX_WINDOW_SIZE - frame->window_update.window_size_increment < + session->remote_window_size) { + return session_handle_invalid_connection(session, frame, + NGHTTP2_ERR_FLOW_CONTROL, NULL); + } + session->remote_window_size += frame->window_update.window_size_increment; + + return session_call_on_frame_received(session, frame); +} + +static int session_on_stream_window_update_received(nghttp2_session *session, + nghttp2_frame *frame) { + int rv; + nghttp2_stream *stream; + + if (session_detect_idle_stream(session, frame->hd.stream_id)) { + return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, + "WINDOW_UPDATE to idle stream"); + } + + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + if (!stream) { + return 0; + } + if (state_reserved_remote(session, stream)) { + return session_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, "WINDOW_UPADATE to reserved stream"); + } + if (frame->window_update.window_size_increment == 0) { + return session_handle_invalid_connection( + session, frame, NGHTTP2_ERR_PROTO, + "WINDOW_UPDATE: window_size_increment == 0"); + } + if (NGHTTP2_MAX_WINDOW_SIZE - frame->window_update.window_size_increment < + stream->remote_window_size) { + return session_handle_invalid_stream(session, frame, + NGHTTP2_ERR_FLOW_CONTROL); + } + stream->remote_window_size += frame->window_update.window_size_increment; + + 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); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + return session_call_on_frame_received(session, frame); +} + +int nghttp2_session_on_window_update_received(nghttp2_session *session, + nghttp2_frame *frame) { + if (frame->hd.stream_id == 0) { + return session_on_connection_window_update_received(session, frame); + } else { + return session_on_stream_window_update_received(session, frame); + } +} + +static int session_process_window_update_frame(nghttp2_session *session) { + nghttp2_inbound_frame *iframe = &session->iframe; + nghttp2_frame *frame = &iframe->frame; + + nghttp2_frame_unpack_window_update_payload(&frame->window_update, + iframe->sbuf.pos); + + return nghttp2_session_on_window_update_received(session, frame); +} + +int nghttp2_session_on_altsvc_received(nghttp2_session *session, + nghttp2_frame *frame) { + nghttp2_ext_altsvc *altsvc; + nghttp2_stream *stream; + + altsvc = frame->ext.payload; + + /* session->server case has been excluded */ + + if (frame->hd.stream_id == 0) { + if (altsvc->origin_len == 0) { + return session_call_on_invalid_frame_recv_callback(session, frame, + NGHTTP2_ERR_PROTO); + } + } else { + if (altsvc->origin_len > 0) { + return session_call_on_invalid_frame_recv_callback(session, frame, + NGHTTP2_ERR_PROTO); + } + + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + if (!stream) { + return 0; + } + + if (stream->state == NGHTTP2_STREAM_CLOSING) { + return 0; + } + } + + if (altsvc->field_value_len == 0) { + return session_call_on_invalid_frame_recv_callback(session, frame, + NGHTTP2_ERR_PROTO); + } + + return session_call_on_frame_received(session, frame); +} + int nghttp2_session_on_origin_received(nghttp2_session *session, nghttp2_frame *frame) { 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; - - nghttp2_frame_unpack_altsvc_payload( - &frame->ext, nghttp2_get_uint16(iframe->sbuf.pos), iframe->lbuf.pos, - nghttp2_buf_len(&iframe->lbuf)); - - /* nghttp2_frame_unpack_altsvc_payload steals buffer from - iframe->lbuf */ - nghttp2_buf_wrap_init(&iframe->lbuf, NULL, 0); - - return nghttp2_session_on_altsvc_received(session, frame); -} - +static int session_process_altsvc_frame(nghttp2_session *session) { + nghttp2_inbound_frame *iframe = &session->iframe; + nghttp2_frame *frame = &iframe->frame; + + nghttp2_frame_unpack_altsvc_payload( + &frame->ext, nghttp2_get_uint16(iframe->sbuf.pos), iframe->lbuf.pos, + nghttp2_buf_len(&iframe->lbuf)); + + /* nghttp2_frame_unpack_altsvc_payload steals buffer from + iframe->lbuf */ + nghttp2_buf_wrap_init(&iframe->lbuf, NULL, 0); + + return nghttp2_session_on_altsvc_received(session, frame); +} + static int session_process_origin_frame(nghttp2_session *session) { nghttp2_inbound_frame *iframe = &session->iframe; nghttp2_frame *frame = &iframe->frame; @@ -4932,451 +4932,451 @@ static int session_process_origin_frame(nghttp2_session *session) { return nghttp2_session_on_origin_received(session, frame); } -static int session_process_extension_frame(nghttp2_session *session) { - int rv; - nghttp2_inbound_frame *iframe = &session->iframe; - nghttp2_frame *frame = &iframe->frame; - - rv = session_call_unpack_extension_callback(session); - if (nghttp2_is_fatal(rv)) { - return rv; - } - - /* This handles the case where rv == NGHTTP2_ERR_CANCEL as well */ - if (rv != 0) { - return 0; - } - - return session_call_on_frame_received(session, frame); -} - -int nghttp2_session_on_data_received(nghttp2_session *session, - nghttp2_frame *frame) { - int rv = 0; - nghttp2_stream *stream; - - /* We don't call on_frame_recv_callback if stream has been closed - already or being closed. */ - stream = nghttp2_session_get_stream(session, frame->hd.stream_id); - if (!stream || stream->state == NGHTTP2_STREAM_CLOSING) { - /* This should be treated as stream error, but it results in lots - of RST_STREAM. So just ignore frame against nonexistent stream - for now. */ - return 0; - } - - if (session_enforce_http_messaging(session) && - (frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) { - if (nghttp2_http_on_remote_end_stream(stream) != 0) { - rv = nghttp2_session_add_rst_stream(session, stream->stream_id, - NGHTTP2_PROTOCOL_ERROR); - if (nghttp2_is_fatal(rv)) { - return rv; - } +static int session_process_extension_frame(nghttp2_session *session) { + int rv; + nghttp2_inbound_frame *iframe = &session->iframe; + nghttp2_frame *frame = &iframe->frame; + + rv = session_call_unpack_extension_callback(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + /* This handles the case where rv == NGHTTP2_ERR_CANCEL as well */ + if (rv != 0) { + return 0; + } + + return session_call_on_frame_received(session, frame); +} + +int nghttp2_session_on_data_received(nghttp2_session *session, + nghttp2_frame *frame) { + int rv = 0; + nghttp2_stream *stream; + + /* We don't call on_frame_recv_callback if stream has been closed + already or being closed. */ + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + if (!stream || stream->state == NGHTTP2_STREAM_CLOSING) { + /* This should be treated as stream error, but it results in lots + of RST_STREAM. So just ignore frame against nonexistent stream + for now. */ + return 0; + } + + if (session_enforce_http_messaging(session) && + (frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) { + if (nghttp2_http_on_remote_end_stream(stream) != 0) { + rv = nghttp2_session_add_rst_stream(session, stream->stream_id, + NGHTTP2_PROTOCOL_ERROR); + if (nghttp2_is_fatal(rv)) { + return rv; + } nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); /* Don't call nghttp2_session_close_stream_if_shut_rdwr because RST_STREAM has been submitted. */ return 0; - } - } - + } + } + rv = session_call_on_frame_received(session, frame); if (nghttp2_is_fatal(rv)) { return rv; - } - - if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { - nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); - rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream); - if (nghttp2_is_fatal(rv)) { - return rv; - } - } - return 0; -} - -/* For errors, this function only returns FATAL error. */ -static int session_process_data_frame(nghttp2_session *session) { - int rv; - nghttp2_frame *public_data_frame = &session->iframe.frame; - rv = nghttp2_session_on_data_received(session, public_data_frame); - if (nghttp2_is_fatal(rv)) { - return rv; - } - return 0; -} - -/* - * Now we have SETTINGS synchronization, flow control error can be - * detected strictly. If DATA frame is received with length > 0 and - * current received window size + delta length is strictly larger than - * local window size, it is subject to FLOW_CONTROL_ERROR, so return - * -1. Note that local_window_size is calculated after SETTINGS ACK is - * received from peer, so peer must honor this limit. If the resulting - * recv_window_size is strictly larger than NGHTTP2_MAX_WINDOW_SIZE, - * return -1 too. - */ -static int adjust_recv_window_size(int32_t *recv_window_size_ptr, size_t delta, - int32_t local_window_size) { - if (*recv_window_size_ptr > local_window_size - (int32_t)delta || - *recv_window_size_ptr > NGHTTP2_MAX_WINDOW_SIZE - (int32_t)delta) { - return -1; - } - *recv_window_size_ptr += (int32_t)delta; - return 0; -} - + } + + if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); + rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream); + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + return 0; +} + +/* For errors, this function only returns FATAL error. */ +static int session_process_data_frame(nghttp2_session *session) { + int rv; + nghttp2_frame *public_data_frame = &session->iframe.frame; + rv = nghttp2_session_on_data_received(session, public_data_frame); + if (nghttp2_is_fatal(rv)) { + return rv; + } + return 0; +} + +/* + * Now we have SETTINGS synchronization, flow control error can be + * detected strictly. If DATA frame is received with length > 0 and + * current received window size + delta length is strictly larger than + * local window size, it is subject to FLOW_CONTROL_ERROR, so return + * -1. Note that local_window_size is calculated after SETTINGS ACK is + * received from peer, so peer must honor this limit. If the resulting + * recv_window_size is strictly larger than NGHTTP2_MAX_WINDOW_SIZE, + * return -1 too. + */ +static int adjust_recv_window_size(int32_t *recv_window_size_ptr, size_t delta, + int32_t local_window_size) { + if (*recv_window_size_ptr > local_window_size - (int32_t)delta || + *recv_window_size_ptr > NGHTTP2_MAX_WINDOW_SIZE - (int32_t)delta) { + return -1; + } + *recv_window_size_ptr += (int32_t)delta; + return 0; +} + int nghttp2_session_update_recv_stream_window_size(nghttp2_session *session, nghttp2_stream *stream, size_t delta_size, int send_window_update) { - int rv; - rv = adjust_recv_window_size(&stream->recv_window_size, delta_size, - stream->local_window_size); - if (rv != 0) { - return nghttp2_session_add_rst_stream(session, stream->stream_id, - NGHTTP2_FLOW_CONTROL_ERROR); - } - /* We don't have to send WINDOW_UPDATE if the data received is the - last chunk in the incoming stream. */ - /* We have to use local_settings here because it is the constraint - the remote endpoint should honor. */ - if (send_window_update && - !(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) && - stream->window_update_queued == 0 && - nghttp2_should_send_window_update(stream->local_window_size, - stream->recv_window_size)) { - rv = nghttp2_session_add_window_update(session, NGHTTP2_FLAG_NONE, - stream->stream_id, - stream->recv_window_size); - if (rv != 0) { - return rv; - } - - stream->recv_window_size = 0; - } - return 0; -} - + int rv; + rv = adjust_recv_window_size(&stream->recv_window_size, delta_size, + stream->local_window_size); + if (rv != 0) { + return nghttp2_session_add_rst_stream(session, stream->stream_id, + NGHTTP2_FLOW_CONTROL_ERROR); + } + /* We don't have to send WINDOW_UPDATE if the data received is the + last chunk in the incoming stream. */ + /* We have to use local_settings here because it is the constraint + the remote endpoint should honor. */ + if (send_window_update && + !(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) && + stream->window_update_queued == 0 && + nghttp2_should_send_window_update(stream->local_window_size, + stream->recv_window_size)) { + rv = nghttp2_session_add_window_update(session, NGHTTP2_FLAG_NONE, + stream->stream_id, + stream->recv_window_size); + if (rv != 0) { + return rv; + } + + stream->recv_window_size = 0; + } + return 0; +} + int nghttp2_session_update_recv_connection_window_size(nghttp2_session *session, size_t delta_size) { - int rv; - rv = adjust_recv_window_size(&session->recv_window_size, delta_size, - session->local_window_size); - if (rv != 0) { - return nghttp2_session_terminate_session(session, - NGHTTP2_FLOW_CONTROL_ERROR); - } - if (!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) && - session->window_update_queued == 0 && - nghttp2_should_send_window_update(session->local_window_size, - session->recv_window_size)) { - /* Use stream ID 0 to update connection-level flow control - window */ - rv = nghttp2_session_add_window_update(session, NGHTTP2_FLAG_NONE, 0, - session->recv_window_size); - if (rv != 0) { - return rv; - } - - session->recv_window_size = 0; - } - return 0; -} - -static int session_update_consumed_size(nghttp2_session *session, - int32_t *consumed_size_ptr, - int32_t *recv_window_size_ptr, - uint8_t window_update_queued, - int32_t stream_id, size_t delta_size, - int32_t local_window_size) { - int32_t recv_size; - int rv; - - if ((size_t)*consumed_size_ptr > NGHTTP2_MAX_WINDOW_SIZE - delta_size) { - return nghttp2_session_terminate_session(session, - NGHTTP2_FLOW_CONTROL_ERROR); - } - - *consumed_size_ptr += (int32_t)delta_size; - - if (window_update_queued == 0) { - /* recv_window_size may be smaller than consumed_size, because it - may be decreased by negative value with - nghttp2_submit_window_update(). */ - recv_size = nghttp2_min(*consumed_size_ptr, *recv_window_size_ptr); - - if (nghttp2_should_send_window_update(local_window_size, recv_size)) { - rv = nghttp2_session_add_window_update(session, NGHTTP2_FLAG_NONE, - stream_id, recv_size); - - if (rv != 0) { - return rv; - } - - *recv_window_size_ptr -= recv_size; - *consumed_size_ptr -= recv_size; - } - } - - return 0; -} - -static int session_update_stream_consumed_size(nghttp2_session *session, - nghttp2_stream *stream, - size_t delta_size) { - return session_update_consumed_size( - session, &stream->consumed_size, &stream->recv_window_size, - stream->window_update_queued, stream->stream_id, delta_size, - stream->local_window_size); -} - -static int session_update_connection_consumed_size(nghttp2_session *session, - size_t delta_size) { - return session_update_consumed_size( - session, &session->consumed_size, &session->recv_window_size, - session->window_update_queued, 0, delta_size, session->local_window_size); -} - -/* - * Checks that we can receive the DATA frame for stream, which is - * indicated by |session->iframe.frame.hd.stream_id|. If it is a - * connection error situation, GOAWAY frame will be issued by this - * function. - * - * If the DATA frame is allowed, returns 0. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_IGN_PAYLOAD - * The reception of DATA frame is connection error; or should be - * ignored. - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -static int session_on_data_received_fail_fast(nghttp2_session *session) { - int rv; - nghttp2_stream *stream; - nghttp2_inbound_frame *iframe; - int32_t stream_id; - const char *failure_reason; - uint32_t error_code = NGHTTP2_PROTOCOL_ERROR; - - iframe = &session->iframe; - stream_id = iframe->frame.hd.stream_id; - - if (stream_id == 0) { - /* The spec says that if a DATA frame is received whose stream ID - is 0, the recipient MUST respond with a connection error of - type PROTOCOL_ERROR. */ - failure_reason = "DATA: stream_id == 0"; - goto fail; - } - - if (session_detect_idle_stream(session, stream_id)) { - failure_reason = "DATA: stream in idle"; - error_code = NGHTTP2_PROTOCOL_ERROR; - goto fail; - } - - stream = nghttp2_session_get_stream(session, stream_id); - if (!stream) { - stream = nghttp2_session_get_stream_raw(session, stream_id); - if (stream && (stream->shut_flags & NGHTTP2_SHUT_RD)) { - failure_reason = "DATA: stream closed"; - error_code = NGHTTP2_STREAM_CLOSED; - goto fail; - } - - return NGHTTP2_ERR_IGN_PAYLOAD; - } - if (stream->shut_flags & NGHTTP2_SHUT_RD) { - failure_reason = "DATA: stream in half-closed(remote)"; - error_code = NGHTTP2_STREAM_CLOSED; - goto fail; - } - - if (nghttp2_session_is_my_stream_id(session, stream_id)) { - if (stream->state == NGHTTP2_STREAM_CLOSING) { - return NGHTTP2_ERR_IGN_PAYLOAD; - } - if (stream->state != NGHTTP2_STREAM_OPENED) { - failure_reason = "DATA: stream not opened"; - goto fail; - } - return 0; - } - if (stream->state == NGHTTP2_STREAM_RESERVED) { - failure_reason = "DATA: stream in reserved"; - goto fail; - } - if (stream->state == NGHTTP2_STREAM_CLOSING) { - return NGHTTP2_ERR_IGN_PAYLOAD; - } - return 0; -fail: - rv = nghttp2_session_terminate_session_with_reason(session, error_code, - failure_reason); - if (nghttp2_is_fatal(rv)) { - return rv; - } - return NGHTTP2_ERR_IGN_PAYLOAD; -} - -static size_t inbound_frame_payload_readlen(nghttp2_inbound_frame *iframe, - const uint8_t *in, - const uint8_t *last) { - return nghttp2_min((size_t)(last - in), iframe->payloadleft); -} - -/* - * Resets iframe->sbuf and advance its mark pointer by |left| bytes. - */ -static void inbound_frame_set_mark(nghttp2_inbound_frame *iframe, size_t left) { - nghttp2_buf_reset(&iframe->sbuf); - iframe->sbuf.mark += left; -} - -static size_t inbound_frame_buf_read(nghttp2_inbound_frame *iframe, - const uint8_t *in, const uint8_t *last) { - size_t readlen; - - readlen = - nghttp2_min((size_t)(last - in), nghttp2_buf_mark_avail(&iframe->sbuf)); - - iframe->sbuf.last = nghttp2_cpymem(iframe->sbuf.last, in, readlen); - - return readlen; -} - -/* - * Unpacks SETTINGS entry in iframe->sbuf. - */ -static void inbound_frame_set_settings_entry(nghttp2_inbound_frame *iframe) { - nghttp2_settings_entry iv; - nghttp2_settings_entry *min_header_table_size_entry; - size_t i; - - nghttp2_frame_unpack_settings_entry(&iv, iframe->sbuf.pos); - - switch (iv.settings_id) { - case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: - case NGHTTP2_SETTINGS_ENABLE_PUSH: - case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: - case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: - case NGHTTP2_SETTINGS_MAX_FRAME_SIZE: - case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: + int rv; + rv = adjust_recv_window_size(&session->recv_window_size, delta_size, + session->local_window_size); + if (rv != 0) { + return nghttp2_session_terminate_session(session, + NGHTTP2_FLOW_CONTROL_ERROR); + } + if (!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) && + session->window_update_queued == 0 && + nghttp2_should_send_window_update(session->local_window_size, + session->recv_window_size)) { + /* Use stream ID 0 to update connection-level flow control + window */ + rv = nghttp2_session_add_window_update(session, NGHTTP2_FLAG_NONE, 0, + session->recv_window_size); + if (rv != 0) { + return rv; + } + + session->recv_window_size = 0; + } + return 0; +} + +static int session_update_consumed_size(nghttp2_session *session, + int32_t *consumed_size_ptr, + int32_t *recv_window_size_ptr, + uint8_t window_update_queued, + int32_t stream_id, size_t delta_size, + int32_t local_window_size) { + int32_t recv_size; + int rv; + + if ((size_t)*consumed_size_ptr > NGHTTP2_MAX_WINDOW_SIZE - delta_size) { + return nghttp2_session_terminate_session(session, + NGHTTP2_FLOW_CONTROL_ERROR); + } + + *consumed_size_ptr += (int32_t)delta_size; + + if (window_update_queued == 0) { + /* recv_window_size may be smaller than consumed_size, because it + may be decreased by negative value with + nghttp2_submit_window_update(). */ + recv_size = nghttp2_min(*consumed_size_ptr, *recv_window_size_ptr); + + if (nghttp2_should_send_window_update(local_window_size, recv_size)) { + rv = nghttp2_session_add_window_update(session, NGHTTP2_FLAG_NONE, + stream_id, recv_size); + + if (rv != 0) { + return rv; + } + + *recv_window_size_ptr -= recv_size; + *consumed_size_ptr -= recv_size; + } + } + + return 0; +} + +static int session_update_stream_consumed_size(nghttp2_session *session, + nghttp2_stream *stream, + size_t delta_size) { + return session_update_consumed_size( + session, &stream->consumed_size, &stream->recv_window_size, + stream->window_update_queued, stream->stream_id, delta_size, + stream->local_window_size); +} + +static int session_update_connection_consumed_size(nghttp2_session *session, + size_t delta_size) { + return session_update_consumed_size( + session, &session->consumed_size, &session->recv_window_size, + session->window_update_queued, 0, delta_size, session->local_window_size); +} + +/* + * Checks that we can receive the DATA frame for stream, which is + * indicated by |session->iframe.frame.hd.stream_id|. If it is a + * connection error situation, GOAWAY frame will be issued by this + * function. + * + * If the DATA frame is allowed, returns 0. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_IGN_PAYLOAD + * The reception of DATA frame is connection error; or should be + * ignored. + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +static int session_on_data_received_fail_fast(nghttp2_session *session) { + int rv; + nghttp2_stream *stream; + nghttp2_inbound_frame *iframe; + int32_t stream_id; + const char *failure_reason; + uint32_t error_code = NGHTTP2_PROTOCOL_ERROR; + + iframe = &session->iframe; + stream_id = iframe->frame.hd.stream_id; + + if (stream_id == 0) { + /* The spec says that if a DATA frame is received whose stream ID + is 0, the recipient MUST respond with a connection error of + type PROTOCOL_ERROR. */ + failure_reason = "DATA: stream_id == 0"; + goto fail; + } + + if (session_detect_idle_stream(session, stream_id)) { + failure_reason = "DATA: stream in idle"; + error_code = NGHTTP2_PROTOCOL_ERROR; + goto fail; + } + + stream = nghttp2_session_get_stream(session, stream_id); + if (!stream) { + stream = nghttp2_session_get_stream_raw(session, stream_id); + if (stream && (stream->shut_flags & NGHTTP2_SHUT_RD)) { + failure_reason = "DATA: stream closed"; + error_code = NGHTTP2_STREAM_CLOSED; + goto fail; + } + + return NGHTTP2_ERR_IGN_PAYLOAD; + } + if (stream->shut_flags & NGHTTP2_SHUT_RD) { + failure_reason = "DATA: stream in half-closed(remote)"; + error_code = NGHTTP2_STREAM_CLOSED; + goto fail; + } + + if (nghttp2_session_is_my_stream_id(session, stream_id)) { + if (stream->state == NGHTTP2_STREAM_CLOSING) { + return NGHTTP2_ERR_IGN_PAYLOAD; + } + if (stream->state != NGHTTP2_STREAM_OPENED) { + failure_reason = "DATA: stream not opened"; + goto fail; + } + return 0; + } + if (stream->state == NGHTTP2_STREAM_RESERVED) { + failure_reason = "DATA: stream in reserved"; + goto fail; + } + if (stream->state == NGHTTP2_STREAM_CLOSING) { + return NGHTTP2_ERR_IGN_PAYLOAD; + } + return 0; +fail: + rv = nghttp2_session_terminate_session_with_reason(session, error_code, + failure_reason); + if (nghttp2_is_fatal(rv)) { + return rv; + } + return NGHTTP2_ERR_IGN_PAYLOAD; +} + +static size_t inbound_frame_payload_readlen(nghttp2_inbound_frame *iframe, + const uint8_t *in, + const uint8_t *last) { + return nghttp2_min((size_t)(last - in), iframe->payloadleft); +} + +/* + * Resets iframe->sbuf and advance its mark pointer by |left| bytes. + */ +static void inbound_frame_set_mark(nghttp2_inbound_frame *iframe, size_t left) { + nghttp2_buf_reset(&iframe->sbuf); + iframe->sbuf.mark += left; +} + +static size_t inbound_frame_buf_read(nghttp2_inbound_frame *iframe, + const uint8_t *in, const uint8_t *last) { + size_t readlen; + + readlen = + nghttp2_min((size_t)(last - in), nghttp2_buf_mark_avail(&iframe->sbuf)); + + iframe->sbuf.last = nghttp2_cpymem(iframe->sbuf.last, in, readlen); + + return readlen; +} + +/* + * Unpacks SETTINGS entry in iframe->sbuf. + */ +static void inbound_frame_set_settings_entry(nghttp2_inbound_frame *iframe) { + nghttp2_settings_entry iv; + nghttp2_settings_entry *min_header_table_size_entry; + size_t i; + + nghttp2_frame_unpack_settings_entry(&iv, iframe->sbuf.pos); + + switch (iv.settings_id) { + case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: + case NGHTTP2_SETTINGS_ENABLE_PUSH: + case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: + case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: + case NGHTTP2_SETTINGS_MAX_FRAME_SIZE: + case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL: - break; - default: - DEBUGF("recv: unknown settings id=0x%02x\n", iv.settings_id); - - iframe->iv[iframe->niv++] = iv; - - return; - } - - for (i = 0; i < iframe->niv; ++i) { - if (iframe->iv[i].settings_id == iv.settings_id) { - iframe->iv[i] = iv; - break; - } - } - - if (i == iframe->niv) { - iframe->iv[iframe->niv++] = iv; - } - - if (iv.settings_id == NGHTTP2_SETTINGS_HEADER_TABLE_SIZE) { - /* Keep track of minimum value of SETTINGS_HEADER_TABLE_SIZE */ - min_header_table_size_entry = &iframe->iv[iframe->max_niv - 1]; - - if (iv.value < min_header_table_size_entry->value) { - min_header_table_size_entry->value = iv.value; - } - } -} - -/* - * Checks PADDED flags and set iframe->sbuf to read them accordingly. - * If padding is set, this function returns 1. If no padding is set, - * this function returns 0. On error, returns -1. - */ -static int inbound_frame_handle_pad(nghttp2_inbound_frame *iframe, - nghttp2_frame_hd *hd) { - if (hd->flags & NGHTTP2_FLAG_PADDED) { - if (hd->length < 1) { - return -1; - } - inbound_frame_set_mark(iframe, 1); - return 1; - } - DEBUGF("recv: no padding in payload\n"); - return 0; -} - -/* - * Computes number of padding based on flags. This function returns - * the calculated length if it succeeds, or -1. - */ -static ssize_t inbound_frame_compute_pad(nghttp2_inbound_frame *iframe) { - size_t padlen; - - /* 1 for Pad Length field */ - padlen = (size_t)(iframe->sbuf.pos[0] + 1); - - DEBUGF("recv: padlen=%zu\n", padlen); - - /* We cannot use iframe->frame.hd.length because of CONTINUATION */ - if (padlen - 1 > iframe->payloadleft) { - return -1; - } - - iframe->padlen = padlen; - - return (ssize_t)padlen; -} - -/* - * This function returns the effective payload length in the data of - * length |readlen| when the remaning payload is |payloadleft|. The - * |payloadleft| does not include |readlen|. If padding was started - * strictly before this data chunk, this function returns -1. - */ -static ssize_t inbound_frame_effective_readlen(nghttp2_inbound_frame *iframe, - size_t payloadleft, - size_t readlen) { - size_t trail_padlen = - nghttp2_frame_trail_padlen(&iframe->frame, iframe->padlen); - - if (trail_padlen > payloadleft) { - size_t padlen; - padlen = trail_padlen - payloadleft; - if (readlen < padlen) { - return -1; - } - return (ssize_t)(readlen - padlen); - } - return (ssize_t)(readlen); -} - + break; + default: + DEBUGF("recv: unknown settings id=0x%02x\n", iv.settings_id); + + iframe->iv[iframe->niv++] = iv; + + return; + } + + for (i = 0; i < iframe->niv; ++i) { + if (iframe->iv[i].settings_id == iv.settings_id) { + iframe->iv[i] = iv; + break; + } + } + + if (i == iframe->niv) { + iframe->iv[iframe->niv++] = iv; + } + + if (iv.settings_id == NGHTTP2_SETTINGS_HEADER_TABLE_SIZE) { + /* Keep track of minimum value of SETTINGS_HEADER_TABLE_SIZE */ + min_header_table_size_entry = &iframe->iv[iframe->max_niv - 1]; + + if (iv.value < min_header_table_size_entry->value) { + min_header_table_size_entry->value = iv.value; + } + } +} + +/* + * Checks PADDED flags and set iframe->sbuf to read them accordingly. + * If padding is set, this function returns 1. If no padding is set, + * this function returns 0. On error, returns -1. + */ +static int inbound_frame_handle_pad(nghttp2_inbound_frame *iframe, + nghttp2_frame_hd *hd) { + if (hd->flags & NGHTTP2_FLAG_PADDED) { + if (hd->length < 1) { + return -1; + } + inbound_frame_set_mark(iframe, 1); + return 1; + } + DEBUGF("recv: no padding in payload\n"); + return 0; +} + +/* + * Computes number of padding based on flags. This function returns + * the calculated length if it succeeds, or -1. + */ +static ssize_t inbound_frame_compute_pad(nghttp2_inbound_frame *iframe) { + size_t padlen; + + /* 1 for Pad Length field */ + padlen = (size_t)(iframe->sbuf.pos[0] + 1); + + DEBUGF("recv: padlen=%zu\n", padlen); + + /* We cannot use iframe->frame.hd.length because of CONTINUATION */ + if (padlen - 1 > iframe->payloadleft) { + return -1; + } + + iframe->padlen = padlen; + + return (ssize_t)padlen; +} + +/* + * This function returns the effective payload length in the data of + * length |readlen| when the remaning payload is |payloadleft|. The + * |payloadleft| does not include |readlen|. If padding was started + * strictly before this data chunk, this function returns -1. + */ +static ssize_t inbound_frame_effective_readlen(nghttp2_inbound_frame *iframe, + size_t payloadleft, + size_t readlen) { + size_t trail_padlen = + nghttp2_frame_trail_padlen(&iframe->frame, iframe->padlen); + + if (trail_padlen > payloadleft) { + size_t padlen; + padlen = trail_padlen - payloadleft; + if (readlen < padlen) { + return -1; + } + return (ssize_t)(readlen - padlen); + } + return (ssize_t)(readlen); +} + static const uint8_t static_in[] = {0}; -ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, - size_t inlen) { +ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, + size_t inlen) { const uint8_t *first, *last; - nghttp2_inbound_frame *iframe = &session->iframe; - size_t readlen; - ssize_t padlen; - int rv; - int busy = 0; - nghttp2_frame_hd cont_hd; - nghttp2_stream *stream; - size_t pri_fieldlen; - nghttp2_mem *mem; - + nghttp2_inbound_frame *iframe = &session->iframe; + size_t readlen; + ssize_t padlen; + int rv; + int busy = 0; + nghttp2_frame_hd cont_hd; + nghttp2_stream *stream; + size_t pri_fieldlen; + nghttp2_mem *mem; + if (in == NULL) { assert(inlen == 0); in = static_in; @@ -5385,309 +5385,309 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, first = in; last = in + inlen; - DEBUGF("recv: connection recv_window_size=%d, local_window=%d\n", - session->recv_window_size, session->local_window_size); - - mem = &session->mem; - - /* We may have idle streams more than we expect (e.g., - nghttp2_session_change_stream_priority() or - nghttp2_session_create_idle_stream()). Adjust them here. */ - rv = nghttp2_session_adjust_idle_stream(session); - if (nghttp2_is_fatal(rv)) { - return rv; - } - - if (!nghttp2_session_want_read(session)) { - return (ssize_t)inlen; - } - - for (;;) { - switch (iframe->state) { - case NGHTTP2_IB_READ_CLIENT_MAGIC: - readlen = nghttp2_min(inlen, iframe->payloadleft); - + DEBUGF("recv: connection recv_window_size=%d, local_window=%d\n", + session->recv_window_size, session->local_window_size); + + mem = &session->mem; + + /* We may have idle streams more than we expect (e.g., + nghttp2_session_change_stream_priority() or + nghttp2_session_create_idle_stream()). Adjust them here. */ + rv = nghttp2_session_adjust_idle_stream(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + if (!nghttp2_session_want_read(session)) { + return (ssize_t)inlen; + } + + for (;;) { + switch (iframe->state) { + case NGHTTP2_IB_READ_CLIENT_MAGIC: + readlen = nghttp2_min(inlen, iframe->payloadleft); + if (memcmp(&NGHTTP2_CLIENT_MAGIC[NGHTTP2_CLIENT_MAGIC_LEN - iframe->payloadleft], - in, readlen) != 0) { - return NGHTTP2_ERR_BAD_CLIENT_MAGIC; - } - - iframe->payloadleft -= readlen; - in += readlen; - - if (iframe->payloadleft == 0) { - session_inbound_frame_reset(session); - iframe->state = NGHTTP2_IB_READ_FIRST_SETTINGS; - } - - break; - case NGHTTP2_IB_READ_FIRST_SETTINGS: - DEBUGF("recv: [IB_READ_FIRST_SETTINGS]\n"); - - readlen = inbound_frame_buf_read(iframe, in, last); - in += readlen; - - if (nghttp2_buf_mark_avail(&iframe->sbuf)) { - return in - first; - } - - if (iframe->sbuf.pos[3] != NGHTTP2_SETTINGS || - (iframe->sbuf.pos[4] & NGHTTP2_FLAG_ACK)) { - rv = session_call_error_callback( - session, NGHTTP2_ERR_SETTINGS_EXPECTED, - "Remote peer returned unexpected data while we expected " - "SETTINGS frame. Perhaps, peer does not support HTTP/2 " - "properly."); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - - rv = nghttp2_session_terminate_session_with_reason( - session, NGHTTP2_PROTOCOL_ERROR, "SETTINGS expected"); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - - return (ssize_t)inlen; - } - - iframe->state = NGHTTP2_IB_READ_HEAD; - - /* Fall through */ - case NGHTTP2_IB_READ_HEAD: { - int on_begin_frame_called = 0; - - DEBUGF("recv: [IB_READ_HEAD]\n"); - - readlen = inbound_frame_buf_read(iframe, in, last); - in += readlen; - - if (nghttp2_buf_mark_avail(&iframe->sbuf)) { - return in - first; - } - - nghttp2_frame_unpack_frame_hd(&iframe->frame.hd, iframe->sbuf.pos); - iframe->payloadleft = iframe->frame.hd.length; - - DEBUGF("recv: payloadlen=%zu, type=%u, flags=0x%02x, stream_id=%d\n", - iframe->frame.hd.length, iframe->frame.hd.type, - iframe->frame.hd.flags, iframe->frame.hd.stream_id); - - if (iframe->frame.hd.length > session->local_settings.max_frame_size) { - DEBUGF("recv: length is too large %zu > %u\n", iframe->frame.hd.length, - session->local_settings.max_frame_size); - - rv = nghttp2_session_terminate_session_with_reason( - session, NGHTTP2_FRAME_SIZE_ERROR, "too large frame size"); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - - return (ssize_t)inlen; - } - - switch (iframe->frame.hd.type) { - case NGHTTP2_DATA: { - DEBUGF("recv: DATA\n"); - - iframe->frame.hd.flags &= - (NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_PADDED); - /* Check stream is open. If it is not open or closing, - ignore payload. */ - busy = 1; - - rv = session_on_data_received_fail_fast(session); - if (iframe->state == NGHTTP2_IB_IGN_ALL) { - return (ssize_t)inlen; - } - if (rv == NGHTTP2_ERR_IGN_PAYLOAD) { - DEBUGF("recv: DATA not allowed stream_id=%d\n", - iframe->frame.hd.stream_id); - iframe->state = NGHTTP2_IB_IGN_DATA; - break; - } - - if (nghttp2_is_fatal(rv)) { - return rv; - } - - rv = inbound_frame_handle_pad(iframe, &iframe->frame.hd); - if (rv < 0) { - rv = nghttp2_session_terminate_session_with_reason( - session, NGHTTP2_PROTOCOL_ERROR, - "DATA: insufficient padding space"); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - return (ssize_t)inlen; - } - - if (rv == 1) { - iframe->state = NGHTTP2_IB_READ_PAD_DATA; - break; - } - - iframe->state = NGHTTP2_IB_READ_DATA; - break; - } - case NGHTTP2_HEADERS: - - DEBUGF("recv: HEADERS\n"); - - iframe->frame.hd.flags &= - (NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS | - NGHTTP2_FLAG_PADDED | NGHTTP2_FLAG_PRIORITY); - - rv = inbound_frame_handle_pad(iframe, &iframe->frame.hd); - if (rv < 0) { - rv = nghttp2_session_terminate_session_with_reason( - session, NGHTTP2_PROTOCOL_ERROR, - "HEADERS: insufficient padding space"); - if (nghttp2_is_fatal(rv)) { - return rv; - } - return (ssize_t)inlen; - } - - if (rv == 1) { - iframe->state = NGHTTP2_IB_READ_NBYTE; - break; - } - - pri_fieldlen = nghttp2_frame_priority_len(iframe->frame.hd.flags); - - if (pri_fieldlen > 0) { - if (iframe->payloadleft < pri_fieldlen) { - busy = 1; - iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; - break; - } - - iframe->state = NGHTTP2_IB_READ_NBYTE; - - inbound_frame_set_mark(iframe, pri_fieldlen); - - break; - } - - /* Call on_begin_frame_callback here because - session_process_headers_frame() may call - on_begin_headers_callback */ - rv = session_call_on_begin_frame(session, &iframe->frame.hd); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - - on_begin_frame_called = 1; - - rv = session_process_headers_frame(session); - if (nghttp2_is_fatal(rv)) { - return rv; - } - - busy = 1; - - if (iframe->state == NGHTTP2_IB_IGN_ALL) { - return (ssize_t)inlen; - } - - if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { - rv = nghttp2_session_add_rst_stream( - session, iframe->frame.hd.stream_id, NGHTTP2_INTERNAL_ERROR); - if (nghttp2_is_fatal(rv)) { - return rv; - } - iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; - break; - } - - if (rv == NGHTTP2_ERR_IGN_HEADER_BLOCK) { - iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; - break; - } - - iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK; - - break; - case NGHTTP2_PRIORITY: - DEBUGF("recv: PRIORITY\n"); - - iframe->frame.hd.flags = NGHTTP2_FLAG_NONE; - - if (iframe->payloadleft != NGHTTP2_PRIORITY_SPECLEN) { - busy = 1; - - iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; - - break; - } - - iframe->state = NGHTTP2_IB_READ_NBYTE; - - inbound_frame_set_mark(iframe, NGHTTP2_PRIORITY_SPECLEN); - - break; - case NGHTTP2_RST_STREAM: - case NGHTTP2_WINDOW_UPDATE: -#ifdef DEBUGBUILD - switch (iframe->frame.hd.type) { - case NGHTTP2_RST_STREAM: - DEBUGF("recv: RST_STREAM\n"); - break; - case NGHTTP2_WINDOW_UPDATE: - DEBUGF("recv: WINDOW_UPDATE\n"); - break; - } -#endif /* DEBUGBUILD */ - - iframe->frame.hd.flags = NGHTTP2_FLAG_NONE; - - if (iframe->payloadleft != 4) { - busy = 1; - iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; - break; - } - - iframe->state = NGHTTP2_IB_READ_NBYTE; - - inbound_frame_set_mark(iframe, 4); - - break; - case NGHTTP2_SETTINGS: - DEBUGF("recv: SETTINGS\n"); - - iframe->frame.hd.flags &= NGHTTP2_FLAG_ACK; - - if ((iframe->frame.hd.length % NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH) || - ((iframe->frame.hd.flags & NGHTTP2_FLAG_ACK) && - iframe->payloadleft > 0)) { - busy = 1; - iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; - break; - } - + in, readlen) != 0) { + return NGHTTP2_ERR_BAD_CLIENT_MAGIC; + } + + iframe->payloadleft -= readlen; + in += readlen; + + if (iframe->payloadleft == 0) { + session_inbound_frame_reset(session); + iframe->state = NGHTTP2_IB_READ_FIRST_SETTINGS; + } + + break; + case NGHTTP2_IB_READ_FIRST_SETTINGS: + DEBUGF("recv: [IB_READ_FIRST_SETTINGS]\n"); + + readlen = inbound_frame_buf_read(iframe, in, last); + in += readlen; + + if (nghttp2_buf_mark_avail(&iframe->sbuf)) { + return in - first; + } + + if (iframe->sbuf.pos[3] != NGHTTP2_SETTINGS || + (iframe->sbuf.pos[4] & NGHTTP2_FLAG_ACK)) { + rv = session_call_error_callback( + session, NGHTTP2_ERR_SETTINGS_EXPECTED, + "Remote peer returned unexpected data while we expected " + "SETTINGS frame. Perhaps, peer does not support HTTP/2 " + "properly."); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + rv = nghttp2_session_terminate_session_with_reason( + session, NGHTTP2_PROTOCOL_ERROR, "SETTINGS expected"); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + return (ssize_t)inlen; + } + + iframe->state = NGHTTP2_IB_READ_HEAD; + + /* Fall through */ + case NGHTTP2_IB_READ_HEAD: { + int on_begin_frame_called = 0; + + DEBUGF("recv: [IB_READ_HEAD]\n"); + + readlen = inbound_frame_buf_read(iframe, in, last); + in += readlen; + + if (nghttp2_buf_mark_avail(&iframe->sbuf)) { + return in - first; + } + + nghttp2_frame_unpack_frame_hd(&iframe->frame.hd, iframe->sbuf.pos); + iframe->payloadleft = iframe->frame.hd.length; + + DEBUGF("recv: payloadlen=%zu, type=%u, flags=0x%02x, stream_id=%d\n", + iframe->frame.hd.length, iframe->frame.hd.type, + iframe->frame.hd.flags, iframe->frame.hd.stream_id); + + if (iframe->frame.hd.length > session->local_settings.max_frame_size) { + DEBUGF("recv: length is too large %zu > %u\n", iframe->frame.hd.length, + session->local_settings.max_frame_size); + + rv = nghttp2_session_terminate_session_with_reason( + session, NGHTTP2_FRAME_SIZE_ERROR, "too large frame size"); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + return (ssize_t)inlen; + } + + switch (iframe->frame.hd.type) { + case NGHTTP2_DATA: { + DEBUGF("recv: DATA\n"); + + iframe->frame.hd.flags &= + (NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_PADDED); + /* Check stream is open. If it is not open or closing, + ignore payload. */ + busy = 1; + + rv = session_on_data_received_fail_fast(session); + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (ssize_t)inlen; + } + if (rv == NGHTTP2_ERR_IGN_PAYLOAD) { + DEBUGF("recv: DATA not allowed stream_id=%d\n", + iframe->frame.hd.stream_id); + iframe->state = NGHTTP2_IB_IGN_DATA; + break; + } + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + rv = inbound_frame_handle_pad(iframe, &iframe->frame.hd); + if (rv < 0) { + rv = nghttp2_session_terminate_session_with_reason( + session, NGHTTP2_PROTOCOL_ERROR, + "DATA: insufficient padding space"); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + return (ssize_t)inlen; + } + + if (rv == 1) { + iframe->state = NGHTTP2_IB_READ_PAD_DATA; + break; + } + + iframe->state = NGHTTP2_IB_READ_DATA; + break; + } + case NGHTTP2_HEADERS: + + DEBUGF("recv: HEADERS\n"); + + iframe->frame.hd.flags &= + (NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS | + NGHTTP2_FLAG_PADDED | NGHTTP2_FLAG_PRIORITY); + + rv = inbound_frame_handle_pad(iframe, &iframe->frame.hd); + if (rv < 0) { + rv = nghttp2_session_terminate_session_with_reason( + session, NGHTTP2_PROTOCOL_ERROR, + "HEADERS: insufficient padding space"); + if (nghttp2_is_fatal(rv)) { + return rv; + } + return (ssize_t)inlen; + } + + if (rv == 1) { + iframe->state = NGHTTP2_IB_READ_NBYTE; + break; + } + + pri_fieldlen = nghttp2_frame_priority_len(iframe->frame.hd.flags); + + if (pri_fieldlen > 0) { + if (iframe->payloadleft < pri_fieldlen) { + busy = 1; + iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; + break; + } + + iframe->state = NGHTTP2_IB_READ_NBYTE; + + inbound_frame_set_mark(iframe, pri_fieldlen); + + break; + } + + /* Call on_begin_frame_callback here because + session_process_headers_frame() may call + on_begin_headers_callback */ + rv = session_call_on_begin_frame(session, &iframe->frame.hd); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + on_begin_frame_called = 1; + + rv = session_process_headers_frame(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + busy = 1; + + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (ssize_t)inlen; + } + + if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { + rv = nghttp2_session_add_rst_stream( + session, iframe->frame.hd.stream_id, NGHTTP2_INTERNAL_ERROR); + if (nghttp2_is_fatal(rv)) { + return rv; + } + iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; + break; + } + + if (rv == NGHTTP2_ERR_IGN_HEADER_BLOCK) { + iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; + break; + } + + iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK; + + break; + case NGHTTP2_PRIORITY: + DEBUGF("recv: PRIORITY\n"); + + iframe->frame.hd.flags = NGHTTP2_FLAG_NONE; + + if (iframe->payloadleft != NGHTTP2_PRIORITY_SPECLEN) { + busy = 1; + + iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; + + break; + } + + iframe->state = NGHTTP2_IB_READ_NBYTE; + + inbound_frame_set_mark(iframe, NGHTTP2_PRIORITY_SPECLEN); + + break; + case NGHTTP2_RST_STREAM: + case NGHTTP2_WINDOW_UPDATE: +#ifdef DEBUGBUILD + switch (iframe->frame.hd.type) { + case NGHTTP2_RST_STREAM: + DEBUGF("recv: RST_STREAM\n"); + break; + case NGHTTP2_WINDOW_UPDATE: + DEBUGF("recv: WINDOW_UPDATE\n"); + break; + } +#endif /* DEBUGBUILD */ + + iframe->frame.hd.flags = NGHTTP2_FLAG_NONE; + + if (iframe->payloadleft != 4) { + busy = 1; + iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; + break; + } + + iframe->state = NGHTTP2_IB_READ_NBYTE; + + inbound_frame_set_mark(iframe, 4); + + break; + case NGHTTP2_SETTINGS: + DEBUGF("recv: SETTINGS\n"); + + iframe->frame.hd.flags &= NGHTTP2_FLAG_ACK; + + if ((iframe->frame.hd.length % NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH) || + ((iframe->frame.hd.flags & NGHTTP2_FLAG_ACK) && + iframe->payloadleft > 0)) { + busy = 1; + iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; + break; + } + /* Check the settings flood counter early to be safe */ if (session->obq_flood_counter_ >= session->max_outbound_ack && !(iframe->frame.hd.flags & NGHTTP2_FLAG_ACK)) { return NGHTTP2_ERR_FLOODED; } - iframe->state = NGHTTP2_IB_READ_SETTINGS; - - if (iframe->payloadleft) { - nghttp2_settings_entry *min_header_table_size_entry; - - /* We allocate iv with additional one entry, to store the - minimum header table size. */ - iframe->max_niv = - iframe->frame.hd.length / NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH + 1; - + iframe->state = NGHTTP2_IB_READ_SETTINGS; + + if (iframe->payloadleft) { + nghttp2_settings_entry *min_header_table_size_entry; + + /* We allocate iv with additional one entry, to store the + minimum header table size. */ + iframe->max_niv = + iframe->frame.hd.length / NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH + 1; + if (iframe->max_niv - 1 > session->max_settings) { rv = nghttp2_session_terminate_session_with_reason( session, NGHTTP2_ENHANCE_YOUR_CALM, @@ -5698,155 +5698,155 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, return (ssize_t)inlen; } - iframe->iv = nghttp2_mem_malloc(mem, sizeof(nghttp2_settings_entry) * - iframe->max_niv); - - if (!iframe->iv) { - return NGHTTP2_ERR_NOMEM; - } - - min_header_table_size_entry = &iframe->iv[iframe->max_niv - 1]; - min_header_table_size_entry->settings_id = - NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; - min_header_table_size_entry->value = UINT32_MAX; - - inbound_frame_set_mark(iframe, NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH); - break; - } - - busy = 1; - - inbound_frame_set_mark(iframe, 0); - - break; - case NGHTTP2_PUSH_PROMISE: - DEBUGF("recv: PUSH_PROMISE\n"); - - iframe->frame.hd.flags &= - (NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PADDED); - - rv = inbound_frame_handle_pad(iframe, &iframe->frame.hd); - if (rv < 0) { - rv = nghttp2_session_terminate_session_with_reason( - session, NGHTTP2_PROTOCOL_ERROR, - "PUSH_PROMISE: insufficient padding space"); - if (nghttp2_is_fatal(rv)) { - return rv; - } - return (ssize_t)inlen; - } - - if (rv == 1) { - iframe->state = NGHTTP2_IB_READ_NBYTE; - break; - } - - if (iframe->payloadleft < 4) { - busy = 1; - iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; - break; - } - - iframe->state = NGHTTP2_IB_READ_NBYTE; - - inbound_frame_set_mark(iframe, 4); - - break; - case NGHTTP2_PING: - DEBUGF("recv: PING\n"); - - iframe->frame.hd.flags &= NGHTTP2_FLAG_ACK; - - if (iframe->payloadleft != 8) { - busy = 1; - iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; - break; - } - - iframe->state = NGHTTP2_IB_READ_NBYTE; - inbound_frame_set_mark(iframe, 8); - - break; - case NGHTTP2_GOAWAY: - DEBUGF("recv: GOAWAY\n"); - - iframe->frame.hd.flags = NGHTTP2_FLAG_NONE; - - if (iframe->payloadleft < 8) { - busy = 1; - iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; - break; - } - - iframe->state = NGHTTP2_IB_READ_NBYTE; - inbound_frame_set_mark(iframe, 8); - - break; - case NGHTTP2_CONTINUATION: - DEBUGF("recv: unexpected CONTINUATION\n"); - - /* Receiving CONTINUATION in this state are subject to - connection error of type PROTOCOL_ERROR */ - rv = nghttp2_session_terminate_session_with_reason( - session, NGHTTP2_PROTOCOL_ERROR, "CONTINUATION: unexpected"); - if (nghttp2_is_fatal(rv)) { - return rv; - } - - return (ssize_t)inlen; - default: - DEBUGF("recv: extension frame\n"); - - if (check_ext_type_set(session->user_recv_ext_types, - iframe->frame.hd.type)) { - if (!session->callbacks.unpack_extension_callback) { - /* Silently ignore unknown frame type. */ - - busy = 1; - - iframe->state = NGHTTP2_IB_IGN_PAYLOAD; - - break; - } - - busy = 1; - - iframe->state = NGHTTP2_IB_READ_EXTENSION_PAYLOAD; - - break; - } else { - switch (iframe->frame.hd.type) { - case NGHTTP2_ALTSVC: - if ((session->builtin_recv_ext_types & NGHTTP2_TYPEMASK_ALTSVC) == - 0) { - busy = 1; - iframe->state = NGHTTP2_IB_IGN_PAYLOAD; - break; - } - - DEBUGF("recv: ALTSVC\n"); - - iframe->frame.hd.flags = NGHTTP2_FLAG_NONE; - iframe->frame.ext.payload = &iframe->ext_frame_payload.altsvc; - - if (session->server) { - busy = 1; - iframe->state = NGHTTP2_IB_IGN_PAYLOAD; - break; - } - - if (iframe->payloadleft < 2) { - busy = 1; - iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; - break; - } - - busy = 1; - - iframe->state = NGHTTP2_IB_READ_NBYTE; - inbound_frame_set_mark(iframe, 2); - - break; + iframe->iv = nghttp2_mem_malloc(mem, sizeof(nghttp2_settings_entry) * + iframe->max_niv); + + if (!iframe->iv) { + return NGHTTP2_ERR_NOMEM; + } + + min_header_table_size_entry = &iframe->iv[iframe->max_niv - 1]; + min_header_table_size_entry->settings_id = + NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; + min_header_table_size_entry->value = UINT32_MAX; + + inbound_frame_set_mark(iframe, NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH); + break; + } + + busy = 1; + + inbound_frame_set_mark(iframe, 0); + + break; + case NGHTTP2_PUSH_PROMISE: + DEBUGF("recv: PUSH_PROMISE\n"); + + iframe->frame.hd.flags &= + (NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PADDED); + + rv = inbound_frame_handle_pad(iframe, &iframe->frame.hd); + if (rv < 0) { + rv = nghttp2_session_terminate_session_with_reason( + session, NGHTTP2_PROTOCOL_ERROR, + "PUSH_PROMISE: insufficient padding space"); + if (nghttp2_is_fatal(rv)) { + return rv; + } + return (ssize_t)inlen; + } + + if (rv == 1) { + iframe->state = NGHTTP2_IB_READ_NBYTE; + break; + } + + if (iframe->payloadleft < 4) { + busy = 1; + iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; + break; + } + + iframe->state = NGHTTP2_IB_READ_NBYTE; + + inbound_frame_set_mark(iframe, 4); + + break; + case NGHTTP2_PING: + DEBUGF("recv: PING\n"); + + iframe->frame.hd.flags &= NGHTTP2_FLAG_ACK; + + if (iframe->payloadleft != 8) { + busy = 1; + iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; + break; + } + + iframe->state = NGHTTP2_IB_READ_NBYTE; + inbound_frame_set_mark(iframe, 8); + + break; + case NGHTTP2_GOAWAY: + DEBUGF("recv: GOAWAY\n"); + + iframe->frame.hd.flags = NGHTTP2_FLAG_NONE; + + if (iframe->payloadleft < 8) { + busy = 1; + iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; + break; + } + + iframe->state = NGHTTP2_IB_READ_NBYTE; + inbound_frame_set_mark(iframe, 8); + + break; + case NGHTTP2_CONTINUATION: + DEBUGF("recv: unexpected CONTINUATION\n"); + + /* Receiving CONTINUATION in this state are subject to + connection error of type PROTOCOL_ERROR */ + rv = nghttp2_session_terminate_session_with_reason( + session, NGHTTP2_PROTOCOL_ERROR, "CONTINUATION: unexpected"); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + return (ssize_t)inlen; + default: + DEBUGF("recv: extension frame\n"); + + if (check_ext_type_set(session->user_recv_ext_types, + iframe->frame.hd.type)) { + if (!session->callbacks.unpack_extension_callback) { + /* Silently ignore unknown frame type. */ + + busy = 1; + + iframe->state = NGHTTP2_IB_IGN_PAYLOAD; + + break; + } + + busy = 1; + + iframe->state = NGHTTP2_IB_READ_EXTENSION_PAYLOAD; + + break; + } else { + switch (iframe->frame.hd.type) { + case NGHTTP2_ALTSVC: + if ((session->builtin_recv_ext_types & NGHTTP2_TYPEMASK_ALTSVC) == + 0) { + busy = 1; + iframe->state = NGHTTP2_IB_IGN_PAYLOAD; + break; + } + + DEBUGF("recv: ALTSVC\n"); + + iframe->frame.hd.flags = NGHTTP2_FLAG_NONE; + iframe->frame.ext.payload = &iframe->ext_frame_payload.altsvc; + + if (session->server) { + busy = 1; + iframe->state = NGHTTP2_IB_IGN_PAYLOAD; + break; + } + + if (iframe->payloadleft < 2) { + busy = 1; + iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; + break; + } + + busy = 1; + + iframe->state = NGHTTP2_IB_READ_NBYTE; + inbound_frame_set_mark(iframe, 2); + + break; case NGHTTP2_ORIGIN: if (!(session->builtin_recv_ext_types & NGHTTP2_TYPEMASK_ORIGIN)) { busy = 1; @@ -5883,866 +5883,866 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, iframe->state = NGHTTP2_IB_READ_ORIGIN_PAYLOAD; break; - default: - busy = 1; - - iframe->state = NGHTTP2_IB_IGN_PAYLOAD; - - break; - } - } - } - - if (!on_begin_frame_called) { - switch (iframe->state) { - case NGHTTP2_IB_IGN_HEADER_BLOCK: - case NGHTTP2_IB_IGN_PAYLOAD: - case NGHTTP2_IB_FRAME_SIZE_ERROR: - case NGHTTP2_IB_IGN_DATA: - case NGHTTP2_IB_IGN_ALL: - break; - default: - rv = session_call_on_begin_frame(session, &iframe->frame.hd); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - } - } - - break; - } - case NGHTTP2_IB_READ_NBYTE: - DEBUGF("recv: [IB_READ_NBYTE]\n"); - - readlen = inbound_frame_buf_read(iframe, in, last); - in += readlen; - iframe->payloadleft -= readlen; - - DEBUGF("recv: readlen=%zu, payloadleft=%zu, left=%zd\n", readlen, - iframe->payloadleft, nghttp2_buf_mark_avail(&iframe->sbuf)); - - if (nghttp2_buf_mark_avail(&iframe->sbuf)) { - return in - first; - } - - switch (iframe->frame.hd.type) { - case NGHTTP2_HEADERS: - if (iframe->padlen == 0 && - (iframe->frame.hd.flags & NGHTTP2_FLAG_PADDED)) { - pri_fieldlen = nghttp2_frame_priority_len(iframe->frame.hd.flags); - padlen = inbound_frame_compute_pad(iframe); - if (padlen < 0 || - (size_t)padlen + pri_fieldlen > 1 + iframe->payloadleft) { - rv = nghttp2_session_terminate_session_with_reason( - session, NGHTTP2_PROTOCOL_ERROR, "HEADERS: invalid padding"); - if (nghttp2_is_fatal(rv)) { - return rv; - } - return (ssize_t)inlen; - } - iframe->frame.headers.padlen = (size_t)padlen; - - if (pri_fieldlen > 0) { - if (iframe->payloadleft < pri_fieldlen) { - busy = 1; - iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; - break; - } - iframe->state = NGHTTP2_IB_READ_NBYTE; - inbound_frame_set_mark(iframe, pri_fieldlen); - break; - } else { - /* Truncate buffers used for padding spec */ - inbound_frame_set_mark(iframe, 0); - } - } - - rv = session_process_headers_frame(session); - if (nghttp2_is_fatal(rv)) { - return rv; - } - - busy = 1; - - if (iframe->state == NGHTTP2_IB_IGN_ALL) { - return (ssize_t)inlen; - } - - if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { - rv = nghttp2_session_add_rst_stream( - session, iframe->frame.hd.stream_id, NGHTTP2_INTERNAL_ERROR); - if (nghttp2_is_fatal(rv)) { - return rv; - } - iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; - break; - } - - if (rv == NGHTTP2_ERR_IGN_HEADER_BLOCK) { - iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; - break; - } - - iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK; - - break; - case NGHTTP2_PRIORITY: - rv = session_process_priority_frame(session); - if (nghttp2_is_fatal(rv)) { - return rv; - } - - if (iframe->state == NGHTTP2_IB_IGN_ALL) { - return (ssize_t)inlen; - } - - session_inbound_frame_reset(session); - - break; - case NGHTTP2_RST_STREAM: - rv = session_process_rst_stream_frame(session); - if (nghttp2_is_fatal(rv)) { - return rv; - } - - if (iframe->state == NGHTTP2_IB_IGN_ALL) { - return (ssize_t)inlen; - } - - session_inbound_frame_reset(session); - - break; - case NGHTTP2_PUSH_PROMISE: - if (iframe->padlen == 0 && - (iframe->frame.hd.flags & NGHTTP2_FLAG_PADDED)) { - padlen = inbound_frame_compute_pad(iframe); - if (padlen < 0 || (size_t)padlen + 4 /* promised stream id */ - > 1 + iframe->payloadleft) { - rv = nghttp2_session_terminate_session_with_reason( - session, NGHTTP2_PROTOCOL_ERROR, - "PUSH_PROMISE: invalid padding"); - if (nghttp2_is_fatal(rv)) { - return rv; - } - return (ssize_t)inlen; - } - - iframe->frame.push_promise.padlen = (size_t)padlen; - - if (iframe->payloadleft < 4) { - busy = 1; - iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; - break; - } - - iframe->state = NGHTTP2_IB_READ_NBYTE; - - inbound_frame_set_mark(iframe, 4); - - break; - } - - rv = session_process_push_promise_frame(session); - if (nghttp2_is_fatal(rv)) { - return rv; - } - - busy = 1; - - if (iframe->state == NGHTTP2_IB_IGN_ALL) { - return (ssize_t)inlen; - } - - if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { - rv = nghttp2_session_add_rst_stream( - session, iframe->frame.push_promise.promised_stream_id, - NGHTTP2_INTERNAL_ERROR); - if (nghttp2_is_fatal(rv)) { - return rv; - } - iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; - break; - } - - if (rv == NGHTTP2_ERR_IGN_HEADER_BLOCK) { - iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; - break; - } - - iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK; - - break; - case NGHTTP2_PING: - rv = session_process_ping_frame(session); - if (nghttp2_is_fatal(rv)) { - return rv; - } - - if (iframe->state == NGHTTP2_IB_IGN_ALL) { - return (ssize_t)inlen; - } - - session_inbound_frame_reset(session); - - break; - case NGHTTP2_GOAWAY: { - size_t debuglen; - - /* 8 is Last-stream-ID + Error Code */ - debuglen = iframe->frame.hd.length - 8; - - if (debuglen > 0) { - iframe->raw_lbuf = nghttp2_mem_malloc(mem, debuglen); - - if (iframe->raw_lbuf == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - nghttp2_buf_wrap_init(&iframe->lbuf, iframe->raw_lbuf, debuglen); - } - - busy = 1; - - iframe->state = NGHTTP2_IB_READ_GOAWAY_DEBUG; - - break; - } - case NGHTTP2_WINDOW_UPDATE: - rv = session_process_window_update_frame(session); - if (nghttp2_is_fatal(rv)) { - return rv; - } - - if (iframe->state == NGHTTP2_IB_IGN_ALL) { - return (ssize_t)inlen; - } - - session_inbound_frame_reset(session); - - break; - case NGHTTP2_ALTSVC: { - size_t origin_len; - - origin_len = nghttp2_get_uint16(iframe->sbuf.pos); - - DEBUGF("recv: origin_len=%zu\n", origin_len); - - if (origin_len > iframe->payloadleft) { - busy = 1; - iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; - break; - } - - if (iframe->frame.hd.length > 2) { - iframe->raw_lbuf = - nghttp2_mem_malloc(mem, iframe->frame.hd.length - 2); - - if (iframe->raw_lbuf == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - nghttp2_buf_wrap_init(&iframe->lbuf, iframe->raw_lbuf, - iframe->frame.hd.length); - } - - busy = 1; - - iframe->state = NGHTTP2_IB_READ_ALTSVC_PAYLOAD; - - break; - } - default: - /* This is unknown frame */ - session_inbound_frame_reset(session); - - break; - } - break; - case NGHTTP2_IB_READ_HEADER_BLOCK: - case NGHTTP2_IB_IGN_HEADER_BLOCK: { - ssize_t data_readlen; - size_t trail_padlen; - int final; -#ifdef DEBUGBUILD - if (iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK) { - DEBUGF("recv: [IB_READ_HEADER_BLOCK]\n"); - } else { - DEBUGF("recv: [IB_IGN_HEADER_BLOCK]\n"); - } -#endif /* DEBUGBUILD */ - - readlen = inbound_frame_payload_readlen(iframe, in, last); - - DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen, - iframe->payloadleft - readlen); - - data_readlen = inbound_frame_effective_readlen( - iframe, iframe->payloadleft - readlen, readlen); - - if (data_readlen == -1) { - /* everything is padding */ - data_readlen = 0; - } - - trail_padlen = nghttp2_frame_trail_padlen(&iframe->frame, iframe->padlen); - - final = (iframe->frame.hd.flags & NGHTTP2_FLAG_END_HEADERS) && - iframe->payloadleft - (size_t)data_readlen == trail_padlen; - - if (data_readlen > 0 || (data_readlen == 0 && final)) { - size_t hd_proclen = 0; - - DEBUGF("recv: block final=%d\n", final); - - rv = - inflate_header_block(session, &iframe->frame, &hd_proclen, - (uint8_t *)in, (size_t)data_readlen, final, - iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - - if (iframe->state == NGHTTP2_IB_IGN_ALL) { - return (ssize_t)inlen; - } - - if (rv == NGHTTP2_ERR_PAUSE) { - in += hd_proclen; - iframe->payloadleft -= hd_proclen; - - return in - first; - } - - if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { - /* The application says no more headers. We decompress the - rest of the header block but not invoke on_header_callback - and on_frame_recv_callback. */ - in += hd_proclen; - iframe->payloadleft -= hd_proclen; - - /* Use promised stream ID for PUSH_PROMISE */ - rv = nghttp2_session_add_rst_stream( - session, - iframe->frame.hd.type == NGHTTP2_PUSH_PROMISE - ? iframe->frame.push_promise.promised_stream_id - : iframe->frame.hd.stream_id, - NGHTTP2_INTERNAL_ERROR); - if (nghttp2_is_fatal(rv)) { - return rv; - } - busy = 1; - iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; - break; - } - - in += readlen; - iframe->payloadleft -= readlen; - - if (rv == NGHTTP2_ERR_HEADER_COMP) { - /* GOAWAY is already issued */ - if (iframe->payloadleft == 0) { - session_inbound_frame_reset(session); - } else { - busy = 1; - iframe->state = NGHTTP2_IB_IGN_PAYLOAD; - } - break; - } - } else { - in += readlen; - iframe->payloadleft -= readlen; - } - - if (iframe->payloadleft) { - break; - } - - if ((iframe->frame.hd.flags & NGHTTP2_FLAG_END_HEADERS) == 0) { - - inbound_frame_set_mark(iframe, NGHTTP2_FRAME_HDLEN); - - iframe->padlen = 0; - - if (iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK) { - iframe->state = NGHTTP2_IB_EXPECT_CONTINUATION; - } else { - iframe->state = NGHTTP2_IB_IGN_CONTINUATION; - } - } else { - if (iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK) { - rv = session_after_header_block_received(session); - if (nghttp2_is_fatal(rv)) { - return rv; - } - } - session_inbound_frame_reset(session); - } - break; - } - case NGHTTP2_IB_IGN_PAYLOAD: - DEBUGF("recv: [IB_IGN_PAYLOAD]\n"); - - readlen = inbound_frame_payload_readlen(iframe, in, last); - iframe->payloadleft -= readlen; - in += readlen; - - DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen, - iframe->payloadleft); - - if (iframe->payloadleft) { - break; - } - - switch (iframe->frame.hd.type) { - case NGHTTP2_HEADERS: - case NGHTTP2_PUSH_PROMISE: - case NGHTTP2_CONTINUATION: - /* Mark inflater bad so that we won't perform further decoding */ - session->hd_inflater.ctx.bad = 1; - break; - default: - break; - } - - session_inbound_frame_reset(session); - - break; - case NGHTTP2_IB_FRAME_SIZE_ERROR: - DEBUGF("recv: [IB_FRAME_SIZE_ERROR]\n"); - - rv = session_handle_frame_size_error(session); - if (nghttp2_is_fatal(rv)) { - return rv; - } - - assert(iframe->state == NGHTTP2_IB_IGN_ALL); - - return (ssize_t)inlen; - case NGHTTP2_IB_READ_SETTINGS: - DEBUGF("recv: [IB_READ_SETTINGS]\n"); - - readlen = inbound_frame_buf_read(iframe, in, last); - iframe->payloadleft -= readlen; - in += readlen; - - DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen, - iframe->payloadleft); - - if (nghttp2_buf_mark_avail(&iframe->sbuf)) { - break; - } - - if (readlen > 0) { - inbound_frame_set_settings_entry(iframe); - } - if (iframe->payloadleft) { - inbound_frame_set_mark(iframe, NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH); - break; - } - - rv = session_process_settings_frame(session); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - - if (iframe->state == NGHTTP2_IB_IGN_ALL) { - return (ssize_t)inlen; - } - - session_inbound_frame_reset(session); - - break; - case NGHTTP2_IB_READ_GOAWAY_DEBUG: - DEBUGF("recv: [IB_READ_GOAWAY_DEBUG]\n"); - - readlen = inbound_frame_payload_readlen(iframe, in, last); - - if (readlen > 0) { - iframe->lbuf.last = nghttp2_cpymem(iframe->lbuf.last, in, readlen); - - iframe->payloadleft -= readlen; - in += readlen; - } - - DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen, - iframe->payloadleft); - - if (iframe->payloadleft) { - assert(nghttp2_buf_avail(&iframe->lbuf) > 0); - - break; - } - - rv = session_process_goaway_frame(session); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - - if (iframe->state == NGHTTP2_IB_IGN_ALL) { - return (ssize_t)inlen; - } - - session_inbound_frame_reset(session); - - break; - case NGHTTP2_IB_EXPECT_CONTINUATION: - case NGHTTP2_IB_IGN_CONTINUATION: -#ifdef DEBUGBUILD - if (iframe->state == NGHTTP2_IB_EXPECT_CONTINUATION) { - fprintf(stderr, "recv: [IB_EXPECT_CONTINUATION]\n"); - } else { - fprintf(stderr, "recv: [IB_IGN_CONTINUATION]\n"); - } -#endif /* DEBUGBUILD */ - - readlen = inbound_frame_buf_read(iframe, in, last); - in += readlen; - - if (nghttp2_buf_mark_avail(&iframe->sbuf)) { - return in - first; - } - - nghttp2_frame_unpack_frame_hd(&cont_hd, iframe->sbuf.pos); - iframe->payloadleft = cont_hd.length; - - DEBUGF("recv: payloadlen=%zu, type=%u, flags=0x%02x, stream_id=%d\n", - cont_hd.length, cont_hd.type, cont_hd.flags, cont_hd.stream_id); - - if (cont_hd.type != NGHTTP2_CONTINUATION || - cont_hd.stream_id != iframe->frame.hd.stream_id) { - DEBUGF("recv: expected stream_id=%d, type=%d, but got stream_id=%d, " - "type=%u\n", - iframe->frame.hd.stream_id, NGHTTP2_CONTINUATION, - cont_hd.stream_id, cont_hd.type); - rv = nghttp2_session_terminate_session_with_reason( - session, NGHTTP2_PROTOCOL_ERROR, - "unexpected non-CONTINUATION frame or stream_id is invalid"); - if (nghttp2_is_fatal(rv)) { - return rv; - } - - return (ssize_t)inlen; - } - - /* CONTINUATION won't bear NGHTTP2_PADDED flag */ - + default: + busy = 1; + + iframe->state = NGHTTP2_IB_IGN_PAYLOAD; + + break; + } + } + } + + if (!on_begin_frame_called) { + switch (iframe->state) { + case NGHTTP2_IB_IGN_HEADER_BLOCK: + case NGHTTP2_IB_IGN_PAYLOAD: + case NGHTTP2_IB_FRAME_SIZE_ERROR: + case NGHTTP2_IB_IGN_DATA: + case NGHTTP2_IB_IGN_ALL: + break; + default: + rv = session_call_on_begin_frame(session, &iframe->frame.hd); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + } + + break; + } + case NGHTTP2_IB_READ_NBYTE: + DEBUGF("recv: [IB_READ_NBYTE]\n"); + + readlen = inbound_frame_buf_read(iframe, in, last); + in += readlen; + iframe->payloadleft -= readlen; + + DEBUGF("recv: readlen=%zu, payloadleft=%zu, left=%zd\n", readlen, + iframe->payloadleft, nghttp2_buf_mark_avail(&iframe->sbuf)); + + if (nghttp2_buf_mark_avail(&iframe->sbuf)) { + return in - first; + } + + switch (iframe->frame.hd.type) { + case NGHTTP2_HEADERS: + if (iframe->padlen == 0 && + (iframe->frame.hd.flags & NGHTTP2_FLAG_PADDED)) { + pri_fieldlen = nghttp2_frame_priority_len(iframe->frame.hd.flags); + padlen = inbound_frame_compute_pad(iframe); + if (padlen < 0 || + (size_t)padlen + pri_fieldlen > 1 + iframe->payloadleft) { + rv = nghttp2_session_terminate_session_with_reason( + session, NGHTTP2_PROTOCOL_ERROR, "HEADERS: invalid padding"); + if (nghttp2_is_fatal(rv)) { + return rv; + } + return (ssize_t)inlen; + } + iframe->frame.headers.padlen = (size_t)padlen; + + if (pri_fieldlen > 0) { + if (iframe->payloadleft < pri_fieldlen) { + busy = 1; + iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; + break; + } + iframe->state = NGHTTP2_IB_READ_NBYTE; + inbound_frame_set_mark(iframe, pri_fieldlen); + break; + } else { + /* Truncate buffers used for padding spec */ + inbound_frame_set_mark(iframe, 0); + } + } + + rv = session_process_headers_frame(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + busy = 1; + + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (ssize_t)inlen; + } + + if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { + rv = nghttp2_session_add_rst_stream( + session, iframe->frame.hd.stream_id, NGHTTP2_INTERNAL_ERROR); + if (nghttp2_is_fatal(rv)) { + return rv; + } + iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; + break; + } + + if (rv == NGHTTP2_ERR_IGN_HEADER_BLOCK) { + iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; + break; + } + + iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK; + + break; + case NGHTTP2_PRIORITY: + rv = session_process_priority_frame(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (ssize_t)inlen; + } + + session_inbound_frame_reset(session); + + break; + case NGHTTP2_RST_STREAM: + rv = session_process_rst_stream_frame(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (ssize_t)inlen; + } + + session_inbound_frame_reset(session); + + break; + case NGHTTP2_PUSH_PROMISE: + if (iframe->padlen == 0 && + (iframe->frame.hd.flags & NGHTTP2_FLAG_PADDED)) { + padlen = inbound_frame_compute_pad(iframe); + if (padlen < 0 || (size_t)padlen + 4 /* promised stream id */ + > 1 + iframe->payloadleft) { + rv = nghttp2_session_terminate_session_with_reason( + session, NGHTTP2_PROTOCOL_ERROR, + "PUSH_PROMISE: invalid padding"); + if (nghttp2_is_fatal(rv)) { + return rv; + } + return (ssize_t)inlen; + } + + iframe->frame.push_promise.padlen = (size_t)padlen; + + if (iframe->payloadleft < 4) { + busy = 1; + iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; + break; + } + + iframe->state = NGHTTP2_IB_READ_NBYTE; + + inbound_frame_set_mark(iframe, 4); + + break; + } + + rv = session_process_push_promise_frame(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + busy = 1; + + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (ssize_t)inlen; + } + + if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { + rv = nghttp2_session_add_rst_stream( + session, iframe->frame.push_promise.promised_stream_id, + NGHTTP2_INTERNAL_ERROR); + if (nghttp2_is_fatal(rv)) { + return rv; + } + iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; + break; + } + + if (rv == NGHTTP2_ERR_IGN_HEADER_BLOCK) { + iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; + break; + } + + iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK; + + break; + case NGHTTP2_PING: + rv = session_process_ping_frame(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (ssize_t)inlen; + } + + session_inbound_frame_reset(session); + + break; + case NGHTTP2_GOAWAY: { + size_t debuglen; + + /* 8 is Last-stream-ID + Error Code */ + debuglen = iframe->frame.hd.length - 8; + + if (debuglen > 0) { + iframe->raw_lbuf = nghttp2_mem_malloc(mem, debuglen); + + if (iframe->raw_lbuf == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + nghttp2_buf_wrap_init(&iframe->lbuf, iframe->raw_lbuf, debuglen); + } + + busy = 1; + + iframe->state = NGHTTP2_IB_READ_GOAWAY_DEBUG; + + break; + } + case NGHTTP2_WINDOW_UPDATE: + rv = session_process_window_update_frame(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (ssize_t)inlen; + } + + session_inbound_frame_reset(session); + + break; + case NGHTTP2_ALTSVC: { + size_t origin_len; + + origin_len = nghttp2_get_uint16(iframe->sbuf.pos); + + DEBUGF("recv: origin_len=%zu\n", origin_len); + + if (origin_len > iframe->payloadleft) { + busy = 1; + iframe->state = NGHTTP2_IB_FRAME_SIZE_ERROR; + break; + } + + if (iframe->frame.hd.length > 2) { + iframe->raw_lbuf = + nghttp2_mem_malloc(mem, iframe->frame.hd.length - 2); + + if (iframe->raw_lbuf == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + nghttp2_buf_wrap_init(&iframe->lbuf, iframe->raw_lbuf, + iframe->frame.hd.length); + } + + busy = 1; + + iframe->state = NGHTTP2_IB_READ_ALTSVC_PAYLOAD; + + break; + } + default: + /* This is unknown frame */ + session_inbound_frame_reset(session); + + break; + } + break; + case NGHTTP2_IB_READ_HEADER_BLOCK: + case NGHTTP2_IB_IGN_HEADER_BLOCK: { + ssize_t data_readlen; + size_t trail_padlen; + int final; +#ifdef DEBUGBUILD + if (iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK) { + DEBUGF("recv: [IB_READ_HEADER_BLOCK]\n"); + } else { + DEBUGF("recv: [IB_IGN_HEADER_BLOCK]\n"); + } +#endif /* DEBUGBUILD */ + + readlen = inbound_frame_payload_readlen(iframe, in, last); + + DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen, + iframe->payloadleft - readlen); + + data_readlen = inbound_frame_effective_readlen( + iframe, iframe->payloadleft - readlen, readlen); + + if (data_readlen == -1) { + /* everything is padding */ + data_readlen = 0; + } + + trail_padlen = nghttp2_frame_trail_padlen(&iframe->frame, iframe->padlen); + + final = (iframe->frame.hd.flags & NGHTTP2_FLAG_END_HEADERS) && + iframe->payloadleft - (size_t)data_readlen == trail_padlen; + + if (data_readlen > 0 || (data_readlen == 0 && final)) { + size_t hd_proclen = 0; + + DEBUGF("recv: block final=%d\n", final); + + rv = + inflate_header_block(session, &iframe->frame, &hd_proclen, + (uint8_t *)in, (size_t)data_readlen, final, + iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (ssize_t)inlen; + } + + if (rv == NGHTTP2_ERR_PAUSE) { + in += hd_proclen; + iframe->payloadleft -= hd_proclen; + + return in - first; + } + + if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { + /* The application says no more headers. We decompress the + rest of the header block but not invoke on_header_callback + and on_frame_recv_callback. */ + in += hd_proclen; + iframe->payloadleft -= hd_proclen; + + /* Use promised stream ID for PUSH_PROMISE */ + rv = nghttp2_session_add_rst_stream( + session, + iframe->frame.hd.type == NGHTTP2_PUSH_PROMISE + ? iframe->frame.push_promise.promised_stream_id + : iframe->frame.hd.stream_id, + NGHTTP2_INTERNAL_ERROR); + if (nghttp2_is_fatal(rv)) { + return rv; + } + busy = 1; + iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; + break; + } + + in += readlen; + iframe->payloadleft -= readlen; + + if (rv == NGHTTP2_ERR_HEADER_COMP) { + /* GOAWAY is already issued */ + if (iframe->payloadleft == 0) { + session_inbound_frame_reset(session); + } else { + busy = 1; + iframe->state = NGHTTP2_IB_IGN_PAYLOAD; + } + break; + } + } else { + in += readlen; + iframe->payloadleft -= readlen; + } + + if (iframe->payloadleft) { + break; + } + + if ((iframe->frame.hd.flags & NGHTTP2_FLAG_END_HEADERS) == 0) { + + inbound_frame_set_mark(iframe, NGHTTP2_FRAME_HDLEN); + + iframe->padlen = 0; + + if (iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK) { + iframe->state = NGHTTP2_IB_EXPECT_CONTINUATION; + } else { + iframe->state = NGHTTP2_IB_IGN_CONTINUATION; + } + } else { + if (iframe->state == NGHTTP2_IB_READ_HEADER_BLOCK) { + rv = session_after_header_block_received(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + session_inbound_frame_reset(session); + } + break; + } + case NGHTTP2_IB_IGN_PAYLOAD: + DEBUGF("recv: [IB_IGN_PAYLOAD]\n"); + + readlen = inbound_frame_payload_readlen(iframe, in, last); + iframe->payloadleft -= readlen; + in += readlen; + + DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen, + iframe->payloadleft); + + if (iframe->payloadleft) { + break; + } + + switch (iframe->frame.hd.type) { + case NGHTTP2_HEADERS: + case NGHTTP2_PUSH_PROMISE: + case NGHTTP2_CONTINUATION: + /* Mark inflater bad so that we won't perform further decoding */ + session->hd_inflater.ctx.bad = 1; + break; + default: + break; + } + + session_inbound_frame_reset(session); + + break; + case NGHTTP2_IB_FRAME_SIZE_ERROR: + DEBUGF("recv: [IB_FRAME_SIZE_ERROR]\n"); + + rv = session_handle_frame_size_error(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + assert(iframe->state == NGHTTP2_IB_IGN_ALL); + + return (ssize_t)inlen; + case NGHTTP2_IB_READ_SETTINGS: + DEBUGF("recv: [IB_READ_SETTINGS]\n"); + + readlen = inbound_frame_buf_read(iframe, in, last); + iframe->payloadleft -= readlen; + in += readlen; + + DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen, + iframe->payloadleft); + + if (nghttp2_buf_mark_avail(&iframe->sbuf)) { + break; + } + + if (readlen > 0) { + inbound_frame_set_settings_entry(iframe); + } + if (iframe->payloadleft) { + inbound_frame_set_mark(iframe, NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH); + break; + } + + rv = session_process_settings_frame(session); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (ssize_t)inlen; + } + + session_inbound_frame_reset(session); + + break; + case NGHTTP2_IB_READ_GOAWAY_DEBUG: + DEBUGF("recv: [IB_READ_GOAWAY_DEBUG]\n"); + + readlen = inbound_frame_payload_readlen(iframe, in, last); + + if (readlen > 0) { + iframe->lbuf.last = nghttp2_cpymem(iframe->lbuf.last, in, readlen); + + iframe->payloadleft -= readlen; + in += readlen; + } + + DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen, + iframe->payloadleft); + + if (iframe->payloadleft) { + assert(nghttp2_buf_avail(&iframe->lbuf) > 0); + + break; + } + + rv = session_process_goaway_frame(session); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (ssize_t)inlen; + } + + session_inbound_frame_reset(session); + + break; + case NGHTTP2_IB_EXPECT_CONTINUATION: + case NGHTTP2_IB_IGN_CONTINUATION: +#ifdef DEBUGBUILD + if (iframe->state == NGHTTP2_IB_EXPECT_CONTINUATION) { + fprintf(stderr, "recv: [IB_EXPECT_CONTINUATION]\n"); + } else { + fprintf(stderr, "recv: [IB_IGN_CONTINUATION]\n"); + } +#endif /* DEBUGBUILD */ + + readlen = inbound_frame_buf_read(iframe, in, last); + in += readlen; + + if (nghttp2_buf_mark_avail(&iframe->sbuf)) { + return in - first; + } + + nghttp2_frame_unpack_frame_hd(&cont_hd, iframe->sbuf.pos); + iframe->payloadleft = cont_hd.length; + + DEBUGF("recv: payloadlen=%zu, type=%u, flags=0x%02x, stream_id=%d\n", + cont_hd.length, cont_hd.type, cont_hd.flags, cont_hd.stream_id); + + if (cont_hd.type != NGHTTP2_CONTINUATION || + cont_hd.stream_id != iframe->frame.hd.stream_id) { + DEBUGF("recv: expected stream_id=%d, type=%d, but got stream_id=%d, " + "type=%u\n", + iframe->frame.hd.stream_id, NGHTTP2_CONTINUATION, + cont_hd.stream_id, cont_hd.type); + rv = nghttp2_session_terminate_session_with_reason( + session, NGHTTP2_PROTOCOL_ERROR, + "unexpected non-CONTINUATION frame or stream_id is invalid"); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + return (ssize_t)inlen; + } + + /* CONTINUATION won't bear NGHTTP2_PADDED flag */ + iframe->frame.hd.flags = (uint8_t)(iframe->frame.hd.flags | (cont_hd.flags & NGHTTP2_FLAG_END_HEADERS)); - iframe->frame.hd.length += cont_hd.length; - - busy = 1; - - if (iframe->state == NGHTTP2_IB_EXPECT_CONTINUATION) { - iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK; - - rv = session_call_on_begin_frame(session, &cont_hd); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - } else { - iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; - } - - break; - case NGHTTP2_IB_READ_PAD_DATA: - DEBUGF("recv: [IB_READ_PAD_DATA]\n"); - - readlen = inbound_frame_buf_read(iframe, in, last); - in += readlen; - iframe->payloadleft -= readlen; - - DEBUGF("recv: readlen=%zu, payloadleft=%zu, left=%zu\n", readlen, - iframe->payloadleft, nghttp2_buf_mark_avail(&iframe->sbuf)); - - if (nghttp2_buf_mark_avail(&iframe->sbuf)) { - return in - first; - } - - /* Pad Length field is subject to flow control */ + iframe->frame.hd.length += cont_hd.length; + + busy = 1; + + if (iframe->state == NGHTTP2_IB_EXPECT_CONTINUATION) { + iframe->state = NGHTTP2_IB_READ_HEADER_BLOCK; + + rv = session_call_on_begin_frame(session, &cont_hd); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + } else { + iframe->state = NGHTTP2_IB_IGN_HEADER_BLOCK; + } + + break; + case NGHTTP2_IB_READ_PAD_DATA: + DEBUGF("recv: [IB_READ_PAD_DATA]\n"); + + readlen = inbound_frame_buf_read(iframe, in, last); + in += readlen; + iframe->payloadleft -= readlen; + + DEBUGF("recv: readlen=%zu, payloadleft=%zu, left=%zu\n", readlen, + iframe->payloadleft, nghttp2_buf_mark_avail(&iframe->sbuf)); + + if (nghttp2_buf_mark_avail(&iframe->sbuf)) { + return in - first; + } + + /* Pad Length field is subject to flow control */ rv = nghttp2_session_update_recv_connection_window_size(session, readlen); - if (nghttp2_is_fatal(rv)) { - return rv; - } - - if (iframe->state == NGHTTP2_IB_IGN_ALL) { - return (ssize_t)inlen; - } - - /* Pad Length field is consumed immediately */ - rv = - nghttp2_session_consume(session, iframe->frame.hd.stream_id, readlen); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - - if (iframe->state == NGHTTP2_IB_IGN_ALL) { - return (ssize_t)inlen; - } - - stream = nghttp2_session_get_stream(session, iframe->frame.hd.stream_id); - if (stream) { + if (nghttp2_is_fatal(rv)) { + return rv; + } + + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (ssize_t)inlen; + } + + /* Pad Length field is consumed immediately */ + rv = + nghttp2_session_consume(session, iframe->frame.hd.stream_id, readlen); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (ssize_t)inlen; + } + + stream = nghttp2_session_get_stream(session, iframe->frame.hd.stream_id); + if (stream) { rv = nghttp2_session_update_recv_stream_window_size( - session, stream, readlen, - iframe->payloadleft || - (iframe->frame.hd.flags & NGHTTP2_FLAG_END_STREAM) == 0); - if (nghttp2_is_fatal(rv)) { - return rv; - } - } - - busy = 1; - - padlen = inbound_frame_compute_pad(iframe); - if (padlen < 0) { - rv = nghttp2_session_terminate_session_with_reason( - session, NGHTTP2_PROTOCOL_ERROR, "DATA: invalid padding"); - if (nghttp2_is_fatal(rv)) { - return rv; - } - return (ssize_t)inlen; - } - - iframe->frame.data.padlen = (size_t)padlen; - - iframe->state = NGHTTP2_IB_READ_DATA; - - break; - case NGHTTP2_IB_READ_DATA: - stream = nghttp2_session_get_stream(session, iframe->frame.hd.stream_id); - - if (!stream) { - busy = 1; - iframe->state = NGHTTP2_IB_IGN_DATA; - break; - } - - DEBUGF("recv: [IB_READ_DATA]\n"); - - readlen = inbound_frame_payload_readlen(iframe, in, last); - iframe->payloadleft -= readlen; - in += readlen; - - DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen, - iframe->payloadleft); - - if (readlen > 0) { - ssize_t data_readlen; - + session, stream, readlen, + iframe->payloadleft || + (iframe->frame.hd.flags & NGHTTP2_FLAG_END_STREAM) == 0); + if (nghttp2_is_fatal(rv)) { + return rv; + } + } + + busy = 1; + + padlen = inbound_frame_compute_pad(iframe); + if (padlen < 0) { + rv = nghttp2_session_terminate_session_with_reason( + session, NGHTTP2_PROTOCOL_ERROR, "DATA: invalid padding"); + if (nghttp2_is_fatal(rv)) { + return rv; + } + return (ssize_t)inlen; + } + + iframe->frame.data.padlen = (size_t)padlen; + + iframe->state = NGHTTP2_IB_READ_DATA; + + break; + case NGHTTP2_IB_READ_DATA: + stream = nghttp2_session_get_stream(session, iframe->frame.hd.stream_id); + + if (!stream) { + busy = 1; + iframe->state = NGHTTP2_IB_IGN_DATA; + break; + } + + DEBUGF("recv: [IB_READ_DATA]\n"); + + readlen = inbound_frame_payload_readlen(iframe, in, last); + iframe->payloadleft -= readlen; + in += readlen; + + DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen, + iframe->payloadleft); + + if (readlen > 0) { + ssize_t data_readlen; + rv = nghttp2_session_update_recv_connection_window_size(session, readlen); - if (nghttp2_is_fatal(rv)) { - return rv; - } - - if (iframe->state == NGHTTP2_IB_IGN_ALL) { - return (ssize_t)inlen; - } - + if (nghttp2_is_fatal(rv)) { + return rv; + } + + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (ssize_t)inlen; + } + rv = nghttp2_session_update_recv_stream_window_size( - session, stream, readlen, - iframe->payloadleft || - (iframe->frame.hd.flags & NGHTTP2_FLAG_END_STREAM) == 0); - if (nghttp2_is_fatal(rv)) { - return rv; - } - - data_readlen = inbound_frame_effective_readlen( - iframe, iframe->payloadleft, readlen); - - if (data_readlen == -1) { - /* everything is padding */ - data_readlen = 0; - } - - padlen = (ssize_t)readlen - data_readlen; - - if (padlen > 0) { - /* Padding is considered as "consumed" immediately */ - rv = nghttp2_session_consume(session, iframe->frame.hd.stream_id, - (size_t)padlen); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - - if (iframe->state == NGHTTP2_IB_IGN_ALL) { - return (ssize_t)inlen; - } - } - - DEBUGF("recv: data_readlen=%zd\n", data_readlen); - - if (data_readlen > 0) { - if (session_enforce_http_messaging(session)) { - if (nghttp2_http_on_data_chunk(stream, (size_t)data_readlen) != 0) { - if (session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) { - /* Consume all data for connection immediately here */ - rv = session_update_connection_consumed_size( - session, (size_t)data_readlen); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - - if (iframe->state == NGHTTP2_IB_IGN_DATA) { - return (ssize_t)inlen; - } - } - - rv = nghttp2_session_add_rst_stream( - session, iframe->frame.hd.stream_id, NGHTTP2_PROTOCOL_ERROR); - if (nghttp2_is_fatal(rv)) { - return rv; - } - busy = 1; - iframe->state = NGHTTP2_IB_IGN_DATA; - break; - } - } - if (session->callbacks.on_data_chunk_recv_callback) { - rv = session->callbacks.on_data_chunk_recv_callback( - session, iframe->frame.hd.flags, iframe->frame.hd.stream_id, - in - readlen, (size_t)data_readlen, session->user_data); - if (rv == NGHTTP2_ERR_PAUSE) { - return in - first; - } - - if (nghttp2_is_fatal(rv)) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - } - } - } - - if (iframe->payloadleft) { - break; - } - - rv = session_process_data_frame(session); - if (nghttp2_is_fatal(rv)) { - return rv; - } - - session_inbound_frame_reset(session); - - break; - case NGHTTP2_IB_IGN_DATA: - DEBUGF("recv: [IB_IGN_DATA]\n"); - - readlen = inbound_frame_payload_readlen(iframe, in, last); - iframe->payloadleft -= readlen; - in += readlen; - - DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen, - iframe->payloadleft); - - if (readlen > 0) { - /* Update connection-level flow control window for ignored - DATA frame too */ + session, stream, readlen, + iframe->payloadleft || + (iframe->frame.hd.flags & NGHTTP2_FLAG_END_STREAM) == 0); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + data_readlen = inbound_frame_effective_readlen( + iframe, iframe->payloadleft, readlen); + + if (data_readlen == -1) { + /* everything is padding */ + data_readlen = 0; + } + + padlen = (ssize_t)readlen - data_readlen; + + if (padlen > 0) { + /* Padding is considered as "consumed" immediately */ + rv = nghttp2_session_consume(session, iframe->frame.hd.stream_id, + (size_t)padlen); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (ssize_t)inlen; + } + } + + DEBUGF("recv: data_readlen=%zd\n", data_readlen); + + if (data_readlen > 0) { + if (session_enforce_http_messaging(session)) { + if (nghttp2_http_on_data_chunk(stream, (size_t)data_readlen) != 0) { + if (session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) { + /* Consume all data for connection immediately here */ + rv = session_update_connection_consumed_size( + session, (size_t)data_readlen); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + if (iframe->state == NGHTTP2_IB_IGN_DATA) { + return (ssize_t)inlen; + } + } + + rv = nghttp2_session_add_rst_stream( + session, iframe->frame.hd.stream_id, NGHTTP2_PROTOCOL_ERROR); + if (nghttp2_is_fatal(rv)) { + return rv; + } + busy = 1; + iframe->state = NGHTTP2_IB_IGN_DATA; + break; + } + } + if (session->callbacks.on_data_chunk_recv_callback) { + rv = session->callbacks.on_data_chunk_recv_callback( + session, iframe->frame.hd.flags, iframe->frame.hd.stream_id, + in - readlen, (size_t)data_readlen, session->user_data); + if (rv == NGHTTP2_ERR_PAUSE) { + return in - first; + } + + if (nghttp2_is_fatal(rv)) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + } + } + + if (iframe->payloadleft) { + break; + } + + rv = session_process_data_frame(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + session_inbound_frame_reset(session); + + break; + case NGHTTP2_IB_IGN_DATA: + DEBUGF("recv: [IB_IGN_DATA]\n"); + + readlen = inbound_frame_payload_readlen(iframe, in, last); + iframe->payloadleft -= readlen; + in += readlen; + + DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen, + iframe->payloadleft); + + if (readlen > 0) { + /* Update connection-level flow control window for ignored + DATA frame too */ rv = nghttp2_session_update_recv_connection_window_size(session, readlen); - if (nghttp2_is_fatal(rv)) { - return rv; - } - - if (iframe->state == NGHTTP2_IB_IGN_ALL) { - return (ssize_t)inlen; - } - - if (session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) { - - /* Ignored DATA is considered as "consumed" immediately. */ - rv = session_update_connection_consumed_size(session, readlen); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - - if (iframe->state == NGHTTP2_IB_IGN_ALL) { - return (ssize_t)inlen; - } - } - } - - if (iframe->payloadleft) { - break; - } - - session_inbound_frame_reset(session); - - break; - case NGHTTP2_IB_IGN_ALL: - return (ssize_t)inlen; - case NGHTTP2_IB_READ_EXTENSION_PAYLOAD: - DEBUGF("recv: [IB_READ_EXTENSION_PAYLOAD]\n"); - - readlen = inbound_frame_payload_readlen(iframe, in, last); - iframe->payloadleft -= readlen; - in += readlen; - - DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen, - iframe->payloadleft); - - if (readlen > 0) { - rv = session_call_on_extension_chunk_recv_callback( - session, in - readlen, readlen); - if (nghttp2_is_fatal(rv)) { - return rv; - } - - if (rv != 0) { - busy = 1; - - iframe->state = NGHTTP2_IB_IGN_PAYLOAD; - - break; - } - } - - if (iframe->payloadleft > 0) { - break; - } - - rv = session_process_extension_frame(session); - if (nghttp2_is_fatal(rv)) { - return rv; - } - - session_inbound_frame_reset(session); - - break; - case NGHTTP2_IB_READ_ALTSVC_PAYLOAD: - DEBUGF("recv: [IB_READ_ALTSVC_PAYLOAD]\n"); - - readlen = inbound_frame_payload_readlen(iframe, in, last); - if (readlen > 0) { - iframe->lbuf.last = nghttp2_cpymem(iframe->lbuf.last, in, readlen); - - iframe->payloadleft -= readlen; - in += readlen; - } - - DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen, - iframe->payloadleft); - - if (iframe->payloadleft) { - assert(nghttp2_buf_avail(&iframe->lbuf) > 0); - - break; - } - - rv = session_process_altsvc_frame(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (ssize_t)inlen; + } + + if (session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) { + + /* Ignored DATA is considered as "consumed" immediately. */ + rv = session_update_connection_consumed_size(session, readlen); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + if (iframe->state == NGHTTP2_IB_IGN_ALL) { + return (ssize_t)inlen; + } + } + } + + if (iframe->payloadleft) { + break; + } + + session_inbound_frame_reset(session); + + break; + case NGHTTP2_IB_IGN_ALL: + return (ssize_t)inlen; + case NGHTTP2_IB_READ_EXTENSION_PAYLOAD: + DEBUGF("recv: [IB_READ_EXTENSION_PAYLOAD]\n"); + + readlen = inbound_frame_payload_readlen(iframe, in, last); + iframe->payloadleft -= readlen; + in += readlen; + + DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen, + iframe->payloadleft); + + if (readlen > 0) { + rv = session_call_on_extension_chunk_recv_callback( + session, in - readlen, readlen); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + if (rv != 0) { + busy = 1; + + iframe->state = NGHTTP2_IB_IGN_PAYLOAD; + + break; + } + } + + if (iframe->payloadleft > 0) { + break; + } + + rv = session_process_extension_frame(session); + if (nghttp2_is_fatal(rv)) { + return rv; + } + + session_inbound_frame_reset(session); + + break; + case NGHTTP2_IB_READ_ALTSVC_PAYLOAD: + DEBUGF("recv: [IB_READ_ALTSVC_PAYLOAD]\n"); + + readlen = inbound_frame_payload_readlen(iframe, in, last); + if (readlen > 0) { + iframe->lbuf.last = nghttp2_cpymem(iframe->lbuf.last, in, readlen); + + iframe->payloadleft -= readlen; + in += readlen; + } + + DEBUGF("recv: readlen=%zu, payloadleft=%zu\n", readlen, + iframe->payloadleft); + + if (iframe->payloadleft) { + assert(nghttp2_buf_avail(&iframe->lbuf) > 0); + + break; + } + + rv = session_process_altsvc_frame(session); if (nghttp2_is_fatal(rv)) { return rv; } - + session_inbound_frame_reset(session); break; @@ -6769,337 +6769,337 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, rv = session_process_origin_frame(session); - if (nghttp2_is_fatal(rv)) { - return rv; - } - + if (nghttp2_is_fatal(rv)) { + return rv; + } + if (iframe->state == NGHTTP2_IB_IGN_ALL) { return (ssize_t)inlen; } - session_inbound_frame_reset(session); - - break; - } - - if (!busy && in == last) { - break; - } - - busy = 0; - } - - assert(in == last); - - return in - first; -} - -int nghttp2_session_recv(nghttp2_session *session) { - uint8_t buf[NGHTTP2_INBOUND_BUFFER_LENGTH]; - while (1) { - ssize_t readlen; - readlen = session_recv(session, buf, sizeof(buf)); - if (readlen > 0) { - ssize_t proclen = nghttp2_session_mem_recv(session, buf, (size_t)readlen); - if (proclen < 0) { - return (int)proclen; - } - assert(proclen == readlen); - } else if (readlen == 0 || readlen == NGHTTP2_ERR_WOULDBLOCK) { - return 0; - } else if (readlen == NGHTTP2_ERR_EOF) { - return NGHTTP2_ERR_EOF; - } else if (readlen < 0) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - } -} - -/* - * Returns the number of active streams, which includes streams in - * reserved state. - */ -static size_t session_get_num_active_streams(nghttp2_session *session) { - return nghttp2_map_size(&session->streams) - session->num_closed_streams - - session->num_idle_streams; -} - -int nghttp2_session_want_read(nghttp2_session *session) { - size_t num_active_streams; - - /* If this flag is set, we don't want to read. The application - should drop the connection. */ - if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_SENT) { - return 0; - } - - num_active_streams = session_get_num_active_streams(session); - - /* Unless termination GOAWAY is sent or received, we always want to - read incoming frames. */ - - if (num_active_streams > 0) { - return 1; - } - - /* If there is no active streams and GOAWAY has been sent or - received, we are done with this session. */ - return (session->goaway_flags & - (NGHTTP2_GOAWAY_SENT | NGHTTP2_GOAWAY_RECV)) == 0; -} - -int nghttp2_session_want_write(nghttp2_session *session) { - /* If these flag is set, we don't want to write any data. The - application should drop the connection. */ - if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_SENT) { - return 0; - } - - /* - * Unless termination GOAWAY is sent or received, we want to write - * frames if there is pending ones. If pending frame is request/push - * response HEADERS and concurrent stream limit is reached, we don't - * want to write them. - */ - return session->aob.item || nghttp2_outbound_queue_top(&session->ob_urgent) || - nghttp2_outbound_queue_top(&session->ob_reg) || - (!nghttp2_pq_empty(&session->root.obq) && - session->remote_window_size > 0) || - (nghttp2_outbound_queue_top(&session->ob_syn) && - !session_is_outgoing_concurrent_streams_max(session)); -} - -int nghttp2_session_add_ping(nghttp2_session *session, uint8_t flags, - const uint8_t *opaque_data) { - int rv; - nghttp2_outbound_item *item; - nghttp2_frame *frame; - nghttp2_mem *mem; - - mem = &session->mem; - - if ((flags & NGHTTP2_FLAG_ACK) && + session_inbound_frame_reset(session); + + break; + } + + if (!busy && in == last) { + break; + } + + busy = 0; + } + + assert(in == last); + + return in - first; +} + +int nghttp2_session_recv(nghttp2_session *session) { + uint8_t buf[NGHTTP2_INBOUND_BUFFER_LENGTH]; + while (1) { + ssize_t readlen; + readlen = session_recv(session, buf, sizeof(buf)); + if (readlen > 0) { + ssize_t proclen = nghttp2_session_mem_recv(session, buf, (size_t)readlen); + if (proclen < 0) { + return (int)proclen; + } + assert(proclen == readlen); + } else if (readlen == 0 || readlen == NGHTTP2_ERR_WOULDBLOCK) { + return 0; + } else if (readlen == NGHTTP2_ERR_EOF) { + return NGHTTP2_ERR_EOF; + } else if (readlen < 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } +} + +/* + * Returns the number of active streams, which includes streams in + * reserved state. + */ +static size_t session_get_num_active_streams(nghttp2_session *session) { + return nghttp2_map_size(&session->streams) - session->num_closed_streams - + session->num_idle_streams; +} + +int nghttp2_session_want_read(nghttp2_session *session) { + size_t num_active_streams; + + /* If this flag is set, we don't want to read. The application + should drop the connection. */ + if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_SENT) { + return 0; + } + + num_active_streams = session_get_num_active_streams(session); + + /* Unless termination GOAWAY is sent or received, we always want to + read incoming frames. */ + + if (num_active_streams > 0) { + return 1; + } + + /* If there is no active streams and GOAWAY has been sent or + received, we are done with this session. */ + return (session->goaway_flags & + (NGHTTP2_GOAWAY_SENT | NGHTTP2_GOAWAY_RECV)) == 0; +} + +int nghttp2_session_want_write(nghttp2_session *session) { + /* If these flag is set, we don't want to write any data. The + application should drop the connection. */ + if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_SENT) { + return 0; + } + + /* + * Unless termination GOAWAY is sent or received, we want to write + * frames if there is pending ones. If pending frame is request/push + * response HEADERS and concurrent stream limit is reached, we don't + * want to write them. + */ + return session->aob.item || nghttp2_outbound_queue_top(&session->ob_urgent) || + nghttp2_outbound_queue_top(&session->ob_reg) || + (!nghttp2_pq_empty(&session->root.obq) && + session->remote_window_size > 0) || + (nghttp2_outbound_queue_top(&session->ob_syn) && + !session_is_outgoing_concurrent_streams_max(session)); +} + +int nghttp2_session_add_ping(nghttp2_session *session, uint8_t flags, + const uint8_t *opaque_data) { + int rv; + nghttp2_outbound_item *item; + nghttp2_frame *frame; + nghttp2_mem *mem; + + mem = &session->mem; + + if ((flags & NGHTTP2_FLAG_ACK) && session->obq_flood_counter_ >= session->max_outbound_ack) { - return NGHTTP2_ERR_FLOODED; - } - - item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); - if (item == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - nghttp2_outbound_item_init(item); - - frame = &item->frame; - - nghttp2_frame_ping_init(&frame->ping, flags, opaque_data); - - rv = nghttp2_session_add_item(session, item); - - if (rv != 0) { - nghttp2_frame_ping_free(&frame->ping); - nghttp2_mem_free(mem, item); - return rv; - } - - if (flags & NGHTTP2_FLAG_ACK) { - ++session->obq_flood_counter_; - } - - return 0; -} - -int nghttp2_session_add_goaway(nghttp2_session *session, int32_t last_stream_id, - uint32_t error_code, const uint8_t *opaque_data, - size_t opaque_data_len, uint8_t aux_flags) { - int rv; - nghttp2_outbound_item *item; - nghttp2_frame *frame; - uint8_t *opaque_data_copy = NULL; - nghttp2_goaway_aux_data *aux_data; - nghttp2_mem *mem; - - mem = &session->mem; - - if (nghttp2_session_is_my_stream_id(session, last_stream_id)) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - - if (opaque_data_len) { - if (opaque_data_len + 8 > NGHTTP2_MAX_PAYLOADLEN) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - opaque_data_copy = nghttp2_mem_malloc(mem, opaque_data_len); - if (opaque_data_copy == NULL) { - return NGHTTP2_ERR_NOMEM; - } - memcpy(opaque_data_copy, opaque_data, opaque_data_len); - } - - item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); - if (item == NULL) { - nghttp2_mem_free(mem, opaque_data_copy); - return NGHTTP2_ERR_NOMEM; - } - - nghttp2_outbound_item_init(item); - - frame = &item->frame; - - /* last_stream_id must not be increased from the value previously - sent */ - last_stream_id = nghttp2_min(last_stream_id, session->local_last_stream_id); - - nghttp2_frame_goaway_init(&frame->goaway, last_stream_id, error_code, - opaque_data_copy, opaque_data_len); - - aux_data = &item->aux_data.goaway; - aux_data->flags = aux_flags; - - rv = nghttp2_session_add_item(session, item); - if (rv != 0) { - nghttp2_frame_goaway_free(&frame->goaway, mem); - nghttp2_mem_free(mem, item); - return rv; - } - return 0; -} - -int nghttp2_session_add_window_update(nghttp2_session *session, uint8_t flags, - int32_t stream_id, - int32_t window_size_increment) { - int rv; - nghttp2_outbound_item *item; - nghttp2_frame *frame; - nghttp2_mem *mem; - - mem = &session->mem; - item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); - if (item == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - nghttp2_outbound_item_init(item); - - frame = &item->frame; - - nghttp2_frame_window_update_init(&frame->window_update, flags, stream_id, - window_size_increment); - - rv = nghttp2_session_add_item(session, item); - - if (rv != 0) { - nghttp2_frame_window_update_free(&frame->window_update); - nghttp2_mem_free(mem, item); - return rv; - } - return 0; -} - -static void -session_append_inflight_settings(nghttp2_session *session, - nghttp2_inflight_settings *settings) { - nghttp2_inflight_settings **i; - - for (i = &session->inflight_settings_head; *i; i = &(*i)->next) - ; - - *i = settings; -} - -int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags, - const nghttp2_settings_entry *iv, size_t niv) { - nghttp2_outbound_item *item; - nghttp2_frame *frame; - nghttp2_settings_entry *iv_copy; - size_t i; - int rv; - nghttp2_mem *mem; - nghttp2_inflight_settings *inflight_settings = NULL; - - mem = &session->mem; - - if (flags & NGHTTP2_FLAG_ACK) { - if (niv != 0) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - + return NGHTTP2_ERR_FLOODED; + } + + item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); + if (item == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + nghttp2_outbound_item_init(item); + + frame = &item->frame; + + nghttp2_frame_ping_init(&frame->ping, flags, opaque_data); + + rv = nghttp2_session_add_item(session, item); + + if (rv != 0) { + nghttp2_frame_ping_free(&frame->ping); + nghttp2_mem_free(mem, item); + return rv; + } + + if (flags & NGHTTP2_FLAG_ACK) { + ++session->obq_flood_counter_; + } + + return 0; +} + +int nghttp2_session_add_goaway(nghttp2_session *session, int32_t last_stream_id, + uint32_t error_code, const uint8_t *opaque_data, + size_t opaque_data_len, uint8_t aux_flags) { + int rv; + nghttp2_outbound_item *item; + nghttp2_frame *frame; + uint8_t *opaque_data_copy = NULL; + nghttp2_goaway_aux_data *aux_data; + nghttp2_mem *mem; + + mem = &session->mem; + + if (nghttp2_session_is_my_stream_id(session, last_stream_id)) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + if (opaque_data_len) { + if (opaque_data_len + 8 > NGHTTP2_MAX_PAYLOADLEN) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + opaque_data_copy = nghttp2_mem_malloc(mem, opaque_data_len); + if (opaque_data_copy == NULL) { + return NGHTTP2_ERR_NOMEM; + } + memcpy(opaque_data_copy, opaque_data, opaque_data_len); + } + + item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); + if (item == NULL) { + nghttp2_mem_free(mem, opaque_data_copy); + return NGHTTP2_ERR_NOMEM; + } + + nghttp2_outbound_item_init(item); + + frame = &item->frame; + + /* last_stream_id must not be increased from the value previously + sent */ + last_stream_id = nghttp2_min(last_stream_id, session->local_last_stream_id); + + nghttp2_frame_goaway_init(&frame->goaway, last_stream_id, error_code, + opaque_data_copy, opaque_data_len); + + aux_data = &item->aux_data.goaway; + aux_data->flags = aux_flags; + + rv = nghttp2_session_add_item(session, item); + if (rv != 0) { + nghttp2_frame_goaway_free(&frame->goaway, mem); + nghttp2_mem_free(mem, item); + return rv; + } + return 0; +} + +int nghttp2_session_add_window_update(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + int32_t window_size_increment) { + int rv; + nghttp2_outbound_item *item; + nghttp2_frame *frame; + nghttp2_mem *mem; + + mem = &session->mem; + item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); + if (item == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + nghttp2_outbound_item_init(item); + + frame = &item->frame; + + nghttp2_frame_window_update_init(&frame->window_update, flags, stream_id, + window_size_increment); + + rv = nghttp2_session_add_item(session, item); + + if (rv != 0) { + nghttp2_frame_window_update_free(&frame->window_update); + nghttp2_mem_free(mem, item); + return rv; + } + return 0; +} + +static void +session_append_inflight_settings(nghttp2_session *session, + nghttp2_inflight_settings *settings) { + nghttp2_inflight_settings **i; + + for (i = &session->inflight_settings_head; *i; i = &(*i)->next) + ; + + *i = settings; +} + +int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags, + const nghttp2_settings_entry *iv, size_t niv) { + nghttp2_outbound_item *item; + nghttp2_frame *frame; + nghttp2_settings_entry *iv_copy; + size_t i; + int rv; + nghttp2_mem *mem; + nghttp2_inflight_settings *inflight_settings = NULL; + + mem = &session->mem; + + if (flags & NGHTTP2_FLAG_ACK) { + if (niv != 0) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + if (session->obq_flood_counter_ >= session->max_outbound_ack) { - return NGHTTP2_ERR_FLOODED; - } - } - - if (!nghttp2_iv_check(iv, niv)) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - - item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); - if (item == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - if (niv > 0) { - iv_copy = nghttp2_frame_iv_copy(iv, niv, mem); - if (iv_copy == NULL) { - nghttp2_mem_free(mem, item); - return NGHTTP2_ERR_NOMEM; - } - } else { - iv_copy = NULL; - } - - if ((flags & NGHTTP2_FLAG_ACK) == 0) { - rv = inflight_settings_new(&inflight_settings, iv, niv, mem); - if (rv != 0) { - assert(nghttp2_is_fatal(rv)); - nghttp2_mem_free(mem, iv_copy); - nghttp2_mem_free(mem, item); - return rv; - } - } - - nghttp2_outbound_item_init(item); - - frame = &item->frame; - - nghttp2_frame_settings_init(&frame->settings, flags, iv_copy, niv); - rv = nghttp2_session_add_item(session, item); - if (rv != 0) { - /* The only expected error is fatal one */ - assert(nghttp2_is_fatal(rv)); - - inflight_settings_del(inflight_settings, mem); - - nghttp2_frame_settings_free(&frame->settings, mem); - nghttp2_mem_free(mem, item); - - return rv; - } - - if (flags & NGHTTP2_FLAG_ACK) { - ++session->obq_flood_counter_; - } else { - session_append_inflight_settings(session, inflight_settings); - } - - /* Extract NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS and ENABLE_PUSH - here. We use it to refuse the incoming stream and PUSH_PROMISE - with RST_STREAM. */ - - for (i = niv; i > 0; --i) { - if (iv[i - 1].settings_id == NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS) { - session->pending_local_max_concurrent_stream = iv[i - 1].value; - break; - } - } - - for (i = niv; i > 0; --i) { - if (iv[i - 1].settings_id == NGHTTP2_SETTINGS_ENABLE_PUSH) { - session->pending_enable_push = (uint8_t)iv[i - 1].value; - break; - } - } - + return NGHTTP2_ERR_FLOODED; + } + } + + if (!nghttp2_iv_check(iv, niv)) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); + if (item == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + if (niv > 0) { + iv_copy = nghttp2_frame_iv_copy(iv, niv, mem); + if (iv_copy == NULL) { + nghttp2_mem_free(mem, item); + return NGHTTP2_ERR_NOMEM; + } + } else { + iv_copy = NULL; + } + + if ((flags & NGHTTP2_FLAG_ACK) == 0) { + rv = inflight_settings_new(&inflight_settings, iv, niv, mem); + if (rv != 0) { + assert(nghttp2_is_fatal(rv)); + nghttp2_mem_free(mem, iv_copy); + nghttp2_mem_free(mem, item); + return rv; + } + } + + nghttp2_outbound_item_init(item); + + frame = &item->frame; + + nghttp2_frame_settings_init(&frame->settings, flags, iv_copy, niv); + rv = nghttp2_session_add_item(session, item); + if (rv != 0) { + /* The only expected error is fatal one */ + assert(nghttp2_is_fatal(rv)); + + inflight_settings_del(inflight_settings, mem); + + nghttp2_frame_settings_free(&frame->settings, mem); + nghttp2_mem_free(mem, item); + + return rv; + } + + if (flags & NGHTTP2_FLAG_ACK) { + ++session->obq_flood_counter_; + } else { + session_append_inflight_settings(session, inflight_settings); + } + + /* Extract NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS and ENABLE_PUSH + here. We use it to refuse the incoming stream and PUSH_PROMISE + with RST_STREAM. */ + + for (i = niv; i > 0; --i) { + if (iv[i - 1].settings_id == NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS) { + session->pending_local_max_concurrent_stream = iv[i - 1].value; + break; + } + } + + for (i = niv; i > 0; --i) { + if (iv[i - 1].settings_id == NGHTTP2_SETTINGS_ENABLE_PUSH) { + session->pending_enable_push = (uint8_t)iv[i - 1].value; + break; + } + } + for (i = niv; i > 0; --i) { if (iv[i - 1].settings_id == NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL) { session->pending_enable_connect_protocol = (uint8_t)iv[i - 1].value; @@ -7107,166 +7107,166 @@ int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags, } } - return 0; -} - -int nghttp2_session_pack_data(nghttp2_session *session, nghttp2_bufs *bufs, - size_t datamax, nghttp2_frame *frame, - nghttp2_data_aux_data *aux_data, - nghttp2_stream *stream) { - int rv; - uint32_t data_flags; - ssize_t payloadlen; - ssize_t padded_payloadlen; - nghttp2_buf *buf; - size_t max_payloadlen; - - assert(bufs->head == bufs->cur); - - buf = &bufs->cur->buf; - - if (session->callbacks.read_length_callback) { - - payloadlen = session->callbacks.read_length_callback( - session, frame->hd.type, stream->stream_id, session->remote_window_size, - stream->remote_window_size, session->remote_settings.max_frame_size, - session->user_data); - - DEBUGF("send: read_length_callback=%zd\n", payloadlen); - - payloadlen = nghttp2_session_enforce_flow_control_limits(session, stream, - payloadlen); - - DEBUGF("send: read_length_callback after flow control=%zd\n", payloadlen); - - if (payloadlen <= 0) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - - if ((size_t)payloadlen > nghttp2_buf_avail(buf)) { - /* Resize the current buffer(s). The reason why we do +1 for - buffer size is for possible padding field. */ - rv = nghttp2_bufs_realloc(&session->aob.framebufs, - (size_t)(NGHTTP2_FRAME_HDLEN + 1 + payloadlen)); - - if (rv != 0) { - DEBUGF("send: realloc buffer failed rv=%d", rv); - /* If reallocation failed, old buffers are still in tact. So - use safe limit. */ - payloadlen = (ssize_t)datamax; - - DEBUGF("send: use safe limit payloadlen=%zd", payloadlen); - } else { - assert(&session->aob.framebufs == bufs); - - buf = &bufs->cur->buf; - } - } - datamax = (size_t)payloadlen; - } - - /* Current max DATA length is less then buffer chunk size */ - assert(nghttp2_buf_avail(buf) >= datamax); - - data_flags = NGHTTP2_DATA_FLAG_NONE; - payloadlen = aux_data->data_prd.read_callback( - session, frame->hd.stream_id, buf->pos, datamax, &data_flags, - &aux_data->data_prd.source, session->user_data); - - if (payloadlen == NGHTTP2_ERR_DEFERRED || - payloadlen == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE || - payloadlen == NGHTTP2_ERR_PAUSE) { - DEBUGF("send: DATA postponed due to %s\n", - nghttp2_strerror((int)payloadlen)); - - return (int)payloadlen; - } - - if (payloadlen < 0 || datamax < (size_t)payloadlen) { - /* This is the error code when callback is failed. */ - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - - buf->last = buf->pos + payloadlen; - buf->pos -= NGHTTP2_FRAME_HDLEN; - - /* Clear flags, because this may contain previous flags of previous - DATA */ - frame->hd.flags = NGHTTP2_FLAG_NONE; - - if (data_flags & NGHTTP2_DATA_FLAG_EOF) { - aux_data->eof = 1; - /* If NGHTTP2_DATA_FLAG_NO_END_STREAM is set, don't set - NGHTTP2_FLAG_END_STREAM */ - if ((aux_data->flags & NGHTTP2_FLAG_END_STREAM) && - (data_flags & NGHTTP2_DATA_FLAG_NO_END_STREAM) == 0) { - frame->hd.flags |= NGHTTP2_FLAG_END_STREAM; - } - } - - if (data_flags & NGHTTP2_DATA_FLAG_NO_COPY) { - if (session->callbacks.send_data_callback == NULL) { - DEBUGF("NGHTTP2_DATA_FLAG_NO_COPY requires send_data_callback set\n"); - - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - aux_data->no_copy = 1; - } - - frame->hd.length = (size_t)payloadlen; - frame->data.padlen = 0; - - max_payloadlen = nghttp2_min(datamax, frame->hd.length + NGHTTP2_MAX_PADLEN); - - padded_payloadlen = - session_call_select_padding(session, frame, max_payloadlen); - - if (nghttp2_is_fatal((int)padded_payloadlen)) { - return (int)padded_payloadlen; - } - - frame->data.padlen = (size_t)(padded_payloadlen - payloadlen); - - nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); - - rv = nghttp2_frame_add_pad(bufs, &frame->hd, frame->data.padlen, - aux_data->no_copy); - if (rv != 0) { - return rv; - } - - reschedule_stream(stream); - - if (frame->hd.length == 0 && (data_flags & NGHTTP2_DATA_FLAG_EOF) && - (data_flags & NGHTTP2_DATA_FLAG_NO_END_STREAM)) { - /* DATA payload length is 0, and DATA frame does not bear - END_STREAM. In this case, there is no point to send 0 length - DATA frame. */ - return NGHTTP2_ERR_CANCEL; - } - - return 0; -} - -void *nghttp2_session_get_stream_user_data(nghttp2_session *session, - int32_t stream_id) { - nghttp2_stream *stream; - stream = nghttp2_session_get_stream(session, stream_id); - if (stream) { - return stream->stream_user_data; - } else { - return NULL; - } -} - -int nghttp2_session_set_stream_user_data(nghttp2_session *session, - int32_t stream_id, - void *stream_user_data) { - nghttp2_stream *stream; + return 0; +} + +int nghttp2_session_pack_data(nghttp2_session *session, nghttp2_bufs *bufs, + size_t datamax, nghttp2_frame *frame, + nghttp2_data_aux_data *aux_data, + nghttp2_stream *stream) { + int rv; + uint32_t data_flags; + ssize_t payloadlen; + ssize_t padded_payloadlen; + nghttp2_buf *buf; + size_t max_payloadlen; + + assert(bufs->head == bufs->cur); + + buf = &bufs->cur->buf; + + if (session->callbacks.read_length_callback) { + + payloadlen = session->callbacks.read_length_callback( + session, frame->hd.type, stream->stream_id, session->remote_window_size, + stream->remote_window_size, session->remote_settings.max_frame_size, + session->user_data); + + DEBUGF("send: read_length_callback=%zd\n", payloadlen); + + payloadlen = nghttp2_session_enforce_flow_control_limits(session, stream, + payloadlen); + + DEBUGF("send: read_length_callback after flow control=%zd\n", payloadlen); + + if (payloadlen <= 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + + if ((size_t)payloadlen > nghttp2_buf_avail(buf)) { + /* Resize the current buffer(s). The reason why we do +1 for + buffer size is for possible padding field. */ + rv = nghttp2_bufs_realloc(&session->aob.framebufs, + (size_t)(NGHTTP2_FRAME_HDLEN + 1 + payloadlen)); + + if (rv != 0) { + DEBUGF("send: realloc buffer failed rv=%d", rv); + /* If reallocation failed, old buffers are still in tact. So + use safe limit. */ + payloadlen = (ssize_t)datamax; + + DEBUGF("send: use safe limit payloadlen=%zd", payloadlen); + } else { + assert(&session->aob.framebufs == bufs); + + buf = &bufs->cur->buf; + } + } + datamax = (size_t)payloadlen; + } + + /* Current max DATA length is less then buffer chunk size */ + assert(nghttp2_buf_avail(buf) >= datamax); + + data_flags = NGHTTP2_DATA_FLAG_NONE; + payloadlen = aux_data->data_prd.read_callback( + session, frame->hd.stream_id, buf->pos, datamax, &data_flags, + &aux_data->data_prd.source, session->user_data); + + if (payloadlen == NGHTTP2_ERR_DEFERRED || + payloadlen == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE || + payloadlen == NGHTTP2_ERR_PAUSE) { + DEBUGF("send: DATA postponed due to %s\n", + nghttp2_strerror((int)payloadlen)); + + return (int)payloadlen; + } + + if (payloadlen < 0 || datamax < (size_t)payloadlen) { + /* This is the error code when callback is failed. */ + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + + buf->last = buf->pos + payloadlen; + buf->pos -= NGHTTP2_FRAME_HDLEN; + + /* Clear flags, because this may contain previous flags of previous + DATA */ + frame->hd.flags = NGHTTP2_FLAG_NONE; + + if (data_flags & NGHTTP2_DATA_FLAG_EOF) { + aux_data->eof = 1; + /* If NGHTTP2_DATA_FLAG_NO_END_STREAM is set, don't set + NGHTTP2_FLAG_END_STREAM */ + if ((aux_data->flags & NGHTTP2_FLAG_END_STREAM) && + (data_flags & NGHTTP2_DATA_FLAG_NO_END_STREAM) == 0) { + frame->hd.flags |= NGHTTP2_FLAG_END_STREAM; + } + } + + if (data_flags & NGHTTP2_DATA_FLAG_NO_COPY) { + if (session->callbacks.send_data_callback == NULL) { + DEBUGF("NGHTTP2_DATA_FLAG_NO_COPY requires send_data_callback set\n"); + + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + aux_data->no_copy = 1; + } + + frame->hd.length = (size_t)payloadlen; + frame->data.padlen = 0; + + max_payloadlen = nghttp2_min(datamax, frame->hd.length + NGHTTP2_MAX_PADLEN); + + padded_payloadlen = + session_call_select_padding(session, frame, max_payloadlen); + + if (nghttp2_is_fatal((int)padded_payloadlen)) { + return (int)padded_payloadlen; + } + + frame->data.padlen = (size_t)(padded_payloadlen - payloadlen); + + nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); + + rv = nghttp2_frame_add_pad(bufs, &frame->hd, frame->data.padlen, + aux_data->no_copy); + if (rv != 0) { + return rv; + } + + reschedule_stream(stream); + + if (frame->hd.length == 0 && (data_flags & NGHTTP2_DATA_FLAG_EOF) && + (data_flags & NGHTTP2_DATA_FLAG_NO_END_STREAM)) { + /* DATA payload length is 0, and DATA frame does not bear + END_STREAM. In this case, there is no point to send 0 length + DATA frame. */ + return NGHTTP2_ERR_CANCEL; + } + + return 0; +} + +void *nghttp2_session_get_stream_user_data(nghttp2_session *session, + int32_t stream_id) { + nghttp2_stream *stream; + stream = nghttp2_session_get_stream(session, stream_id); + if (stream) { + return stream->stream_user_data; + } else { + return NULL; + } +} + +int nghttp2_session_set_stream_user_data(nghttp2_session *session, + int32_t stream_id, + void *stream_user_data) { + nghttp2_stream *stream; nghttp2_frame *frame; nghttp2_outbound_item *item; - stream = nghttp2_session_get_stream(session, stream_id); + stream = nghttp2_session_get_stream(session, stream_id); if (stream) { stream->stream_user_data = stream_user_data; return 0; @@ -7274,8 +7274,8 @@ int nghttp2_session_set_stream_user_data(nghttp2_session *session, if (session->server || !nghttp2_session_is_my_stream_id(session, stream_id) || !nghttp2_outbound_queue_top(&session->ob_syn)) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } + return NGHTTP2_ERR_INVALID_ARGUMENT; + } frame = &nghttp2_outbound_queue_top(&session->ob_syn)->frame; assert(frame->hd.type == NGHTTP2_HEADERS); @@ -7299,500 +7299,500 @@ int nghttp2_session_set_stream_user_data(nghttp2_session *session, } return NGHTTP2_ERR_INVALID_ARGUMENT; -} - -int nghttp2_session_resume_data(nghttp2_session *session, int32_t stream_id) { - int rv; - nghttp2_stream *stream; - stream = nghttp2_session_get_stream(session, stream_id); - if (stream == NULL || !nghttp2_stream_check_deferred_item(stream)) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - - rv = nghttp2_stream_resume_deferred_item(stream, - NGHTTP2_STREAM_FLAG_DEFERRED_USER); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - - return 0; -} - -size_t nghttp2_session_get_outbound_queue_size(nghttp2_session *session) { - return nghttp2_outbound_queue_size(&session->ob_urgent) + - nghttp2_outbound_queue_size(&session->ob_reg) + - nghttp2_outbound_queue_size(&session->ob_syn); - /* TODO account for item attached to stream */ -} - -int32_t -nghttp2_session_get_stream_effective_recv_data_length(nghttp2_session *session, - int32_t stream_id) { - nghttp2_stream *stream; - stream = nghttp2_session_get_stream(session, stream_id); - if (stream == NULL) { - return -1; - } - return stream->recv_window_size < 0 ? 0 : stream->recv_window_size; -} - -int32_t -nghttp2_session_get_stream_effective_local_window_size(nghttp2_session *session, - int32_t stream_id) { - nghttp2_stream *stream; - stream = nghttp2_session_get_stream(session, stream_id); - if (stream == NULL) { - return -1; - } - return stream->local_window_size; -} - -int32_t nghttp2_session_get_stream_local_window_size(nghttp2_session *session, - int32_t stream_id) { - nghttp2_stream *stream; - int32_t size; - stream = nghttp2_session_get_stream(session, stream_id); - if (stream == NULL) { - return -1; - } - - size = stream->local_window_size - stream->recv_window_size; - - /* size could be negative if local endpoint reduced - SETTINGS_INITIAL_WINDOW_SIZE */ - if (size < 0) { - return 0; - } - - return size; -} - -int32_t -nghttp2_session_get_effective_recv_data_length(nghttp2_session *session) { - return session->recv_window_size < 0 ? 0 : session->recv_window_size; -} - -int32_t -nghttp2_session_get_effective_local_window_size(nghttp2_session *session) { - return session->local_window_size; -} - -int32_t nghttp2_session_get_local_window_size(nghttp2_session *session) { - return session->local_window_size - session->recv_window_size; -} - -int32_t nghttp2_session_get_stream_remote_window_size(nghttp2_session *session, - int32_t stream_id) { - nghttp2_stream *stream; - - stream = nghttp2_session_get_stream(session, stream_id); - if (stream == NULL) { - return -1; - } - - /* stream->remote_window_size can be negative when - SETTINGS_INITIAL_WINDOW_SIZE is changed. */ - return nghttp2_max(0, stream->remote_window_size); -} - -int32_t nghttp2_session_get_remote_window_size(nghttp2_session *session) { - return session->remote_window_size; -} - -uint32_t nghttp2_session_get_remote_settings(nghttp2_session *session, - nghttp2_settings_id id) { - switch (id) { - case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: - return session->remote_settings.header_table_size; - case NGHTTP2_SETTINGS_ENABLE_PUSH: - return session->remote_settings.enable_push; - case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: - return session->remote_settings.max_concurrent_streams; - case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: - return session->remote_settings.initial_window_size; - case NGHTTP2_SETTINGS_MAX_FRAME_SIZE: - return session->remote_settings.max_frame_size; - case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: - return session->remote_settings.max_header_list_size; +} + +int nghttp2_session_resume_data(nghttp2_session *session, int32_t stream_id) { + int rv; + nghttp2_stream *stream; + stream = nghttp2_session_get_stream(session, stream_id); + if (stream == NULL || !nghttp2_stream_check_deferred_item(stream)) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + rv = nghttp2_stream_resume_deferred_item(stream, + NGHTTP2_STREAM_FLAG_DEFERRED_USER); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + return 0; +} + +size_t nghttp2_session_get_outbound_queue_size(nghttp2_session *session) { + return nghttp2_outbound_queue_size(&session->ob_urgent) + + nghttp2_outbound_queue_size(&session->ob_reg) + + nghttp2_outbound_queue_size(&session->ob_syn); + /* TODO account for item attached to stream */ +} + +int32_t +nghttp2_session_get_stream_effective_recv_data_length(nghttp2_session *session, + int32_t stream_id) { + nghttp2_stream *stream; + stream = nghttp2_session_get_stream(session, stream_id); + if (stream == NULL) { + return -1; + } + return stream->recv_window_size < 0 ? 0 : stream->recv_window_size; +} + +int32_t +nghttp2_session_get_stream_effective_local_window_size(nghttp2_session *session, + int32_t stream_id) { + nghttp2_stream *stream; + stream = nghttp2_session_get_stream(session, stream_id); + if (stream == NULL) { + return -1; + } + return stream->local_window_size; +} + +int32_t nghttp2_session_get_stream_local_window_size(nghttp2_session *session, + int32_t stream_id) { + nghttp2_stream *stream; + int32_t size; + stream = nghttp2_session_get_stream(session, stream_id); + if (stream == NULL) { + return -1; + } + + size = stream->local_window_size - stream->recv_window_size; + + /* size could be negative if local endpoint reduced + SETTINGS_INITIAL_WINDOW_SIZE */ + if (size < 0) { + return 0; + } + + return size; +} + +int32_t +nghttp2_session_get_effective_recv_data_length(nghttp2_session *session) { + return session->recv_window_size < 0 ? 0 : session->recv_window_size; +} + +int32_t +nghttp2_session_get_effective_local_window_size(nghttp2_session *session) { + return session->local_window_size; +} + +int32_t nghttp2_session_get_local_window_size(nghttp2_session *session) { + return session->local_window_size - session->recv_window_size; +} + +int32_t nghttp2_session_get_stream_remote_window_size(nghttp2_session *session, + int32_t stream_id) { + nghttp2_stream *stream; + + stream = nghttp2_session_get_stream(session, stream_id); + if (stream == NULL) { + return -1; + } + + /* stream->remote_window_size can be negative when + SETTINGS_INITIAL_WINDOW_SIZE is changed. */ + return nghttp2_max(0, stream->remote_window_size); +} + +int32_t nghttp2_session_get_remote_window_size(nghttp2_session *session) { + return session->remote_window_size; +} + +uint32_t nghttp2_session_get_remote_settings(nghttp2_session *session, + nghttp2_settings_id id) { + switch (id) { + case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: + return session->remote_settings.header_table_size; + case NGHTTP2_SETTINGS_ENABLE_PUSH: + return session->remote_settings.enable_push; + case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: + return session->remote_settings.max_concurrent_streams; + case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: + return session->remote_settings.initial_window_size; + case NGHTTP2_SETTINGS_MAX_FRAME_SIZE: + return session->remote_settings.max_frame_size; + case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: + return session->remote_settings.max_header_list_size; case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL: return session->remote_settings.enable_connect_protocol; - } - - assert(0); - abort(); /* if NDEBUG is set */ -} - -uint32_t nghttp2_session_get_local_settings(nghttp2_session *session, - nghttp2_settings_id id) { - switch (id) { - case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: - return session->local_settings.header_table_size; - case NGHTTP2_SETTINGS_ENABLE_PUSH: - return session->local_settings.enable_push; - case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: - return session->local_settings.max_concurrent_streams; - case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: - return session->local_settings.initial_window_size; - case NGHTTP2_SETTINGS_MAX_FRAME_SIZE: - return session->local_settings.max_frame_size; - case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: - return session->local_settings.max_header_list_size; + } + + assert(0); + abort(); /* if NDEBUG is set */ +} + +uint32_t nghttp2_session_get_local_settings(nghttp2_session *session, + nghttp2_settings_id id) { + switch (id) { + case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE: + return session->local_settings.header_table_size; + case NGHTTP2_SETTINGS_ENABLE_PUSH: + return session->local_settings.enable_push; + case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: + return session->local_settings.max_concurrent_streams; + case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: + return session->local_settings.initial_window_size; + case NGHTTP2_SETTINGS_MAX_FRAME_SIZE: + return session->local_settings.max_frame_size; + case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: + return session->local_settings.max_header_list_size; case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL: return session->local_settings.enable_connect_protocol; - } - - assert(0); - abort(); /* if NDEBUG is set */ -} - -static int nghttp2_session_upgrade_internal(nghttp2_session *session, - const uint8_t *settings_payload, - size_t settings_payloadlen, - void *stream_user_data) { - nghttp2_stream *stream; - nghttp2_frame frame; - nghttp2_settings_entry *iv; - size_t niv; - int rv; - nghttp2_priority_spec pri_spec; - nghttp2_mem *mem; - - mem = &session->mem; - - if ((!session->server && session->next_stream_id != 1) || - (session->server && session->last_recv_stream_id >= 1)) { - return NGHTTP2_ERR_PROTO; - } - if (settings_payloadlen % NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } + } + + assert(0); + abort(); /* if NDEBUG is set */ +} + +static int nghttp2_session_upgrade_internal(nghttp2_session *session, + const uint8_t *settings_payload, + size_t settings_payloadlen, + void *stream_user_data) { + nghttp2_stream *stream; + nghttp2_frame frame; + nghttp2_settings_entry *iv; + size_t niv; + int rv; + nghttp2_priority_spec pri_spec; + nghttp2_mem *mem; + + mem = &session->mem; + + if ((!session->server && session->next_stream_id != 1) || + (session->server && session->last_recv_stream_id >= 1)) { + return NGHTTP2_ERR_PROTO; + } + if (settings_payloadlen % NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } /* SETTINGS frame contains too many settings */ if (settings_payloadlen / NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH > session->max_settings) { return NGHTTP2_ERR_TOO_MANY_SETTINGS; } - rv = nghttp2_frame_unpack_settings_payload2(&iv, &niv, settings_payload, - settings_payloadlen, mem); - if (rv != 0) { - return rv; - } - - if (session->server) { - nghttp2_frame_hd_init(&frame.hd, settings_payloadlen, NGHTTP2_SETTINGS, - NGHTTP2_FLAG_NONE, 0); - frame.settings.iv = iv; - frame.settings.niv = niv; - rv = nghttp2_session_on_settings_received(session, &frame, 1 /* No ACK */); - } else { - rv = nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, niv); - } - nghttp2_mem_free(mem, iv); - if (rv != 0) { - return rv; - } - - nghttp2_priority_spec_default_init(&pri_spec); - - stream = nghttp2_session_open_stream( - session, 1, NGHTTP2_STREAM_FLAG_NONE, &pri_spec, NGHTTP2_STREAM_OPENING, - session->server ? NULL : stream_user_data); - if (stream == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - /* We don't call nghttp2_session_adjust_closed_stream(), since this - should be the first stream open. */ - - if (session->server) { - nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); - session->last_recv_stream_id = 1; - session->last_proc_stream_id = 1; - } else { - nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR); - session->last_sent_stream_id = 1; - session->next_stream_id += 2; - } - return 0; -} - -int nghttp2_session_upgrade(nghttp2_session *session, - const uint8_t *settings_payload, - size_t settings_payloadlen, - void *stream_user_data) { - int rv; - nghttp2_stream *stream; - - rv = nghttp2_session_upgrade_internal(session, settings_payload, - settings_payloadlen, stream_user_data); - if (rv != 0) { - return rv; - } - - stream = nghttp2_session_get_stream(session, 1); - assert(stream); - - /* We have no information about request header fields when Upgrade - was happened. So we don't know the request method here. If - request method is HEAD, we have a trouble because we may have - nonzero content-length header field in response headers, and we - will going to check it against the actual DATA frames, but we may - get mismatch because HEAD response body must be empty. Because - of this reason, nghttp2_session_upgrade() was deprecated in favor - of nghttp2_session_upgrade2(), which has |head_request| parameter - to indicate that request method is HEAD or not. */ - stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_UPGRADE_WORKAROUND; - return 0; -} - -int nghttp2_session_upgrade2(nghttp2_session *session, - const uint8_t *settings_payload, - size_t settings_payloadlen, int head_request, - void *stream_user_data) { - int rv; - nghttp2_stream *stream; - - rv = nghttp2_session_upgrade_internal(session, settings_payload, - settings_payloadlen, stream_user_data); - if (rv != 0) { - return rv; - } - - stream = nghttp2_session_get_stream(session, 1); - assert(stream); - - if (head_request) { - stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD; - } - - return 0; -} - -int nghttp2_session_get_stream_local_close(nghttp2_session *session, - int32_t stream_id) { - nghttp2_stream *stream; - - stream = nghttp2_session_get_stream(session, stream_id); - - if (!stream) { - return -1; - } - - return (stream->shut_flags & NGHTTP2_SHUT_WR) != 0; -} - -int nghttp2_session_get_stream_remote_close(nghttp2_session *session, - int32_t stream_id) { - nghttp2_stream *stream; - - stream = nghttp2_session_get_stream(session, stream_id); - - if (!stream) { - return -1; - } - - return (stream->shut_flags & NGHTTP2_SHUT_RD) != 0; -} - -int nghttp2_session_consume(nghttp2_session *session, int32_t stream_id, - size_t size) { - int rv; - nghttp2_stream *stream; - - if (stream_id == 0) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - - if (!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE)) { - return NGHTTP2_ERR_INVALID_STATE; - } - - rv = session_update_connection_consumed_size(session, size); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - - stream = nghttp2_session_get_stream(session, stream_id); - - if (!stream) { - return 0; - } - - rv = session_update_stream_consumed_size(session, stream, size); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - - return 0; -} - -int nghttp2_session_consume_connection(nghttp2_session *session, size_t size) { - int rv; - - if (!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE)) { - return NGHTTP2_ERR_INVALID_STATE; - } - - rv = session_update_connection_consumed_size(session, size); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - - return 0; -} - -int nghttp2_session_consume_stream(nghttp2_session *session, int32_t stream_id, - size_t size) { - int rv; - nghttp2_stream *stream; - - if (stream_id == 0) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - - if (!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE)) { - return NGHTTP2_ERR_INVALID_STATE; - } - - stream = nghttp2_session_get_stream(session, stream_id); - - if (!stream) { - return 0; - } - - rv = session_update_stream_consumed_size(session, stream, size); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - - return 0; -} - -int nghttp2_session_set_next_stream_id(nghttp2_session *session, - int32_t next_stream_id) { - if (next_stream_id <= 0 || - session->next_stream_id > (uint32_t)next_stream_id) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - - if (session->server) { - if (next_stream_id % 2) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - } else if (next_stream_id % 2 == 0) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - - session->next_stream_id = (uint32_t)next_stream_id; - return 0; -} - -uint32_t nghttp2_session_get_next_stream_id(nghttp2_session *session) { - return session->next_stream_id; -} - -int32_t nghttp2_session_get_last_proc_stream_id(nghttp2_session *session) { - return session->last_proc_stream_id; -} - -nghttp2_stream *nghttp2_session_find_stream(nghttp2_session *session, - int32_t stream_id) { - if (stream_id == 0) { - return &session->root; - } - - return nghttp2_session_get_stream_raw(session, stream_id); -} - -nghttp2_stream *nghttp2_session_get_root_stream(nghttp2_session *session) { - return &session->root; -} - -int nghttp2_session_check_server_session(nghttp2_session *session) { - return session->server; -} - -int nghttp2_session_change_stream_priority( - nghttp2_session *session, int32_t stream_id, - const nghttp2_priority_spec *pri_spec) { - int rv; - nghttp2_stream *stream; - nghttp2_priority_spec pri_spec_copy; - - if (stream_id == 0 || stream_id == pri_spec->stream_id) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - - stream = nghttp2_session_get_stream_raw(session, stream_id); - if (!stream) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - - pri_spec_copy = *pri_spec; - nghttp2_priority_spec_normalize_weight(&pri_spec_copy); - - rv = nghttp2_session_reprioritize_stream(session, stream, &pri_spec_copy); - - if (nghttp2_is_fatal(rv)) { - return rv; - } - - /* We don't intentionally call nghttp2_session_adjust_idle_stream() - so that idle stream created by this function, and existing ones - are kept for application. We will adjust number of idle stream - in nghttp2_session_mem_send or nghttp2_session_mem_recv is - called. */ - return 0; -} - -int nghttp2_session_create_idle_stream(nghttp2_session *session, - int32_t stream_id, - const nghttp2_priority_spec *pri_spec) { - nghttp2_stream *stream; - nghttp2_priority_spec pri_spec_copy; - - if (stream_id == 0 || stream_id == pri_spec->stream_id || - !session_detect_idle_stream(session, stream_id)) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - - stream = nghttp2_session_get_stream_raw(session, stream_id); - if (stream) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - - pri_spec_copy = *pri_spec; - nghttp2_priority_spec_normalize_weight(&pri_spec_copy); - - stream = - nghttp2_session_open_stream(session, stream_id, NGHTTP2_STREAM_FLAG_NONE, - &pri_spec_copy, NGHTTP2_STREAM_IDLE, NULL); - if (!stream) { - return NGHTTP2_ERR_NOMEM; - } - - /* We don't intentionally call nghttp2_session_adjust_idle_stream() - so that idle stream created by this function, and existing ones - are kept for application. We will adjust number of idle stream - in nghttp2_session_mem_send or nghttp2_session_mem_recv is - called. */ - return 0; -} - -size_t -nghttp2_session_get_hd_inflate_dynamic_table_size(nghttp2_session *session) { - return nghttp2_hd_inflate_get_dynamic_table_size(&session->hd_inflater); -} - -size_t -nghttp2_session_get_hd_deflate_dynamic_table_size(nghttp2_session *session) { - return nghttp2_hd_deflate_get_dynamic_table_size(&session->hd_deflater); -} - -void nghttp2_session_set_user_data(nghttp2_session *session, void *user_data) { - session->user_data = user_data; -} + rv = nghttp2_frame_unpack_settings_payload2(&iv, &niv, settings_payload, + settings_payloadlen, mem); + if (rv != 0) { + return rv; + } + + if (session->server) { + nghttp2_frame_hd_init(&frame.hd, settings_payloadlen, NGHTTP2_SETTINGS, + NGHTTP2_FLAG_NONE, 0); + frame.settings.iv = iv; + frame.settings.niv = niv; + rv = nghttp2_session_on_settings_received(session, &frame, 1 /* No ACK */); + } else { + rv = nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, niv); + } + nghttp2_mem_free(mem, iv); + if (rv != 0) { + return rv; + } + + nghttp2_priority_spec_default_init(&pri_spec); + + stream = nghttp2_session_open_stream( + session, 1, NGHTTP2_STREAM_FLAG_NONE, &pri_spec, NGHTTP2_STREAM_OPENING, + session->server ? NULL : stream_user_data); + if (stream == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + /* We don't call nghttp2_session_adjust_closed_stream(), since this + should be the first stream open. */ + + if (session->server) { + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); + session->last_recv_stream_id = 1; + session->last_proc_stream_id = 1; + } else { + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_WR); + session->last_sent_stream_id = 1; + session->next_stream_id += 2; + } + return 0; +} + +int nghttp2_session_upgrade(nghttp2_session *session, + const uint8_t *settings_payload, + size_t settings_payloadlen, + void *stream_user_data) { + int rv; + nghttp2_stream *stream; + + rv = nghttp2_session_upgrade_internal(session, settings_payload, + settings_payloadlen, stream_user_data); + if (rv != 0) { + return rv; + } + + stream = nghttp2_session_get_stream(session, 1); + assert(stream); + + /* We have no information about request header fields when Upgrade + was happened. So we don't know the request method here. If + request method is HEAD, we have a trouble because we may have + nonzero content-length header field in response headers, and we + will going to check it against the actual DATA frames, but we may + get mismatch because HEAD response body must be empty. Because + of this reason, nghttp2_session_upgrade() was deprecated in favor + of nghttp2_session_upgrade2(), which has |head_request| parameter + to indicate that request method is HEAD or not. */ + stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_UPGRADE_WORKAROUND; + return 0; +} + +int nghttp2_session_upgrade2(nghttp2_session *session, + const uint8_t *settings_payload, + size_t settings_payloadlen, int head_request, + void *stream_user_data) { + int rv; + nghttp2_stream *stream; + + rv = nghttp2_session_upgrade_internal(session, settings_payload, + settings_payloadlen, stream_user_data); + if (rv != 0) { + return rv; + } + + stream = nghttp2_session_get_stream(session, 1); + assert(stream); + + if (head_request) { + stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD; + } + + return 0; +} + +int nghttp2_session_get_stream_local_close(nghttp2_session *session, + int32_t stream_id) { + nghttp2_stream *stream; + + stream = nghttp2_session_get_stream(session, stream_id); + + if (!stream) { + return -1; + } + + return (stream->shut_flags & NGHTTP2_SHUT_WR) != 0; +} + +int nghttp2_session_get_stream_remote_close(nghttp2_session *session, + int32_t stream_id) { + nghttp2_stream *stream; + + stream = nghttp2_session_get_stream(session, stream_id); + + if (!stream) { + return -1; + } + + return (stream->shut_flags & NGHTTP2_SHUT_RD) != 0; +} + +int nghttp2_session_consume(nghttp2_session *session, int32_t stream_id, + size_t size) { + int rv; + nghttp2_stream *stream; + + if (stream_id == 0) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + if (!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE)) { + return NGHTTP2_ERR_INVALID_STATE; + } + + rv = session_update_connection_consumed_size(session, size); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + stream = nghttp2_session_get_stream(session, stream_id); + + if (!stream) { + return 0; + } + + rv = session_update_stream_consumed_size(session, stream, size); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + return 0; +} + +int nghttp2_session_consume_connection(nghttp2_session *session, size_t size) { + int rv; + + if (!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE)) { + return NGHTTP2_ERR_INVALID_STATE; + } + + rv = session_update_connection_consumed_size(session, size); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + return 0; +} + +int nghttp2_session_consume_stream(nghttp2_session *session, int32_t stream_id, + size_t size) { + int rv; + nghttp2_stream *stream; + + if (stream_id == 0) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + if (!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE)) { + return NGHTTP2_ERR_INVALID_STATE; + } + + stream = nghttp2_session_get_stream(session, stream_id); + + if (!stream) { + return 0; + } + + rv = session_update_stream_consumed_size(session, stream, size); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + return 0; +} + +int nghttp2_session_set_next_stream_id(nghttp2_session *session, + int32_t next_stream_id) { + if (next_stream_id <= 0 || + session->next_stream_id > (uint32_t)next_stream_id) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + if (session->server) { + if (next_stream_id % 2) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + } else if (next_stream_id % 2 == 0) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + session->next_stream_id = (uint32_t)next_stream_id; + return 0; +} + +uint32_t nghttp2_session_get_next_stream_id(nghttp2_session *session) { + return session->next_stream_id; +} + +int32_t nghttp2_session_get_last_proc_stream_id(nghttp2_session *session) { + return session->last_proc_stream_id; +} + +nghttp2_stream *nghttp2_session_find_stream(nghttp2_session *session, + int32_t stream_id) { + if (stream_id == 0) { + return &session->root; + } + + return nghttp2_session_get_stream_raw(session, stream_id); +} + +nghttp2_stream *nghttp2_session_get_root_stream(nghttp2_session *session) { + return &session->root; +} + +int nghttp2_session_check_server_session(nghttp2_session *session) { + return session->server; +} + +int nghttp2_session_change_stream_priority( + nghttp2_session *session, int32_t stream_id, + const nghttp2_priority_spec *pri_spec) { + int rv; + nghttp2_stream *stream; + nghttp2_priority_spec pri_spec_copy; + + if (stream_id == 0 || stream_id == pri_spec->stream_id) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + stream = nghttp2_session_get_stream_raw(session, stream_id); + if (!stream) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + pri_spec_copy = *pri_spec; + nghttp2_priority_spec_normalize_weight(&pri_spec_copy); + + rv = nghttp2_session_reprioritize_stream(session, stream, &pri_spec_copy); + + if (nghttp2_is_fatal(rv)) { + return rv; + } + + /* We don't intentionally call nghttp2_session_adjust_idle_stream() + so that idle stream created by this function, and existing ones + are kept for application. We will adjust number of idle stream + in nghttp2_session_mem_send or nghttp2_session_mem_recv is + called. */ + return 0; +} + +int nghttp2_session_create_idle_stream(nghttp2_session *session, + int32_t stream_id, + const nghttp2_priority_spec *pri_spec) { + nghttp2_stream *stream; + nghttp2_priority_spec pri_spec_copy; + + if (stream_id == 0 || stream_id == pri_spec->stream_id || + !session_detect_idle_stream(session, stream_id)) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + stream = nghttp2_session_get_stream_raw(session, stream_id); + if (stream) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + pri_spec_copy = *pri_spec; + nghttp2_priority_spec_normalize_weight(&pri_spec_copy); + + stream = + nghttp2_session_open_stream(session, stream_id, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_copy, NGHTTP2_STREAM_IDLE, NULL); + if (!stream) { + return NGHTTP2_ERR_NOMEM; + } + + /* We don't intentionally call nghttp2_session_adjust_idle_stream() + so that idle stream created by this function, and existing ones + are kept for application. We will adjust number of idle stream + in nghttp2_session_mem_send or nghttp2_session_mem_recv is + called. */ + return 0; +} + +size_t +nghttp2_session_get_hd_inflate_dynamic_table_size(nghttp2_session *session) { + return nghttp2_hd_inflate_get_dynamic_table_size(&session->hd_inflater); +} + +size_t +nghttp2_session_get_hd_deflate_dynamic_table_size(nghttp2_session *session) { + return nghttp2_hd_deflate_get_dynamic_table_size(&session->hd_deflater); +} + +void nghttp2_session_set_user_data(nghttp2_session *session, void *user_data) { + session->user_data = user_data; +} diff --git a/contrib/libs/nghttp2/lib/nghttp2_session.h b/contrib/libs/nghttp2/lib/nghttp2_session.h index e5909ffb78..07bfbb6c90 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_session.h +++ b/contrib/libs/nghttp2/lib/nghttp2_session.h @@ -1,766 +1,766 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * 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_SESSION_H -#define NGHTTP2_SESSION_H - -#ifdef HAVE_CONFIG_H +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * 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_SESSION_H +#define NGHTTP2_SESSION_H + +#ifdef HAVE_CONFIG_H # include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <nghttp2/nghttp2.h> -#include "nghttp2_map.h" -#include "nghttp2_frame.h" -#include "nghttp2_hd.h" -#include "nghttp2_stream.h" -#include "nghttp2_outbound_item.h" -#include "nghttp2_int.h" -#include "nghttp2_buf.h" -#include "nghttp2_callbacks.h" -#include "nghttp2_mem.h" - -/* The global variable for tests where we want to disable strict - preface handling. */ -extern int nghttp2_enable_strict_preface; - -/* - * Option flags. - */ -typedef enum { - NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE = 1 << 0, - 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; - -/* - * bitmask for built-in type to enable the default handling for that - * type of the frame. - */ -typedef enum { - NGHTTP2_TYPEMASK_NONE = 0, +#endif /* HAVE_CONFIG_H */ + +#include <nghttp2/nghttp2.h> +#include "nghttp2_map.h" +#include "nghttp2_frame.h" +#include "nghttp2_hd.h" +#include "nghttp2_stream.h" +#include "nghttp2_outbound_item.h" +#include "nghttp2_int.h" +#include "nghttp2_buf.h" +#include "nghttp2_callbacks.h" +#include "nghttp2_mem.h" + +/* The global variable for tests where we want to disable strict + preface handling. */ +extern int nghttp2_enable_strict_preface; + +/* + * Option flags. + */ +typedef enum { + NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE = 1 << 0, + 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; + +/* + * bitmask for built-in type to enable the default handling for that + * type of the frame. + */ +typedef enum { + NGHTTP2_TYPEMASK_NONE = 0, NGHTTP2_TYPEMASK_ALTSVC = 1 << 0, NGHTTP2_TYPEMASK_ORIGIN = 1 << 1 -} nghttp2_typemask; - -typedef enum { - NGHTTP2_OB_POP_ITEM, - NGHTTP2_OB_SEND_DATA, - NGHTTP2_OB_SEND_NO_COPY, - NGHTTP2_OB_SEND_CLIENT_MAGIC -} nghttp2_outbound_state; - -typedef struct { - nghttp2_outbound_item *item; - nghttp2_bufs framebufs; - nghttp2_outbound_state state; -} nghttp2_active_outbound_item; - -/* Buffer length for inbound raw byte stream used in - nghttp2_session_recv(). */ -#define NGHTTP2_INBOUND_BUFFER_LENGTH 16384 - -/* The default maximum number of incoming reserved streams */ -#define NGHTTP2_MAX_INCOMING_RESERVED_STREAMS 200 - -/* Even if we have less SETTINGS_MAX_CONCURRENT_STREAMS than this - number, we keep NGHTTP2_MIN_IDLE_STREAMS streams in idle state */ -#define NGHTTP2_MIN_IDLE_STREAMS 16 - -/* The maximum number of items in outbound queue, which is considered - as flooding caused by peer. All frames are not considered here. - We only consider PING + ACK and SETTINGS + ACK. This is because - they both are response to the frame initiated by peer and peer can - send as many of them as they want. If peer does not read network, - response frames are stacked up, which leads to memory exhaustion. - The value selected here is arbitrary, but safe value and if we have - these frames in this number, it is considered suspicious. */ +} nghttp2_typemask; + +typedef enum { + NGHTTP2_OB_POP_ITEM, + NGHTTP2_OB_SEND_DATA, + NGHTTP2_OB_SEND_NO_COPY, + NGHTTP2_OB_SEND_CLIENT_MAGIC +} nghttp2_outbound_state; + +typedef struct { + nghttp2_outbound_item *item; + nghttp2_bufs framebufs; + nghttp2_outbound_state state; +} nghttp2_active_outbound_item; + +/* Buffer length for inbound raw byte stream used in + nghttp2_session_recv(). */ +#define NGHTTP2_INBOUND_BUFFER_LENGTH 16384 + +/* The default maximum number of incoming reserved streams */ +#define NGHTTP2_MAX_INCOMING_RESERVED_STREAMS 200 + +/* Even if we have less SETTINGS_MAX_CONCURRENT_STREAMS than this + number, we keep NGHTTP2_MIN_IDLE_STREAMS streams in idle state */ +#define NGHTTP2_MIN_IDLE_STREAMS 16 + +/* The maximum number of items in outbound queue, which is considered + as flooding caused by peer. All frames are not considered here. + We only consider PING + ACK and SETTINGS + ACK. This is because + they both are response to the frame initiated by peer and peer can + send as many of them as they want. If peer does not read network, + response frames are stacked up, which leads to memory exhaustion. + The value selected here is arbitrary, but safe value and if we have + these frames in this number, it is considered suspicious. */ #define NGHTTP2_DEFAULT_MAX_OBQ_FLOOD_ITEM 1000 - -/* The default value of maximum number of concurrent streams. */ -#define NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS 0xffffffffu - -/* Internal state when receiving incoming frame */ -typedef enum { - /* Receiving frame header */ - NGHTTP2_IB_READ_CLIENT_MAGIC, - NGHTTP2_IB_READ_FIRST_SETTINGS, - NGHTTP2_IB_READ_HEAD, - NGHTTP2_IB_READ_NBYTE, - NGHTTP2_IB_READ_HEADER_BLOCK, - NGHTTP2_IB_IGN_HEADER_BLOCK, - NGHTTP2_IB_IGN_PAYLOAD, - NGHTTP2_IB_FRAME_SIZE_ERROR, - NGHTTP2_IB_READ_SETTINGS, - NGHTTP2_IB_READ_GOAWAY_DEBUG, - NGHTTP2_IB_EXPECT_CONTINUATION, - NGHTTP2_IB_IGN_CONTINUATION, - NGHTTP2_IB_READ_PAD_DATA, - NGHTTP2_IB_READ_DATA, - NGHTTP2_IB_IGN_DATA, - NGHTTP2_IB_IGN_ALL, - NGHTTP2_IB_READ_ALTSVC_PAYLOAD, + +/* The default value of maximum number of concurrent streams. */ +#define NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS 0xffffffffu + +/* Internal state when receiving incoming frame */ +typedef enum { + /* Receiving frame header */ + NGHTTP2_IB_READ_CLIENT_MAGIC, + NGHTTP2_IB_READ_FIRST_SETTINGS, + NGHTTP2_IB_READ_HEAD, + NGHTTP2_IB_READ_NBYTE, + NGHTTP2_IB_READ_HEADER_BLOCK, + NGHTTP2_IB_IGN_HEADER_BLOCK, + NGHTTP2_IB_IGN_PAYLOAD, + NGHTTP2_IB_FRAME_SIZE_ERROR, + NGHTTP2_IB_READ_SETTINGS, + NGHTTP2_IB_READ_GOAWAY_DEBUG, + NGHTTP2_IB_EXPECT_CONTINUATION, + NGHTTP2_IB_IGN_CONTINUATION, + NGHTTP2_IB_READ_PAD_DATA, + NGHTTP2_IB_READ_DATA, + NGHTTP2_IB_IGN_DATA, + NGHTTP2_IB_IGN_ALL, + NGHTTP2_IB_READ_ALTSVC_PAYLOAD, NGHTTP2_IB_READ_ORIGIN_PAYLOAD, - NGHTTP2_IB_READ_EXTENSION_PAYLOAD -} nghttp2_inbound_state; - -typedef struct { - nghttp2_frame frame; - /* Storage for extension frame payload. frame->ext.payload points - to this structure to avoid frequent memory allocation. */ - nghttp2_ext_frame_payload ext_frame_payload; - /* The received SETTINGS entry. For the standard settings entries, - we only keep the last seen value. For - SETTINGS_HEADER_TABLE_SIZE, we also keep minimum value in the - last index. */ - nghttp2_settings_entry *iv; - /* buffer pointers to small buffer, raw_sbuf */ - nghttp2_buf sbuf; - /* buffer pointers to large buffer, raw_lbuf */ - nghttp2_buf lbuf; - /* Large buffer, malloced on demand */ - uint8_t *raw_lbuf; - /* The number of entry filled in |iv| */ - size_t niv; - /* The number of entries |iv| can store. */ - size_t max_niv; - /* How many bytes we still need to receive for current frame */ - size_t payloadleft; - /* 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]; -} nghttp2_inbound_frame; - -typedef struct { - uint32_t header_table_size; - uint32_t enable_push; - uint32_t max_concurrent_streams; - uint32_t initial_window_size; - uint32_t max_frame_size; - uint32_t max_header_list_size; + NGHTTP2_IB_READ_EXTENSION_PAYLOAD +} nghttp2_inbound_state; + +typedef struct { + nghttp2_frame frame; + /* Storage for extension frame payload. frame->ext.payload points + to this structure to avoid frequent memory allocation. */ + nghttp2_ext_frame_payload ext_frame_payload; + /* The received SETTINGS entry. For the standard settings entries, + we only keep the last seen value. For + SETTINGS_HEADER_TABLE_SIZE, we also keep minimum value in the + last index. */ + nghttp2_settings_entry *iv; + /* buffer pointers to small buffer, raw_sbuf */ + nghttp2_buf sbuf; + /* buffer pointers to large buffer, raw_lbuf */ + nghttp2_buf lbuf; + /* Large buffer, malloced on demand */ + uint8_t *raw_lbuf; + /* The number of entry filled in |iv| */ + size_t niv; + /* The number of entries |iv| can store. */ + size_t max_niv; + /* How many bytes we still need to receive for current frame */ + size_t payloadleft; + /* 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]; +} nghttp2_inbound_frame; + +typedef struct { + uint32_t header_table_size; + uint32_t enable_push; + uint32_t max_concurrent_streams; + uint32_t initial_window_size; + uint32_t max_frame_size; + uint32_t max_header_list_size; uint32_t enable_connect_protocol; -} nghttp2_settings_storage; - -typedef enum { - NGHTTP2_GOAWAY_NONE = 0, - /* Flag means that connection should be terminated after sending GOAWAY. */ - NGHTTP2_GOAWAY_TERM_ON_SEND = 0x1, - /* Flag means GOAWAY to terminate session has been sent */ - NGHTTP2_GOAWAY_TERM_SENT = 0x2, - /* Flag means GOAWAY was sent */ - NGHTTP2_GOAWAY_SENT = 0x4, - /* Flag means GOAWAY was received */ - NGHTTP2_GOAWAY_RECV = 0x8 -} nghttp2_goaway_flag; - -/* nghttp2_inflight_settings stores the SETTINGS entries which local - endpoint has sent to the remote endpoint, and has not received ACK - yet. */ -struct nghttp2_inflight_settings { - struct nghttp2_inflight_settings *next; - nghttp2_settings_entry *iv; - size_t niv; -}; - -typedef struct nghttp2_inflight_settings nghttp2_inflight_settings; - -struct nghttp2_session { - nghttp2_map /* <nghttp2_stream*> */ streams; - /* root of dependency tree*/ - nghttp2_stream root; - /* Queue for outbound urgent frames (PING and SETTINGS) */ - nghttp2_outbound_queue ob_urgent; - /* Queue for non-DATA frames */ - nghttp2_outbound_queue ob_reg; - /* Queue for outbound stream-creating HEADERS (request or push - response) frame, which are subject to - SETTINGS_MAX_CONCURRENT_STREAMS limit. */ - nghttp2_outbound_queue ob_syn; - nghttp2_active_outbound_item aob; - nghttp2_inbound_frame iframe; - nghttp2_hd_deflater hd_deflater; - nghttp2_hd_inflater hd_inflater; - nghttp2_session_callbacks callbacks; - /* Memory allocator */ - nghttp2_mem mem; - void *user_data; - /* Points to the latest incoming closed stream. NULL if there is no - closed stream. Only used when session is initialized as - server. */ - nghttp2_stream *closed_stream_head; - /* Points to the oldest incoming closed stream. NULL if there is no - closed stream. Only used when session is initialized as - server. */ - nghttp2_stream *closed_stream_tail; - /* Points to the latest idle stream. NULL if there is no idle - stream. Only used when session is initialized as server .*/ - nghttp2_stream *idle_stream_head; - /* Points to the oldest idle stream. NULL if there is no idle - stream. Only used when session is initialized as erver. */ - nghttp2_stream *idle_stream_tail; - /* Queue of In-flight SETTINGS values. SETTINGS bearing ACK is not - considered as in-flight. */ - nghttp2_inflight_settings *inflight_settings_head; - /* The number of outgoing streams. This will be capped by - remote_settings.max_concurrent_streams. */ - size_t num_outgoing_streams; - /* The number of incoming streams. This will be capped by - local_settings.max_concurrent_streams. */ - size_t num_incoming_streams; - /* The number of incoming reserved streams. This is the number of - streams in reserved (remote) state. RFC 7540 does not limit this - number. nghttp2 offers - nghttp2_option_set_max_reserved_remote_streams() to achieve this. - If it is used, num_incoming_streams is capped by - max_incoming_reserved_streams. Client application should - consider to set this because without that server can send - arbitrary number of PUSH_PROMISE, and exhaust client's memory. */ - size_t num_incoming_reserved_streams; - /* The maximum number of incoming reserved streams (reserved - (remote) state). RST_STREAM will be sent for the pushed stream - which exceeds this limit. */ - size_t max_incoming_reserved_streams; - /* The number of closed streams still kept in |streams| hash. The - closed streams can be accessed through single linked list - |closed_stream_head|. The current implementation only keeps - incoming streams and session is initialized as server. */ - size_t num_closed_streams; - /* The number of idle streams kept in |streams| hash. The idle - streams can be accessed through doubly linked list - |idle_stream_head|. The current implementation only keeps idle - streams if session is initialized as server. */ - size_t num_idle_streams; - /* The number of bytes allocated for nvbuf */ - size_t nvbuflen; +} nghttp2_settings_storage; + +typedef enum { + NGHTTP2_GOAWAY_NONE = 0, + /* Flag means that connection should be terminated after sending GOAWAY. */ + NGHTTP2_GOAWAY_TERM_ON_SEND = 0x1, + /* Flag means GOAWAY to terminate session has been sent */ + NGHTTP2_GOAWAY_TERM_SENT = 0x2, + /* Flag means GOAWAY was sent */ + NGHTTP2_GOAWAY_SENT = 0x4, + /* Flag means GOAWAY was received */ + NGHTTP2_GOAWAY_RECV = 0x8 +} nghttp2_goaway_flag; + +/* nghttp2_inflight_settings stores the SETTINGS entries which local + endpoint has sent to the remote endpoint, and has not received ACK + yet. */ +struct nghttp2_inflight_settings { + struct nghttp2_inflight_settings *next; + nghttp2_settings_entry *iv; + size_t niv; +}; + +typedef struct nghttp2_inflight_settings nghttp2_inflight_settings; + +struct nghttp2_session { + nghttp2_map /* <nghttp2_stream*> */ streams; + /* root of dependency tree*/ + nghttp2_stream root; + /* Queue for outbound urgent frames (PING and SETTINGS) */ + nghttp2_outbound_queue ob_urgent; + /* Queue for non-DATA frames */ + nghttp2_outbound_queue ob_reg; + /* Queue for outbound stream-creating HEADERS (request or push + response) frame, which are subject to + SETTINGS_MAX_CONCURRENT_STREAMS limit. */ + nghttp2_outbound_queue ob_syn; + nghttp2_active_outbound_item aob; + nghttp2_inbound_frame iframe; + nghttp2_hd_deflater hd_deflater; + nghttp2_hd_inflater hd_inflater; + nghttp2_session_callbacks callbacks; + /* Memory allocator */ + nghttp2_mem mem; + void *user_data; + /* Points to the latest incoming closed stream. NULL if there is no + closed stream. Only used when session is initialized as + server. */ + nghttp2_stream *closed_stream_head; + /* Points to the oldest incoming closed stream. NULL if there is no + closed stream. Only used when session is initialized as + server. */ + nghttp2_stream *closed_stream_tail; + /* Points to the latest idle stream. NULL if there is no idle + stream. Only used when session is initialized as server .*/ + nghttp2_stream *idle_stream_head; + /* Points to the oldest idle stream. NULL if there is no idle + stream. Only used when session is initialized as erver. */ + nghttp2_stream *idle_stream_tail; + /* Queue of In-flight SETTINGS values. SETTINGS bearing ACK is not + considered as in-flight. */ + nghttp2_inflight_settings *inflight_settings_head; + /* The number of outgoing streams. This will be capped by + remote_settings.max_concurrent_streams. */ + size_t num_outgoing_streams; + /* The number of incoming streams. This will be capped by + local_settings.max_concurrent_streams. */ + size_t num_incoming_streams; + /* The number of incoming reserved streams. This is the number of + streams in reserved (remote) state. RFC 7540 does not limit this + number. nghttp2 offers + nghttp2_option_set_max_reserved_remote_streams() to achieve this. + If it is used, num_incoming_streams is capped by + max_incoming_reserved_streams. Client application should + consider to set this because without that server can send + arbitrary number of PUSH_PROMISE, and exhaust client's memory. */ + size_t num_incoming_reserved_streams; + /* The maximum number of incoming reserved streams (reserved + (remote) state). RST_STREAM will be sent for the pushed stream + which exceeds this limit. */ + size_t max_incoming_reserved_streams; + /* The number of closed streams still kept in |streams| hash. The + closed streams can be accessed through single linked list + |closed_stream_head|. The current implementation only keeps + incoming streams and session is initialized as server. */ + size_t num_closed_streams; + /* The number of idle streams kept in |streams| hash. The idle + streams can be accessed through doubly linked list + |idle_stream_head|. The current implementation only keeps idle + streams if session is initialized as server. */ + size_t num_idle_streams; + /* The number of bytes allocated for nvbuf */ + size_t nvbuflen; /* Counter for detecting flooding in outbound queue. If it exceeds max_outbound_ack, session will be closed. */ - size_t obq_flood_counter_; + size_t obq_flood_counter_; /* The maximum number of outgoing SETTINGS ACK and PING ACK in outbound queue. */ size_t max_outbound_ack; - /* The maximum length of header block to send. Calculated by the - same way as nghttp2_hd_deflate_bound() does. */ - size_t max_send_header_block_length; + /* The maximum length of header block to send. Calculated by the + same way as nghttp2_hd_deflate_bound() does. */ + size_t max_send_header_block_length; /* The maximum number of settings accepted per SETTINGS frame. */ size_t max_settings; - /* Next Stream ID. Made unsigned int to detect >= (1 << 31). */ - uint32_t next_stream_id; - /* The last stream ID this session initiated. For client session, - this is the last stream ID it has sent. For server session, it - is the last promised stream ID sent in PUSH_PROMISE. */ - int32_t last_sent_stream_id; - /* The largest stream ID received so far */ - int32_t last_recv_stream_id; - /* The largest stream ID which has been processed in some way. This - value will be used as last-stream-id when sending GOAWAY - frame. */ - int32_t last_proc_stream_id; - /* Counter of unique ID of PING. Wraps when it exceeds - NGHTTP2_MAX_UNIQUE_ID */ - uint32_t next_unique_id; - /* This is the last-stream-ID we have sent in GOAWAY */ - int32_t local_last_stream_id; - /* This is the value in GOAWAY frame received from remote endpoint. */ - int32_t remote_last_stream_id; - /* Current sender window size. This value is computed against the - current initial window size of remote endpoint. */ - int32_t remote_window_size; - /* Keep track of the number of bytes received without - WINDOW_UPDATE. This could be negative after submitting negative - value to WINDOW_UPDATE. */ - int32_t recv_window_size; - /* The number of bytes consumed by the application and now is - subject to WINDOW_UPDATE. This is only used when auto - WINDOW_UPDATE is turned off. */ - int32_t consumed_size; - /* The amount of recv_window_size cut using submitting negative - value to WINDOW_UPDATE */ - int32_t recv_reduction; - /* window size for local flow control. It is initially set to - NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE and could be - increased/decreased by submitting WINDOW_UPDATE. See - nghttp2_submit_window_update(). */ - int32_t local_window_size; + /* Next Stream ID. Made unsigned int to detect >= (1 << 31). */ + uint32_t next_stream_id; + /* The last stream ID this session initiated. For client session, + this is the last stream ID it has sent. For server session, it + is the last promised stream ID sent in PUSH_PROMISE. */ + int32_t last_sent_stream_id; + /* The largest stream ID received so far */ + int32_t last_recv_stream_id; + /* The largest stream ID which has been processed in some way. This + value will be used as last-stream-id when sending GOAWAY + frame. */ + int32_t last_proc_stream_id; + /* Counter of unique ID of PING. Wraps when it exceeds + NGHTTP2_MAX_UNIQUE_ID */ + uint32_t next_unique_id; + /* This is the last-stream-ID we have sent in GOAWAY */ + int32_t local_last_stream_id; + /* This is the value in GOAWAY frame received from remote endpoint. */ + int32_t remote_last_stream_id; + /* Current sender window size. This value is computed against the + current initial window size of remote endpoint. */ + int32_t remote_window_size; + /* Keep track of the number of bytes received without + WINDOW_UPDATE. This could be negative after submitting negative + value to WINDOW_UPDATE. */ + int32_t recv_window_size; + /* The number of bytes consumed by the application and now is + subject to WINDOW_UPDATE. This is only used when auto + WINDOW_UPDATE is turned off. */ + int32_t consumed_size; + /* The amount of recv_window_size cut using submitting negative + value to WINDOW_UPDATE */ + int32_t recv_reduction; + /* window size for local flow control. It is initially set to + NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE and could be + increased/decreased by submitting WINDOW_UPDATE. See + nghttp2_submit_window_update(). */ + int32_t local_window_size; /* This flag is used to indicate that the local endpoint received initial SETTINGS frame from the remote endpoint. */ uint8_t remote_settings_received; /* Settings value received from the remote endpoint. */ - nghttp2_settings_storage remote_settings; - /* Settings value of the local endpoint. */ - nghttp2_settings_storage local_settings; - /* Option flags. This is bitwise-OR of 0 or more of nghttp2_optmask. */ - uint32_t opt_flags; - /* Unacked local SETTINGS_MAX_CONCURRENT_STREAMS value. We use this - to refuse the incoming stream if it exceeds this value. */ - uint32_t pending_local_max_concurrent_stream; - /* The bitwise OR of zero or more of nghttp2_typemask to indicate - that the default handling of extension frame is enabled. */ - uint32_t builtin_recv_ext_types; - /* Unacked local ENABLE_PUSH value. We use this to refuse - PUSH_PROMISE before SETTINGS ACK is received. */ - uint8_t pending_enable_push; + nghttp2_settings_storage remote_settings; + /* Settings value of the local endpoint. */ + nghttp2_settings_storage local_settings; + /* Option flags. This is bitwise-OR of 0 or more of nghttp2_optmask. */ + uint32_t opt_flags; + /* Unacked local SETTINGS_MAX_CONCURRENT_STREAMS value. We use this + to refuse the incoming stream if it exceeds this value. */ + uint32_t pending_local_max_concurrent_stream; + /* The bitwise OR of zero or more of nghttp2_typemask to indicate + that the default handling of extension frame is enabled. */ + uint32_t builtin_recv_ext_types; + /* Unacked local ENABLE_PUSH value. We use this to refuse + PUSH_PROMISE before SETTINGS ACK is received. */ + uint8_t pending_enable_push; /* 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; - /* Nonzero if the session is server side. */ - uint8_t server; - /* Flags indicating GOAWAY is sent and/or received. The flags are - composed by bitwise OR-ing nghttp2_goaway_flag. */ - uint8_t goaway_flags; - /* This flag is used to reduce excessive queuing of WINDOW_UPDATE to - this session. The nonzero does not necessarily mean - WINDOW_UPDATE is not queued. */ - uint8_t window_update_queued; - /* Bitfield of extension frame types that application is willing to - receive. To designate the bit of given frame type i, use - user_recv_ext_types[i / 8] & (1 << (i & 0x7)). First 10 frame - types are standard frame types and not used in this bitfield. If - bit is set, it indicates that incoming frame with that type is - passed to user defined callbacks, otherwise they are ignored. */ - uint8_t user_recv_ext_types[32]; -}; - -/* Struct used when updating initial window size of each active - stream. */ -typedef struct { - nghttp2_session *session; - int32_t new_window_size, old_window_size; -} nghttp2_update_window_size_arg; - -typedef struct { - nghttp2_session *session; - /* linked list of streams to close */ - nghttp2_stream *head; - int32_t last_stream_id; - /* nonzero if GOAWAY is sent to peer, which means we are going to - close incoming streams. zero if GOAWAY is received from peer and - we are going to close outgoing streams. */ - int incoming; -} nghttp2_close_stream_on_goaway_arg; - -/* TODO stream timeout etc */ - -/* - * Returns nonzero value if |stream_id| is initiated by local - * endpoint. - */ -int nghttp2_session_is_my_stream_id(nghttp2_session *session, - int32_t stream_id); - -/* - * Adds |item| to the outbound queue in |session|. When this function - * succeeds, it takes ownership of |item|. So caller must not free it - * on success. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - * NGHTTP2_ERR_STREAM_CLOSED - * Stream already closed (DATA and PUSH_PROMISE frame only) - */ -int nghttp2_session_add_item(nghttp2_session *session, - nghttp2_outbound_item *item); - -/* - * Adds RST_STREAM frame for the stream |stream_id| with the error - * code |error_code|. This is a convenient function built on top of - * nghttp2_session_add_frame() to add RST_STREAM easily. - * - * This function simply returns 0 without adding RST_STREAM frame if - * given stream is in NGHTTP2_STREAM_CLOSING state, because multiple - * RST_STREAM for a stream is redundant. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -int nghttp2_session_add_rst_stream(nghttp2_session *session, int32_t stream_id, - uint32_t error_code); - -/* - * Adds PING frame. This is a convenient functin built on top of - * nghttp2_session_add_frame() to add PING easily. - * - * If the |opaque_data| is not NULL, it must point to 8 bytes memory - * region of data. The data pointed by |opaque_data| is copied. It can - * be NULL. In this case, 8 bytes NULL is used. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - * NGHTTP2_ERR_FLOODED - * There are too many items in outbound queue; this only happens - * if NGHTTP2_FLAG_ACK is set in |flags| - */ -int nghttp2_session_add_ping(nghttp2_session *session, uint8_t flags, - const uint8_t *opaque_data); - -/* - * Adds GOAWAY frame with the last-stream-ID |last_stream_id| and the - * error code |error_code|. This is a convenient function built on top - * of nghttp2_session_add_frame() to add GOAWAY easily. The - * |aux_flags| are bitwise-OR of one or more of - * nghttp2_goaway_aux_flag. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - * NGHTTP2_ERR_INVALID_ARGUMENT - * The |opaque_data_len| is too large. - */ -int nghttp2_session_add_goaway(nghttp2_session *session, int32_t last_stream_id, - uint32_t error_code, const uint8_t *opaque_data, - size_t opaque_data_len, uint8_t aux_flags); - -/* - * Adds WINDOW_UPDATE frame with stream ID |stream_id| and - * window-size-increment |window_size_increment|. This is a convenient - * function built on top of nghttp2_session_add_frame() to add - * WINDOW_UPDATE easily. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - */ -int nghttp2_session_add_window_update(nghttp2_session *session, uint8_t flags, - int32_t stream_id, - int32_t window_size_increment); - -/* - * Adds SETTINGS frame. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - * NGHTTP2_ERR_FLOODED - * There are too many items in outbound queue; this only happens - * if NGHTTP2_FLAG_ACK is set in |flags| - */ -int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags, - const nghttp2_settings_entry *iv, size_t niv); - -/* - * Creates new stream in |session| with stream ID |stream_id|, - * priority |pri_spec| and flags |flags|. The |flags| is bitwise OR - * of nghttp2_stream_flag. Since this function is called when initial - * HEADERS is sent or received, these flags are taken from it. The - * state of stream is set to |initial_state|. The |stream_user_data| - * is a pointer to the arbitrary user supplied data to be associated - * to this stream. - * - * If |initial_state| is NGHTTP2_STREAM_RESERVED, this function sets - * NGHTTP2_STREAM_FLAG_PUSH flag set. - * - * This function returns a pointer to created new stream object, or - * NULL. - * - * This function adjusts neither the number of closed streams or idle - * streams. The caller should manually call - * nghttp2_session_adjust_closed_stream() or - * nghttp2_session_adjust_idle_stream() respectively. - */ -nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session, - int32_t stream_id, uint8_t flags, - nghttp2_priority_spec *pri_spec, - nghttp2_stream_state initial_state, - void *stream_user_data); - -/* - * Closes stream whose stream ID is |stream_id|. The reason of closure - * is indicated by the |error_code|. When closing the stream, - * on_stream_close_callback will be called. - * - * If the session is initialized as server and |stream| is incoming - * stream, stream is just marked closed and this function calls - * nghttp2_session_keep_closed_stream() with |stream|. Otherwise, - * |stream| will be deleted from memory. - * - * This function returns 0 if it succeeds, or one the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory - * NGHTTP2_ERR_INVALID_ARGUMENT - * The specified stream does not exist. - * NGHTTP2_ERR_CALLBACK_FAILURE - * The callback function failed. - */ -int nghttp2_session_close_stream(nghttp2_session *session, int32_t stream_id, - uint32_t error_code); - -/* - * Deletes |stream| from memory. After this function returns, stream - * cannot be accessed. - * - * This function returns 0 if it succeeds, or one the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory - */ -int nghttp2_session_destroy_stream(nghttp2_session *session, - nghttp2_stream *stream); - -/* - * Tries to keep incoming closed stream |stream|. Due to the - * limitation of maximum number of streams in memory, |stream| is not - * closed and just deleted from memory (see - * nghttp2_session_destroy_stream). - */ -void nghttp2_session_keep_closed_stream(nghttp2_session *session, - nghttp2_stream *stream); - -/* - * Appends |stream| to linked list |session->idle_stream_head|. We - * apply fixed limit for list size. To fit into that limit, one or - * more oldest streams are removed from list as necessary. - */ -void nghttp2_session_keep_idle_stream(nghttp2_session *session, - nghttp2_stream *stream); - -/* - * Detaches |stream| from idle streams linked list. - */ -void nghttp2_session_detach_idle_stream(nghttp2_session *session, - nghttp2_stream *stream); - -/* - * Deletes closed stream to ensure that number of incoming streams - * including active and closed is in the maximum number of allowed - * stream. - * - * This function returns 0 if it succeeds, or one the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory - */ -int nghttp2_session_adjust_closed_stream(nghttp2_session *session); - -/* - * Deletes idle stream to ensure that number of idle streams is in - * certain limit. - * - * This function returns 0 if it succeeds, or one the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory - */ -int nghttp2_session_adjust_idle_stream(nghttp2_session *session); - -/* - * If further receptions and transmissions over the stream |stream_id| - * are disallowed, close the stream with error code NGHTTP2_NO_ERROR. - * - * This function returns 0 if it - * succeeds, or one of the following negative error codes: - * - * NGHTTP2_ERR_INVALID_ARGUMENT - * The specified stream does not exist. - */ -int nghttp2_session_close_stream_if_shut_rdwr(nghttp2_session *session, - nghttp2_stream *stream); - -int nghttp2_session_on_request_headers_received(nghttp2_session *session, - nghttp2_frame *frame); - -int nghttp2_session_on_response_headers_received(nghttp2_session *session, - nghttp2_frame *frame, - nghttp2_stream *stream); - -int nghttp2_session_on_push_response_headers_received(nghttp2_session *session, - nghttp2_frame *frame, - nghttp2_stream *stream); - -/* - * Called when HEADERS is received, assuming |frame| is properly - * initialized. This function does first validate received frame and - * then open stream and call callback functions. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - * NGHTTP2_ERR_IGN_HEADER_BLOCK - * Frame was rejected and header block must be decoded but - * result must be ignored. - * NGHTTP2_ERR_CALLBACK_FAILURE - * The read_callback failed - */ -int nghttp2_session_on_headers_received(nghttp2_session *session, - nghttp2_frame *frame, - nghttp2_stream *stream); - -/* - * Called when PRIORITY is received, assuming |frame| is properly - * initialized. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - * NGHTTP2_ERR_CALLBACK_FAILURE - * The read_callback failed - */ -int nghttp2_session_on_priority_received(nghttp2_session *session, - nghttp2_frame *frame); - -/* - * Called when RST_STREAM is received, assuming |frame| is properly - * initialized. - * - * This function returns 0 if it succeeds, or one the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory - * NGHTTP2_ERR_CALLBACK_FAILURE - * The read_callback failed - */ -int nghttp2_session_on_rst_stream_received(nghttp2_session *session, - nghttp2_frame *frame); - -/* - * Called when SETTINGS is received, assuming |frame| is properly - * initialized. If |noack| is non-zero, SETTINGS with ACK will not be - * submitted. If |frame| has NGHTTP2_FLAG_ACK flag set, no SETTINGS - * with ACK will not be submitted regardless of |noack|. - * - * This function returns 0 if it succeeds, or one the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory - * NGHTTP2_ERR_CALLBACK_FAILURE - * The read_callback failed - * NGHTTP2_ERR_FLOODED - * There are too many items in outbound queue, and this is most - * likely caused by misbehaviour of peer. - */ -int nghttp2_session_on_settings_received(nghttp2_session *session, - nghttp2_frame *frame, int noack); - -/* - * Called when PUSH_PROMISE is received, assuming |frame| is properly - * initialized. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - * NGHTTP2_ERR_IGN_HEADER_BLOCK - * Frame was rejected and header block must be decoded but - * result must be ignored. - * NGHTTP2_ERR_CALLBACK_FAILURE - * The read_callback failed - */ -int nghttp2_session_on_push_promise_received(nghttp2_session *session, - nghttp2_frame *frame); - -/* - * Called when PING is received, assuming |frame| is properly - * initialized. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - * NGHTTP2_ERR_CALLBACK_FAILURE + /* Nonzero if the session is server side. */ + uint8_t server; + /* Flags indicating GOAWAY is sent and/or received. The flags are + composed by bitwise OR-ing nghttp2_goaway_flag. */ + uint8_t goaway_flags; + /* This flag is used to reduce excessive queuing of WINDOW_UPDATE to + this session. The nonzero does not necessarily mean + WINDOW_UPDATE is not queued. */ + uint8_t window_update_queued; + /* Bitfield of extension frame types that application is willing to + receive. To designate the bit of given frame type i, use + user_recv_ext_types[i / 8] & (1 << (i & 0x7)). First 10 frame + types are standard frame types and not used in this bitfield. If + bit is set, it indicates that incoming frame with that type is + passed to user defined callbacks, otherwise they are ignored. */ + uint8_t user_recv_ext_types[32]; +}; + +/* Struct used when updating initial window size of each active + stream. */ +typedef struct { + nghttp2_session *session; + int32_t new_window_size, old_window_size; +} nghttp2_update_window_size_arg; + +typedef struct { + nghttp2_session *session; + /* linked list of streams to close */ + nghttp2_stream *head; + int32_t last_stream_id; + /* nonzero if GOAWAY is sent to peer, which means we are going to + close incoming streams. zero if GOAWAY is received from peer and + we are going to close outgoing streams. */ + int incoming; +} nghttp2_close_stream_on_goaway_arg; + +/* TODO stream timeout etc */ + +/* + * Returns nonzero value if |stream_id| is initiated by local + * endpoint. + */ +int nghttp2_session_is_my_stream_id(nghttp2_session *session, + int32_t stream_id); + +/* + * Adds |item| to the outbound queue in |session|. When this function + * succeeds, it takes ownership of |item|. So caller must not free it + * on success. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_STREAM_CLOSED + * Stream already closed (DATA and PUSH_PROMISE frame only) + */ +int nghttp2_session_add_item(nghttp2_session *session, + nghttp2_outbound_item *item); + +/* + * Adds RST_STREAM frame for the stream |stream_id| with the error + * code |error_code|. This is a convenient function built on top of + * nghttp2_session_add_frame() to add RST_STREAM easily. + * + * This function simply returns 0 without adding RST_STREAM frame if + * given stream is in NGHTTP2_STREAM_CLOSING state, because multiple + * RST_STREAM for a stream is redundant. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_session_add_rst_stream(nghttp2_session *session, int32_t stream_id, + uint32_t error_code); + +/* + * Adds PING frame. This is a convenient functin built on top of + * nghttp2_session_add_frame() to add PING easily. + * + * If the |opaque_data| is not NULL, it must point to 8 bytes memory + * region of data. The data pointed by |opaque_data| is copied. It can + * be NULL. In this case, 8 bytes NULL is used. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_FLOODED + * There are too many items in outbound queue; this only happens + * if NGHTTP2_FLAG_ACK is set in |flags| + */ +int nghttp2_session_add_ping(nghttp2_session *session, uint8_t flags, + const uint8_t *opaque_data); + +/* + * Adds GOAWAY frame with the last-stream-ID |last_stream_id| and the + * error code |error_code|. This is a convenient function built on top + * of nghttp2_session_add_frame() to add GOAWAY easily. The + * |aux_flags| are bitwise-OR of one or more of + * nghttp2_goaway_aux_flag. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_INVALID_ARGUMENT + * The |opaque_data_len| is too large. + */ +int nghttp2_session_add_goaway(nghttp2_session *session, int32_t last_stream_id, + uint32_t error_code, const uint8_t *opaque_data, + size_t opaque_data_len, uint8_t aux_flags); + +/* + * Adds WINDOW_UPDATE frame with stream ID |stream_id| and + * window-size-increment |window_size_increment|. This is a convenient + * function built on top of nghttp2_session_add_frame() to add + * WINDOW_UPDATE easily. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_session_add_window_update(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + int32_t window_size_increment); + +/* + * Adds SETTINGS frame. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_FLOODED + * There are too many items in outbound queue; this only happens + * if NGHTTP2_FLAG_ACK is set in |flags| + */ +int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags, + const nghttp2_settings_entry *iv, size_t niv); + +/* + * Creates new stream in |session| with stream ID |stream_id|, + * priority |pri_spec| and flags |flags|. The |flags| is bitwise OR + * of nghttp2_stream_flag. Since this function is called when initial + * HEADERS is sent or received, these flags are taken from it. The + * state of stream is set to |initial_state|. The |stream_user_data| + * is a pointer to the arbitrary user supplied data to be associated + * to this stream. + * + * If |initial_state| is NGHTTP2_STREAM_RESERVED, this function sets + * NGHTTP2_STREAM_FLAG_PUSH flag set. + * + * This function returns a pointer to created new stream object, or + * NULL. + * + * This function adjusts neither the number of closed streams or idle + * streams. The caller should manually call + * nghttp2_session_adjust_closed_stream() or + * nghttp2_session_adjust_idle_stream() respectively. + */ +nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session, + int32_t stream_id, uint8_t flags, + nghttp2_priority_spec *pri_spec, + nghttp2_stream_state initial_state, + void *stream_user_data); + +/* + * Closes stream whose stream ID is |stream_id|. The reason of closure + * is indicated by the |error_code|. When closing the stream, + * on_stream_close_callback will be called. + * + * If the session is initialized as server and |stream| is incoming + * stream, stream is just marked closed and this function calls + * nghttp2_session_keep_closed_stream() with |stream|. Otherwise, + * |stream| will be deleted from memory. + * + * This function returns 0 if it succeeds, or one the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + * NGHTTP2_ERR_INVALID_ARGUMENT + * The specified stream does not exist. + * NGHTTP2_ERR_CALLBACK_FAILURE * The callback function failed. - * NGHTTP2_ERR_FLOODED - * There are too many items in outbound queue, and this is most - * likely caused by misbehaviour of peer. - */ -int nghttp2_session_on_ping_received(nghttp2_session *session, - nghttp2_frame *frame); - -/* - * Called when GOAWAY is received, assuming |frame| is properly - * initialized. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - * NGHTTP2_ERR_CALLBACK_FAILURE + */ +int nghttp2_session_close_stream(nghttp2_session *session, int32_t stream_id, + uint32_t error_code); + +/* + * Deletes |stream| from memory. After this function returns, stream + * cannot be accessed. + * + * This function returns 0 if it succeeds, or one the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_session_destroy_stream(nghttp2_session *session, + nghttp2_stream *stream); + +/* + * Tries to keep incoming closed stream |stream|. Due to the + * limitation of maximum number of streams in memory, |stream| is not + * closed and just deleted from memory (see + * nghttp2_session_destroy_stream). + */ +void nghttp2_session_keep_closed_stream(nghttp2_session *session, + nghttp2_stream *stream); + +/* + * Appends |stream| to linked list |session->idle_stream_head|. We + * apply fixed limit for list size. To fit into that limit, one or + * more oldest streams are removed from list as necessary. + */ +void nghttp2_session_keep_idle_stream(nghttp2_session *session, + nghttp2_stream *stream); + +/* + * Detaches |stream| from idle streams linked list. + */ +void nghttp2_session_detach_idle_stream(nghttp2_session *session, + nghttp2_stream *stream); + +/* + * Deletes closed stream to ensure that number of incoming streams + * including active and closed is in the maximum number of allowed + * stream. + * + * This function returns 0 if it succeeds, or one the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_session_adjust_closed_stream(nghttp2_session *session); + +/* + * Deletes idle stream to ensure that number of idle streams is in + * certain limit. + * + * This function returns 0 if it succeeds, or one the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_session_adjust_idle_stream(nghttp2_session *session); + +/* + * If further receptions and transmissions over the stream |stream_id| + * are disallowed, close the stream with error code NGHTTP2_NO_ERROR. + * + * This function returns 0 if it + * succeeds, or one of the following negative error codes: + * + * NGHTTP2_ERR_INVALID_ARGUMENT + * The specified stream does not exist. + */ +int nghttp2_session_close_stream_if_shut_rdwr(nghttp2_session *session, + nghttp2_stream *stream); + +int nghttp2_session_on_request_headers_received(nghttp2_session *session, + nghttp2_frame *frame); + +int nghttp2_session_on_response_headers_received(nghttp2_session *session, + nghttp2_frame *frame, + nghttp2_stream *stream); + +int nghttp2_session_on_push_response_headers_received(nghttp2_session *session, + nghttp2_frame *frame, + nghttp2_stream *stream); + +/* + * Called when HEADERS is received, assuming |frame| is properly + * initialized. This function does first validate received frame and + * then open stream and call callback functions. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_IGN_HEADER_BLOCK + * Frame was rejected and header block must be decoded but + * result must be ignored. + * NGHTTP2_ERR_CALLBACK_FAILURE + * The read_callback failed + */ +int nghttp2_session_on_headers_received(nghttp2_session *session, + nghttp2_frame *frame, + nghttp2_stream *stream); + +/* + * Called when PRIORITY is received, assuming |frame| is properly + * initialized. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_CALLBACK_FAILURE + * The read_callback failed + */ +int nghttp2_session_on_priority_received(nghttp2_session *session, + nghttp2_frame *frame); + +/* + * Called when RST_STREAM is received, assuming |frame| is properly + * initialized. + * + * This function returns 0 if it succeeds, or one the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + * NGHTTP2_ERR_CALLBACK_FAILURE + * The read_callback failed + */ +int nghttp2_session_on_rst_stream_received(nghttp2_session *session, + nghttp2_frame *frame); + +/* + * Called when SETTINGS is received, assuming |frame| is properly + * initialized. If |noack| is non-zero, SETTINGS with ACK will not be + * submitted. If |frame| has NGHTTP2_FLAG_ACK flag set, no SETTINGS + * with ACK will not be submitted regardless of |noack|. + * + * This function returns 0 if it succeeds, or one the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + * NGHTTP2_ERR_CALLBACK_FAILURE + * The read_callback failed + * NGHTTP2_ERR_FLOODED + * There are too many items in outbound queue, and this is most + * likely caused by misbehaviour of peer. + */ +int nghttp2_session_on_settings_received(nghttp2_session *session, + nghttp2_frame *frame, int noack); + +/* + * Called when PUSH_PROMISE is received, assuming |frame| is properly + * initialized. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_IGN_HEADER_BLOCK + * Frame was rejected and header block must be decoded but + * result must be ignored. + * NGHTTP2_ERR_CALLBACK_FAILURE + * The read_callback failed + */ +int nghttp2_session_on_push_promise_received(nghttp2_session *session, + nghttp2_frame *frame); + +/* + * Called when PING is received, assuming |frame| is properly + * initialized. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_CALLBACK_FAILURE * The callback function failed. - */ -int nghttp2_session_on_goaway_received(nghttp2_session *session, - nghttp2_frame *frame); - -/* - * Called when WINDOW_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_NOMEM - * Out of memory. - * NGHTTP2_ERR_CALLBACK_FAILURE + * NGHTTP2_ERR_FLOODED + * There are too many items in outbound queue, and this is most + * likely caused by misbehaviour of peer. + */ +int nghttp2_session_on_ping_received(nghttp2_session *session, + nghttp2_frame *frame); + +/* + * Called when GOAWAY is received, assuming |frame| is properly + * initialized. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_CALLBACK_FAILURE * The callback function failed. - */ -int nghttp2_session_on_window_update_received(nghttp2_session *session, - nghttp2_frame *frame); - -/* - * Called when ALTSVC 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 + */ +int nghttp2_session_on_goaway_received(nghttp2_session *session, + nghttp2_frame *frame); + +/* + * Called when WINDOW_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_NOMEM + * Out of memory. + * NGHTTP2_ERR_CALLBACK_FAILURE * The callback function failed. - */ -int nghttp2_session_on_altsvc_received(nghttp2_session *session, - nghttp2_frame *frame); - -/* + */ +int nghttp2_session_on_window_update_received(nghttp2_session *session, + nghttp2_frame *frame); + +/* + * Called when ALTSVC 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_altsvc_received(nghttp2_session *session, + nghttp2_frame *frame); + +/* * Called when ORIGIN is received, assuming |frame| is properly * initialized. * @@ -774,132 +774,132 @@ int nghttp2_session_on_origin_received(nghttp2_session *session, nghttp2_frame *frame); /* - * Called when DATA is received, assuming |frame| is properly - * initialized. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - * NGHTTP2_ERR_CALLBACK_FAILURE + * Called when DATA is received, assuming |frame| is properly + * initialized. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_CALLBACK_FAILURE * The callback function failed. - */ -int nghttp2_session_on_data_received(nghttp2_session *session, - nghttp2_frame *frame); - -/* - * Returns nghttp2_stream* object whose stream ID is |stream_id|. It - * could be NULL if such stream does not exist. This function returns - * NULL if stream is marked as closed. - */ -nghttp2_stream *nghttp2_session_get_stream(nghttp2_session *session, - int32_t stream_id); - -/* - * This function behaves like nghttp2_session_get_stream(), but it - * returns stream object even if it is marked as closed or in - * NGHTTP2_STREAM_IDLE state. - */ -nghttp2_stream *nghttp2_session_get_stream_raw(nghttp2_session *session, - int32_t stream_id); - -/* - * Packs DATA frame |frame| in wire frame format and stores it in - * |bufs|. Payload will be read using |aux_data->data_prd|. The - * length of payload is at most |datamax| bytes. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_DEFERRED - * The DATA frame is postponed. - * NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE - * The read_callback failed (stream error). - * NGHTTP2_ERR_NOMEM - * Out of memory. - * NGHTTP2_ERR_CALLBACK_FAILURE - * The read_callback failed (session error). - */ -int nghttp2_session_pack_data(nghttp2_session *session, nghttp2_bufs *bufs, - size_t datamax, nghttp2_frame *frame, - nghttp2_data_aux_data *aux_data, - nghttp2_stream *stream); - -/* - * Pops and returns next item to send. If there is no such item, - * returns NULL. This function takes into account max concurrent - * streams. That means if session->ob_syn has item and max concurrent - * streams is reached, the even if other queues contain items, then - * this function returns NULL. - */ -nghttp2_outbound_item * -nghttp2_session_pop_next_ob_item(nghttp2_session *session); - -/* - * Returns next item to send. If there is no such item, this function - * returns NULL. This function takes into account max concurrent - * streams. That means if session->ob_syn has item and max concurrent - * streams is reached, the even if other queues contain items, then - * this function returns NULL. - */ -nghttp2_outbound_item * -nghttp2_session_get_next_ob_item(nghttp2_session *session); - -/* - * Updates local settings with the |iv|. The number of elements in the - * array pointed by the |iv| is given by the |niv|. This function - * assumes that the all settings_id member in |iv| are in range 1 to - * NGHTTP2_SETTINGS_MAX, inclusive. - * - * While updating individual stream's local window size, if the window - * size becomes strictly larger than NGHTTP2_MAX_WINDOW_SIZE, - * RST_STREAM is issued against such a stream. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory - */ -int nghttp2_session_update_local_settings(nghttp2_session *session, - nghttp2_settings_entry *iv, - size_t niv); - -/* - * Re-prioritize |stream|. The new priority specification is - * |pri_spec|. Caller must ensure that stream->hd.stream_id != - * pri_spec->stream_id. - * - * This function does not adjust the number of idle streams. The - * caller should call nghttp2_session_adjust_idle_stream() later. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory - */ -int nghttp2_session_reprioritize_stream(nghttp2_session *session, - nghttp2_stream *stream, - const nghttp2_priority_spec *pri_spec); - -/* - * Terminates current |session| with the |error_code|. The |reason| - * is NULL-terminated debug string. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. - * NGHTTP2_ERR_INVALID_ARGUMENT - * The |reason| is too long. - */ -int nghttp2_session_terminate_session_with_reason(nghttp2_session *session, - uint32_t error_code, - const char *reason); - + */ +int nghttp2_session_on_data_received(nghttp2_session *session, + nghttp2_frame *frame); + +/* + * Returns nghttp2_stream* object whose stream ID is |stream_id|. It + * could be NULL if such stream does not exist. This function returns + * NULL if stream is marked as closed. + */ +nghttp2_stream *nghttp2_session_get_stream(nghttp2_session *session, + int32_t stream_id); + +/* + * This function behaves like nghttp2_session_get_stream(), but it + * returns stream object even if it is marked as closed or in + * NGHTTP2_STREAM_IDLE state. + */ +nghttp2_stream *nghttp2_session_get_stream_raw(nghttp2_session *session, + int32_t stream_id); + +/* + * Packs DATA frame |frame| in wire frame format and stores it in + * |bufs|. Payload will be read using |aux_data->data_prd|. The + * length of payload is at most |datamax| bytes. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_DEFERRED + * The DATA frame is postponed. + * NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE + * The read_callback failed (stream error). + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_CALLBACK_FAILURE + * The read_callback failed (session error). + */ +int nghttp2_session_pack_data(nghttp2_session *session, nghttp2_bufs *bufs, + size_t datamax, nghttp2_frame *frame, + nghttp2_data_aux_data *aux_data, + nghttp2_stream *stream); + +/* + * Pops and returns next item to send. If there is no such item, + * returns NULL. This function takes into account max concurrent + * streams. That means if session->ob_syn has item and max concurrent + * streams is reached, the even if other queues contain items, then + * this function returns NULL. + */ +nghttp2_outbound_item * +nghttp2_session_pop_next_ob_item(nghttp2_session *session); + +/* + * Returns next item to send. If there is no such item, this function + * returns NULL. This function takes into account max concurrent + * streams. That means if session->ob_syn has item and max concurrent + * streams is reached, the even if other queues contain items, then + * this function returns NULL. + */ +nghttp2_outbound_item * +nghttp2_session_get_next_ob_item(nghttp2_session *session); + +/* + * Updates local settings with the |iv|. The number of elements in the + * array pointed by the |iv| is given by the |niv|. This function + * assumes that the all settings_id member in |iv| are in range 1 to + * NGHTTP2_SETTINGS_MAX, inclusive. + * + * While updating individual stream's local window size, if the window + * size becomes strictly larger than NGHTTP2_MAX_WINDOW_SIZE, + * RST_STREAM is issued against such a stream. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_session_update_local_settings(nghttp2_session *session, + nghttp2_settings_entry *iv, + size_t niv); + +/* + * Re-prioritize |stream|. The new priority specification is + * |pri_spec|. Caller must ensure that stream->hd.stream_id != + * pri_spec->stream_id. + * + * This function does not adjust the number of idle streams. The + * caller should call nghttp2_session_adjust_idle_stream() later. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_session_reprioritize_stream(nghttp2_session *session, + nghttp2_stream *stream, + const nghttp2_priority_spec *pri_spec); + +/* + * Terminates current |session| with the |error_code|. The |reason| + * is NULL-terminated debug string. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_INVALID_ARGUMENT + * The |reason| is too long. + */ +int nghttp2_session_terminate_session_with_reason(nghttp2_session *session, + uint32_t error_code, + const char *reason); + /* * Accumulates received bytes |delta_size| for connection-level flow * control and decides whether to send WINDOW_UPDATE to the @@ -932,4 +932,4 @@ int nghttp2_session_update_recv_stream_window_size(nghttp2_session *session, size_t delta_size, int send_window_update); -#endif /* NGHTTP2_SESSION_H */ +#endif /* NGHTTP2_SESSION_H */ diff --git a/contrib/libs/nghttp2/lib/nghttp2_stream.c b/contrib/libs/nghttp2/lib/nghttp2_stream.c index fbc5dd0752..96e1d9fe0f 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_stream.c +++ b/contrib/libs/nghttp2/lib/nghttp2_stream.c @@ -1,1000 +1,1000 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * 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_stream.h" - -#include <assert.h> -#include <stdio.h> - -#include "nghttp2_session.h" -#include "nghttp2_helper.h" -#include "nghttp2_debug.h" +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * 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_stream.h" + +#include <assert.h> +#include <stdio.h> + +#include "nghttp2_session.h" +#include "nghttp2_helper.h" +#include "nghttp2_debug.h" #include "nghttp2_frame.h" - -/* Maximum distance between any two stream's cycle in the same - prirority queue. Imagine stream A's cycle is A, and stream B's - cycle is B, and A < B. The cycle is unsigned 32 bit integer, it - may get overflow. Because of how we calculate the next cycle - value, if B - A is less than or equals to - NGHTTP2_MAX_CYCLE_DISTANCE, A and B are in the same scale, in other - words, B is really greater than or equal to A. Otherwise, A is a - result of overflow, and it is actually A > B if we consider that - fact. */ + +/* Maximum distance between any two stream's cycle in the same + prirority queue. Imagine stream A's cycle is A, and stream B's + cycle is B, and A < B. The cycle is unsigned 32 bit integer, it + may get overflow. Because of how we calculate the next cycle + value, if B - A is less than or equals to + NGHTTP2_MAX_CYCLE_DISTANCE, A and B are in the same scale, in other + words, B is really greater than or equal to A. Otherwise, A is a + result of overflow, and it is actually A > B if we consider that + fact. */ #define NGHTTP2_MAX_CYCLE_DISTANCE \ ((uint64_t)NGHTTP2_MAX_FRAME_SIZE_MAX * 256 + 255) - -static int stream_less(const void *lhsx, const void *rhsx) { - const nghttp2_stream *lhs, *rhs; - - lhs = nghttp2_struct_of(lhsx, nghttp2_stream, pq_entry); - rhs = nghttp2_struct_of(rhsx, nghttp2_stream, pq_entry); - - if (lhs->cycle == rhs->cycle) { - return lhs->seq < rhs->seq; - } - + +static int stream_less(const void *lhsx, const void *rhsx) { + const nghttp2_stream *lhs, *rhs; + + lhs = nghttp2_struct_of(lhsx, nghttp2_stream, pq_entry); + rhs = nghttp2_struct_of(rhsx, nghttp2_stream, pq_entry); + + if (lhs->cycle == rhs->cycle) { + return lhs->seq < rhs->seq; + } + return rhs->cycle - lhs->cycle <= NGHTTP2_MAX_CYCLE_DISTANCE; -} - -void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id, - uint8_t flags, nghttp2_stream_state initial_state, - int32_t weight, int32_t remote_initial_window_size, - int32_t local_initial_window_size, - void *stream_user_data, nghttp2_mem *mem) { - nghttp2_pq_init(&stream->obq, stream_less, mem); - - stream->stream_id = stream_id; - stream->flags = flags; - stream->state = initial_state; - stream->shut_flags = NGHTTP2_SHUT_NONE; - stream->stream_user_data = stream_user_data; - stream->item = NULL; - stream->remote_window_size = remote_initial_window_size; - stream->local_window_size = local_initial_window_size; - stream->recv_window_size = 0; - stream->consumed_size = 0; - stream->recv_reduction = 0; - stream->window_update_queued = 0; - - stream->dep_prev = NULL; - stream->dep_next = NULL; - stream->sib_prev = NULL; - stream->sib_next = NULL; - - stream->closed_prev = NULL; - stream->closed_next = NULL; - - stream->weight = weight; - stream->sum_dep_weight = 0; - - stream->http_flags = NGHTTP2_HTTP_FLAG_NONE; - stream->content_length = -1; - stream->recv_content_length = 0; - stream->status_code = -1; - - stream->queued = 0; - stream->descendant_last_cycle = 0; - stream->cycle = 0; - stream->pending_penalty = 0; - stream->descendant_next_seq = 0; - stream->seq = 0; - stream->last_writelen = 0; -} - -void nghttp2_stream_free(nghttp2_stream *stream) { - nghttp2_pq_free(&stream->obq); - /* We don't free stream->item. If it is assigned to aob, then - active_outbound_item_reset() will delete it. Otherwise, - nghttp2_stream_close() or session_del() will delete it. */ -} - -void nghttp2_stream_shutdown(nghttp2_stream *stream, nghttp2_shut_flag flag) { - stream->shut_flags = (uint8_t)(stream->shut_flags | flag); -} - -/* - * Returns nonzero if |stream| is active. This function does not take - * into account its descendants. - */ -static int stream_active(nghttp2_stream *stream) { - return stream->item && - (stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL) == 0; -} - -/* - * Returns nonzero if |stream| or one of its descendants is active - */ -static int stream_subtree_active(nghttp2_stream *stream) { - return stream_active(stream) || !nghttp2_pq_empty(&stream->obq); -} - -/* - * Returns next cycle for |stream|. - */ +} + +void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id, + uint8_t flags, nghttp2_stream_state initial_state, + int32_t weight, int32_t remote_initial_window_size, + int32_t local_initial_window_size, + void *stream_user_data, nghttp2_mem *mem) { + nghttp2_pq_init(&stream->obq, stream_less, mem); + + stream->stream_id = stream_id; + stream->flags = flags; + stream->state = initial_state; + stream->shut_flags = NGHTTP2_SHUT_NONE; + stream->stream_user_data = stream_user_data; + stream->item = NULL; + stream->remote_window_size = remote_initial_window_size; + stream->local_window_size = local_initial_window_size; + stream->recv_window_size = 0; + stream->consumed_size = 0; + stream->recv_reduction = 0; + stream->window_update_queued = 0; + + stream->dep_prev = NULL; + stream->dep_next = NULL; + stream->sib_prev = NULL; + stream->sib_next = NULL; + + stream->closed_prev = NULL; + stream->closed_next = NULL; + + stream->weight = weight; + stream->sum_dep_weight = 0; + + stream->http_flags = NGHTTP2_HTTP_FLAG_NONE; + stream->content_length = -1; + stream->recv_content_length = 0; + stream->status_code = -1; + + stream->queued = 0; + stream->descendant_last_cycle = 0; + stream->cycle = 0; + stream->pending_penalty = 0; + stream->descendant_next_seq = 0; + stream->seq = 0; + stream->last_writelen = 0; +} + +void nghttp2_stream_free(nghttp2_stream *stream) { + nghttp2_pq_free(&stream->obq); + /* We don't free stream->item. If it is assigned to aob, then + active_outbound_item_reset() will delete it. Otherwise, + nghttp2_stream_close() or session_del() will delete it. */ +} + +void nghttp2_stream_shutdown(nghttp2_stream *stream, nghttp2_shut_flag flag) { + stream->shut_flags = (uint8_t)(stream->shut_flags | flag); +} + +/* + * Returns nonzero if |stream| is active. This function does not take + * into account its descendants. + */ +static int stream_active(nghttp2_stream *stream) { + return stream->item && + (stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL) == 0; +} + +/* + * Returns nonzero if |stream| or one of its descendants is active + */ +static int stream_subtree_active(nghttp2_stream *stream) { + return stream_active(stream) || !nghttp2_pq_empty(&stream->obq); +} + +/* + * Returns next cycle for |stream|. + */ static void stream_next_cycle(nghttp2_stream *stream, uint64_t last_cycle) { uint64_t penalty; - + penalty = (uint64_t)stream->last_writelen * NGHTTP2_MAX_WEIGHT + - stream->pending_penalty; - - stream->cycle = last_cycle + penalty / (uint32_t)stream->weight; + stream->pending_penalty; + + stream->cycle = last_cycle + penalty / (uint32_t)stream->weight; stream->pending_penalty = (uint32_t)(penalty % (uint32_t)stream->weight); -} - -static int stream_obq_push(nghttp2_stream *dep_stream, nghttp2_stream *stream) { - int rv; - - for (; dep_stream && !stream->queued; - stream = dep_stream, dep_stream = dep_stream->dep_prev) { - stream_next_cycle(stream, dep_stream->descendant_last_cycle); - stream->seq = dep_stream->descendant_next_seq++; - +} + +static int stream_obq_push(nghttp2_stream *dep_stream, nghttp2_stream *stream) { + int rv; + + for (; dep_stream && !stream->queued; + stream = dep_stream, dep_stream = dep_stream->dep_prev) { + stream_next_cycle(stream, dep_stream->descendant_last_cycle); + stream->seq = dep_stream->descendant_next_seq++; + DEBUGF("stream: stream=%d obq push cycle=%lu\n", stream->stream_id, - stream->cycle); - - DEBUGF("stream: push stream %d to stream %d\n", stream->stream_id, - dep_stream->stream_id); - - rv = nghttp2_pq_push(&dep_stream->obq, &stream->pq_entry); - if (rv != 0) { - return rv; - } - stream->queued = 1; - } - - return 0; -} - -/* - * Removes |stream| from parent's obq. If removal of |stream| makes - * parent's obq empty, and parent is not active, then parent is also - * removed. This process is repeated recursively. - */ -static void stream_obq_remove(nghttp2_stream *stream) { - nghttp2_stream *dep_stream; - - dep_stream = stream->dep_prev; - - if (!stream->queued) { - return; - } - - for (; dep_stream; stream = dep_stream, dep_stream = dep_stream->dep_prev) { - DEBUGF("stream: remove stream %d from stream %d\n", stream->stream_id, - dep_stream->stream_id); - - nghttp2_pq_remove(&dep_stream->obq, &stream->pq_entry); - - assert(stream->queued); - - stream->queued = 0; - stream->cycle = 0; - stream->pending_penalty = 0; - stream->descendant_last_cycle = 0; - stream->last_writelen = 0; - - if (stream_subtree_active(dep_stream)) { - return; - } - } -} - -/* - * Moves |stream| from |src|'s obq to |dest|'s obq. Removal from - * |src|'s obq is just done calling nghttp2_pq_remove(), so it does - * not recursively remove |src| and ancestors, like - * stream_obq_remove(). - */ -static int stream_obq_move(nghttp2_stream *dest, nghttp2_stream *src, - nghttp2_stream *stream) { - if (!stream->queued) { - return 0; - } - - DEBUGF("stream: remove stream %d from stream %d (move)\n", stream->stream_id, - src->stream_id); - - nghttp2_pq_remove(&src->obq, &stream->pq_entry); - stream->queued = 0; - - return stream_obq_push(dest, stream); -} - -void nghttp2_stream_reschedule(nghttp2_stream *stream) { - nghttp2_stream *dep_stream; - - assert(stream->queued); - - dep_stream = stream->dep_prev; - - for (; dep_stream; stream = dep_stream, dep_stream = dep_stream->dep_prev) { - nghttp2_pq_remove(&dep_stream->obq, &stream->pq_entry); - - stream_next_cycle(stream, dep_stream->descendant_last_cycle); - stream->seq = dep_stream->descendant_next_seq++; - - nghttp2_pq_push(&dep_stream->obq, &stream->pq_entry); - + stream->cycle); + + DEBUGF("stream: push stream %d to stream %d\n", stream->stream_id, + dep_stream->stream_id); + + rv = nghttp2_pq_push(&dep_stream->obq, &stream->pq_entry); + if (rv != 0) { + return rv; + } + stream->queued = 1; + } + + return 0; +} + +/* + * Removes |stream| from parent's obq. If removal of |stream| makes + * parent's obq empty, and parent is not active, then parent is also + * removed. This process is repeated recursively. + */ +static void stream_obq_remove(nghttp2_stream *stream) { + nghttp2_stream *dep_stream; + + dep_stream = stream->dep_prev; + + if (!stream->queued) { + return; + } + + for (; dep_stream; stream = dep_stream, dep_stream = dep_stream->dep_prev) { + DEBUGF("stream: remove stream %d from stream %d\n", stream->stream_id, + dep_stream->stream_id); + + nghttp2_pq_remove(&dep_stream->obq, &stream->pq_entry); + + assert(stream->queued); + + stream->queued = 0; + stream->cycle = 0; + stream->pending_penalty = 0; + stream->descendant_last_cycle = 0; + stream->last_writelen = 0; + + if (stream_subtree_active(dep_stream)) { + return; + } + } +} + +/* + * Moves |stream| from |src|'s obq to |dest|'s obq. Removal from + * |src|'s obq is just done calling nghttp2_pq_remove(), so it does + * not recursively remove |src| and ancestors, like + * stream_obq_remove(). + */ +static int stream_obq_move(nghttp2_stream *dest, nghttp2_stream *src, + nghttp2_stream *stream) { + if (!stream->queued) { + return 0; + } + + DEBUGF("stream: remove stream %d from stream %d (move)\n", stream->stream_id, + src->stream_id); + + nghttp2_pq_remove(&src->obq, &stream->pq_entry); + stream->queued = 0; + + return stream_obq_push(dest, stream); +} + +void nghttp2_stream_reschedule(nghttp2_stream *stream) { + nghttp2_stream *dep_stream; + + assert(stream->queued); + + dep_stream = stream->dep_prev; + + for (; dep_stream; stream = dep_stream, dep_stream = dep_stream->dep_prev) { + nghttp2_pq_remove(&dep_stream->obq, &stream->pq_entry); + + stream_next_cycle(stream, dep_stream->descendant_last_cycle); + stream->seq = dep_stream->descendant_next_seq++; + + nghttp2_pq_push(&dep_stream->obq, &stream->pq_entry); + DEBUGF("stream: stream=%d obq resched cycle=%lu\n", stream->stream_id, - stream->cycle); - - dep_stream->last_writelen = stream->last_writelen; - } -} - -void nghttp2_stream_change_weight(nghttp2_stream *stream, int32_t weight) { - nghttp2_stream *dep_stream; + stream->cycle); + + dep_stream->last_writelen = stream->last_writelen; + } +} + +void nghttp2_stream_change_weight(nghttp2_stream *stream, int32_t weight) { + nghttp2_stream *dep_stream; uint64_t last_cycle; - int32_t old_weight; + int32_t old_weight; uint64_t wlen_penalty; - - if (stream->weight == weight) { - return; - } - - old_weight = stream->weight; - stream->weight = weight; - - dep_stream = stream->dep_prev; - - if (!dep_stream) { - return; - } - - dep_stream->sum_dep_weight += weight - old_weight; - - if (!stream->queued) { - return; - } - - nghttp2_pq_remove(&dep_stream->obq, &stream->pq_entry); - + + if (stream->weight == weight) { + return; + } + + old_weight = stream->weight; + stream->weight = weight; + + dep_stream = stream->dep_prev; + + if (!dep_stream) { + return; + } + + dep_stream->sum_dep_weight += weight - old_weight; + + if (!stream->queued) { + return; + } + + nghttp2_pq_remove(&dep_stream->obq, &stream->pq_entry); + wlen_penalty = (uint64_t)stream->last_writelen * NGHTTP2_MAX_WEIGHT; - - /* Compute old stream->pending_penalty we used to calculate - stream->cycle */ - stream->pending_penalty = - (uint32_t)((stream->pending_penalty + (uint32_t)old_weight - - (wlen_penalty % (uint32_t)old_weight)) % - (uint32_t)old_weight); - - last_cycle = stream->cycle - - (wlen_penalty + stream->pending_penalty) / (uint32_t)old_weight; - - /* Now we have old stream->pending_penalty and new stream->weight in - place */ - stream_next_cycle(stream, last_cycle); - + + /* Compute old stream->pending_penalty we used to calculate + stream->cycle */ + stream->pending_penalty = + (uint32_t)((stream->pending_penalty + (uint32_t)old_weight - + (wlen_penalty % (uint32_t)old_weight)) % + (uint32_t)old_weight); + + last_cycle = stream->cycle - + (wlen_penalty + stream->pending_penalty) / (uint32_t)old_weight; + + /* Now we have old stream->pending_penalty and new stream->weight in + place */ + stream_next_cycle(stream, last_cycle); + if (dep_stream->descendant_last_cycle - stream->cycle <= NGHTTP2_MAX_CYCLE_DISTANCE) { - stream->cycle = dep_stream->descendant_last_cycle; - } - - /* Continue to use same stream->seq */ - - nghttp2_pq_push(&dep_stream->obq, &stream->pq_entry); - + stream->cycle = dep_stream->descendant_last_cycle; + } + + /* Continue to use same stream->seq */ + + nghttp2_pq_push(&dep_stream->obq, &stream->pq_entry); + DEBUGF("stream: stream=%d obq resched cycle=%lu\n", stream->stream_id, - stream->cycle); -} - -static nghttp2_stream *stream_last_sib(nghttp2_stream *stream) { - for (; stream->sib_next; stream = stream->sib_next) - ; - - return stream; -} - -int32_t nghttp2_stream_dep_distributed_weight(nghttp2_stream *stream, - int32_t weight) { - weight = stream->weight * weight / stream->sum_dep_weight; - - return nghttp2_max(1, weight); -} - -#ifdef STREAM_DEP_DEBUG - -static void ensure_inactive(nghttp2_stream *stream) { - nghttp2_stream *si; - - if (stream->queued) { - fprintf(stderr, "stream(%p)=%d, stream->queued = 1; want 0\n", stream, - stream->stream_id); - assert(0); - } - - if (stream_active(stream)) { - fprintf(stderr, "stream(%p)=%d, stream_active(stream) = 1; want 0\n", - stream, stream->stream_id); - assert(0); - } - - if (!nghttp2_pq_empty(&stream->obq)) { - fprintf(stderr, "stream(%p)=%d, nghttp2_pq_size() = %zu; want 0\n", stream, - stream->stream_id, nghttp2_pq_size(&stream->obq)); - assert(0); - } - - for (si = stream->dep_next; si; si = si->sib_next) { - ensure_inactive(si); - } -} - -static void check_queued(nghttp2_stream *stream) { - nghttp2_stream *si; - int queued; - - if (stream->queued) { - if (!stream_subtree_active(stream)) { - fprintf(stderr, - "stream(%p)=%d, stream->queued == 1, but " - "stream_active() == %d and nghttp2_pq_size(&stream->obq) = %zu\n", - stream, stream->stream_id, stream_active(stream), - nghttp2_pq_size(&stream->obq)); - assert(0); - } - if (!stream_active(stream)) { - queued = 0; - for (si = stream->dep_next; si; si = si->sib_next) { - if (si->queued) { - ++queued; - } - } - if (queued == 0) { - fprintf(stderr, - "stream(%p)=%d, stream->queued == 1, and " - "!stream_active(), but no descendants is queued\n", - stream, stream->stream_id); - assert(0); - } - } - - for (si = stream->dep_next; si; si = si->sib_next) { - check_queued(si); - } - } else { - if (stream_active(stream) || !nghttp2_pq_empty(&stream->obq)) { - fprintf(stderr, - "stream(%p) = %d, stream->queued == 0, but " - "stream_active(stream) == %d and " - "nghttp2_pq_size(&stream->obq) = %zu\n", - stream, stream->stream_id, stream_active(stream), - nghttp2_pq_size(&stream->obq)); - assert(0); - } - for (si = stream->dep_next; si; si = si->sib_next) { - ensure_inactive(si); - } - } -} - -static void check_sum_dep(nghttp2_stream *stream) { - nghttp2_stream *si; - int32_t n = 0; - for (si = stream->dep_next; si; si = si->sib_next) { - n += si->weight; - } - if (n != stream->sum_dep_weight) { - fprintf(stderr, "stream(%p)=%d, sum_dep_weight = %d; want %d\n", stream, - stream->stream_id, n, stream->sum_dep_weight); - assert(0); - } - for (si = stream->dep_next; si; si = si->sib_next) { - check_sum_dep(si); - } -} - -static void check_dep_prev(nghttp2_stream *stream) { - nghttp2_stream *si; - for (si = stream->dep_next; si; si = si->sib_next) { - if (si->dep_prev != stream) { - fprintf(stderr, "si->dep_prev = %p; want %p\n", si->dep_prev, stream); - assert(0); - } - check_dep_prev(si); - } -} - -#endif /* STREAM_DEP_DEBUG */ - -#ifdef STREAM_DEP_DEBUG -static void validate_tree(nghttp2_stream *stream) { - nghttp2_stream *si; - - if (!stream) { - return; - } - - for (; stream->dep_prev; stream = stream->dep_prev) - ; - - assert(stream->stream_id == 0); - assert(!stream->queued); - - fprintf(stderr, "checking...\n"); - if (nghttp2_pq_empty(&stream->obq)) { - fprintf(stderr, "root obq empty\n"); - for (si = stream->dep_next; si; si = si->sib_next) { - ensure_inactive(si); - } - } else { - for (si = stream->dep_next; si; si = si->sib_next) { - check_queued(si); - } - } - - check_sum_dep(stream); - check_dep_prev(stream); -} -#else /* !STREAM_DEP_DEBUG */ -static void validate_tree(nghttp2_stream *stream) { (void)stream; } -#endif /* !STREAM_DEP_DEBUG*/ - -static int stream_update_dep_on_attach_item(nghttp2_stream *stream) { - int rv; - - rv = stream_obq_push(stream->dep_prev, stream); - if (rv != 0) { - return rv; - } - - validate_tree(stream); - return 0; -} - -static int stream_update_dep_on_detach_item(nghttp2_stream *stream) { - if (nghttp2_pq_empty(&stream->obq)) { - stream_obq_remove(stream); - } - - validate_tree(stream); - - return 0; -} - -int nghttp2_stream_attach_item(nghttp2_stream *stream, - nghttp2_outbound_item *item) { - int rv; - - assert((stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL) == 0); - assert(stream->item == NULL); - - DEBUGF("stream: stream=%d attach item=%p\n", stream->stream_id, item); - - stream->item = item; - - rv = stream_update_dep_on_attach_item(stream); - if (rv != 0) { - /* This may relave stream->queued == 1, but stream->item == NULL. - But only consequence of this error is fatal one, and session - destruction. In that execution path, these inconsistency does - not matter. */ - stream->item = NULL; - return rv; - } - - return 0; -} - -int nghttp2_stream_detach_item(nghttp2_stream *stream) { - DEBUGF("stream: stream=%d detach item=%p\n", stream->stream_id, stream->item); - - stream->item = NULL; - stream->flags = (uint8_t)(stream->flags & ~NGHTTP2_STREAM_FLAG_DEFERRED_ALL); - - return stream_update_dep_on_detach_item(stream); -} - -int nghttp2_stream_defer_item(nghttp2_stream *stream, uint8_t flags) { - assert(stream->item); - - DEBUGF("stream: stream=%d defer item=%p cause=%02x\n", stream->stream_id, - stream->item, flags); - - stream->flags |= flags; - - return stream_update_dep_on_detach_item(stream); -} - -int nghttp2_stream_resume_deferred_item(nghttp2_stream *stream, uint8_t flags) { - assert(stream->item); - - DEBUGF("stream: stream=%d resume item=%p flags=%02x\n", stream->stream_id, - stream->item, flags); - - stream->flags = (uint8_t)(stream->flags & ~flags); - - if (stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL) { - return 0; - } - - return stream_update_dep_on_attach_item(stream); -} - -int nghttp2_stream_check_deferred_item(nghttp2_stream *stream) { - return stream->item && (stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL); -} - -int nghttp2_stream_check_deferred_by_flow_control(nghttp2_stream *stream) { - return stream->item && - (stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL); -} - -static int update_initial_window_size(int32_t *window_size_ptr, - int32_t new_initial_window_size, - int32_t old_initial_window_size) { - int64_t new_window_size = (int64_t)(*window_size_ptr) + - new_initial_window_size - old_initial_window_size; - if (INT32_MIN > new_window_size || - new_window_size > NGHTTP2_MAX_WINDOW_SIZE) { - return -1; - } - *window_size_ptr = (int32_t)new_window_size; - return 0; -} - -int nghttp2_stream_update_remote_initial_window_size( - nghttp2_stream *stream, int32_t new_initial_window_size, - int32_t old_initial_window_size) { - return update_initial_window_size(&stream->remote_window_size, - new_initial_window_size, - old_initial_window_size); -} - -int nghttp2_stream_update_local_initial_window_size( - nghttp2_stream *stream, int32_t new_initial_window_size, - int32_t old_initial_window_size) { - return update_initial_window_size(&stream->local_window_size, - new_initial_window_size, - old_initial_window_size); -} - -void nghttp2_stream_promise_fulfilled(nghttp2_stream *stream) { - stream->state = NGHTTP2_STREAM_OPENED; - stream->flags = (uint8_t)(stream->flags & ~NGHTTP2_STREAM_FLAG_PUSH); -} - -int nghttp2_stream_dep_find_ancestor(nghttp2_stream *stream, - nghttp2_stream *target) { - for (; stream; stream = stream->dep_prev) { - if (stream == target) { - return 1; - } - } - return 0; -} - -int nghttp2_stream_dep_insert(nghttp2_stream *dep_stream, - nghttp2_stream *stream) { - nghttp2_stream *si; - int rv; - - DEBUGF("stream: dep_insert dep_stream(%p)=%d, stream(%p)=%d\n", dep_stream, - dep_stream->stream_id, stream, stream->stream_id); - - stream->sum_dep_weight = dep_stream->sum_dep_weight; - dep_stream->sum_dep_weight = stream->weight; - - if (dep_stream->dep_next) { - for (si = dep_stream->dep_next; si; si = si->sib_next) { - si->dep_prev = stream; - if (si->queued) { - rv = stream_obq_move(stream, dep_stream, si); - if (rv != 0) { - return rv; - } - } - } - - if (stream_subtree_active(stream)) { - rv = stream_obq_push(dep_stream, stream); - if (rv != 0) { - return rv; - } - } - - stream->dep_next = dep_stream->dep_next; - } - - dep_stream->dep_next = stream; - stream->dep_prev = dep_stream; - - validate_tree(stream); - - return 0; -} - -static void set_dep_prev(nghttp2_stream *stream, nghttp2_stream *dep) { - for (; stream; stream = stream->sib_next) { - stream->dep_prev = dep; - } -} - -static void link_dep(nghttp2_stream *dep_stream, nghttp2_stream *stream) { - dep_stream->dep_next = stream; - if (stream) { - stream->dep_prev = dep_stream; - } -} - -static void link_sib(nghttp2_stream *a, nghttp2_stream *b) { - a->sib_next = b; - if (b) { - b->sib_prev = a; - } -} - -static void insert_link_dep(nghttp2_stream *dep_stream, - nghttp2_stream *stream) { - nghttp2_stream *sib_next; - - assert(stream->sib_prev == NULL); - - sib_next = dep_stream->dep_next; - - link_sib(stream, sib_next); - - link_dep(dep_stream, stream); -} - -static void unlink_sib(nghttp2_stream *stream) { - nghttp2_stream *prev, *next, *dep_next; - - prev = stream->sib_prev; - dep_next = stream->dep_next; - - assert(prev); - - if (dep_next) { - /* - * prev--stream(--sib_next--...) - * | - * dep_next - */ - - link_sib(prev, dep_next); - - set_dep_prev(dep_next, stream->dep_prev); - - if (stream->sib_next) { - link_sib(stream_last_sib(dep_next), stream->sib_next); - } - } else { - /* - * prev--stream(--sib_next--...) - */ - next = stream->sib_next; - - prev->sib_next = next; - - if (next) { - next->sib_prev = prev; - } - } -} - -static void unlink_dep(nghttp2_stream *stream) { - nghttp2_stream *prev, *next, *dep_next; - - prev = stream->dep_prev; - dep_next = stream->dep_next; - - assert(prev); - - if (dep_next) { - /* - * prev - * | - * stream(--sib_next--...) - * | - * dep_next - */ - link_dep(prev, dep_next); - - set_dep_prev(dep_next, stream->dep_prev); - - if (stream->sib_next) { - link_sib(stream_last_sib(dep_next), stream->sib_next); - } - - } else if (stream->sib_next) { - /* - * prev - * | - * stream--sib_next - */ - next = stream->sib_next; - - next->sib_prev = NULL; - - link_dep(prev, next); - } else { - prev->dep_next = NULL; - } -} - -void nghttp2_stream_dep_add(nghttp2_stream *dep_stream, - nghttp2_stream *stream) { - DEBUGF("stream: dep_add dep_stream(%p)=%d, stream(%p)=%d\n", dep_stream, - dep_stream->stream_id, stream, stream->stream_id); - - dep_stream->sum_dep_weight += stream->weight; - - if (dep_stream->dep_next == NULL) { - link_dep(dep_stream, stream); - } else { - insert_link_dep(dep_stream, stream); - } - - validate_tree(stream); -} - -int nghttp2_stream_dep_remove(nghttp2_stream *stream) { - nghttp2_stream *dep_prev, *si; - int32_t sum_dep_weight_delta; - int rv; - - DEBUGF("stream: dep_remove stream(%p)=%d\n", stream, stream->stream_id); - - /* Distribute weight of |stream| to direct descendants */ - sum_dep_weight_delta = -stream->weight; - - for (si = stream->dep_next; si; si = si->sib_next) { - si->weight = nghttp2_stream_dep_distributed_weight(stream, si->weight); - - sum_dep_weight_delta += si->weight; - - if (si->queued) { - rv = stream_obq_move(stream->dep_prev, stream, si); - if (rv != 0) { - return rv; - } - } - } - - assert(stream->dep_prev); - - dep_prev = stream->dep_prev; - - dep_prev->sum_dep_weight += sum_dep_weight_delta; - - if (stream->queued) { - stream_obq_remove(stream); - } - - if (stream->sib_prev) { - unlink_sib(stream); - } else { - unlink_dep(stream); - } - - stream->sum_dep_weight = 0; - - stream->dep_prev = NULL; - stream->dep_next = NULL; - stream->sib_prev = NULL; - stream->sib_next = NULL; - - validate_tree(dep_prev); - - return 0; -} - -int nghttp2_stream_dep_insert_subtree(nghttp2_stream *dep_stream, - nghttp2_stream *stream) { - nghttp2_stream *last_sib; - nghttp2_stream *dep_next; - nghttp2_stream *si; - int rv; - - DEBUGF("stream: dep_insert_subtree dep_stream(%p)=%d stream(%p)=%d\n", - dep_stream, dep_stream->stream_id, stream, stream->stream_id); - - stream->sum_dep_weight += dep_stream->sum_dep_weight; - dep_stream->sum_dep_weight = stream->weight; - - if (dep_stream->dep_next) { - dep_next = dep_stream->dep_next; - - link_dep(dep_stream, stream); - - if (stream->dep_next) { - last_sib = stream_last_sib(stream->dep_next); - - link_sib(last_sib, dep_next); - } else { - link_dep(stream, dep_next); - } - - for (si = dep_next; si; si = si->sib_next) { - si->dep_prev = stream; - if (si->queued) { - rv = stream_obq_move(stream, dep_stream, si); - if (rv != 0) { - return rv; - } - } - } - } else { - link_dep(dep_stream, stream); - } - - if (stream_subtree_active(stream)) { - rv = stream_obq_push(dep_stream, stream); - if (rv != 0) { - return rv; - } - } - - validate_tree(dep_stream); - - return 0; -} - -int nghttp2_stream_dep_add_subtree(nghttp2_stream *dep_stream, - nghttp2_stream *stream) { - int rv; - - DEBUGF("stream: dep_add_subtree dep_stream(%p)=%d stream(%p)=%d\n", - dep_stream, dep_stream->stream_id, stream, stream->stream_id); - - dep_stream->sum_dep_weight += stream->weight; - - if (dep_stream->dep_next) { - insert_link_dep(dep_stream, stream); - } else { - link_dep(dep_stream, stream); - } - - if (stream_subtree_active(stream)) { - rv = stream_obq_push(dep_stream, stream); - if (rv != 0) { - return rv; - } - } - - validate_tree(dep_stream); - - return 0; -} - -void nghttp2_stream_dep_remove_subtree(nghttp2_stream *stream) { - nghttp2_stream *next, *dep_prev; - - DEBUGF("stream: dep_remove_subtree stream(%p)=%d\n", stream, - stream->stream_id); - - assert(stream->dep_prev); - - dep_prev = stream->dep_prev; - - if (stream->sib_prev) { - link_sib(stream->sib_prev, stream->sib_next); - } else { - next = stream->sib_next; - - link_dep(dep_prev, next); - - if (next) { - next->sib_prev = NULL; - } - } - - dep_prev->sum_dep_weight -= stream->weight; - - if (stream->queued) { - stream_obq_remove(stream); - } - - validate_tree(dep_prev); - - stream->sib_prev = NULL; - stream->sib_next = NULL; - stream->dep_prev = NULL; -} - -int nghttp2_stream_in_dep_tree(nghttp2_stream *stream) { - return stream->dep_prev || stream->dep_next || stream->sib_prev || - stream->sib_next; -} - -nghttp2_outbound_item * -nghttp2_stream_next_outbound_item(nghttp2_stream *stream) { - nghttp2_pq_entry *ent; - nghttp2_stream *si; - - for (;;) { - if (stream_active(stream)) { - /* Update ascendant's descendant_last_cycle here, so that we can - assure that new stream is scheduled based on it. */ - for (si = stream; si->dep_prev; si = si->dep_prev) { - si->dep_prev->descendant_last_cycle = si->cycle; - } - return stream->item; - } - ent = nghttp2_pq_top(&stream->obq); - if (!ent) { - return NULL; - } - stream = nghttp2_struct_of(ent, nghttp2_stream, pq_entry); - } -} - -nghttp2_stream_proto_state nghttp2_stream_get_state(nghttp2_stream *stream) { - if (stream->flags & NGHTTP2_STREAM_FLAG_CLOSED) { - return NGHTTP2_STREAM_STATE_CLOSED; - } - - if (stream->flags & NGHTTP2_STREAM_FLAG_PUSH) { - if (stream->shut_flags & NGHTTP2_SHUT_RD) { - return NGHTTP2_STREAM_STATE_RESERVED_LOCAL; - } - - if (stream->shut_flags & NGHTTP2_SHUT_WR) { - return NGHTTP2_STREAM_STATE_RESERVED_REMOTE; - } - } - - if (stream->shut_flags & NGHTTP2_SHUT_RD) { - return NGHTTP2_STREAM_STATE_HALF_CLOSED_REMOTE; - } - - if (stream->shut_flags & NGHTTP2_SHUT_WR) { - return NGHTTP2_STREAM_STATE_HALF_CLOSED_LOCAL; - } - - if (stream->state == NGHTTP2_STREAM_IDLE) { - return NGHTTP2_STREAM_STATE_IDLE; - } - - return NGHTTP2_STREAM_STATE_OPEN; -} - -nghttp2_stream *nghttp2_stream_get_parent(nghttp2_stream *stream) { - return stream->dep_prev; -} - -nghttp2_stream *nghttp2_stream_get_next_sibling(nghttp2_stream *stream) { - return stream->sib_next; -} - -nghttp2_stream *nghttp2_stream_get_previous_sibling(nghttp2_stream *stream) { - return stream->sib_prev; -} - -nghttp2_stream *nghttp2_stream_get_first_child(nghttp2_stream *stream) { - return stream->dep_next; -} - -int32_t nghttp2_stream_get_weight(nghttp2_stream *stream) { - return stream->weight; -} - -int32_t nghttp2_stream_get_sum_dependency_weight(nghttp2_stream *stream) { - return stream->sum_dep_weight; -} - -int32_t nghttp2_stream_get_stream_id(nghttp2_stream *stream) { - return stream->stream_id; -} + stream->cycle); +} + +static nghttp2_stream *stream_last_sib(nghttp2_stream *stream) { + for (; stream->sib_next; stream = stream->sib_next) + ; + + return stream; +} + +int32_t nghttp2_stream_dep_distributed_weight(nghttp2_stream *stream, + int32_t weight) { + weight = stream->weight * weight / stream->sum_dep_weight; + + return nghttp2_max(1, weight); +} + +#ifdef STREAM_DEP_DEBUG + +static void ensure_inactive(nghttp2_stream *stream) { + nghttp2_stream *si; + + if (stream->queued) { + fprintf(stderr, "stream(%p)=%d, stream->queued = 1; want 0\n", stream, + stream->stream_id); + assert(0); + } + + if (stream_active(stream)) { + fprintf(stderr, "stream(%p)=%d, stream_active(stream) = 1; want 0\n", + stream, stream->stream_id); + assert(0); + } + + if (!nghttp2_pq_empty(&stream->obq)) { + fprintf(stderr, "stream(%p)=%d, nghttp2_pq_size() = %zu; want 0\n", stream, + stream->stream_id, nghttp2_pq_size(&stream->obq)); + assert(0); + } + + for (si = stream->dep_next; si; si = si->sib_next) { + ensure_inactive(si); + } +} + +static void check_queued(nghttp2_stream *stream) { + nghttp2_stream *si; + int queued; + + if (stream->queued) { + if (!stream_subtree_active(stream)) { + fprintf(stderr, + "stream(%p)=%d, stream->queued == 1, but " + "stream_active() == %d and nghttp2_pq_size(&stream->obq) = %zu\n", + stream, stream->stream_id, stream_active(stream), + nghttp2_pq_size(&stream->obq)); + assert(0); + } + if (!stream_active(stream)) { + queued = 0; + for (si = stream->dep_next; si; si = si->sib_next) { + if (si->queued) { + ++queued; + } + } + if (queued == 0) { + fprintf(stderr, + "stream(%p)=%d, stream->queued == 1, and " + "!stream_active(), but no descendants is queued\n", + stream, stream->stream_id); + assert(0); + } + } + + for (si = stream->dep_next; si; si = si->sib_next) { + check_queued(si); + } + } else { + if (stream_active(stream) || !nghttp2_pq_empty(&stream->obq)) { + fprintf(stderr, + "stream(%p) = %d, stream->queued == 0, but " + "stream_active(stream) == %d and " + "nghttp2_pq_size(&stream->obq) = %zu\n", + stream, stream->stream_id, stream_active(stream), + nghttp2_pq_size(&stream->obq)); + assert(0); + } + for (si = stream->dep_next; si; si = si->sib_next) { + ensure_inactive(si); + } + } +} + +static void check_sum_dep(nghttp2_stream *stream) { + nghttp2_stream *si; + int32_t n = 0; + for (si = stream->dep_next; si; si = si->sib_next) { + n += si->weight; + } + if (n != stream->sum_dep_weight) { + fprintf(stderr, "stream(%p)=%d, sum_dep_weight = %d; want %d\n", stream, + stream->stream_id, n, stream->sum_dep_weight); + assert(0); + } + for (si = stream->dep_next; si; si = si->sib_next) { + check_sum_dep(si); + } +} + +static void check_dep_prev(nghttp2_stream *stream) { + nghttp2_stream *si; + for (si = stream->dep_next; si; si = si->sib_next) { + if (si->dep_prev != stream) { + fprintf(stderr, "si->dep_prev = %p; want %p\n", si->dep_prev, stream); + assert(0); + } + check_dep_prev(si); + } +} + +#endif /* STREAM_DEP_DEBUG */ + +#ifdef STREAM_DEP_DEBUG +static void validate_tree(nghttp2_stream *stream) { + nghttp2_stream *si; + + if (!stream) { + return; + } + + for (; stream->dep_prev; stream = stream->dep_prev) + ; + + assert(stream->stream_id == 0); + assert(!stream->queued); + + fprintf(stderr, "checking...\n"); + if (nghttp2_pq_empty(&stream->obq)) { + fprintf(stderr, "root obq empty\n"); + for (si = stream->dep_next; si; si = si->sib_next) { + ensure_inactive(si); + } + } else { + for (si = stream->dep_next; si; si = si->sib_next) { + check_queued(si); + } + } + + check_sum_dep(stream); + check_dep_prev(stream); +} +#else /* !STREAM_DEP_DEBUG */ +static void validate_tree(nghttp2_stream *stream) { (void)stream; } +#endif /* !STREAM_DEP_DEBUG*/ + +static int stream_update_dep_on_attach_item(nghttp2_stream *stream) { + int rv; + + rv = stream_obq_push(stream->dep_prev, stream); + if (rv != 0) { + return rv; + } + + validate_tree(stream); + return 0; +} + +static int stream_update_dep_on_detach_item(nghttp2_stream *stream) { + if (nghttp2_pq_empty(&stream->obq)) { + stream_obq_remove(stream); + } + + validate_tree(stream); + + return 0; +} + +int nghttp2_stream_attach_item(nghttp2_stream *stream, + nghttp2_outbound_item *item) { + int rv; + + assert((stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL) == 0); + assert(stream->item == NULL); + + DEBUGF("stream: stream=%d attach item=%p\n", stream->stream_id, item); + + stream->item = item; + + rv = stream_update_dep_on_attach_item(stream); + if (rv != 0) { + /* This may relave stream->queued == 1, but stream->item == NULL. + But only consequence of this error is fatal one, and session + destruction. In that execution path, these inconsistency does + not matter. */ + stream->item = NULL; + return rv; + } + + return 0; +} + +int nghttp2_stream_detach_item(nghttp2_stream *stream) { + DEBUGF("stream: stream=%d detach item=%p\n", stream->stream_id, stream->item); + + stream->item = NULL; + stream->flags = (uint8_t)(stream->flags & ~NGHTTP2_STREAM_FLAG_DEFERRED_ALL); + + return stream_update_dep_on_detach_item(stream); +} + +int nghttp2_stream_defer_item(nghttp2_stream *stream, uint8_t flags) { + assert(stream->item); + + DEBUGF("stream: stream=%d defer item=%p cause=%02x\n", stream->stream_id, + stream->item, flags); + + stream->flags |= flags; + + return stream_update_dep_on_detach_item(stream); +} + +int nghttp2_stream_resume_deferred_item(nghttp2_stream *stream, uint8_t flags) { + assert(stream->item); + + DEBUGF("stream: stream=%d resume item=%p flags=%02x\n", stream->stream_id, + stream->item, flags); + + stream->flags = (uint8_t)(stream->flags & ~flags); + + if (stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL) { + return 0; + } + + return stream_update_dep_on_attach_item(stream); +} + +int nghttp2_stream_check_deferred_item(nghttp2_stream *stream) { + return stream->item && (stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL); +} + +int nghttp2_stream_check_deferred_by_flow_control(nghttp2_stream *stream) { + return stream->item && + (stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL); +} + +static int update_initial_window_size(int32_t *window_size_ptr, + int32_t new_initial_window_size, + int32_t old_initial_window_size) { + int64_t new_window_size = (int64_t)(*window_size_ptr) + + new_initial_window_size - old_initial_window_size; + if (INT32_MIN > new_window_size || + new_window_size > NGHTTP2_MAX_WINDOW_SIZE) { + return -1; + } + *window_size_ptr = (int32_t)new_window_size; + return 0; +} + +int nghttp2_stream_update_remote_initial_window_size( + nghttp2_stream *stream, int32_t new_initial_window_size, + int32_t old_initial_window_size) { + return update_initial_window_size(&stream->remote_window_size, + new_initial_window_size, + old_initial_window_size); +} + +int nghttp2_stream_update_local_initial_window_size( + nghttp2_stream *stream, int32_t new_initial_window_size, + int32_t old_initial_window_size) { + return update_initial_window_size(&stream->local_window_size, + new_initial_window_size, + old_initial_window_size); +} + +void nghttp2_stream_promise_fulfilled(nghttp2_stream *stream) { + stream->state = NGHTTP2_STREAM_OPENED; + stream->flags = (uint8_t)(stream->flags & ~NGHTTP2_STREAM_FLAG_PUSH); +} + +int nghttp2_stream_dep_find_ancestor(nghttp2_stream *stream, + nghttp2_stream *target) { + for (; stream; stream = stream->dep_prev) { + if (stream == target) { + return 1; + } + } + return 0; +} + +int nghttp2_stream_dep_insert(nghttp2_stream *dep_stream, + nghttp2_stream *stream) { + nghttp2_stream *si; + int rv; + + DEBUGF("stream: dep_insert dep_stream(%p)=%d, stream(%p)=%d\n", dep_stream, + dep_stream->stream_id, stream, stream->stream_id); + + stream->sum_dep_weight = dep_stream->sum_dep_weight; + dep_stream->sum_dep_weight = stream->weight; + + if (dep_stream->dep_next) { + for (si = dep_stream->dep_next; si; si = si->sib_next) { + si->dep_prev = stream; + if (si->queued) { + rv = stream_obq_move(stream, dep_stream, si); + if (rv != 0) { + return rv; + } + } + } + + if (stream_subtree_active(stream)) { + rv = stream_obq_push(dep_stream, stream); + if (rv != 0) { + return rv; + } + } + + stream->dep_next = dep_stream->dep_next; + } + + dep_stream->dep_next = stream; + stream->dep_prev = dep_stream; + + validate_tree(stream); + + return 0; +} + +static void set_dep_prev(nghttp2_stream *stream, nghttp2_stream *dep) { + for (; stream; stream = stream->sib_next) { + stream->dep_prev = dep; + } +} + +static void link_dep(nghttp2_stream *dep_stream, nghttp2_stream *stream) { + dep_stream->dep_next = stream; + if (stream) { + stream->dep_prev = dep_stream; + } +} + +static void link_sib(nghttp2_stream *a, nghttp2_stream *b) { + a->sib_next = b; + if (b) { + b->sib_prev = a; + } +} + +static void insert_link_dep(nghttp2_stream *dep_stream, + nghttp2_stream *stream) { + nghttp2_stream *sib_next; + + assert(stream->sib_prev == NULL); + + sib_next = dep_stream->dep_next; + + link_sib(stream, sib_next); + + link_dep(dep_stream, stream); +} + +static void unlink_sib(nghttp2_stream *stream) { + nghttp2_stream *prev, *next, *dep_next; + + prev = stream->sib_prev; + dep_next = stream->dep_next; + + assert(prev); + + if (dep_next) { + /* + * prev--stream(--sib_next--...) + * | + * dep_next + */ + + link_sib(prev, dep_next); + + set_dep_prev(dep_next, stream->dep_prev); + + if (stream->sib_next) { + link_sib(stream_last_sib(dep_next), stream->sib_next); + } + } else { + /* + * prev--stream(--sib_next--...) + */ + next = stream->sib_next; + + prev->sib_next = next; + + if (next) { + next->sib_prev = prev; + } + } +} + +static void unlink_dep(nghttp2_stream *stream) { + nghttp2_stream *prev, *next, *dep_next; + + prev = stream->dep_prev; + dep_next = stream->dep_next; + + assert(prev); + + if (dep_next) { + /* + * prev + * | + * stream(--sib_next--...) + * | + * dep_next + */ + link_dep(prev, dep_next); + + set_dep_prev(dep_next, stream->dep_prev); + + if (stream->sib_next) { + link_sib(stream_last_sib(dep_next), stream->sib_next); + } + + } else if (stream->sib_next) { + /* + * prev + * | + * stream--sib_next + */ + next = stream->sib_next; + + next->sib_prev = NULL; + + link_dep(prev, next); + } else { + prev->dep_next = NULL; + } +} + +void nghttp2_stream_dep_add(nghttp2_stream *dep_stream, + nghttp2_stream *stream) { + DEBUGF("stream: dep_add dep_stream(%p)=%d, stream(%p)=%d\n", dep_stream, + dep_stream->stream_id, stream, stream->stream_id); + + dep_stream->sum_dep_weight += stream->weight; + + if (dep_stream->dep_next == NULL) { + link_dep(dep_stream, stream); + } else { + insert_link_dep(dep_stream, stream); + } + + validate_tree(stream); +} + +int nghttp2_stream_dep_remove(nghttp2_stream *stream) { + nghttp2_stream *dep_prev, *si; + int32_t sum_dep_weight_delta; + int rv; + + DEBUGF("stream: dep_remove stream(%p)=%d\n", stream, stream->stream_id); + + /* Distribute weight of |stream| to direct descendants */ + sum_dep_weight_delta = -stream->weight; + + for (si = stream->dep_next; si; si = si->sib_next) { + si->weight = nghttp2_stream_dep_distributed_weight(stream, si->weight); + + sum_dep_weight_delta += si->weight; + + if (si->queued) { + rv = stream_obq_move(stream->dep_prev, stream, si); + if (rv != 0) { + return rv; + } + } + } + + assert(stream->dep_prev); + + dep_prev = stream->dep_prev; + + dep_prev->sum_dep_weight += sum_dep_weight_delta; + + if (stream->queued) { + stream_obq_remove(stream); + } + + if (stream->sib_prev) { + unlink_sib(stream); + } else { + unlink_dep(stream); + } + + stream->sum_dep_weight = 0; + + stream->dep_prev = NULL; + stream->dep_next = NULL; + stream->sib_prev = NULL; + stream->sib_next = NULL; + + validate_tree(dep_prev); + + return 0; +} + +int nghttp2_stream_dep_insert_subtree(nghttp2_stream *dep_stream, + nghttp2_stream *stream) { + nghttp2_stream *last_sib; + nghttp2_stream *dep_next; + nghttp2_stream *si; + int rv; + + DEBUGF("stream: dep_insert_subtree dep_stream(%p)=%d stream(%p)=%d\n", + dep_stream, dep_stream->stream_id, stream, stream->stream_id); + + stream->sum_dep_weight += dep_stream->sum_dep_weight; + dep_stream->sum_dep_weight = stream->weight; + + if (dep_stream->dep_next) { + dep_next = dep_stream->dep_next; + + link_dep(dep_stream, stream); + + if (stream->dep_next) { + last_sib = stream_last_sib(stream->dep_next); + + link_sib(last_sib, dep_next); + } else { + link_dep(stream, dep_next); + } + + for (si = dep_next; si; si = si->sib_next) { + si->dep_prev = stream; + if (si->queued) { + rv = stream_obq_move(stream, dep_stream, si); + if (rv != 0) { + return rv; + } + } + } + } else { + link_dep(dep_stream, stream); + } + + if (stream_subtree_active(stream)) { + rv = stream_obq_push(dep_stream, stream); + if (rv != 0) { + return rv; + } + } + + validate_tree(dep_stream); + + return 0; +} + +int nghttp2_stream_dep_add_subtree(nghttp2_stream *dep_stream, + nghttp2_stream *stream) { + int rv; + + DEBUGF("stream: dep_add_subtree dep_stream(%p)=%d stream(%p)=%d\n", + dep_stream, dep_stream->stream_id, stream, stream->stream_id); + + dep_stream->sum_dep_weight += stream->weight; + + if (dep_stream->dep_next) { + insert_link_dep(dep_stream, stream); + } else { + link_dep(dep_stream, stream); + } + + if (stream_subtree_active(stream)) { + rv = stream_obq_push(dep_stream, stream); + if (rv != 0) { + return rv; + } + } + + validate_tree(dep_stream); + + return 0; +} + +void nghttp2_stream_dep_remove_subtree(nghttp2_stream *stream) { + nghttp2_stream *next, *dep_prev; + + DEBUGF("stream: dep_remove_subtree stream(%p)=%d\n", stream, + stream->stream_id); + + assert(stream->dep_prev); + + dep_prev = stream->dep_prev; + + if (stream->sib_prev) { + link_sib(stream->sib_prev, stream->sib_next); + } else { + next = stream->sib_next; + + link_dep(dep_prev, next); + + if (next) { + next->sib_prev = NULL; + } + } + + dep_prev->sum_dep_weight -= stream->weight; + + if (stream->queued) { + stream_obq_remove(stream); + } + + validate_tree(dep_prev); + + stream->sib_prev = NULL; + stream->sib_next = NULL; + stream->dep_prev = NULL; +} + +int nghttp2_stream_in_dep_tree(nghttp2_stream *stream) { + return stream->dep_prev || stream->dep_next || stream->sib_prev || + stream->sib_next; +} + +nghttp2_outbound_item * +nghttp2_stream_next_outbound_item(nghttp2_stream *stream) { + nghttp2_pq_entry *ent; + nghttp2_stream *si; + + for (;;) { + if (stream_active(stream)) { + /* Update ascendant's descendant_last_cycle here, so that we can + assure that new stream is scheduled based on it. */ + for (si = stream; si->dep_prev; si = si->dep_prev) { + si->dep_prev->descendant_last_cycle = si->cycle; + } + return stream->item; + } + ent = nghttp2_pq_top(&stream->obq); + if (!ent) { + return NULL; + } + stream = nghttp2_struct_of(ent, nghttp2_stream, pq_entry); + } +} + +nghttp2_stream_proto_state nghttp2_stream_get_state(nghttp2_stream *stream) { + if (stream->flags & NGHTTP2_STREAM_FLAG_CLOSED) { + return NGHTTP2_STREAM_STATE_CLOSED; + } + + if (stream->flags & NGHTTP2_STREAM_FLAG_PUSH) { + if (stream->shut_flags & NGHTTP2_SHUT_RD) { + return NGHTTP2_STREAM_STATE_RESERVED_LOCAL; + } + + if (stream->shut_flags & NGHTTP2_SHUT_WR) { + return NGHTTP2_STREAM_STATE_RESERVED_REMOTE; + } + } + + if (stream->shut_flags & NGHTTP2_SHUT_RD) { + return NGHTTP2_STREAM_STATE_HALF_CLOSED_REMOTE; + } + + if (stream->shut_flags & NGHTTP2_SHUT_WR) { + return NGHTTP2_STREAM_STATE_HALF_CLOSED_LOCAL; + } + + if (stream->state == NGHTTP2_STREAM_IDLE) { + return NGHTTP2_STREAM_STATE_IDLE; + } + + return NGHTTP2_STREAM_STATE_OPEN; +} + +nghttp2_stream *nghttp2_stream_get_parent(nghttp2_stream *stream) { + return stream->dep_prev; +} + +nghttp2_stream *nghttp2_stream_get_next_sibling(nghttp2_stream *stream) { + return stream->sib_next; +} + +nghttp2_stream *nghttp2_stream_get_previous_sibling(nghttp2_stream *stream) { + return stream->sib_prev; +} + +nghttp2_stream *nghttp2_stream_get_first_child(nghttp2_stream *stream) { + return stream->dep_next; +} + +int32_t nghttp2_stream_get_weight(nghttp2_stream *stream) { + return stream->weight; +} + +int32_t nghttp2_stream_get_sum_dependency_weight(nghttp2_stream *stream) { + return stream->sum_dep_weight; +} + +int32_t nghttp2_stream_get_stream_id(nghttp2_stream *stream) { + return stream->stream_id; +} diff --git a/contrib/libs/nghttp2/lib/nghttp2_stream.h b/contrib/libs/nghttp2/lib/nghttp2_stream.h index d3866f6d2f..2846c6aab7 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_stream.h +++ b/contrib/libs/nghttp2/lib/nghttp2_stream.h @@ -1,435 +1,435 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * 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_STREAM_H -#define NGHTTP2_STREAM_H - -#ifdef HAVE_CONFIG_H +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * 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_STREAM_H +#define NGHTTP2_STREAM_H + +#ifdef HAVE_CONFIG_H # include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <nghttp2/nghttp2.h> -#include "nghttp2_outbound_item.h" -#include "nghttp2_map.h" -#include "nghttp2_pq.h" -#include "nghttp2_int.h" - -/* - * If local peer is stream initiator: - * NGHTTP2_STREAM_OPENING : upon sending request HEADERS - * NGHTTP2_STREAM_OPENED : upon receiving response HEADERS - * NGHTTP2_STREAM_CLOSING : upon queuing RST_STREAM - * - * If remote peer is stream initiator: - * NGHTTP2_STREAM_OPENING : upon receiving request HEADERS - * NGHTTP2_STREAM_OPENED : upon sending response HEADERS - * NGHTTP2_STREAM_CLOSING : upon queuing RST_STREAM - */ -typedef enum { - /* Initial state */ - NGHTTP2_STREAM_INITIAL, - /* For stream initiator: request HEADERS has been sent, but response - HEADERS has not been received yet. For receiver: request HEADERS - has been received, but it does not send response HEADERS yet. */ - NGHTTP2_STREAM_OPENING, - /* For stream initiator: response HEADERS is received. For receiver: - response HEADERS is sent. */ - NGHTTP2_STREAM_OPENED, - /* RST_STREAM is received, but somehow we need to keep stream in - memory. */ - NGHTTP2_STREAM_CLOSING, - /* PUSH_PROMISE is received or sent */ - NGHTTP2_STREAM_RESERVED, - /* Stream is created in this state if it is used as anchor in - dependency tree. */ - NGHTTP2_STREAM_IDLE -} nghttp2_stream_state; - -typedef enum { - NGHTTP2_SHUT_NONE = 0, - /* Indicates further receptions will be disallowed. */ - NGHTTP2_SHUT_RD = 0x01, - /* Indicates further transmissions will be disallowed. */ - NGHTTP2_SHUT_WR = 0x02, - /* Indicates both further receptions and transmissions will be - disallowed. */ - NGHTTP2_SHUT_RDWR = NGHTTP2_SHUT_RD | NGHTTP2_SHUT_WR -} nghttp2_shut_flag; - -typedef enum { - NGHTTP2_STREAM_FLAG_NONE = 0, - /* Indicates that this stream is pushed stream and not opened - yet. */ - NGHTTP2_STREAM_FLAG_PUSH = 0x01, - /* Indicates that this stream was closed */ - NGHTTP2_STREAM_FLAG_CLOSED = 0x02, - /* Indicates the item is deferred due to flow control. */ - NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL = 0x04, - /* Indicates the item is deferred by user callback */ - 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; - -/* HTTP related flags to enforce HTTP semantics */ -typedef enum { - NGHTTP2_HTTP_FLAG_NONE = 0, - /* header field seen so far */ - NGHTTP2_HTTP_FLAG__AUTHORITY = 1, - NGHTTP2_HTTP_FLAG__PATH = 1 << 1, - NGHTTP2_HTTP_FLAG__METHOD = 1 << 2, - NGHTTP2_HTTP_FLAG__SCHEME = 1 << 3, - /* host is not pseudo header, but we require either host or - :authority */ - NGHTTP2_HTTP_FLAG_HOST = 1 << 4, - NGHTTP2_HTTP_FLAG__STATUS = 1 << 5, - /* required header fields for HTTP request except for CONNECT - method. */ - NGHTTP2_HTTP_FLAG_REQ_HEADERS = NGHTTP2_HTTP_FLAG__METHOD | - NGHTTP2_HTTP_FLAG__PATH | - NGHTTP2_HTTP_FLAG__SCHEME, - NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED = 1 << 6, - /* HTTP method flags */ - NGHTTP2_HTTP_FLAG_METH_CONNECT = 1 << 7, - NGHTTP2_HTTP_FLAG_METH_HEAD = 1 << 8, - NGHTTP2_HTTP_FLAG_METH_OPTIONS = 1 << 9, - NGHTTP2_HTTP_FLAG_METH_UPGRADE_WORKAROUND = 1 << 10, - NGHTTP2_HTTP_FLAG_METH_ALL = NGHTTP2_HTTP_FLAG_METH_CONNECT | - NGHTTP2_HTTP_FLAG_METH_HEAD | - NGHTTP2_HTTP_FLAG_METH_OPTIONS | - NGHTTP2_HTTP_FLAG_METH_UPGRADE_WORKAROUND, - /* :path category */ - /* path starts with "/" */ - NGHTTP2_HTTP_FLAG_PATH_REGULAR = 1 << 11, - /* path "*" */ - NGHTTP2_HTTP_FLAG_PATH_ASTERISK = 1 << 12, - /* scheme */ - /* "http" or "https" scheme */ - NGHTTP2_HTTP_FLAG_SCHEME_HTTP = 1 << 13, - /* set if final response is expected */ +#endif /* HAVE_CONFIG_H */ + +#include <nghttp2/nghttp2.h> +#include "nghttp2_outbound_item.h" +#include "nghttp2_map.h" +#include "nghttp2_pq.h" +#include "nghttp2_int.h" + +/* + * If local peer is stream initiator: + * NGHTTP2_STREAM_OPENING : upon sending request HEADERS + * NGHTTP2_STREAM_OPENED : upon receiving response HEADERS + * NGHTTP2_STREAM_CLOSING : upon queuing RST_STREAM + * + * If remote peer is stream initiator: + * NGHTTP2_STREAM_OPENING : upon receiving request HEADERS + * NGHTTP2_STREAM_OPENED : upon sending response HEADERS + * NGHTTP2_STREAM_CLOSING : upon queuing RST_STREAM + */ +typedef enum { + /* Initial state */ + NGHTTP2_STREAM_INITIAL, + /* For stream initiator: request HEADERS has been sent, but response + HEADERS has not been received yet. For receiver: request HEADERS + has been received, but it does not send response HEADERS yet. */ + NGHTTP2_STREAM_OPENING, + /* For stream initiator: response HEADERS is received. For receiver: + response HEADERS is sent. */ + NGHTTP2_STREAM_OPENED, + /* RST_STREAM is received, but somehow we need to keep stream in + memory. */ + NGHTTP2_STREAM_CLOSING, + /* PUSH_PROMISE is received or sent */ + NGHTTP2_STREAM_RESERVED, + /* Stream is created in this state if it is used as anchor in + dependency tree. */ + NGHTTP2_STREAM_IDLE +} nghttp2_stream_state; + +typedef enum { + NGHTTP2_SHUT_NONE = 0, + /* Indicates further receptions will be disallowed. */ + NGHTTP2_SHUT_RD = 0x01, + /* Indicates further transmissions will be disallowed. */ + NGHTTP2_SHUT_WR = 0x02, + /* Indicates both further receptions and transmissions will be + disallowed. */ + NGHTTP2_SHUT_RDWR = NGHTTP2_SHUT_RD | NGHTTP2_SHUT_WR +} nghttp2_shut_flag; + +typedef enum { + NGHTTP2_STREAM_FLAG_NONE = 0, + /* Indicates that this stream is pushed stream and not opened + yet. */ + NGHTTP2_STREAM_FLAG_PUSH = 0x01, + /* Indicates that this stream was closed */ + NGHTTP2_STREAM_FLAG_CLOSED = 0x02, + /* Indicates the item is deferred due to flow control. */ + NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL = 0x04, + /* Indicates the item is deferred by user callback */ + 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; + +/* HTTP related flags to enforce HTTP semantics */ +typedef enum { + NGHTTP2_HTTP_FLAG_NONE = 0, + /* header field seen so far */ + NGHTTP2_HTTP_FLAG__AUTHORITY = 1, + NGHTTP2_HTTP_FLAG__PATH = 1 << 1, + NGHTTP2_HTTP_FLAG__METHOD = 1 << 2, + NGHTTP2_HTTP_FLAG__SCHEME = 1 << 3, + /* host is not pseudo header, but we require either host or + :authority */ + NGHTTP2_HTTP_FLAG_HOST = 1 << 4, + NGHTTP2_HTTP_FLAG__STATUS = 1 << 5, + /* required header fields for HTTP request except for CONNECT + method. */ + NGHTTP2_HTTP_FLAG_REQ_HEADERS = NGHTTP2_HTTP_FLAG__METHOD | + NGHTTP2_HTTP_FLAG__PATH | + NGHTTP2_HTTP_FLAG__SCHEME, + NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED = 1 << 6, + /* HTTP method flags */ + NGHTTP2_HTTP_FLAG_METH_CONNECT = 1 << 7, + NGHTTP2_HTTP_FLAG_METH_HEAD = 1 << 8, + NGHTTP2_HTTP_FLAG_METH_OPTIONS = 1 << 9, + NGHTTP2_HTTP_FLAG_METH_UPGRADE_WORKAROUND = 1 << 10, + NGHTTP2_HTTP_FLAG_METH_ALL = NGHTTP2_HTTP_FLAG_METH_CONNECT | + NGHTTP2_HTTP_FLAG_METH_HEAD | + NGHTTP2_HTTP_FLAG_METH_OPTIONS | + NGHTTP2_HTTP_FLAG_METH_UPGRADE_WORKAROUND, + /* :path category */ + /* path starts with "/" */ + NGHTTP2_HTTP_FLAG_PATH_REGULAR = 1 << 11, + /* path "*" */ + NGHTTP2_HTTP_FLAG_PATH_ASTERISK = 1 << 12, + /* scheme */ + /* "http" or "https" scheme */ + NGHTTP2_HTTP_FLAG_SCHEME_HTTP = 1 << 13, + /* set if final response is expected */ NGHTTP2_HTTP_FLAG_EXPECT_FINAL_RESPONSE = 1 << 14, NGHTTP2_HTTP_FLAG__PROTOCOL = 1 << 15, -} nghttp2_http_flag; - -struct nghttp2_stream { - /* Entry for dep_prev->obq */ - nghttp2_pq_entry pq_entry; - /* Priority Queue storing direct descendant (nghttp2_stream). Only - streams which itself has some data to send, or has a descendant - which has some data to sent. */ - nghttp2_pq obq; - /* Content-Length of request/response body. -1 if unknown. */ - int64_t content_length; - /* Received body so far */ - int64_t recv_content_length; - /* Base last_cycle for direct descendent streams. */ +} nghttp2_http_flag; + +struct nghttp2_stream { + /* Entry for dep_prev->obq */ + nghttp2_pq_entry pq_entry; + /* Priority Queue storing direct descendant (nghttp2_stream). Only + streams which itself has some data to send, or has a descendant + which has some data to sent. */ + nghttp2_pq obq; + /* Content-Length of request/response body. -1 if unknown. */ + int64_t content_length; + /* Received body so far */ + int64_t recv_content_length; + /* Base last_cycle for direct descendent streams. */ uint64_t descendant_last_cycle; - /* Next scheduled time to sent item */ + /* Next scheduled time to sent item */ uint64_t cycle; - /* Next seq used for direct descendant streams */ - uint64_t descendant_next_seq; - /* Secondary key for prioritization to break a tie for cycle. This - value is monotonically increased for single parent stream. */ - uint64_t seq; - /* pointers to form dependency tree. If multiple streams depend on - a stream, only one stream (left most) has non-NULL dep_prev which - points to the stream it depends on. The remaining streams are - linked using sib_prev and sib_next. The stream which has - non-NULL dep_prev always NULL sib_prev. The right most stream - has NULL sib_next. If this stream is a root of dependency tree, - dep_prev and sib_prev are NULL. */ - nghttp2_stream *dep_prev, *dep_next; - nghttp2_stream *sib_prev, *sib_next; - /* When stream is kept after closure, it may be kept in doubly - linked list pointed by nghttp2_session closed_stream_head. - closed_next points to the next stream object if it is the element - of the list. */ - nghttp2_stream *closed_prev, *closed_next; - /* The arbitrary data provided by user for this stream. */ - void *stream_user_data; - /* Item to send */ - nghttp2_outbound_item *item; - /* Last written length of frame payload */ - size_t last_writelen; - /* stream ID */ - int32_t stream_id; - /* Current remote window size. This value is computed against the - current initial window size of remote endpoint. */ - int32_t remote_window_size; - /* Keep track of the number of bytes received without - WINDOW_UPDATE. This could be negative after submitting negative - value to WINDOW_UPDATE */ - int32_t recv_window_size; - /* The number of bytes consumed by the application and now is - subject to WINDOW_UPDATE. This is only used when auto - WINDOW_UPDATE is turned off. */ - int32_t consumed_size; - /* The amount of recv_window_size cut using submitting negative - value to WINDOW_UPDATE */ - int32_t recv_reduction; - /* window size for local flow control. It is initially set to - NGHTTP2_INITIAL_WINDOW_SIZE and could be increased/decreased by - submitting WINDOW_UPDATE. See nghttp2_submit_window_update(). */ - int32_t local_window_size; - /* weight of this stream */ - int32_t weight; - /* This is unpaid penalty (offset) when calculating cycle. */ - uint32_t pending_penalty; - /* sum of weight of direct descendants */ - int32_t sum_dep_weight; - nghttp2_stream_state state; - /* status code from remote server */ - int16_t status_code; - /* Bitwise OR of zero or more nghttp2_http_flag values */ - uint16_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 */ - uint8_t shut_flags; - /* Nonzero if this stream has been queued to stream pointed by - dep_prev. We maintain the invariant that if a stream is queued, - then its ancestors, except for root, are also queued. This - invariant may break in fatal error condition. */ - uint8_t queued; - /* This flag is used to reduce excessive queuing of WINDOW_UPDATE to - this stream. The nonzero does not necessarily mean WINDOW_UPDATE - is not queued. */ - uint8_t window_update_queued; -}; - -void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id, - uint8_t flags, nghttp2_stream_state initial_state, - int32_t weight, int32_t remote_initial_window_size, - int32_t local_initial_window_size, - void *stream_user_data, nghttp2_mem *mem); - -void nghttp2_stream_free(nghttp2_stream *stream); - -/* - * Disallow either further receptions or transmissions, or both. - * |flag| is bitwise OR of one or more of nghttp2_shut_flag. - */ -void nghttp2_stream_shutdown(nghttp2_stream *stream, nghttp2_shut_flag flag); - -/* - * Defer |stream->item|. We won't call this function in the situation - * where |stream->item| == NULL. The |flags| is bitwise OR of zero or - * more of NGHTTP2_STREAM_FLAG_DEFERRED_USER and - * NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL. The |flags| indicates - * the reason of this action. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory - */ -int nghttp2_stream_defer_item(nghttp2_stream *stream, uint8_t flags); - -/* - * Put back deferred data in this stream to active state. The |flags| - * are one or more of bitwise OR of the following values: - * NGHTTP2_STREAM_FLAG_DEFERRED_USER and - * NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL and given masks are - * cleared if they are set. So even if this function is called, if - * one of flag is still set, data does not become active. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory - */ -int nghttp2_stream_resume_deferred_item(nghttp2_stream *stream, uint8_t flags); - -/* - * Returns nonzero if item is deferred by whatever reason. - */ -int nghttp2_stream_check_deferred_item(nghttp2_stream *stream); - -/* - * Returns nonzero if item is deferred by flow control. - */ -int nghttp2_stream_check_deferred_by_flow_control(nghttp2_stream *stream); - -/* - * Updates the remote window size with the new value - * |new_initial_window_size|. The |old_initial_window_size| is used to - * calculate the current window size. - * - * This function returns 0 if it succeeds or -1. The failure is due to - * overflow. - */ -int nghttp2_stream_update_remote_initial_window_size( - nghttp2_stream *stream, int32_t new_initial_window_size, - int32_t old_initial_window_size); - -/* - * Updates the local window size with the new value - * |new_initial_window_size|. The |old_initial_window_size| is used to - * calculate the current window size. - * - * This function returns 0 if it succeeds or -1. The failure is due to - * overflow. - */ -int nghttp2_stream_update_local_initial_window_size( - nghttp2_stream *stream, int32_t new_initial_window_size, - int32_t old_initial_window_size); - -/* - * Call this function if promised stream |stream| is replied with - * HEADERS. This function makes the state of the |stream| to - * NGHTTP2_STREAM_OPENED. - */ -void nghttp2_stream_promise_fulfilled(nghttp2_stream *stream); - -/* - * Returns nonzero if |target| is an ancestor of |stream|. - */ -int nghttp2_stream_dep_find_ancestor(nghttp2_stream *stream, - nghttp2_stream *target); - -/* - * Computes distributed weight of a stream of the |weight| under the - * |stream| if |stream| is removed from a dependency tree. - */ -int32_t nghttp2_stream_dep_distributed_weight(nghttp2_stream *stream, - int32_t weight); - -/* - * Makes the |stream| depend on the |dep_stream|. This dependency is - * exclusive. All existing direct descendants of |dep_stream| become - * the descendants of the |stream|. This function assumes - * |stream->item| is NULL. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory - */ -int nghttp2_stream_dep_insert(nghttp2_stream *dep_stream, - nghttp2_stream *stream); - -/* - * Makes the |stream| depend on the |dep_stream|. This dependency is - * not exclusive. This function assumes |stream->item| is NULL. - */ -void nghttp2_stream_dep_add(nghttp2_stream *dep_stream, nghttp2_stream *stream); - -/* - * Removes the |stream| from the current dependency tree. This - * function assumes |stream->item| is NULL. - */ -int nghttp2_stream_dep_remove(nghttp2_stream *stream); - -/* - * Attaches |item| to |stream|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory - */ -int nghttp2_stream_attach_item(nghttp2_stream *stream, - nghttp2_outbound_item *item); - -/* - * Detaches |stream->item|. This function does not free - * |stream->item|. The caller must free it. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory - */ -int nghttp2_stream_detach_item(nghttp2_stream *stream); - -/* - * Makes the |stream| depend on the |dep_stream|. This dependency is - * exclusive. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory - */ -int nghttp2_stream_dep_insert_subtree(nghttp2_stream *dep_stream, - nghttp2_stream *stream); - -/* - * Makes the |stream| depend on the |dep_stream|. This dependency is - * not exclusive. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory - */ -int nghttp2_stream_dep_add_subtree(nghttp2_stream *dep_stream, - nghttp2_stream *stream); - -/* - * Removes subtree whose root stream is |stream|. The - * effective_weight of streams in removed subtree is not updated. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory - */ -void nghttp2_stream_dep_remove_subtree(nghttp2_stream *stream); - -/* - * Returns nonzero if |stream| is in any dependency tree. - */ -int nghttp2_stream_in_dep_tree(nghttp2_stream *stream); - -/* - * Schedules transmission of |stream|'s item, assuming stream->item is - * attached, and stream->last_writelen was updated. - */ -void nghttp2_stream_reschedule(nghttp2_stream *stream); - -/* - * Changes |stream|'s weight to |weight|. If |stream| is queued, it - * will be rescheduled based on new weight. - */ -void nghttp2_stream_change_weight(nghttp2_stream *stream, int32_t weight); - -/* - * Returns a stream which has highest priority, updating - * descendant_last_cycle of selected stream's ancestors. - */ -nghttp2_outbound_item * -nghttp2_stream_next_outbound_item(nghttp2_stream *stream); - -#endif /* NGHTTP2_STREAM */ + /* Next seq used for direct descendant streams */ + uint64_t descendant_next_seq; + /* Secondary key for prioritization to break a tie for cycle. This + value is monotonically increased for single parent stream. */ + uint64_t seq; + /* pointers to form dependency tree. If multiple streams depend on + a stream, only one stream (left most) has non-NULL dep_prev which + points to the stream it depends on. The remaining streams are + linked using sib_prev and sib_next. The stream which has + non-NULL dep_prev always NULL sib_prev. The right most stream + has NULL sib_next. If this stream is a root of dependency tree, + dep_prev and sib_prev are NULL. */ + nghttp2_stream *dep_prev, *dep_next; + nghttp2_stream *sib_prev, *sib_next; + /* When stream is kept after closure, it may be kept in doubly + linked list pointed by nghttp2_session closed_stream_head. + closed_next points to the next stream object if it is the element + of the list. */ + nghttp2_stream *closed_prev, *closed_next; + /* The arbitrary data provided by user for this stream. */ + void *stream_user_data; + /* Item to send */ + nghttp2_outbound_item *item; + /* Last written length of frame payload */ + size_t last_writelen; + /* stream ID */ + int32_t stream_id; + /* Current remote window size. This value is computed against the + current initial window size of remote endpoint. */ + int32_t remote_window_size; + /* Keep track of the number of bytes received without + WINDOW_UPDATE. This could be negative after submitting negative + value to WINDOW_UPDATE */ + int32_t recv_window_size; + /* The number of bytes consumed by the application and now is + subject to WINDOW_UPDATE. This is only used when auto + WINDOW_UPDATE is turned off. */ + int32_t consumed_size; + /* The amount of recv_window_size cut using submitting negative + value to WINDOW_UPDATE */ + int32_t recv_reduction; + /* window size for local flow control. It is initially set to + NGHTTP2_INITIAL_WINDOW_SIZE and could be increased/decreased by + submitting WINDOW_UPDATE. See nghttp2_submit_window_update(). */ + int32_t local_window_size; + /* weight of this stream */ + int32_t weight; + /* This is unpaid penalty (offset) when calculating cycle. */ + uint32_t pending_penalty; + /* sum of weight of direct descendants */ + int32_t sum_dep_weight; + nghttp2_stream_state state; + /* status code from remote server */ + int16_t status_code; + /* Bitwise OR of zero or more nghttp2_http_flag values */ + uint16_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 */ + uint8_t shut_flags; + /* Nonzero if this stream has been queued to stream pointed by + dep_prev. We maintain the invariant that if a stream is queued, + then its ancestors, except for root, are also queued. This + invariant may break in fatal error condition. */ + uint8_t queued; + /* This flag is used to reduce excessive queuing of WINDOW_UPDATE to + this stream. The nonzero does not necessarily mean WINDOW_UPDATE + is not queued. */ + uint8_t window_update_queued; +}; + +void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id, + uint8_t flags, nghttp2_stream_state initial_state, + int32_t weight, int32_t remote_initial_window_size, + int32_t local_initial_window_size, + void *stream_user_data, nghttp2_mem *mem); + +void nghttp2_stream_free(nghttp2_stream *stream); + +/* + * Disallow either further receptions or transmissions, or both. + * |flag| is bitwise OR of one or more of nghttp2_shut_flag. + */ +void nghttp2_stream_shutdown(nghttp2_stream *stream, nghttp2_shut_flag flag); + +/* + * Defer |stream->item|. We won't call this function in the situation + * where |stream->item| == NULL. The |flags| is bitwise OR of zero or + * more of NGHTTP2_STREAM_FLAG_DEFERRED_USER and + * NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL. The |flags| indicates + * the reason of this action. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_stream_defer_item(nghttp2_stream *stream, uint8_t flags); + +/* + * Put back deferred data in this stream to active state. The |flags| + * are one or more of bitwise OR of the following values: + * NGHTTP2_STREAM_FLAG_DEFERRED_USER and + * NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL and given masks are + * cleared if they are set. So even if this function is called, if + * one of flag is still set, data does not become active. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_stream_resume_deferred_item(nghttp2_stream *stream, uint8_t flags); + +/* + * Returns nonzero if item is deferred by whatever reason. + */ +int nghttp2_stream_check_deferred_item(nghttp2_stream *stream); + +/* + * Returns nonzero if item is deferred by flow control. + */ +int nghttp2_stream_check_deferred_by_flow_control(nghttp2_stream *stream); + +/* + * Updates the remote window size with the new value + * |new_initial_window_size|. The |old_initial_window_size| is used to + * calculate the current window size. + * + * This function returns 0 if it succeeds or -1. The failure is due to + * overflow. + */ +int nghttp2_stream_update_remote_initial_window_size( + nghttp2_stream *stream, int32_t new_initial_window_size, + int32_t old_initial_window_size); + +/* + * Updates the local window size with the new value + * |new_initial_window_size|. The |old_initial_window_size| is used to + * calculate the current window size. + * + * This function returns 0 if it succeeds or -1. The failure is due to + * overflow. + */ +int nghttp2_stream_update_local_initial_window_size( + nghttp2_stream *stream, int32_t new_initial_window_size, + int32_t old_initial_window_size); + +/* + * Call this function if promised stream |stream| is replied with + * HEADERS. This function makes the state of the |stream| to + * NGHTTP2_STREAM_OPENED. + */ +void nghttp2_stream_promise_fulfilled(nghttp2_stream *stream); + +/* + * Returns nonzero if |target| is an ancestor of |stream|. + */ +int nghttp2_stream_dep_find_ancestor(nghttp2_stream *stream, + nghttp2_stream *target); + +/* + * Computes distributed weight of a stream of the |weight| under the + * |stream| if |stream| is removed from a dependency tree. + */ +int32_t nghttp2_stream_dep_distributed_weight(nghttp2_stream *stream, + int32_t weight); + +/* + * Makes the |stream| depend on the |dep_stream|. This dependency is + * exclusive. All existing direct descendants of |dep_stream| become + * the descendants of the |stream|. This function assumes + * |stream->item| is NULL. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_stream_dep_insert(nghttp2_stream *dep_stream, + nghttp2_stream *stream); + +/* + * Makes the |stream| depend on the |dep_stream|. This dependency is + * not exclusive. This function assumes |stream->item| is NULL. + */ +void nghttp2_stream_dep_add(nghttp2_stream *dep_stream, nghttp2_stream *stream); + +/* + * Removes the |stream| from the current dependency tree. This + * function assumes |stream->item| is NULL. + */ +int nghttp2_stream_dep_remove(nghttp2_stream *stream); + +/* + * Attaches |item| to |stream|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_stream_attach_item(nghttp2_stream *stream, + nghttp2_outbound_item *item); + +/* + * Detaches |stream->item|. This function does not free + * |stream->item|. The caller must free it. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_stream_detach_item(nghttp2_stream *stream); + +/* + * Makes the |stream| depend on the |dep_stream|. This dependency is + * exclusive. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_stream_dep_insert_subtree(nghttp2_stream *dep_stream, + nghttp2_stream *stream); + +/* + * Makes the |stream| depend on the |dep_stream|. This dependency is + * not exclusive. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_stream_dep_add_subtree(nghttp2_stream *dep_stream, + nghttp2_stream *stream); + +/* + * Removes subtree whose root stream is |stream|. The + * effective_weight of streams in removed subtree is not updated. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +void nghttp2_stream_dep_remove_subtree(nghttp2_stream *stream); + +/* + * Returns nonzero if |stream| is in any dependency tree. + */ +int nghttp2_stream_in_dep_tree(nghttp2_stream *stream); + +/* + * Schedules transmission of |stream|'s item, assuming stream->item is + * attached, and stream->last_writelen was updated. + */ +void nghttp2_stream_reschedule(nghttp2_stream *stream); + +/* + * Changes |stream|'s weight to |weight|. If |stream| is queued, it + * will be rescheduled based on new weight. + */ +void nghttp2_stream_change_weight(nghttp2_stream *stream, int32_t weight); + +/* + * Returns a stream which has highest priority, updating + * descendant_last_cycle of selected stream's ancestors. + */ +nghttp2_outbound_item * +nghttp2_stream_next_outbound_item(nghttp2_stream *stream); + +#endif /* NGHTTP2_STREAM */ diff --git a/contrib/libs/nghttp2/lib/nghttp2_submit.c b/contrib/libs/nghttp2/lib/nghttp2_submit.c index 2caaba737a..744a49cf60 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_submit.c +++ b/contrib/libs/nghttp2/lib/nghttp2_submit.c @@ -1,455 +1,455 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa - * - * 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_submit.h" - -#include <string.h> -#include <assert.h> - -#include "nghttp2_session.h" -#include "nghttp2_frame.h" -#include "nghttp2_helper.h" -#include "nghttp2_priority_spec.h" - -/* - * Detects the dependency error, that is stream attempted to depend on - * itself. If |stream_id| is -1, we use session->next_stream_id as - * stream ID. - * - * This function returns 0 if it succeeds, or one of the following - * error codes: - * - * NGHTTP2_ERR_INVALID_ARGUMENT - * Stream attempted to depend on itself. - */ -static int detect_self_dependency(nghttp2_session *session, int32_t stream_id, - const nghttp2_priority_spec *pri_spec) { - assert(pri_spec); - - if (stream_id == -1) { - if ((int32_t)session->next_stream_id == pri_spec->stream_id) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - return 0; - } - - if (stream_id == pri_spec->stream_id) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - - return 0; -} - -/* This function takes ownership of |nva_copy|. Regardless of the - return value, the caller must not free |nva_copy| after this - function returns. */ -static int32_t submit_headers_shared(nghttp2_session *session, uint8_t flags, - int32_t stream_id, - const nghttp2_priority_spec *pri_spec, - nghttp2_nv *nva_copy, size_t nvlen, - const nghttp2_data_provider *data_prd, - void *stream_user_data) { - int rv; - uint8_t flags_copy; - nghttp2_outbound_item *item = NULL; - nghttp2_frame *frame = NULL; - nghttp2_headers_category hcat; - nghttp2_mem *mem; - - mem = &session->mem; - - item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); - if (item == NULL) { - rv = NGHTTP2_ERR_NOMEM; - goto fail; - } - - nghttp2_outbound_item_init(item); - - if (data_prd != NULL && data_prd->read_callback != NULL) { - item->aux_data.headers.data_prd = *data_prd; - } - - item->aux_data.headers.stream_user_data = stream_user_data; - - flags_copy = - (uint8_t)((flags & (NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_PRIORITY)) | - NGHTTP2_FLAG_END_HEADERS); - - if (stream_id == -1) { - if (session->next_stream_id > INT32_MAX) { - rv = NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE; - goto fail; - } - - stream_id = (int32_t)session->next_stream_id; - session->next_stream_id += 2; - - hcat = NGHTTP2_HCAT_REQUEST; - } else { - /* More specific categorization will be done later. */ - hcat = NGHTTP2_HCAT_HEADERS; - } - - frame = &item->frame; - - nghttp2_frame_headers_init(&frame->headers, flags_copy, stream_id, hcat, - pri_spec, nva_copy, nvlen); - - rv = nghttp2_session_add_item(session, item); - - if (rv != 0) { - nghttp2_frame_headers_free(&frame->headers, mem); - goto fail2; - } - - if (hcat == NGHTTP2_HCAT_REQUEST) { - return stream_id; - } - - return 0; - -fail: - /* nghttp2_frame_headers_init() takes ownership of nva_copy. */ - nghttp2_nv_array_del(nva_copy, mem); -fail2: - nghttp2_mem_free(mem, item); - - return rv; -} - -static int32_t submit_headers_shared_nva(nghttp2_session *session, - uint8_t flags, int32_t stream_id, - const nghttp2_priority_spec *pri_spec, - const nghttp2_nv *nva, size_t nvlen, - const nghttp2_data_provider *data_prd, - void *stream_user_data) { - int rv; - nghttp2_nv *nva_copy; - nghttp2_priority_spec copy_pri_spec; - nghttp2_mem *mem; - - mem = &session->mem; - - if (pri_spec) { - copy_pri_spec = *pri_spec; - nghttp2_priority_spec_normalize_weight(©_pri_spec); - } else { - nghttp2_priority_spec_default_init(©_pri_spec); - } - - rv = nghttp2_nv_array_copy(&nva_copy, nva, nvlen, mem); - if (rv < 0) { - return rv; - } - - return submit_headers_shared(session, flags, stream_id, ©_pri_spec, - nva_copy, nvlen, data_prd, stream_user_data); -} - -int nghttp2_submit_trailer(nghttp2_session *session, int32_t stream_id, - const nghttp2_nv *nva, size_t nvlen) { - if (stream_id <= 0) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - - return (int)submit_headers_shared_nva(session, NGHTTP2_FLAG_END_STREAM, - stream_id, NULL, nva, nvlen, NULL, - NULL); -} - -int32_t nghttp2_submit_headers(nghttp2_session *session, uint8_t flags, - int32_t stream_id, - const nghttp2_priority_spec *pri_spec, - const nghttp2_nv *nva, size_t nvlen, - void *stream_user_data) { - int rv; - - if (stream_id == -1) { - if (session->server) { - return NGHTTP2_ERR_PROTO; - } - } else if (stream_id <= 0) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - - flags &= NGHTTP2_FLAG_END_STREAM; - - if (pri_spec && !nghttp2_priority_spec_check_default(pri_spec)) { - rv = detect_self_dependency(session, stream_id, pri_spec); - if (rv != 0) { - return rv; - } - - flags |= NGHTTP2_FLAG_PRIORITY; - } else { - pri_spec = NULL; - } - - return submit_headers_shared_nva(session, flags, stream_id, pri_spec, nva, - nvlen, NULL, stream_user_data); -} - -int nghttp2_submit_ping(nghttp2_session *session, uint8_t flags, - const uint8_t *opaque_data) { - flags &= NGHTTP2_FLAG_ACK; - return nghttp2_session_add_ping(session, flags, opaque_data); -} - -int nghttp2_submit_priority(nghttp2_session *session, uint8_t flags, - int32_t stream_id, - const nghttp2_priority_spec *pri_spec) { - int rv; - nghttp2_outbound_item *item; - nghttp2_frame *frame; - nghttp2_priority_spec copy_pri_spec; - nghttp2_mem *mem; - (void)flags; - - mem = &session->mem; - - if (stream_id == 0 || pri_spec == NULL) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - - if (stream_id == pri_spec->stream_id) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - - copy_pri_spec = *pri_spec; - - nghttp2_priority_spec_normalize_weight(©_pri_spec); - - item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); - - if (item == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - nghttp2_outbound_item_init(item); - - frame = &item->frame; - - nghttp2_frame_priority_init(&frame->priority, stream_id, ©_pri_spec); - - rv = nghttp2_session_add_item(session, item); - - if (rv != 0) { - nghttp2_frame_priority_free(&frame->priority); - nghttp2_mem_free(mem, item); - - return rv; - } - - return 0; -} - -int nghttp2_submit_rst_stream(nghttp2_session *session, uint8_t flags, - int32_t stream_id, uint32_t error_code) { - (void)flags; - - if (stream_id == 0) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - - return nghttp2_session_add_rst_stream(session, stream_id, error_code); -} - -int nghttp2_submit_goaway(nghttp2_session *session, uint8_t flags, - int32_t last_stream_id, uint32_t error_code, - const uint8_t *opaque_data, size_t opaque_data_len) { - (void)flags; - - if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) { - return 0; - } - return nghttp2_session_add_goaway(session, last_stream_id, error_code, - opaque_data, opaque_data_len, - NGHTTP2_GOAWAY_AUX_NONE); -} - -int nghttp2_submit_shutdown_notice(nghttp2_session *session) { - if (!session->server) { - return NGHTTP2_ERR_INVALID_STATE; - } - if (session->goaway_flags) { - return 0; - } - return nghttp2_session_add_goaway(session, (1u << 31) - 1, NGHTTP2_NO_ERROR, - NULL, 0, - NGHTTP2_GOAWAY_AUX_SHUTDOWN_NOTICE); -} - -int nghttp2_submit_settings(nghttp2_session *session, uint8_t flags, - const nghttp2_settings_entry *iv, size_t niv) { - (void)flags; - return nghttp2_session_add_settings(session, NGHTTP2_FLAG_NONE, iv, niv); -} - -int32_t nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags, - int32_t stream_id, const nghttp2_nv *nva, - size_t nvlen, - void *promised_stream_user_data) { - nghttp2_outbound_item *item; - nghttp2_frame *frame; - nghttp2_nv *nva_copy; - uint8_t flags_copy; - int32_t promised_stream_id; - int rv; - nghttp2_mem *mem; - (void)flags; - - mem = &session->mem; - - if (stream_id <= 0 || nghttp2_session_is_my_stream_id(session, stream_id)) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - - if (!session->server) { - return NGHTTP2_ERR_PROTO; - } - - /* All 32bit signed stream IDs are spent. */ - if (session->next_stream_id > INT32_MAX) { - return NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE; - } - - item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); - if (item == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - nghttp2_outbound_item_init(item); - - item->aux_data.headers.stream_user_data = promised_stream_user_data; - - frame = &item->frame; - - rv = nghttp2_nv_array_copy(&nva_copy, nva, nvlen, mem); - if (rv < 0) { - nghttp2_mem_free(mem, item); - return rv; - } - - flags_copy = NGHTTP2_FLAG_END_HEADERS; - - promised_stream_id = (int32_t)session->next_stream_id; - session->next_stream_id += 2; - - nghttp2_frame_push_promise_init(&frame->push_promise, flags_copy, stream_id, - promised_stream_id, nva_copy, nvlen); - - rv = nghttp2_session_add_item(session, item); - - if (rv != 0) { - nghttp2_frame_push_promise_free(&frame->push_promise, mem); - nghttp2_mem_free(mem, item); - - return rv; - } - - return promised_stream_id; -} - -int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags, - int32_t stream_id, - int32_t window_size_increment) { - int rv; - nghttp2_stream *stream = 0; - (void)flags; - - if (window_size_increment == 0) { - return 0; - } - if (stream_id == 0) { - rv = nghttp2_adjust_local_window_size( - &session->local_window_size, &session->recv_window_size, - &session->recv_reduction, &window_size_increment); - if (rv != 0) { - return rv; - } - } else { - stream = nghttp2_session_get_stream(session, stream_id); - if (!stream) { - return 0; - } - - rv = nghttp2_adjust_local_window_size( - &stream->local_window_size, &stream->recv_window_size, - &stream->recv_reduction, &window_size_increment); - if (rv != 0) { - return rv; - } - } - - if (window_size_increment > 0) { - if (stream_id == 0) { - session->consumed_size = - nghttp2_max(0, session->consumed_size - window_size_increment); - } else { - stream->consumed_size = - nghttp2_max(0, stream->consumed_size - window_size_increment); - } - - return nghttp2_session_add_window_update(session, 0, stream_id, - window_size_increment); - } - return 0; -} - -int nghttp2_session_set_local_window_size(nghttp2_session *session, - uint8_t flags, int32_t stream_id, - int32_t window_size) { - int32_t window_size_increment; - nghttp2_stream *stream; - int rv; - (void)flags; - - if (window_size < 0) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - - if (stream_id == 0) { - window_size_increment = window_size - session->local_window_size; - - if (window_size_increment == 0) { - return 0; - } - - if (window_size_increment < 0) { - return nghttp2_adjust_local_window_size( - &session->local_window_size, &session->recv_window_size, - &session->recv_reduction, &window_size_increment); - } - - rv = nghttp2_increase_local_window_size( - &session->local_window_size, &session->recv_window_size, - &session->recv_reduction, &window_size_increment); - - if (rv != 0) { - return rv; - } +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa + * + * 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_submit.h" + +#include <string.h> +#include <assert.h> + +#include "nghttp2_session.h" +#include "nghttp2_frame.h" +#include "nghttp2_helper.h" +#include "nghttp2_priority_spec.h" + +/* + * Detects the dependency error, that is stream attempted to depend on + * itself. If |stream_id| is -1, we use session->next_stream_id as + * stream ID. + * + * This function returns 0 if it succeeds, or one of the following + * error codes: + * + * NGHTTP2_ERR_INVALID_ARGUMENT + * Stream attempted to depend on itself. + */ +static int detect_self_dependency(nghttp2_session *session, int32_t stream_id, + const nghttp2_priority_spec *pri_spec) { + assert(pri_spec); + + if (stream_id == -1) { + if ((int32_t)session->next_stream_id == pri_spec->stream_id) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + return 0; + } + + if (stream_id == pri_spec->stream_id) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + return 0; +} + +/* This function takes ownership of |nva_copy|. Regardless of the + return value, the caller must not free |nva_copy| after this + function returns. */ +static int32_t submit_headers_shared(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + const nghttp2_priority_spec *pri_spec, + nghttp2_nv *nva_copy, size_t nvlen, + const nghttp2_data_provider *data_prd, + void *stream_user_data) { + int rv; + uint8_t flags_copy; + nghttp2_outbound_item *item = NULL; + nghttp2_frame *frame = NULL; + nghttp2_headers_category hcat; + nghttp2_mem *mem; + + mem = &session->mem; + + item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); + if (item == NULL) { + rv = NGHTTP2_ERR_NOMEM; + goto fail; + } + + nghttp2_outbound_item_init(item); + + if (data_prd != NULL && data_prd->read_callback != NULL) { + item->aux_data.headers.data_prd = *data_prd; + } + + item->aux_data.headers.stream_user_data = stream_user_data; + + flags_copy = + (uint8_t)((flags & (NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_PRIORITY)) | + NGHTTP2_FLAG_END_HEADERS); + + if (stream_id == -1) { + if (session->next_stream_id > INT32_MAX) { + rv = NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE; + goto fail; + } + + stream_id = (int32_t)session->next_stream_id; + session->next_stream_id += 2; + + hcat = NGHTTP2_HCAT_REQUEST; + } else { + /* More specific categorization will be done later. */ + hcat = NGHTTP2_HCAT_HEADERS; + } + + frame = &item->frame; + + nghttp2_frame_headers_init(&frame->headers, flags_copy, stream_id, hcat, + pri_spec, nva_copy, nvlen); + + rv = nghttp2_session_add_item(session, item); + + if (rv != 0) { + nghttp2_frame_headers_free(&frame->headers, mem); + goto fail2; + } + + if (hcat == NGHTTP2_HCAT_REQUEST) { + return stream_id; + } + + return 0; + +fail: + /* nghttp2_frame_headers_init() takes ownership of nva_copy. */ + nghttp2_nv_array_del(nva_copy, mem); +fail2: + nghttp2_mem_free(mem, item); + + return rv; +} + +static int32_t submit_headers_shared_nva(nghttp2_session *session, + uint8_t flags, int32_t stream_id, + const nghttp2_priority_spec *pri_spec, + const nghttp2_nv *nva, size_t nvlen, + const nghttp2_data_provider *data_prd, + void *stream_user_data) { + int rv; + nghttp2_nv *nva_copy; + nghttp2_priority_spec copy_pri_spec; + nghttp2_mem *mem; + + mem = &session->mem; + + if (pri_spec) { + copy_pri_spec = *pri_spec; + nghttp2_priority_spec_normalize_weight(©_pri_spec); + } else { + nghttp2_priority_spec_default_init(©_pri_spec); + } + + rv = nghttp2_nv_array_copy(&nva_copy, nva, nvlen, mem); + if (rv < 0) { + return rv; + } + + return submit_headers_shared(session, flags, stream_id, ©_pri_spec, + nva_copy, nvlen, data_prd, stream_user_data); +} + +int nghttp2_submit_trailer(nghttp2_session *session, int32_t stream_id, + const nghttp2_nv *nva, size_t nvlen) { + if (stream_id <= 0) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + return (int)submit_headers_shared_nva(session, NGHTTP2_FLAG_END_STREAM, + stream_id, NULL, nva, nvlen, NULL, + NULL); +} + +int32_t nghttp2_submit_headers(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + const nghttp2_priority_spec *pri_spec, + const nghttp2_nv *nva, size_t nvlen, + void *stream_user_data) { + int rv; + + if (stream_id == -1) { + if (session->server) { + return NGHTTP2_ERR_PROTO; + } + } else if (stream_id <= 0) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + flags &= NGHTTP2_FLAG_END_STREAM; + + if (pri_spec && !nghttp2_priority_spec_check_default(pri_spec)) { + rv = detect_self_dependency(session, stream_id, pri_spec); + if (rv != 0) { + return rv; + } + + flags |= NGHTTP2_FLAG_PRIORITY; + } else { + pri_spec = NULL; + } + + return submit_headers_shared_nva(session, flags, stream_id, pri_spec, nva, + nvlen, NULL, stream_user_data); +} + +int nghttp2_submit_ping(nghttp2_session *session, uint8_t flags, + const uint8_t *opaque_data) { + flags &= NGHTTP2_FLAG_ACK; + return nghttp2_session_add_ping(session, flags, opaque_data); +} + +int nghttp2_submit_priority(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + const nghttp2_priority_spec *pri_spec) { + int rv; + nghttp2_outbound_item *item; + nghttp2_frame *frame; + nghttp2_priority_spec copy_pri_spec; + nghttp2_mem *mem; + (void)flags; + + mem = &session->mem; + + if (stream_id == 0 || pri_spec == NULL) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + if (stream_id == pri_spec->stream_id) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + copy_pri_spec = *pri_spec; + + nghttp2_priority_spec_normalize_weight(©_pri_spec); + + item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); + + if (item == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + nghttp2_outbound_item_init(item); + + frame = &item->frame; + + nghttp2_frame_priority_init(&frame->priority, stream_id, ©_pri_spec); + + rv = nghttp2_session_add_item(session, item); + + if (rv != 0) { + nghttp2_frame_priority_free(&frame->priority); + nghttp2_mem_free(mem, item); + + return rv; + } + + return 0; +} + +int nghttp2_submit_rst_stream(nghttp2_session *session, uint8_t flags, + int32_t stream_id, uint32_t error_code) { + (void)flags; + + if (stream_id == 0) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + return nghttp2_session_add_rst_stream(session, stream_id, error_code); +} + +int nghttp2_submit_goaway(nghttp2_session *session, uint8_t flags, + int32_t last_stream_id, uint32_t error_code, + const uint8_t *opaque_data, size_t opaque_data_len) { + (void)flags; + + if (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) { + return 0; + } + return nghttp2_session_add_goaway(session, last_stream_id, error_code, + opaque_data, opaque_data_len, + NGHTTP2_GOAWAY_AUX_NONE); +} + +int nghttp2_submit_shutdown_notice(nghttp2_session *session) { + if (!session->server) { + return NGHTTP2_ERR_INVALID_STATE; + } + if (session->goaway_flags) { + return 0; + } + return nghttp2_session_add_goaway(session, (1u << 31) - 1, NGHTTP2_NO_ERROR, + NULL, 0, + NGHTTP2_GOAWAY_AUX_SHUTDOWN_NOTICE); +} + +int nghttp2_submit_settings(nghttp2_session *session, uint8_t flags, + const nghttp2_settings_entry *iv, size_t niv) { + (void)flags; + return nghttp2_session_add_settings(session, NGHTTP2_FLAG_NONE, iv, niv); +} + +int32_t nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags, + int32_t stream_id, const nghttp2_nv *nva, + size_t nvlen, + void *promised_stream_user_data) { + nghttp2_outbound_item *item; + nghttp2_frame *frame; + nghttp2_nv *nva_copy; + uint8_t flags_copy; + int32_t promised_stream_id; + int rv; + nghttp2_mem *mem; + (void)flags; + + mem = &session->mem; + + if (stream_id <= 0 || nghttp2_session_is_my_stream_id(session, stream_id)) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + if (!session->server) { + return NGHTTP2_ERR_PROTO; + } + + /* All 32bit signed stream IDs are spent. */ + if (session->next_stream_id > INT32_MAX) { + return NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE; + } + + item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); + if (item == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + nghttp2_outbound_item_init(item); + + item->aux_data.headers.stream_user_data = promised_stream_user_data; + + frame = &item->frame; + + rv = nghttp2_nv_array_copy(&nva_copy, nva, nvlen, mem); + if (rv < 0) { + nghttp2_mem_free(mem, item); + return rv; + } + + flags_copy = NGHTTP2_FLAG_END_HEADERS; + + promised_stream_id = (int32_t)session->next_stream_id; + session->next_stream_id += 2; + + nghttp2_frame_push_promise_init(&frame->push_promise, flags_copy, stream_id, + promised_stream_id, nva_copy, nvlen); + + rv = nghttp2_session_add_item(session, item); + + if (rv != 0) { + nghttp2_frame_push_promise_free(&frame->push_promise, mem); + nghttp2_mem_free(mem, item); + + return rv; + } + + return promised_stream_id; +} + +int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + int32_t window_size_increment) { + int rv; + nghttp2_stream *stream = 0; + (void)flags; + + if (window_size_increment == 0) { + return 0; + } + if (stream_id == 0) { + rv = nghttp2_adjust_local_window_size( + &session->local_window_size, &session->recv_window_size, + &session->recv_reduction, &window_size_increment); + if (rv != 0) { + return rv; + } + } else { + stream = nghttp2_session_get_stream(session, stream_id); + if (!stream) { + return 0; + } + + rv = nghttp2_adjust_local_window_size( + &stream->local_window_size, &stream->recv_window_size, + &stream->recv_reduction, &window_size_increment); + if (rv != 0) { + return rv; + } + } + + if (window_size_increment > 0) { + if (stream_id == 0) { + session->consumed_size = + nghttp2_max(0, session->consumed_size - window_size_increment); + } else { + stream->consumed_size = + nghttp2_max(0, stream->consumed_size - window_size_increment); + } + + return nghttp2_session_add_window_update(session, 0, stream_id, + window_size_increment); + } + return 0; +} + +int nghttp2_session_set_local_window_size(nghttp2_session *session, + uint8_t flags, int32_t stream_id, + int32_t window_size) { + int32_t window_size_increment; + nghttp2_stream *stream; + int rv; + (void)flags; + + if (window_size < 0) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + if (stream_id == 0) { + window_size_increment = window_size - session->local_window_size; + + if (window_size_increment == 0) { + return 0; + } + + if (window_size_increment < 0) { + return nghttp2_adjust_local_window_size( + &session->local_window_size, &session->recv_window_size, + &session->recv_reduction, &window_size_increment); + } + + rv = nghttp2_increase_local_window_size( + &session->local_window_size, &session->recv_window_size, + &session->recv_reduction, &window_size_increment); + + if (rv != 0) { + return rv; + } if (window_size_increment > 0) { return nghttp2_session_add_window_update(session, 0, stream_id, @@ -457,33 +457,33 @@ int nghttp2_session_set_local_window_size(nghttp2_session *session, } return nghttp2_session_update_recv_connection_window_size(session, 0); - } else { - stream = nghttp2_session_get_stream(session, stream_id); - - if (stream == NULL) { - return 0; - } - - window_size_increment = window_size - stream->local_window_size; - - if (window_size_increment == 0) { - return 0; - } - - if (window_size_increment < 0) { - return nghttp2_adjust_local_window_size( - &stream->local_window_size, &stream->recv_window_size, - &stream->recv_reduction, &window_size_increment); - } - - rv = nghttp2_increase_local_window_size( - &stream->local_window_size, &stream->recv_window_size, - &stream->recv_reduction, &window_size_increment); - - if (rv != 0) { - return rv; - } - + } else { + stream = nghttp2_session_get_stream(session, stream_id); + + if (stream == NULL) { + return 0; + } + + window_size_increment = window_size - stream->local_window_size; + + if (window_size_increment == 0) { + return 0; + } + + if (window_size_increment < 0) { + return nghttp2_adjust_local_window_size( + &stream->local_window_size, &stream->recv_window_size, + &stream->recv_reduction, &window_size_increment); + } + + rv = nghttp2_increase_local_window_size( + &stream->local_window_size, &stream->recv_window_size, + &stream->recv_reduction, &window_size_increment); + + if (rv != 0) { + return rv; + } + if (window_size_increment > 0) { return nghttp2_session_add_window_update(session, 0, stream_id, window_size_increment); @@ -491,96 +491,96 @@ int nghttp2_session_set_local_window_size(nghttp2_session *session, return nghttp2_session_update_recv_stream_window_size(session, stream, 0, 1); - } - - return 0; -} - -int nghttp2_submit_altsvc(nghttp2_session *session, uint8_t flags, - int32_t stream_id, const uint8_t *origin, - size_t origin_len, const uint8_t *field_value, - size_t field_value_len) { - nghttp2_mem *mem; - uint8_t *buf, *p; - uint8_t *origin_copy; - uint8_t *field_value_copy; - nghttp2_outbound_item *item; - nghttp2_frame *frame; - nghttp2_ext_altsvc *altsvc; - int rv; - (void)flags; - - mem = &session->mem; - - if (!session->server) { - return NGHTTP2_ERR_INVALID_STATE; - } - - if (2 + origin_len + field_value_len > NGHTTP2_MAX_PAYLOADLEN) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - - if (stream_id == 0) { - if (origin_len == 0) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - } else if (origin_len != 0) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - - buf = nghttp2_mem_malloc(mem, origin_len + field_value_len + 2); - if (buf == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - p = buf; - - origin_copy = p; - if (origin_len) { - p = nghttp2_cpymem(p, origin, origin_len); - } - *p++ = '\0'; - - field_value_copy = p; - if (field_value_len) { - p = nghttp2_cpymem(p, field_value, field_value_len); - } - *p++ = '\0'; - - 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; - - altsvc = &item->ext_frame_payload.altsvc; - - frame = &item->frame; - frame->ext.payload = altsvc; - - nghttp2_frame_altsvc_init(&frame->ext, stream_id, origin_copy, origin_len, - field_value_copy, field_value_len); - - rv = nghttp2_session_add_item(session, item); - if (rv != 0) { - nghttp2_frame_altsvc_free(&frame->ext, mem); - nghttp2_mem_free(mem, item); - - return rv; - } - - return 0; - -fail_item_malloc: - free(buf); - - return rv; -} - + } + + return 0; +} + +int nghttp2_submit_altsvc(nghttp2_session *session, uint8_t flags, + int32_t stream_id, const uint8_t *origin, + size_t origin_len, const uint8_t *field_value, + size_t field_value_len) { + nghttp2_mem *mem; + uint8_t *buf, *p; + uint8_t *origin_copy; + uint8_t *field_value_copy; + nghttp2_outbound_item *item; + nghttp2_frame *frame; + nghttp2_ext_altsvc *altsvc; + int rv; + (void)flags; + + mem = &session->mem; + + if (!session->server) { + return NGHTTP2_ERR_INVALID_STATE; + } + + if (2 + origin_len + field_value_len > NGHTTP2_MAX_PAYLOADLEN) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + if (stream_id == 0) { + if (origin_len == 0) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + } else if (origin_len != 0) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + buf = nghttp2_mem_malloc(mem, origin_len + field_value_len + 2); + if (buf == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + p = buf; + + origin_copy = p; + if (origin_len) { + p = nghttp2_cpymem(p, origin, origin_len); + } + *p++ = '\0'; + + field_value_copy = p; + if (field_value_len) { + p = nghttp2_cpymem(p, field_value, field_value_len); + } + *p++ = '\0'; + + 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; + + altsvc = &item->ext_frame_payload.altsvc; + + frame = &item->frame; + frame->ext.payload = altsvc; + + nghttp2_frame_altsvc_init(&frame->ext, stream_id, origin_copy, origin_len, + field_value_copy, field_value_len); + + rv = nghttp2_session_add_item(session, item); + if (rv != 0) { + nghttp2_frame_altsvc_free(&frame->ext, mem); + nghttp2_mem_free(mem, item); + + return rv; + } + + return 0; + +fail_item_malloc: + free(buf); + + return rv; +} + int nghttp2_submit_origin(nghttp2_session *session, uint8_t flags, const nghttp2_origin_entry *ov, size_t nov) { nghttp2_mem *mem; @@ -664,161 +664,161 @@ fail_item_malloc: 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; - if (data_prd == NULL || data_prd->read_callback == NULL) { - flags |= NGHTTP2_FLAG_END_STREAM; - } - - if (pri_spec) { - flags |= NGHTTP2_FLAG_PRIORITY; - } - - return flags; -} - -int32_t nghttp2_submit_request(nghttp2_session *session, - const nghttp2_priority_spec *pri_spec, - const nghttp2_nv *nva, size_t nvlen, - const nghttp2_data_provider *data_prd, - void *stream_user_data) { - uint8_t flags; - int rv; - - if (session->server) { - return NGHTTP2_ERR_PROTO; - } - - if (pri_spec && !nghttp2_priority_spec_check_default(pri_spec)) { - rv = detect_self_dependency(session, -1, pri_spec); - if (rv != 0) { - return rv; - } - } else { - pri_spec = NULL; - } - - flags = set_request_flags(pri_spec, data_prd); - - return submit_headers_shared_nva(session, flags, -1, pri_spec, nva, nvlen, - data_prd, stream_user_data); -} - -static uint8_t set_response_flags(const nghttp2_data_provider *data_prd) { - uint8_t flags = NGHTTP2_FLAG_NONE; - if (data_prd == NULL || data_prd->read_callback == NULL) { - flags |= NGHTTP2_FLAG_END_STREAM; - } - return flags; -} - -int nghttp2_submit_response(nghttp2_session *session, int32_t stream_id, - const nghttp2_nv *nva, size_t nvlen, - const nghttp2_data_provider *data_prd) { - uint8_t flags; - - if (stream_id <= 0) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - - if (!session->server) { - return NGHTTP2_ERR_PROTO; - } - - flags = set_response_flags(data_prd); - return submit_headers_shared_nva(session, flags, stream_id, NULL, nva, nvlen, - data_prd, NULL); -} - -int nghttp2_submit_data(nghttp2_session *session, uint8_t flags, - int32_t stream_id, - const nghttp2_data_provider *data_prd) { - int rv; - nghttp2_outbound_item *item; - nghttp2_frame *frame; - nghttp2_data_aux_data *aux_data; - uint8_t nflags = flags & NGHTTP2_FLAG_END_STREAM; - nghttp2_mem *mem; - - mem = &session->mem; - - if (stream_id == 0) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - - item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); - if (item == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - nghttp2_outbound_item_init(item); - - frame = &item->frame; - aux_data = &item->aux_data.data; - aux_data->data_prd = *data_prd; - aux_data->eof = 0; - aux_data->flags = nflags; - - /* flags are sent on transmission */ - nghttp2_frame_data_init(&frame->data, NGHTTP2_FLAG_NONE, stream_id); - - rv = nghttp2_session_add_item(session, item); - if (rv != 0) { - nghttp2_frame_data_free(&frame->data); - nghttp2_mem_free(mem, item); - return rv; - } - return 0; -} - -ssize_t nghttp2_pack_settings_payload(uint8_t *buf, size_t buflen, - const nghttp2_settings_entry *iv, - size_t niv) { - if (!nghttp2_iv_check(iv, niv)) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - - if (buflen < (niv * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH)) { - return NGHTTP2_ERR_INSUFF_BUFSIZE; - } - - return (ssize_t)nghttp2_frame_pack_settings_payload(buf, iv, niv); -} - -int nghttp2_submit_extension(nghttp2_session *session, uint8_t type, - uint8_t flags, int32_t stream_id, void *payload) { - int rv; - nghttp2_outbound_item *item; - nghttp2_frame *frame; - nghttp2_mem *mem; - - mem = &session->mem; - - if (type <= NGHTTP2_CONTINUATION) { - return NGHTTP2_ERR_INVALID_ARGUMENT; - } - - if (!session->callbacks.pack_extension_callback) { - return NGHTTP2_ERR_INVALID_STATE; - } - - item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); - if (item == NULL) { - return NGHTTP2_ERR_NOMEM; - } - - nghttp2_outbound_item_init(item); - - frame = &item->frame; - nghttp2_frame_extension_init(&frame->ext, type, flags, stream_id, payload); - - rv = nghttp2_session_add_item(session, item); - if (rv != 0) { - nghttp2_frame_extension_free(&frame->ext); - nghttp2_mem_free(mem, item); - return rv; - } - - return 0; -} +static uint8_t set_request_flags(const nghttp2_priority_spec *pri_spec, + const nghttp2_data_provider *data_prd) { + uint8_t flags = NGHTTP2_FLAG_NONE; + if (data_prd == NULL || data_prd->read_callback == NULL) { + flags |= NGHTTP2_FLAG_END_STREAM; + } + + if (pri_spec) { + flags |= NGHTTP2_FLAG_PRIORITY; + } + + return flags; +} + +int32_t nghttp2_submit_request(nghttp2_session *session, + const nghttp2_priority_spec *pri_spec, + const nghttp2_nv *nva, size_t nvlen, + const nghttp2_data_provider *data_prd, + void *stream_user_data) { + uint8_t flags; + int rv; + + if (session->server) { + return NGHTTP2_ERR_PROTO; + } + + if (pri_spec && !nghttp2_priority_spec_check_default(pri_spec)) { + rv = detect_self_dependency(session, -1, pri_spec); + if (rv != 0) { + return rv; + } + } else { + pri_spec = NULL; + } + + flags = set_request_flags(pri_spec, data_prd); + + return submit_headers_shared_nva(session, flags, -1, pri_spec, nva, nvlen, + data_prd, stream_user_data); +} + +static uint8_t set_response_flags(const nghttp2_data_provider *data_prd) { + uint8_t flags = NGHTTP2_FLAG_NONE; + if (data_prd == NULL || data_prd->read_callback == NULL) { + flags |= NGHTTP2_FLAG_END_STREAM; + } + return flags; +} + +int nghttp2_submit_response(nghttp2_session *session, int32_t stream_id, + const nghttp2_nv *nva, size_t nvlen, + const nghttp2_data_provider *data_prd) { + uint8_t flags; + + if (stream_id <= 0) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + if (!session->server) { + return NGHTTP2_ERR_PROTO; + } + + flags = set_response_flags(data_prd); + return submit_headers_shared_nva(session, flags, stream_id, NULL, nva, nvlen, + data_prd, NULL); +} + +int nghttp2_submit_data(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + const nghttp2_data_provider *data_prd) { + int rv; + nghttp2_outbound_item *item; + nghttp2_frame *frame; + nghttp2_data_aux_data *aux_data; + uint8_t nflags = flags & NGHTTP2_FLAG_END_STREAM; + nghttp2_mem *mem; + + mem = &session->mem; + + if (stream_id == 0) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); + if (item == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + nghttp2_outbound_item_init(item); + + frame = &item->frame; + aux_data = &item->aux_data.data; + aux_data->data_prd = *data_prd; + aux_data->eof = 0; + aux_data->flags = nflags; + + /* flags are sent on transmission */ + nghttp2_frame_data_init(&frame->data, NGHTTP2_FLAG_NONE, stream_id); + + rv = nghttp2_session_add_item(session, item); + if (rv != 0) { + nghttp2_frame_data_free(&frame->data); + nghttp2_mem_free(mem, item); + return rv; + } + return 0; +} + +ssize_t nghttp2_pack_settings_payload(uint8_t *buf, size_t buflen, + const nghttp2_settings_entry *iv, + size_t niv) { + if (!nghttp2_iv_check(iv, niv)) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + if (buflen < (niv * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH)) { + return NGHTTP2_ERR_INSUFF_BUFSIZE; + } + + return (ssize_t)nghttp2_frame_pack_settings_payload(buf, iv, niv); +} + +int nghttp2_submit_extension(nghttp2_session *session, uint8_t type, + uint8_t flags, int32_t stream_id, void *payload) { + int rv; + nghttp2_outbound_item *item; + nghttp2_frame *frame; + nghttp2_mem *mem; + + mem = &session->mem; + + if (type <= NGHTTP2_CONTINUATION) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + if (!session->callbacks.pack_extension_callback) { + return NGHTTP2_ERR_INVALID_STATE; + } + + item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); + if (item == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + nghttp2_outbound_item_init(item); + + frame = &item->frame; + nghttp2_frame_extension_init(&frame->ext, type, flags, stream_id, payload); + + rv = nghttp2_session_add_item(session, item); + if (rv != 0) { + nghttp2_frame_extension_free(&frame->ext); + nghttp2_mem_free(mem, item); + return rv; + } + + return 0; +} diff --git a/contrib/libs/nghttp2/lib/nghttp2_submit.h b/contrib/libs/nghttp2/lib/nghttp2_submit.h index 41647c4a9e..74d702fbcf 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_submit.h +++ b/contrib/libs/nghttp2/lib/nghttp2_submit.h @@ -1,34 +1,34 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012 Tatsuhiro Tsujikawa - * - * 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_SUBMIT_H -#define NGHTTP2_SUBMIT_H - -#ifdef HAVE_CONFIG_H +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * 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_SUBMIT_H +#define NGHTTP2_SUBMIT_H + +#ifdef HAVE_CONFIG_H # include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <nghttp2/nghttp2.h> - -#endif /* NGHTTP2_SUBMIT_H */ +#endif /* HAVE_CONFIG_H */ + +#include <nghttp2/nghttp2.h> + +#endif /* NGHTTP2_SUBMIT_H */ diff --git a/contrib/libs/nghttp2/lib/nghttp2_version.c b/contrib/libs/nghttp2/lib/nghttp2_version.c index 393d376d30..4211f2cf8f 100644 --- a/contrib/libs/nghttp2/lib/nghttp2_version.c +++ b/contrib/libs/nghttp2/lib/nghttp2_version.c @@ -1,38 +1,38 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa - * - * 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. - */ -#ifdef HAVE_CONFIG_H +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa + * + * 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. + */ +#ifdef HAVE_CONFIG_H # include <config.h> -#endif /* HAVE_CONFIG_H */ - -#include <nghttp2/nghttp2.h> - -static nghttp2_info version = {NGHTTP2_VERSION_AGE, NGHTTP2_VERSION_NUM, - NGHTTP2_VERSION, NGHTTP2_PROTO_VERSION_ID}; - -nghttp2_info *nghttp2_version(int least_version) { - if (least_version > NGHTTP2_VERSION_NUM) - return NULL; - return &version; -} +#endif /* HAVE_CONFIG_H */ + +#include <nghttp2/nghttp2.h> + +static nghttp2_info version = {NGHTTP2_VERSION_AGE, NGHTTP2_VERSION_NUM, + NGHTTP2_VERSION, NGHTTP2_PROTO_VERSION_ID}; + +nghttp2_info *nghttp2_version(int least_version) { + if (least_version > NGHTTP2_VERSION_NUM) + return NULL; + return &version; +} |