deps: update nghttp2 to 1.65.0

PR-URL: https://github.com/nodejs/node/pull/57269
Reviewed-By: Michaël Zasso <targos@protonmail.com>
Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
This commit is contained in:
Node.js GitHub Bot 2025-06-07 05:30:45 -04:00 committed by GitHub
parent 26ebedf9ef
commit d74daeab96
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 1226 additions and 2498 deletions

View file

@ -3133,14 +3133,12 @@ nghttp2_option_set_max_deflate_dynamic_table_size(nghttp2_option *option,
/**
* @function
*
* This option prevents the library from retaining closed streams to
* maintain the priority tree. If this option is set to nonzero,
* applications can discard closed stream completely to save memory.
* .. warning::
*
* If
* :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES`
* of value of 1 is submitted via `nghttp2_submit_settings()`, any
* closed streams are not retained regardless of this option.
* Deprecated. Closed streams are not retained anymore.
*
* This function works as before, but it does not take any effect
* against :type:`nghttp2_session`.
*/
NGHTTP2_EXTERN void nghttp2_option_set_no_closed_streams(nghttp2_option *option,
int val);
@ -3170,16 +3168,11 @@ NGHTTP2_EXTERN void nghttp2_option_set_max_settings(nghttp2_option *option,
/**
* @function
*
* This option, if set to nonzero, allows server to fallback to
* :rfc:`7540` priorities if SETTINGS_NO_RFC7540_PRIORITIES was not
* received from client, and server submitted
* :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES`
* = 1 via `nghttp2_submit_settings()`. Most of the advanced
* functionality for RFC 7540 priorities are still disabled. This
* fallback only enables the minimal feature set of RFC 7540
* priorities to deal with priority signaling from client.
* .. warning::
* Deprecated. :rfc:`7540` priorities have been removed.
*
* Client session ignores this option.
* This function works as before, but it does not take any effect
* against :type:`nghttp2_session`.
*/
NGHTTP2_EXTERN void
nghttp2_option_set_server_fallback_rfc7540_priorities(nghttp2_option *option,
@ -4179,39 +4172,9 @@ NGHTTP2_EXTERN int nghttp2_session_consume_stream(nghttp2_session *session,
*
* Deprecated. :rfc:`7540` priorities are deprecated by
* :rfc:`9113`. Consider migrating to :rfc:`9218` extensible
* prioritization scheme. In the future release after the end of
* 2024, this function will always return 0 without doing anything.
* prioritization scheme.
*
* Changes priority of existing stream denoted by |stream_id|. The
* new priority specification is |pri_spec|.
*
* The priority is changed silently and instantly, and no PRIORITY
* frame will be sent to notify the peer of this change. This
* function may be useful for server to change the priority of pushed
* stream.
*
* If |session| is initialized as server, and ``pri_spec->stream_id``
* points to the idle stream, the idle stream is created if it does
* not exist. The created idle stream will depend on root stream
* (stream 0) with weight 16.
*
* Otherwise, if stream denoted by ``pri_spec->stream_id`` is not
* found, we use default priority instead of given |pri_spec|. That
* is make stream depend on root stream with weight 16.
*
* If
* :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES`
* of value of 1 is submitted via `nghttp2_submit_settings()`, this
* function does nothing and returns 0.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`
* Out of memory.
* :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT`
* Attempted to depend on itself; or no stream exist for the given
* |stream_id|; or |stream_id| is 0
* This function is noop. It always returns 0.
*/
NGHTTP2_EXTERN int
nghttp2_session_change_stream_priority(nghttp2_session *session,
@ -4225,51 +4188,9 @@ nghttp2_session_change_stream_priority(nghttp2_session *session,
*
* Deprecated. :rfc:`7540` priorities are deprecated by
* :rfc:`9113`. Consider migrating to :rfc:`9218` extensible
* prioritization scheme. In the future release after the end of
* 2024, this function will always return 0 without doing anything.
* prioritization scheme.
*
* Creates idle stream with the given |stream_id|, and priority
* |pri_spec|.
*
* The stream creation is done without sending PRIORITY frame, which
* means that peer does not know about the existence of this idle
* stream in the local endpoint.
*
* RFC 7540 does not disallow the use of creation of idle stream with
* odd or even stream ID regardless of client or server. So this
* function can create odd or even stream ID regardless of client or
* server. But probably it is a bit safer to use the stream ID the
* local endpoint can initiate (in other words, use odd stream ID for
* client, and even stream ID for server), to avoid potential
* collision from peer's instruction. Also we can use
* `nghttp2_session_set_next_stream_id()` to avoid to open created
* idle streams accidentally if we follow this recommendation.
*
* If |session| is initialized as server, and ``pri_spec->stream_id``
* points to the idle stream, the idle stream is created if it does
* not exist. The created idle stream will depend on root stream
* (stream 0) with weight 16.
*
* Otherwise, if stream denoted by ``pri_spec->stream_id`` is not
* found, we use default priority instead of given |pri_spec|. That
* is make stream depend on root stream with weight 16.
*
* If
* :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES`
* of value of 1 is submitted via `nghttp2_submit_settings()`, this
* function does nothing and returns 0.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`
* Out of memory.
* :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT`
* Attempted to depend on itself; or stream denoted by |stream_id|
* already exists; or |stream_id| cannot be used to create idle
* stream (in other words, local endpoint has already opened
* stream ID greater than or equal to the given stream ID; or
* |stream_id| is 0
* This function is noop. It always returns 0.
*/
NGHTTP2_EXTERN int
nghttp2_session_create_idle_stream(nghttp2_session *session, int32_t stream_id,
@ -4505,23 +4426,7 @@ nghttp2_priority_spec_check_default(const nghttp2_priority_spec *pri_spec);
*
* Submits HEADERS frame and optionally one or more DATA frames.
*
* The |pri_spec| is a deprecated priority specification of this
* request. ``NULL`` means the default priority (see
* `nghttp2_priority_spec_default_init()`). To specify the priority,
* use `nghttp2_priority_spec_init()`. If |pri_spec| is not ``NULL``,
* this function will copy its data members.
*
* The ``pri_spec->weight`` must be in [:macro:`NGHTTP2_MIN_WEIGHT`,
* :macro:`NGHTTP2_MAX_WEIGHT`], inclusive. If ``pri_spec->weight``
* is strictly less than :macro:`NGHTTP2_MIN_WEIGHT`, it becomes
* :macro:`NGHTTP2_MIN_WEIGHT`. If it is strictly greater than
* :macro:`NGHTTP2_MAX_WEIGHT`, it becomes
* :macro:`NGHTTP2_MAX_WEIGHT`.
*
* If
* :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES`
* of value of 1 is received by a remote endpoint, |pri_spec| is
* ignored, and treated as if ``NULL`` is specified.
* The |pri_spec| is ignored.
*
* The |nva| is an array of name/value pair :type:`nghttp2_nv` with
* |nvlen| elements. The application is responsible to include
@ -4564,9 +4469,6 @@ nghttp2_priority_spec_check_default(const nghttp2_priority_spec *pri_spec);
* :enum:`nghttp2_error.NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE`
* No stream ID is available because maximum stream ID was
* reached.
* :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT`
* Trying to depend on itself (new stream ID equals
* ``pri_spec->stream_id``).
* :enum:`nghttp2_error.NGHTTP2_ERR_PROTO`
* The |session| is server session.
*
@ -4594,25 +4496,7 @@ NGHTTP2_EXTERN int32_t nghttp2_submit_request(
*
* Submits HEADERS frame and optionally one or more DATA frames.
*
* The |pri_spec| is a deprecated priority specification of this
* request. ``NULL`` means the default priority (see
* `nghttp2_priority_spec_default_init()`). To specify the priority,
* use `nghttp2_priority_spec_init()`. If |pri_spec| is not ``NULL``,
* this function will copy its data members. In the future release
* after the end of 2024, this function will ignore |pri_spec| and
* behave as if ``NULL`` is given.
*
* The ``pri_spec->weight`` must be in [:macro:`NGHTTP2_MIN_WEIGHT`,
* :macro:`NGHTTP2_MAX_WEIGHT`], inclusive. If ``pri_spec->weight``
* is strictly less than :macro:`NGHTTP2_MIN_WEIGHT`, it becomes
* :macro:`NGHTTP2_MIN_WEIGHT`. If it is strictly greater than
* :macro:`NGHTTP2_MAX_WEIGHT`, it becomes
* :macro:`NGHTTP2_MAX_WEIGHT`.
*
* If
* :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES`
* of value of 1 is received by a remote endpoint, |pri_spec| is
* ignored, and treated as if ``NULL`` is specified.
* The |pri_spec| is ignored.
*
* The |nva| is an array of name/value pair :type:`nghttp2_nv` with
* |nvlen| elements. The application is responsible to include
@ -4655,9 +4539,6 @@ NGHTTP2_EXTERN int32_t nghttp2_submit_request(
* :enum:`nghttp2_error.NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE`
* No stream ID is available because maximum stream ID was
* reached.
* :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT`
* Trying to depend on itself (new stream ID equals
* ``pri_spec->stream_id``).
* :enum:`nghttp2_error.NGHTTP2_ERR_PROTO`
* The |session| is server session.
*
@ -4899,24 +4780,7 @@ NGHTTP2_EXTERN int nghttp2_submit_trailer(nghttp2_session *session,
* assigned stream ID will be returned. Otherwise, specify stream ID
* in |stream_id|.
*
* The |pri_spec| is a deprecated priority specification of this
* request. ``NULL`` means the default priority (see
* `nghttp2_priority_spec_default_init()`). To specify the priority,
* use `nghttp2_priority_spec_init()`. If |pri_spec| is not ``NULL``,
* this function will copy its data members. In the future release
* after the end of 2024, this function will ignore |pri_spec| and
* behave as if ``NULL`` is given.
*
* The ``pri_spec->weight`` must be in [:macro:`NGHTTP2_MIN_WEIGHT`,
* :macro:`NGHTTP2_MAX_WEIGHT`], inclusive. If ``pri_spec->weight``
* is strictly less than :macro:`NGHTTP2_MIN_WEIGHT`, it becomes
* :macro:`NGHTTP2_MIN_WEIGHT`. If it is strictly greater than
* :macro:`NGHTTP2_MAX_WEIGHT`, it becomes :macro:`NGHTTP2_MAX_WEIGHT`.
*
* If
* :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES`
* of value of 1 is received by a remote endpoint, |pri_spec| is
* ignored, and treated as if ``NULL`` is specified.
* The |pri_spec| is ignored.
*
* The |nva| is an array of name/value pair :type:`nghttp2_nv` with
* |nvlen| elements. The application is responsible to include
@ -4956,8 +4820,7 @@ NGHTTP2_EXTERN int nghttp2_submit_trailer(nghttp2_session *session,
* No stream ID is available because maximum stream ID was
* reached.
* :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT`
* The |stream_id| is 0; or trying to depend on itself (stream ID
* equals ``pri_spec->stream_id``).
* The |stream_id| is 0.
* :enum:`nghttp2_error.NGHTTP2_ERR_DATA_EXIST`
* DATA or HEADERS has been already submitted and not fully
* processed yet. This happens if stream denoted by |stream_id|
@ -5083,40 +4946,9 @@ NGHTTP2_EXTERN int nghttp2_submit_data2(nghttp2_session *session, uint8_t flags,
*
* Deprecated. :rfc:`7540` priorities are deprecated by
* :rfc:`9113`. Consider migrating to :rfc:`9218` extensible
* prioritization scheme. In the future release after the end of
* 2024, this function will always return 0 without doing anything.
* prioritization scheme.
*
* Submits PRIORITY frame to change the priority of stream |stream_id|
* to the priority specification |pri_spec|.
*
* The |flags| is currently ignored and should be
* :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`.
*
* The |pri_spec| is a deprecated priority specification of this
* request. ``NULL`` is not allowed for this function. To specify the
* priority, use `nghttp2_priority_spec_init()`. This function will
* copy its data members.
*
* The ``pri_spec->weight`` must be in [:macro:`NGHTTP2_MIN_WEIGHT`,
* :macro:`NGHTTP2_MAX_WEIGHT`], inclusive. If ``pri_spec->weight``
* is strictly less than :macro:`NGHTTP2_MIN_WEIGHT`, it becomes
* :macro:`NGHTTP2_MIN_WEIGHT`. If it is strictly greater than
* :macro:`NGHTTP2_MAX_WEIGHT`, it becomes
* :macro:`NGHTTP2_MAX_WEIGHT`.
*
* If
* :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES`
* of value of 1 is received by a remote endpoint, this function does
* nothing and returns 0.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`
* Out of memory.
* :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT`
* The |stream_id| is 0; or the |pri_spec| is NULL; or trying to
* depend on itself.
* This function is noop. It always returns 0.
*/
NGHTTP2_EXTERN int
nghttp2_submit_priority(nghttp2_session *session, uint8_t flags,
@ -6885,11 +6717,9 @@ nghttp2_session_get_root_stream(nghttp2_session *session);
*
* Deprecated. :rfc:`7540` priorities are deprecated by
* :rfc:`9113`. Consider migrating to :rfc:`9218` extensible
* prioritization scheme. In the future release after the end of
* 2024, this function will always return NULL.
* prioritization scheme.
*
* Returns the parent stream of |stream| in dependency tree. Returns
* NULL if there is no such stream.
* This function always returns NULL.
*/
NGHTTP2_EXTERN nghttp2_stream *
nghttp2_stream_get_parent(nghttp2_stream *stream);
@ -6903,11 +6733,9 @@ NGHTTP2_EXTERN int32_t nghttp2_stream_get_stream_id(nghttp2_stream *stream);
*
* Deprecated. :rfc:`7540` priorities are deprecated by
* :rfc:`9113`. Consider migrating to :rfc:`9218` extensible
* prioritization scheme. In the future release after the end of
* 2024, this function will always return NULL.
* prioritization scheme.
*
* Returns the next sibling stream of |stream| in dependency tree.
* Returns NULL if there is no such stream.
* This function always returns NULL.
*/
NGHTTP2_EXTERN nghttp2_stream *
nghttp2_stream_get_next_sibling(nghttp2_stream *stream);
@ -6919,11 +6747,9 @@ nghttp2_stream_get_next_sibling(nghttp2_stream *stream);
*
* Deprecated. :rfc:`7540` priorities are deprecated by
* :rfc:`9113`. Consider migrating to :rfc:`9218` extensible
* prioritization scheme. In the future release after the end of
* 2024, this function will always return NULL.
* prioritization scheme.
*
* Returns the previous sibling stream of |stream| in dependency tree.
* Returns NULL if there is no such stream.
* This function always returns NULL.
*/
NGHTTP2_EXTERN nghttp2_stream *
nghttp2_stream_get_previous_sibling(nghttp2_stream *stream);
@ -6935,11 +6761,9 @@ nghttp2_stream_get_previous_sibling(nghttp2_stream *stream);
*
* Deprecated. :rfc:`7540` priorities are deprecated by
* :rfc:`9113`. Consider migrating to :rfc:`9218` extensible
* prioritization scheme. In the future release after the end of
* 2024, this function will always return NULL.
* prioritization scheme.
*
* Returns the first child stream of |stream| in dependency tree.
* Returns NULL if there is no such stream.
* This function always returns NULL.
*/
NGHTTP2_EXTERN nghttp2_stream *
nghttp2_stream_get_first_child(nghttp2_stream *stream);
@ -6951,11 +6775,9 @@ nghttp2_stream_get_first_child(nghttp2_stream *stream);
*
* Deprecated. :rfc:`7540` priorities are deprecated by
* :rfc:`9113`. Consider migrating to :rfc:`9218` extensible
* prioritization scheme. In the future release after the end of
* 2024, this function will always return
* :macro:`NGHTTP2_DEFAULT_WEIGHT`.
* prioritization scheme.
*
* Returns dependency weight to the parent stream of |stream|.
* This function always returns :macro:`NGHTTP2_DEFAULT_WEIGHT`.
*/
NGHTTP2_EXTERN int32_t nghttp2_stream_get_weight(nghttp2_stream *stream);
@ -6966,10 +6788,9 @@ NGHTTP2_EXTERN int32_t nghttp2_stream_get_weight(nghttp2_stream *stream);
*
* Deprecated. :rfc:`7540` priorities are deprecated by
* :rfc:`9113`. Consider migrating to :rfc:`9218` extensible
* prioritization scheme. In the future release after the end of
* 2024, this function will always return 0.
* prioritization scheme.
*
* Returns the sum of the weight for |stream|'s children.
* This function always returns 0.
*/
NGHTTP2_EXTERN int32_t
nghttp2_stream_get_sum_dependency_weight(nghttp2_stream *stream);

View file

@ -29,7 +29,7 @@
* @macro
* Version number of the nghttp2 library release
*/
#define NGHTTP2_VERSION "1.64.0"
#define NGHTTP2_VERSION "1.65.0"
/**
* @macro
@ -37,6 +37,6 @@
* release. This is a 24 bit number with 8 bits for major number, 8 bits
* for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203.
*/
#define NGHTTP2_VERSION_NUM 0x014000
#define NGHTTP2_VERSION_NUM 0x014100
#endif /* NGHTTP2VER_H */

View file

@ -594,8 +594,19 @@ static void hd_map_remove(nghttp2_hd_map *map, nghttp2_hd_entry *ent) {
static int hd_ringbuf_init(nghttp2_hd_ringbuf *ringbuf, size_t bufsize,
nghttp2_mem *mem) {
size_t size;
const size_t max_size = SIZE_MAX / sizeof(nghttp2_hd_entry *);
if (bufsize > max_size) {
return NGHTTP2_ERR_NOMEM;
}
for (size = 1; size < bufsize; size <<= 1)
;
if (size > max_size) {
return NGHTTP2_ERR_NOMEM;
}
ringbuf->buffer = nghttp2_mem_malloc(mem, sizeof(nghttp2_hd_entry *) * size);
if (ringbuf->buffer == NULL) {
return NGHTTP2_ERR_NOMEM;

View file

@ -207,7 +207,6 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
if (!trailer &&
/* Do not parse the header field in PUSH_PROMISE. */
(stream->stream_id & 1) &&
(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) &&
!(stream->http_flags & NGHTTP2_HTTP_FLAG_BAD_PRIORITY)) {
nghttp2_extpri_from_uint8(&extpri, stream->http_extpri);
if (nghttp2_http_parse_priority(&extpri, nv->value->base,
@ -660,17 +659,17 @@ void nghttp2_http_record_request_method(nghttp2_stream *stream,
int nghttp2_http_parse_priority(nghttp2_extpri *dest, const uint8_t *value,
size_t valuelen) {
nghttp2_extpri pri = *dest;
sf_parser sfp;
sf_vec key;
sf_value val;
sfparse_parser sfp;
sfparse_vec key;
sfparse_value val;
int rv;
sf_parser_init(&sfp, value, valuelen);
sfparse_parser_init(&sfp, value, valuelen);
for (;;) {
rv = sf_parser_dict(&sfp, &key, &val);
rv = sfparse_parser_dict(&sfp, &key, &val);
if (rv != 0) {
if (rv == SF_ERR_EOF) {
if (rv == SFPARSE_ERR_EOF) {
break;
}
@ -683,7 +682,7 @@ int nghttp2_http_parse_priority(nghttp2_extpri *dest, const uint8_t *value,
switch (key.base[0]) {
case 'i':
if (val.type != SF_TYPE_BOOLEAN) {
if (val.type != SFPARSE_TYPE_BOOLEAN) {
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
@ -691,7 +690,7 @@ int nghttp2_http_parse_priority(nghttp2_extpri *dest, const uint8_t *value,
break;
case 'u':
if (val.type != SF_TYPE_INTEGER ||
if (val.type != SFPARSE_TYPE_INTEGER ||
val.integer < NGHTTP2_EXTPRI_URGENCY_HIGH ||
NGHTTP2_EXTPRI_URGENCY_LOW < val.integer) {
return NGHTTP2_ERR_INVALID_ARGUMENT;

File diff suppressed because it is too large Load diff

View file

@ -45,6 +45,8 @@
preface handling. */
extern int nghttp2_enable_strict_preface;
extern nghttp2_stream root;
/*
* Option flags.
*/
@ -53,8 +55,6 @@ typedef enum {
NGHTTP2_OPTMASK_NO_RECV_CLIENT_MAGIC = 1 << 1,
NGHTTP2_OPTMASK_NO_HTTP_MESSAGING = 1 << 2,
NGHTTP2_OPTMASK_NO_AUTO_PING_ACK = 1 << 3,
NGHTTP2_OPTMASK_NO_CLOSED_STREAMS = 1 << 4,
NGHTTP2_OPTMASK_SERVER_FALLBACK_RFC7540_PRIORITIES = 1 << 5,
NGHTTP2_OPTMASK_NO_RFC9113_LEADING_AND_TRAILING_WS_VALIDATION = 1 << 6,
} nghttp2_optmask;
@ -89,10 +89,6 @@ typedef struct {
/* The default maximum number of incoming reserved streams */
#define NGHTTP2_MAX_INCOMING_RESERVED_STREAMS 200
/* Even if we have less SETTINGS_MAX_CONCURRENT_STREAMS than this
number, we keep NGHTTP2_MIN_IDLE_STREAMS streams in idle state */
#define NGHTTP2_MIN_IDLE_STREAMS 16
/* The maximum number of items in outbound queue, which is considered
as flooding caused by peer. All frames are not considered here.
We only consider PING + ACK and SETTINGS + ACK. This is because
@ -205,8 +201,6 @@ 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 */
@ -229,20 +223,6 @@ struct nghttp2_session {
/* Memory allocator */
nghttp2_mem mem;
void *user_data;
/* Points to the latest incoming closed stream. NULL if there is no
closed stream. Only used when session is initialized as
server. */
nghttp2_stream *closed_stream_head;
/* Points to the oldest incoming closed stream. NULL if there is no
closed stream. Only used when session is initialized as
server. */
nghttp2_stream *closed_stream_tail;
/* Points to the latest idle stream. NULL if there is no idle
stream. Only used when session is initialized as server .*/
nghttp2_stream *idle_stream_head;
/* Points to the oldest idle stream. NULL if there is no idle
stream. Only used when session is initialized as erver. */
nghttp2_stream *idle_stream_tail;
/* Queue of In-flight SETTINGS values. SETTINGS bearing ACK is not
considered as in-flight. */
nghttp2_inflight_settings *inflight_settings_head;
@ -276,10 +256,9 @@ struct nghttp2_session {
|closed_stream_head|. The current implementation only keeps
incoming streams and session is initialized as server. */
size_t num_closed_streams;
/* The number of idle streams kept in |streams| hash. The idle
streams can be accessed through doubly linked list
|idle_stream_head|. The current implementation only keeps idle
streams if session is initialized as server. */
/* The number of idle streams kept in |streams| hash. The current
implementation only keeps idle streams if session is initialized
as server. */
size_t num_idle_streams;
/* The number of bytes allocated for nvbuf */
size_t nvbuflen;
@ -362,8 +341,6 @@ struct nghttp2_session {
/* Unacked local SETTINGS_NO_RFC7540_PRIORITIES value, which is
effective before it is acknowledged. */
uint8_t pending_no_rfc7540_priorities;
/* Turn on fallback to RFC 7540 priorities; for server use only. */
uint8_t fallback_rfc7540_priorities;
/* Nonzero if the session is server side. */
uint8_t server;
/* Flags indicating GOAWAY is sent and/or received. The flags are
@ -527,15 +504,9 @@ int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags,
*
* This function returns a pointer to created new stream object, or
* NULL.
*
* This function adjusts neither the number of closed streams or idle
* streams. The caller should manually call
* nghttp2_session_adjust_closed_stream() or
* nghttp2_session_adjust_idle_stream() respectively.
*/
nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session,
int32_t stream_id, uint8_t flags,
nghttp2_priority_spec *pri_spec,
nghttp2_stream_state initial_state,
void *stream_user_data);
@ -544,11 +515,6 @@ nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session,
* is indicated by the |error_code|. When closing the stream,
* on_stream_close_callback will be called.
*
* If the session is initialized as server and |stream| is incoming
* stream, stream is just marked closed and this function calls
* nghttp2_session_keep_closed_stream() with |stream|. Otherwise,
* |stream| will be deleted from memory.
*
* This function returns 0 if it succeeds, or one the following
* negative error codes:
*
@ -565,63 +531,9 @@ int nghttp2_session_close_stream(nghttp2_session *session, int32_t stream_id,
/*
* Deletes |stream| from memory. After this function returns, stream
* cannot be accessed.
*
* This function returns 0 if it succeeds, or one the following
* negative error codes:
*
* NGHTTP2_ERR_NOMEM
* Out of memory
*/
int nghttp2_session_destroy_stream(nghttp2_session *session,
nghttp2_stream *stream);
/*
* Tries to keep incoming closed stream |stream|. Due to the
* limitation of maximum number of streams in memory, |stream| is not
* closed and just deleted from memory (see
* nghttp2_session_destroy_stream).
*/
void nghttp2_session_keep_closed_stream(nghttp2_session *session,
nghttp2_stream *stream);
/*
* Appends |stream| to linked list |session->idle_stream_head|. We
* apply fixed limit for list size. To fit into that limit, one or
* more oldest streams are removed from list as necessary.
*/
void nghttp2_session_keep_idle_stream(nghttp2_session *session,
nghttp2_stream *stream);
/*
* Detaches |stream| from idle streams linked list.
*/
void nghttp2_session_detach_idle_stream(nghttp2_session *session,
nghttp2_stream *stream);
/*
* Deletes closed stream to ensure that number of incoming streams
* including active and closed is in the maximum number of allowed
* stream.
*
* This function returns 0 if it succeeds, or one the following
* negative error codes:
*
* NGHTTP2_ERR_NOMEM
* Out of memory
*/
int nghttp2_session_adjust_closed_stream(nghttp2_session *session);
/*
* Deletes idle stream to ensure that number of idle streams is in
* certain limit.
*
* This function returns 0 if it succeeds, or one the following
* negative error codes:
*
* NGHTTP2_ERR_NOMEM
* Out of memory
*/
int nghttp2_session_adjust_idle_stream(nghttp2_session *session);
void nghttp2_session_destroy_stream(nghttp2_session *session,
nghttp2_stream *stream);
/*
* If further receptions and transmissions over the stream |stream_id|
@ -915,24 +827,6 @@ int nghttp2_session_update_local_settings(nghttp2_session *session,
nghttp2_settings_entry *iv,
size_t niv);
/*
* Re-prioritize |stream|. The new priority specification is
* |pri_spec|. Caller must ensure that stream->hd.stream_id !=
* pri_spec->stream_id.
*
* This function does not adjust the number of idle streams. The
* caller should call nghttp2_session_adjust_idle_stream() later.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* NGHTTP2_ERR_NOMEM
* Out of memory
*/
int nghttp2_session_reprioritize_stream(nghttp2_session *session,
nghttp2_stream *stream,
const nghttp2_priority_spec *pri_spec);
/*
* Terminates current |session| with the |error_code|. The |reason|
* is NULL-terminated debug string.

View file

@ -25,45 +25,17 @@
#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
priority queue. Imagine stream A's cycle is A, and stream B's
cycle is B, and A < B. The cycle is unsigned 32 bit integer, it
may get overflow. Because of how we calculate the next cycle
value, if B - A is less than or equals to
NGHTTP2_MAX_CYCLE_DISTANCE, A and B are in the same scale, in other
words, B is really greater than or equal to A. Otherwise, A is a
result of overflow, and it is actually A > B if we consider that
fact. */
#define NGHTTP2_MAX_CYCLE_DISTANCE \
((uint64_t)NGHTTP2_MAX_FRAME_SIZE_MAX * 256 + 255)
static int stream_less(const void *lhsx, const void *rhsx) {
const nghttp2_stream *lhs, *rhs;
lhs = nghttp2_struct_of(lhsx, nghttp2_stream, pq_entry);
rhs = nghttp2_struct_of(rhsx, nghttp2_stream, pq_entry);
if (lhs->cycle == rhs->cycle) {
return lhs->seq < rhs->seq;
}
return rhs->cycle - lhs->cycle <= NGHTTP2_MAX_CYCLE_DISTANCE;
}
void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id,
uint8_t flags, nghttp2_stream_state initial_state,
int32_t weight, int32_t remote_initial_window_size,
int32_t remote_initial_window_size,
int32_t local_initial_window_size,
void *stream_user_data, nghttp2_mem *mem) {
nghttp2_pq_init(&stream->obq, stream_less, mem);
void *stream_user_data) {
stream->stream_id = stream_id;
stream->flags = flags;
stream->state = initial_state;
@ -77,428 +49,36 @@ void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id,
stream->recv_reduction = 0;
stream->window_update_queued = 0;
stream->dep_prev = NULL;
stream->dep_next = NULL;
stream->sib_prev = NULL;
stream->sib_next = NULL;
stream->closed_prev = NULL;
stream->closed_next = NULL;
stream->weight = weight;
stream->sum_dep_weight = 0;
stream->http_flags = NGHTTP2_HTTP_FLAG_NONE;
stream->content_length = -1;
stream->recv_content_length = 0;
stream->status_code = -1;
stream->queued = 0;
stream->descendant_last_cycle = 0;
stream->cycle = 0;
stream->pending_penalty = 0;
stream->descendant_next_seq = 0;
stream->seq = 0;
stream->last_writelen = 0;
stream->extpri = stream->http_extpri = NGHTTP2_EXTPRI_DEFAULT_URGENCY;
}
void nghttp2_stream_free(nghttp2_stream *stream) {
nghttp2_pq_free(&stream->obq);
/* We don't free stream->item. If it is assigned to aob, then
active_outbound_item_reset() will delete it. Otherwise,
nghttp2_stream_close() or session_del() will delete it. */
}
void nghttp2_stream_free(nghttp2_stream *stream) { (void)stream; }
void nghttp2_stream_shutdown(nghttp2_stream *stream, nghttp2_shut_flag flag) {
stream->shut_flags = (uint8_t)(stream->shut_flags | flag);
}
/*
* Returns nonzero if |stream| is active. This function does not take
* into account its descendants.
*/
static int stream_active(nghttp2_stream *stream) {
return stream->item &&
(stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL) == 0;
}
/*
* Returns nonzero if |stream| or one of its descendants is active
*/
static int stream_subtree_active(nghttp2_stream *stream) {
return stream_active(stream) || !nghttp2_pq_empty(&stream->obq);
}
/*
* Returns next cycle for |stream|.
*/
static void stream_next_cycle(nghttp2_stream *stream, uint64_t last_cycle) {
uint64_t penalty;
penalty = (uint64_t)stream->last_writelen * NGHTTP2_MAX_WEIGHT +
stream->pending_penalty;
stream->cycle = last_cycle + penalty / (uint32_t)stream->weight;
stream->pending_penalty = (uint32_t)(penalty % (uint32_t)stream->weight);
}
static int stream_obq_push(nghttp2_stream *dep_stream, nghttp2_stream *stream) {
int rv;
for (; dep_stream && !stream->queued;
stream = dep_stream, dep_stream = dep_stream->dep_prev) {
stream_next_cycle(stream, dep_stream->descendant_last_cycle);
stream->seq = dep_stream->descendant_next_seq++;
DEBUGF("stream: stream=%d obq push cycle=%lu\n", stream->stream_id,
stream->cycle);
DEBUGF("stream: push stream %d to stream %d\n", stream->stream_id,
dep_stream->stream_id);
rv = nghttp2_pq_push(&dep_stream->obq, &stream->pq_entry);
if (rv != 0) {
return rv;
}
stream->queued = 1;
}
return 0;
}
/*
* Removes |stream| from parent's obq. If removal of |stream| makes
* parent's obq empty, and parent is not active, then parent is also
* removed. This process is repeated recursively.
*/
static void stream_obq_remove(nghttp2_stream *stream) {
nghttp2_stream *dep_stream;
dep_stream = stream->dep_prev;
if (!stream->queued) {
return;
}
for (; dep_stream; stream = dep_stream, dep_stream = dep_stream->dep_prev) {
DEBUGF("stream: remove stream %d from stream %d\n", stream->stream_id,
dep_stream->stream_id);
nghttp2_pq_remove(&dep_stream->obq, &stream->pq_entry);
assert(stream->queued);
stream->queued = 0;
stream->cycle = 0;
stream->pending_penalty = 0;
stream->descendant_last_cycle = 0;
stream->last_writelen = 0;
if (stream_subtree_active(dep_stream)) {
return;
}
}
}
/*
* Moves |stream| from |src|'s obq to |dest|'s obq. Removal from
* |src|'s obq is just done calling nghttp2_pq_remove(), so it does
* not recursively remove |src| and ancestors, like
* stream_obq_remove().
*/
static int stream_obq_move(nghttp2_stream *dest, nghttp2_stream *src,
nghttp2_stream *stream) {
if (!stream->queued) {
return 0;
}
DEBUGF("stream: remove stream %d from stream %d (move)\n", stream->stream_id,
src->stream_id);
nghttp2_pq_remove(&src->obq, &stream->pq_entry);
stream->queued = 0;
return stream_obq_push(dest, stream);
}
void nghttp2_stream_reschedule(nghttp2_stream *stream) {
nghttp2_stream *dep_stream;
assert(stream->queued);
dep_stream = stream->dep_prev;
for (; dep_stream; stream = dep_stream, dep_stream = dep_stream->dep_prev) {
nghttp2_pq_remove(&dep_stream->obq, &stream->pq_entry);
stream_next_cycle(stream, dep_stream->descendant_last_cycle);
stream->seq = dep_stream->descendant_next_seq++;
nghttp2_pq_push(&dep_stream->obq, &stream->pq_entry);
DEBUGF("stream: stream=%d obq resched cycle=%lu\n", stream->stream_id,
stream->cycle);
dep_stream->last_writelen = stream->last_writelen;
}
}
void nghttp2_stream_change_weight(nghttp2_stream *stream, int32_t weight) {
nghttp2_stream *dep_stream;
uint64_t last_cycle;
int32_t old_weight;
uint64_t wlen_penalty;
if (stream->weight == weight) {
return;
}
old_weight = stream->weight;
stream->weight = weight;
dep_stream = stream->dep_prev;
if (!dep_stream) {
return;
}
dep_stream->sum_dep_weight += weight - old_weight;
if (!stream->queued) {
return;
}
nghttp2_pq_remove(&dep_stream->obq, &stream->pq_entry);
wlen_penalty = (uint64_t)stream->last_writelen * NGHTTP2_MAX_WEIGHT;
/* Compute old stream->pending_penalty we used to calculate
stream->cycle */
stream->pending_penalty =
(uint32_t)((stream->pending_penalty + (uint32_t)old_weight -
(wlen_penalty % (uint32_t)old_weight)) %
(uint32_t)old_weight);
last_cycle = stream->cycle -
(wlen_penalty + stream->pending_penalty) / (uint32_t)old_weight;
/* Now we have old stream->pending_penalty and new stream->weight in
place */
stream_next_cycle(stream, last_cycle);
if (dep_stream->descendant_last_cycle - stream->cycle <=
NGHTTP2_MAX_CYCLE_DISTANCE) {
stream->cycle = dep_stream->descendant_last_cycle;
}
/* Continue to use same stream->seq */
nghttp2_pq_push(&dep_stream->obq, &stream->pq_entry);
DEBUGF("stream: stream=%d obq resched cycle=%lu\n", stream->stream_id,
stream->cycle);
}
static nghttp2_stream *stream_last_sib(nghttp2_stream *stream) {
for (; stream->sib_next; stream = stream->sib_next)
;
return stream;
}
int32_t nghttp2_stream_dep_distributed_weight(nghttp2_stream *stream,
int32_t weight) {
weight = stream->weight * weight / stream->sum_dep_weight;
return nghttp2_max_int32(1, weight);
}
#ifdef STREAM_DEP_DEBUG
static void ensure_inactive(nghttp2_stream *stream) {
nghttp2_stream *si;
if (stream->queued) {
fprintf(stderr, "stream(%p)=%d, stream->queued = 1; want 0\n", stream,
stream->stream_id);
assert(0);
}
if (stream_active(stream)) {
fprintf(stderr, "stream(%p)=%d, stream_active(stream) = 1; want 0\n",
stream, stream->stream_id);
assert(0);
}
if (!nghttp2_pq_empty(&stream->obq)) {
fprintf(stderr, "stream(%p)=%d, nghttp2_pq_size() = %zu; want 0\n", stream,
stream->stream_id, nghttp2_pq_size(&stream->obq));
assert(0);
}
for (si = stream->dep_next; si; si = si->sib_next) {
ensure_inactive(si);
}
}
static void check_queued(nghttp2_stream *stream) {
nghttp2_stream *si;
int queued;
if (stream->queued) {
if (!stream_subtree_active(stream)) {
fprintf(stderr,
"stream(%p)=%d, stream->queued == 1, but "
"stream_active() == %d and nghttp2_pq_size(&stream->obq) = %zu\n",
stream, stream->stream_id, stream_active(stream),
nghttp2_pq_size(&stream->obq));
assert(0);
}
if (!stream_active(stream)) {
queued = 0;
for (si = stream->dep_next; si; si = si->sib_next) {
if (si->queued) {
++queued;
}
}
if (queued == 0) {
fprintf(stderr,
"stream(%p)=%d, stream->queued == 1, and "
"!stream_active(), but no descendants is queued\n",
stream, stream->stream_id);
assert(0);
}
}
for (si = stream->dep_next; si; si = si->sib_next) {
check_queued(si);
}
} else {
if (stream_active(stream) || !nghttp2_pq_empty(&stream->obq)) {
fprintf(stderr,
"stream(%p) = %d, stream->queued == 0, but "
"stream_active(stream) == %d and "
"nghttp2_pq_size(&stream->obq) = %zu\n",
stream, stream->stream_id, stream_active(stream),
nghttp2_pq_size(&stream->obq));
assert(0);
}
for (si = stream->dep_next; si; si = si->sib_next) {
ensure_inactive(si);
}
}
}
static void check_sum_dep(nghttp2_stream *stream) {
nghttp2_stream *si;
int32_t n = 0;
for (si = stream->dep_next; si; si = si->sib_next) {
n += si->weight;
}
if (n != stream->sum_dep_weight) {
fprintf(stderr, "stream(%p)=%d, sum_dep_weight = %d; want %d\n", stream,
stream->stream_id, n, stream->sum_dep_weight);
assert(0);
}
for (si = stream->dep_next; si; si = si->sib_next) {
check_sum_dep(si);
}
}
static void check_dep_prev(nghttp2_stream *stream) {
nghttp2_stream *si;
for (si = stream->dep_next; si; si = si->sib_next) {
if (si->dep_prev != stream) {
fprintf(stderr, "si->dep_prev = %p; want %p\n", si->dep_prev, stream);
assert(0);
}
check_dep_prev(si);
}
}
#endif /* STREAM_DEP_DEBUG */
#ifdef STREAM_DEP_DEBUG
static void validate_tree(nghttp2_stream *stream) {
nghttp2_stream *si;
if (!stream) {
return;
}
for (; stream->dep_prev; stream = stream->dep_prev)
;
assert(stream->stream_id == 0);
assert(!stream->queued);
fprintf(stderr, "checking...\n");
if (nghttp2_pq_empty(&stream->obq)) {
fprintf(stderr, "root obq empty\n");
for (si = stream->dep_next; si; si = si->sib_next) {
ensure_inactive(si);
}
} else {
for (si = stream->dep_next; si; si = si->sib_next) {
check_queued(si);
}
}
check_sum_dep(stream);
check_dep_prev(stream);
}
#else /* !STREAM_DEP_DEBUG */
static void validate_tree(nghttp2_stream *stream) { (void)stream; }
#endif /* !STREAM_DEP_DEBUG*/
static int stream_update_dep_on_attach_item(nghttp2_stream *stream) {
int rv;
rv = stream_obq_push(stream->dep_prev, stream);
if (rv != 0) {
return rv;
}
validate_tree(stream);
return 0;
}
static void stream_update_dep_on_detach_item(nghttp2_stream *stream) {
if (nghttp2_pq_empty(&stream->obq)) {
stream_obq_remove(stream);
}
validate_tree(stream);
}
int nghttp2_stream_attach_item(nghttp2_stream *stream,
nghttp2_outbound_item *item) {
int rv;
void nghttp2_stream_attach_item(nghttp2_stream *stream,
nghttp2_outbound_item *item) {
assert((stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL) == 0);
assert(stream->item == NULL);
DEBUGF("stream: stream=%d attach item=%p\n", stream->stream_id, item);
stream->item = item;
if (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) {
return 0;
}
rv = stream_update_dep_on_attach_item(stream);
if (rv != 0) {
/* This may relave stream->queued == 1, but stream->item == NULL.
But only consequence of this error is fatal one, and session
destruction. In that execution path, these inconsistency does
not matter. */
stream->item = NULL;
return rv;
}
return 0;
}
void nghttp2_stream_detach_item(nghttp2_stream *stream) {
@ -506,12 +86,6 @@ void nghttp2_stream_detach_item(nghttp2_stream *stream) {
stream->item = NULL;
stream->flags = (uint8_t)(stream->flags & ~NGHTTP2_STREAM_FLAG_DEFERRED_ALL);
if (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) {
return;
}
stream_update_dep_on_detach_item(stream);
}
void nghttp2_stream_defer_item(nghttp2_stream *stream, uint8_t flags) {
@ -521,31 +95,16 @@ void nghttp2_stream_defer_item(nghttp2_stream *stream, uint8_t flags) {
stream->item, flags);
stream->flags |= flags;
if (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) {
return;
}
stream_update_dep_on_detach_item(stream);
}
int nghttp2_stream_resume_deferred_item(nghttp2_stream *stream, uint8_t flags) {
void nghttp2_stream_resume_deferred_item(nghttp2_stream *stream,
uint8_t flags) {
assert(stream->item);
DEBUGF("stream: stream=%d resume item=%p flags=%02x\n", stream->stream_id,
stream->item, flags);
stream->flags = (uint8_t)(stream->flags & ~flags);
if (stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL) {
return 0;
}
if (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) {
return 0;
}
return stream_update_dep_on_attach_item(stream);
}
int nghttp2_stream_check_deferred_item(nghttp2_stream *stream) {
@ -591,373 +150,11 @@ void nghttp2_stream_promise_fulfilled(nghttp2_stream *stream) {
stream->flags = (uint8_t)(stream->flags & ~NGHTTP2_STREAM_FLAG_PUSH);
}
int nghttp2_stream_dep_find_ancestor(nghttp2_stream *stream,
nghttp2_stream *target) {
for (; stream; stream = stream->dep_prev) {
if (stream == target) {
return 1;
}
}
return 0;
}
int nghttp2_stream_dep_insert(nghttp2_stream *dep_stream,
nghttp2_stream *stream) {
nghttp2_stream *si;
int rv;
DEBUGF("stream: dep_insert dep_stream(%p)=%d, stream(%p)=%d\n", dep_stream,
dep_stream->stream_id, stream, stream->stream_id);
stream->sum_dep_weight = dep_stream->sum_dep_weight;
dep_stream->sum_dep_weight = stream->weight;
if (dep_stream->dep_next) {
for (si = dep_stream->dep_next; si; si = si->sib_next) {
si->dep_prev = stream;
if (si->queued) {
rv = stream_obq_move(stream, dep_stream, si);
if (rv != 0) {
return rv;
}
}
}
if (stream_subtree_active(stream)) {
rv = stream_obq_push(dep_stream, stream);
if (rv != 0) {
return rv;
}
}
stream->dep_next = dep_stream->dep_next;
}
dep_stream->dep_next = stream;
stream->dep_prev = dep_stream;
validate_tree(stream);
return 0;
}
static void set_dep_prev(nghttp2_stream *stream, nghttp2_stream *dep) {
for (; stream; stream = stream->sib_next) {
stream->dep_prev = dep;
}
}
static void link_dep(nghttp2_stream *dep_stream, nghttp2_stream *stream) {
dep_stream->dep_next = stream;
if (stream) {
stream->dep_prev = dep_stream;
}
}
static void link_sib(nghttp2_stream *a, nghttp2_stream *b) {
a->sib_next = b;
if (b) {
b->sib_prev = a;
}
}
static void insert_link_dep(nghttp2_stream *dep_stream,
nghttp2_stream *stream) {
nghttp2_stream *sib_next;
assert(stream->sib_prev == NULL);
sib_next = dep_stream->dep_next;
link_sib(stream, sib_next);
link_dep(dep_stream, stream);
}
static void unlink_sib(nghttp2_stream *stream) {
nghttp2_stream *prev, *next, *dep_next;
prev = stream->sib_prev;
dep_next = stream->dep_next;
assert(prev);
if (dep_next) {
/*
* prev--stream(--sib_next--...)
* |
* dep_next
*/
link_sib(prev, dep_next);
set_dep_prev(dep_next, stream->dep_prev);
if (stream->sib_next) {
link_sib(stream_last_sib(dep_next), stream->sib_next);
}
} else {
/*
* prev--stream(--sib_next--...)
*/
next = stream->sib_next;
prev->sib_next = next;
if (next) {
next->sib_prev = prev;
}
}
}
static void unlink_dep(nghttp2_stream *stream) {
nghttp2_stream *prev, *next, *dep_next;
prev = stream->dep_prev;
dep_next = stream->dep_next;
assert(prev);
if (dep_next) {
/*
* prev
* |
* stream(--sib_next--...)
* |
* dep_next
*/
link_dep(prev, dep_next);
set_dep_prev(dep_next, stream->dep_prev);
if (stream->sib_next) {
link_sib(stream_last_sib(dep_next), stream->sib_next);
}
} else if (stream->sib_next) {
/*
* prev
* |
* stream--sib_next
*/
next = stream->sib_next;
next->sib_prev = NULL;
link_dep(prev, next);
} else {
prev->dep_next = NULL;
}
}
void nghttp2_stream_dep_add(nghttp2_stream *dep_stream,
nghttp2_stream *stream) {
DEBUGF("stream: dep_add dep_stream(%p)=%d, stream(%p)=%d\n", dep_stream,
dep_stream->stream_id, stream, stream->stream_id);
dep_stream->sum_dep_weight += stream->weight;
if (dep_stream->dep_next == NULL) {
link_dep(dep_stream, stream);
} else {
insert_link_dep(dep_stream, stream);
}
validate_tree(stream);
}
int nghttp2_stream_dep_remove(nghttp2_stream *stream) {
nghttp2_stream *dep_prev, *si;
int32_t sum_dep_weight_delta;
int rv;
DEBUGF("stream: dep_remove stream(%p)=%d\n", stream, stream->stream_id);
/* Distribute weight of |stream| to direct descendants */
sum_dep_weight_delta = -stream->weight;
for (si = stream->dep_next; si; si = si->sib_next) {
si->weight = nghttp2_stream_dep_distributed_weight(stream, si->weight);
sum_dep_weight_delta += si->weight;
if (si->queued) {
rv = stream_obq_move(stream->dep_prev, stream, si);
if (rv != 0) {
return rv;
}
}
}
assert(stream->dep_prev);
dep_prev = stream->dep_prev;
dep_prev->sum_dep_weight += sum_dep_weight_delta;
if (stream->queued) {
stream_obq_remove(stream);
}
if (stream->sib_prev) {
unlink_sib(stream);
} else {
unlink_dep(stream);
}
stream->sum_dep_weight = 0;
stream->dep_prev = NULL;
stream->dep_next = NULL;
stream->sib_prev = NULL;
stream->sib_next = NULL;
validate_tree(dep_prev);
return 0;
}
int nghttp2_stream_dep_insert_subtree(nghttp2_stream *dep_stream,
nghttp2_stream *stream) {
nghttp2_stream *last_sib;
nghttp2_stream *dep_next;
nghttp2_stream *si;
int rv;
DEBUGF("stream: dep_insert_subtree dep_stream(%p)=%d stream(%p)=%d\n",
dep_stream, dep_stream->stream_id, stream, stream->stream_id);
stream->sum_dep_weight += dep_stream->sum_dep_weight;
dep_stream->sum_dep_weight = stream->weight;
if (dep_stream->dep_next) {
dep_next = dep_stream->dep_next;
link_dep(dep_stream, stream);
if (stream->dep_next) {
last_sib = stream_last_sib(stream->dep_next);
link_sib(last_sib, dep_next);
} else {
link_dep(stream, dep_next);
}
for (si = dep_next; si; si = si->sib_next) {
si->dep_prev = stream;
if (si->queued) {
rv = stream_obq_move(stream, dep_stream, si);
if (rv != 0) {
return rv;
}
}
}
} else {
link_dep(dep_stream, stream);
}
if (stream_subtree_active(stream)) {
rv = stream_obq_push(dep_stream, stream);
if (rv != 0) {
return rv;
}
}
validate_tree(dep_stream);
return 0;
}
int nghttp2_stream_dep_add_subtree(nghttp2_stream *dep_stream,
nghttp2_stream *stream) {
int rv;
DEBUGF("stream: dep_add_subtree dep_stream(%p)=%d stream(%p)=%d\n",
dep_stream, dep_stream->stream_id, stream, stream->stream_id);
dep_stream->sum_dep_weight += stream->weight;
if (dep_stream->dep_next) {
insert_link_dep(dep_stream, stream);
} else {
link_dep(dep_stream, stream);
}
if (stream_subtree_active(stream)) {
rv = stream_obq_push(dep_stream, stream);
if (rv != 0) {
return rv;
}
}
validate_tree(dep_stream);
return 0;
}
void nghttp2_stream_dep_remove_subtree(nghttp2_stream *stream) {
nghttp2_stream *next, *dep_prev;
DEBUGF("stream: dep_remove_subtree stream(%p)=%d\n", stream,
stream->stream_id);
assert(stream->dep_prev);
dep_prev = stream->dep_prev;
if (stream->sib_prev) {
link_sib(stream->sib_prev, stream->sib_next);
} else {
next = stream->sib_next;
link_dep(dep_prev, next);
if (next) {
next->sib_prev = NULL;
}
}
dep_prev->sum_dep_weight -= stream->weight;
if (stream->queued) {
stream_obq_remove(stream);
}
validate_tree(dep_prev);
stream->sib_prev = NULL;
stream->sib_next = NULL;
stream->dep_prev = NULL;
}
int nghttp2_stream_in_dep_tree(nghttp2_stream *stream) {
return stream->dep_prev || stream->dep_next || stream->sib_prev ||
stream->sib_next;
}
nghttp2_outbound_item *
nghttp2_stream_next_outbound_item(nghttp2_stream *stream) {
nghttp2_pq_entry *ent;
nghttp2_stream *si;
for (;;) {
if (stream_active(stream)) {
/* Update ascendant's descendant_last_cycle here, so that we can
assure that new stream is scheduled based on it. */
for (si = stream; si->dep_prev; si = si->dep_prev) {
si->dep_prev->descendant_last_cycle = si->cycle;
}
return stream->item;
}
ent = nghttp2_pq_top(&stream->obq);
if (!ent) {
return NULL;
}
stream = nghttp2_struct_of(ent, nghttp2_stream, pq_entry);
}
}
nghttp2_stream_proto_state nghttp2_stream_get_state(nghttp2_stream *stream) {
if (stream == &root) {
return NGHTTP2_STREAM_STATE_IDLE;
}
if (stream->flags & NGHTTP2_STREAM_FLAG_CLOSED) {
return NGHTTP2_STREAM_STATE_CLOSED;
}
@ -988,27 +185,39 @@ nghttp2_stream_proto_state nghttp2_stream_get_state(nghttp2_stream *stream) {
}
nghttp2_stream *nghttp2_stream_get_parent(nghttp2_stream *stream) {
return stream->dep_prev;
(void)stream;
return NULL;
}
nghttp2_stream *nghttp2_stream_get_next_sibling(nghttp2_stream *stream) {
return stream->sib_next;
(void)stream;
return NULL;
}
nghttp2_stream *nghttp2_stream_get_previous_sibling(nghttp2_stream *stream) {
return stream->sib_prev;
(void)stream;
return NULL;
}
nghttp2_stream *nghttp2_stream_get_first_child(nghttp2_stream *stream) {
return stream->dep_next;
(void)stream;
return NULL;
}
int32_t nghttp2_stream_get_weight(nghttp2_stream *stream) {
return stream->weight;
(void)stream;
return NGHTTP2_DEFAULT_WEIGHT;
}
int32_t nghttp2_stream_get_sum_dependency_weight(nghttp2_stream *stream) {
return stream->sum_dep_weight;
(void)stream;
return 0;
}
int32_t nghttp2_stream_get_stream_id(nghttp2_stream *stream) {

View file

@ -91,9 +91,6 @@ typedef enum {
/* bitwise OR of NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL and
NGHTTP2_STREAM_FLAG_DEFERRED_USER. */
NGHTTP2_STREAM_FLAG_DEFERRED_ALL = 0x0c,
/* Indicates that this stream is not subject to RFC7540
priorities scheme. */
NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES = 0x10,
/* Ignore client RFC 9218 priority signal. */
NGHTTP2_STREAM_FLAG_IGNORE_CLIENT_PRIORITIES = 0x20,
/* Indicates that RFC 9113 leading and trailing white spaces
@ -146,39 +143,18 @@ typedef enum {
} nghttp2_http_flag;
struct nghttp2_stream {
/* Entry for dep_prev->obq */
nghttp2_stream_state state;
nghttp2_pq_entry pq_entry;
/* Priority Queue storing direct descendant (nghttp2_stream). Only
streams which itself has some data to send, or has a descendant
which has some data to sent. */
nghttp2_pq obq;
/* Content-Length of request/response body. -1 if unknown. */
int64_t content_length;
/* Received body so far */
int64_t recv_content_length;
/* Base last_cycle for direct descendent streams. */
uint64_t descendant_last_cycle;
/* Next scheduled time to sent item */
uint64_t cycle;
/* Next seq used for direct descendant streams */
uint64_t descendant_next_seq;
/* Secondary key for prioritization to break a tie for cycle. This
value is monotonically increased for single parent stream. */
uint64_t seq;
/* pointers to form dependency tree. If multiple streams depend on
a stream, only one stream (left most) has non-NULL dep_prev which
points to the stream it depends on. The remaining streams are
linked using sib_prev and sib_next. The stream which has
non-NULL dep_prev always NULL sib_prev. The right most stream
has NULL sib_next. If this stream is a root of dependency tree,
dep_prev and sib_prev are NULL. */
nghttp2_stream *dep_prev, *dep_next;
nghttp2_stream *sib_prev, *sib_next;
/* When stream is kept after closure, it may be kept in doubly
linked list pointed by nghttp2_session closed_stream_head.
closed_next points to the next stream object if it is the element
of the list. */
nghttp2_stream *closed_prev, *closed_next;
nghttp2_stream *closed_next;
/* The arbitrary data provided by user for this stream. */
void *stream_user_data;
/* Item to send */
@ -205,13 +181,8 @@ struct nghttp2_stream {
NGHTTP2_INITIAL_WINDOW_SIZE and could be increased/decreased by
submitting WINDOW_UPDATE. See nghttp2_submit_window_update(). */
int32_t local_window_size;
/* weight of this stream */
int32_t weight;
/* This is unpaid penalty (offset) when calculating cycle. */
uint32_t pending_penalty;
/* sum of weight of direct descendants */
int32_t sum_dep_weight;
nghttp2_stream_state state;
/* status code from remote server */
int16_t status_code;
/* Bitwise OR of zero or more nghttp2_http_flag values */
@ -239,9 +210,9 @@ struct nghttp2_stream {
void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id,
uint8_t flags, nghttp2_stream_state initial_state,
int32_t weight, int32_t remote_initial_window_size,
int32_t remote_initial_window_size,
int32_t local_initial_window_size,
void *stream_user_data, nghttp2_mem *mem);
void *stream_user_data);
void nghttp2_stream_free(nghttp2_stream *stream);
@ -267,14 +238,8 @@ void nghttp2_stream_defer_item(nghttp2_stream *stream, uint8_t flags);
* NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL and given masks are
* cleared if they are set. So even if this function is called, if
* one of flag is still set, data does not become active.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* NGHTTP2_ERR_NOMEM
* Out of memory
*/
int nghttp2_stream_resume_deferred_item(nghttp2_stream *stream, uint8_t flags);
void nghttp2_stream_resume_deferred_item(nghttp2_stream *stream, uint8_t flags);
/*
* Returns nonzero if item is deferred by whatever reason.
@ -317,57 +282,11 @@ int nghttp2_stream_update_local_initial_window_size(
*/
void nghttp2_stream_promise_fulfilled(nghttp2_stream *stream);
/*
* Returns nonzero if |target| is an ancestor of |stream|.
*/
int nghttp2_stream_dep_find_ancestor(nghttp2_stream *stream,
nghttp2_stream *target);
/*
* Computes distributed weight of a stream of the |weight| under the
* |stream| if |stream| is removed from a dependency tree.
*/
int32_t nghttp2_stream_dep_distributed_weight(nghttp2_stream *stream,
int32_t weight);
/*
* Makes the |stream| depend on the |dep_stream|. This dependency is
* exclusive. All existing direct descendants of |dep_stream| become
* the descendants of the |stream|. This function assumes
* |stream->item| is NULL.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* NGHTTP2_ERR_NOMEM
* Out of memory
*/
int nghttp2_stream_dep_insert(nghttp2_stream *dep_stream,
nghttp2_stream *stream);
/*
* Makes the |stream| depend on the |dep_stream|. This dependency is
* not exclusive. This function assumes |stream->item| is NULL.
*/
void nghttp2_stream_dep_add(nghttp2_stream *dep_stream, nghttp2_stream *stream);
/*
* Removes the |stream| from the current dependency tree. This
* function assumes |stream->item| is NULL.
*/
int nghttp2_stream_dep_remove(nghttp2_stream *stream);
/*
* Attaches |item| to |stream|.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* NGHTTP2_ERR_NOMEM
* Out of memory
*/
int nghttp2_stream_attach_item(nghttp2_stream *stream,
nghttp2_outbound_item *item);
void nghttp2_stream_attach_item(nghttp2_stream *stream,
nghttp2_outbound_item *item);
/*
* Detaches |stream->item|. This function does not free
@ -375,66 +294,4 @@ int nghttp2_stream_attach_item(nghttp2_stream *stream,
*/
void nghttp2_stream_detach_item(nghttp2_stream *stream);
/*
* Makes the |stream| depend on the |dep_stream|. This dependency is
* exclusive.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* NGHTTP2_ERR_NOMEM
* Out of memory
*/
int nghttp2_stream_dep_insert_subtree(nghttp2_stream *dep_stream,
nghttp2_stream *stream);
/*
* Makes the |stream| depend on the |dep_stream|. This dependency is
* not exclusive.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* NGHTTP2_ERR_NOMEM
* Out of memory
*/
int nghttp2_stream_dep_add_subtree(nghttp2_stream *dep_stream,
nghttp2_stream *stream);
/*
* Removes subtree whose root stream is |stream|. The
* effective_weight of streams in removed subtree is not updated.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* NGHTTP2_ERR_NOMEM
* Out of memory
*/
void nghttp2_stream_dep_remove_subtree(nghttp2_stream *stream);
/*
* Returns nonzero if |stream| is in any dependency tree.
*/
int nghttp2_stream_in_dep_tree(nghttp2_stream *stream);
/*
* Schedules transmission of |stream|'s item, assuming stream->item is
* attached, and stream->last_writelen was updated.
*/
void nghttp2_stream_reschedule(nghttp2_stream *stream);
/*
* Changes |stream|'s weight to |weight|. If |stream| is queued, it
* will be rescheduled based on new weight.
*/
void nghttp2_stream_change_weight(nghttp2_stream *stream, int32_t weight);
/*
* Returns a stream which has highest priority, updating
* descendant_last_cycle of selected stream's ancestors.
*/
nghttp2_outbound_item *
nghttp2_stream_next_outbound_item(nghttp2_stream *stream);
#endif /* NGHTTP2_STREAM */

View file

@ -32,42 +32,12 @@
#include "nghttp2_helper.h"
#include "nghttp2_priority_spec.h"
/*
* Detects the dependency error, that is stream attempted to depend on
* itself. If |stream_id| is -1, we use session->next_stream_id as
* stream ID.
*
* This function returns 0 if it succeeds, or one of the following
* error codes:
*
* NGHTTP2_ERR_INVALID_ARGUMENT
* Stream attempted to depend on itself.
*/
static int detect_self_dependency(nghttp2_session *session, int32_t stream_id,
const nghttp2_priority_spec *pri_spec) {
assert(pri_spec);
if (stream_id == -1) {
if ((int32_t)session->next_stream_id == pri_spec->stream_id) {
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
return 0;
}
if (stream_id == pri_spec->stream_id) {
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
return 0;
}
/* This function takes ownership of |nva_copy|. Regardless of the
return value, the caller must not free |nva_copy| after this
function returns. */
static int32_t submit_headers_shared(nghttp2_session *session, uint8_t flags,
int32_t stream_id,
const nghttp2_priority_spec *pri_spec,
nghttp2_nv *nva_copy, size_t nvlen,
int32_t stream_id, nghttp2_nv *nva_copy,
size_t nvlen,
const nghttp2_data_provider_wrap *dpw,
void *stream_user_data) {
int rv;
@ -114,8 +84,8 @@ static int32_t submit_headers_shared(nghttp2_session *session, uint8_t flags,
frame = &item->frame;
nghttp2_frame_headers_init(&frame->headers, flags_copy, stream_id, hcat,
pri_spec, nva_copy, nvlen);
nghttp2_frame_headers_init(&frame->headers, flags_copy, stream_id, hcat, NULL,
nva_copy, nvlen);
rv = nghttp2_session_add_item(session, item);
@ -141,31 +111,22 @@ fail2:
static int32_t submit_headers_shared_nva(nghttp2_session *session,
uint8_t flags, int32_t stream_id,
const nghttp2_priority_spec *pri_spec,
const nghttp2_nv *nva, size_t nvlen,
const nghttp2_data_provider_wrap *dpw,
void *stream_user_data) {
int rv;
nghttp2_nv *nva_copy;
nghttp2_priority_spec copy_pri_spec;
nghttp2_mem *mem;
mem = &session->mem;
if (pri_spec) {
copy_pri_spec = *pri_spec;
nghttp2_priority_spec_normalize_weight(&copy_pri_spec);
} else {
nghttp2_priority_spec_default_init(&copy_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, &copy_pri_spec,
nva_copy, nvlen, dpw, stream_user_data);
return submit_headers_shared(session, flags, stream_id, nva_copy, nvlen, dpw,
stream_user_data);
}
int nghttp2_submit_trailer(nghttp2_session *session, int32_t stream_id,
@ -174,8 +135,8 @@ int nghttp2_submit_trailer(nghttp2_session *session, int32_t stream_id,
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
return (int)submit_headers_shared_nva(
session, NGHTTP2_FLAG_END_STREAM, stream_id, NULL, nva, nvlen, NULL, NULL);
return (int)submit_headers_shared_nva(session, NGHTTP2_FLAG_END_STREAM,
stream_id, nva, nvlen, NULL, NULL);
}
int32_t nghttp2_submit_headers(nghttp2_session *session, uint8_t flags,
@ -183,7 +144,7 @@ int32_t nghttp2_submit_headers(nghttp2_session *session, uint8_t flags,
const nghttp2_priority_spec *pri_spec,
const nghttp2_nv *nva, size_t nvlen,
void *stream_user_data) {
int rv;
(void)pri_spec;
if (stream_id == -1) {
if (session->server) {
@ -195,20 +156,8 @@ int32_t nghttp2_submit_headers(nghttp2_session *session, uint8_t flags,
flags &= NGHTTP2_FLAG_END_STREAM;
if (pri_spec && !nghttp2_priority_spec_check_default(pri_spec) &&
session->remote_settings.no_rfc7540_priorities != 1) {
rv = detect_self_dependency(session, stream_id, pri_spec);
if (rv != 0) {
return rv;
}
flags |= NGHTTP2_FLAG_PRIORITY;
} else {
pri_spec = NULL;
}
return submit_headers_shared_nva(session, flags, stream_id, pri_spec, nva,
nvlen, NULL, stream_user_data);
return submit_headers_shared_nva(session, flags, stream_id, nva, nvlen, NULL,
stream_user_data);
}
int nghttp2_submit_ping(nghttp2_session *session, uint8_t flags,
@ -220,51 +169,10 @@ int nghttp2_submit_ping(nghttp2_session *session, uint8_t flags,
int nghttp2_submit_priority(nghttp2_session *session, uint8_t flags,
int32_t stream_id,
const nghttp2_priority_spec *pri_spec) {
int rv;
nghttp2_outbound_item *item;
nghttp2_frame *frame;
nghttp2_priority_spec copy_pri_spec;
nghttp2_mem *mem;
(void)session;
(void)flags;
mem = &session->mem;
if (session->remote_settings.no_rfc7540_priorities == 1) {
return 0;
}
if (stream_id == 0 || pri_spec == NULL) {
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
if (stream_id == pri_spec->stream_id) {
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
copy_pri_spec = *pri_spec;
nghttp2_priority_spec_normalize_weight(&copy_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, &copy_pri_spec);
rv = nghttp2_session_add_item(session, item);
if (rv != 0) {
nghttp2_frame_priority_free(&frame->priority);
nghttp2_mem_free(mem, item);
return rv;
}
(void)stream_id;
(void)pri_spec;
return 0;
}
@ -738,46 +646,29 @@ fail_item_malloc:
return rv;
}
static uint8_t set_request_flags(const nghttp2_priority_spec *pri_spec,
const nghttp2_data_provider_wrap *dpw) {
static uint8_t set_request_flags(const nghttp2_data_provider_wrap *dpw) {
uint8_t flags = NGHTTP2_FLAG_NONE;
if (dpw == NULL || dpw->data_prd.read_callback == NULL) {
flags |= NGHTTP2_FLAG_END_STREAM;
}
if (pri_spec) {
flags |= NGHTTP2_FLAG_PRIORITY;
}
return flags;
}
static int32_t submit_request_shared(nghttp2_session *session,
const nghttp2_priority_spec *pri_spec,
const nghttp2_nv *nva, size_t nvlen,
const nghttp2_data_provider_wrap *dpw,
void *stream_user_data) {
uint8_t flags;
int rv;
if (session->server) {
return NGHTTP2_ERR_PROTO;
}
if (pri_spec && !nghttp2_priority_spec_check_default(pri_spec) &&
session->remote_settings.no_rfc7540_priorities != 1) {
rv = detect_self_dependency(session, -1, pri_spec);
if (rv != 0) {
return rv;
}
} else {
pri_spec = NULL;
}
flags = set_request_flags(dpw);
flags = set_request_flags(pri_spec, dpw);
return submit_headers_shared_nva(session, flags, -1, pri_spec, nva, nvlen,
dpw, stream_user_data);
return submit_headers_shared_nva(session, flags, -1, nva, nvlen, dpw,
stream_user_data);
}
int32_t nghttp2_submit_request(nghttp2_session *session,
@ -786,8 +677,9 @@ int32_t nghttp2_submit_request(nghttp2_session *session,
const nghttp2_data_provider *data_prd,
void *stream_user_data) {
nghttp2_data_provider_wrap dpw;
(void)pri_spec;
return submit_request_shared(session, pri_spec, nva, nvlen,
return submit_request_shared(session, nva, nvlen,
nghttp2_data_provider_wrap_v1(&dpw, data_prd),
stream_user_data);
}
@ -798,8 +690,9 @@ int32_t nghttp2_submit_request2(nghttp2_session *session,
const nghttp2_data_provider2 *data_prd,
void *stream_user_data) {
nghttp2_data_provider_wrap dpw;
(void)pri_spec;
return submit_request_shared(session, pri_spec, nva, nvlen,
return submit_request_shared(session, nva, nvlen,
nghttp2_data_provider_wrap_v2(&dpw, data_prd),
stream_user_data);
}
@ -826,8 +719,8 @@ static int submit_response_shared(nghttp2_session *session, int32_t stream_id,
}
flags = set_response_flags(dpw);
return submit_headers_shared_nva(session, flags, stream_id, NULL, nva, nvlen,
dpw, NULL);
return submit_headers_shared_nva(session, flags, stream_id, nva, nvlen, dpw,
NULL);
}
int nghttp2_submit_response(nghttp2_session *session, int32_t stream_id,

File diff suppressed because it is too large Load diff

View file

@ -31,86 +31,90 @@
libcurl) */
#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32)
# define WIN32
#endif
#endif /* (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32) */
#ifdef __cplusplus
extern "C" {
#endif
#endif /* defined(__cplusplus) */
#if defined(_MSC_VER) && (_MSC_VER < 1800)
/* MSVC < 2013 does not have inttypes.h because it is not C99
compliant. See compiler macros and version number in
https://sourceforge.net/p/predef/wiki/Compilers/ */
# include <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) */
#endif /* !(defined(_MSC_VER) && (_MSC_VER < 1800)) */
#include <sys/types.h>
#include <stddef.h>
/**
* @enum
*
* :type:`sf_type` defines value type.
* :type:`sfparse_type` defines value type.
*/
typedef enum sf_type {
typedef enum sfparse_type {
/**
* :enum:`SF_TYPE_BOOLEAN` indicates boolean type.
* :enum:`SFPARSE_TYPE_BOOLEAN` indicates boolean type.
*/
SF_TYPE_BOOLEAN,
SFPARSE_TYPE_BOOLEAN,
/**
* :enum:`SF_TYPE_INTEGER` indicates integer type.
* :enum:`SFPARSE_TYPE_INTEGER` indicates integer type.
*/
SF_TYPE_INTEGER,
SFPARSE_TYPE_INTEGER,
/**
* :enum:`SF_TYPE_DECIMAL` indicates decimal type.
* :enum:`SFPARSE_TYPE_DECIMAL` indicates decimal type.
*/
SF_TYPE_DECIMAL,
SFPARSE_TYPE_DECIMAL,
/**
* :enum:`SF_TYPE_STRING` indicates string type.
* :enum:`SFPARSE_TYPE_STRING` indicates string type.
*/
SF_TYPE_STRING,
SFPARSE_TYPE_STRING,
/**
* :enum:`SF_TYPE_TOKEN` indicates token type.
* :enum:`SFPARSE_TYPE_TOKEN` indicates token type.
*/
SF_TYPE_TOKEN,
SFPARSE_TYPE_TOKEN,
/**
* :enum:`SF_TYPE_BYTESEQ` indicates byte sequence type.
* :enum:`SFPARSE_TYPE_BYTESEQ` indicates byte sequence type.
*/
SF_TYPE_BYTESEQ,
SFPARSE_TYPE_BYTESEQ,
/**
* :enum:`SF_TYPE_INNER_LIST` indicates inner list type.
* :enum:`SFPARSE_TYPE_INNER_LIST` indicates inner list type.
*/
SF_TYPE_INNER_LIST,
SFPARSE_TYPE_INNER_LIST,
/**
* :enum:`SF_TYPE_DATE` indicates date type.
* :enum:`SFPARSE_TYPE_DATE` indicates date type.
*/
SF_TYPE_DATE
} sf_type;
SFPARSE_TYPE_DATE,
/**
* :enum:`SFPARSE_TYPE_DISPSTRING` indicates display string type.
*/
SFPARSE_TYPE_DISPSTRING
} sfparse_type;
/**
* @macro
*
* :macro:`SF_ERR_PARSE_ERROR` indicates fatal parse error has
* :macro:`SFPARSE_ERR_PARSE` indicates fatal parse error has
* occurred, and it is not possible to continue the processing.
*/
#define SF_ERR_PARSE_ERROR -1
#define SFPARSE_ERR_PARSE -1
/**
* @macro
*
* :macro:`SF_ERR_EOF` indicates that there is nothing left to read.
* The context of this error varies depending on the function that
* returns this error code.
* :macro:`SFPARSE_ERR_EOF` indicates that there is nothing left to
* read. The context of this error varies depending on the function
* that returns this error code.
*/
#define SF_ERR_EOF -2
#define SFPARSE_ERR_EOF -2
/**
* @struct
*
* :type:`sf_vec` stores sequence of bytes.
* :type:`sfparse_vec` stores sequence of bytes.
*/
typedef struct sf_vec {
typedef struct sfparse_vec {
/**
* :member:`base` points to the beginning of the sequence of bytes.
*/
@ -119,29 +123,29 @@ typedef struct sf_vec {
* :member:`len` is the number of bytes contained in this sequence.
*/
size_t len;
} sf_vec;
} sfparse_vec;
/**
* @macro
*
* :macro:`SF_VALUE_FLAG_NONE` indicates no flag set.
* :macro:`SFPARSE_VALUE_FLAG_NONE` indicates no flag set.
*/
#define SF_VALUE_FLAG_NONE 0x0u
#define SFPARSE_VALUE_FLAG_NONE 0x0u
/**
* @macro
*
* :macro:`SF_VALUE_FLAG_ESCAPED_STRING` indicates that a string
* :macro:`SFPARSE_VALUE_FLAG_ESCAPED_STRING` indicates that a string
* contains escaped character(s).
*/
#define SF_VALUE_FLAG_ESCAPED_STRING 0x1u
#define SFPARSE_VALUE_FLAG_ESCAPED_STRING 0x1u
/**
* @struct
*
* :type:`sf_decimal` contains decimal value.
* :type:`sfparse_decimal` contains decimal value.
*/
typedef struct sf_decimal {
typedef struct sfparse_decimal {
/**
* :member:`numer` contains numerator of the decimal value.
*/
@ -150,260 +154,289 @@ typedef struct sf_decimal {
* :member:`denom` contains denominator of the decimal value.
*/
int64_t denom;
} sf_decimal;
} sfparse_decimal;
/**
* @struct
*
* :type:`sf_value` stores a Structured Field item. For Inner List,
* only type is set to :enum:`sf_type.SF_TYPE_INNER_LIST`. In order
* to read the items contained in an inner list, call
* `sf_parser_inner_list`.
* :type:`sfparse_value` stores a Structured Field item. For Inner
* List, only type is set to
* :enum:`sfparse_type.SFPARSE_TYPE_INNER_LIST`. In order to read the
* items contained in an inner list, call `sfparse_parser_inner_list`.
*/
typedef struct sf_value {
typedef struct sfparse_value {
/**
* :member:`type` is the type of the value contained in this
* particular object.
*/
sf_type type;
sfparse_type type;
/**
* :member:`flags` is bitwise OR of one or more of
* :macro:`SF_VALUE_FLAG_* <SF_VALUE_FLAG_NONE>`.
* :macro:`SFPARSE_VALUE_FLAG_* <SFPARSE_VALUE_FLAG_NONE>`.
*/
uint32_t flags;
/**
* @anonunion_start
*
* @sf_value_value
* @sfparse_value_value
*/
union {
/**
* :member:`boolean` contains boolean value if :member:`type` ==
* :enum:`sf_type.SF_TYPE_BOOLEAN`. 1 indicates true, and 0
* indicates false.
* :enum:`sfparse_type.SFPARSE_TYPE_BOOLEAN`. 1 indicates true,
* and 0 indicates false.
*/
int boolean;
/**
* :member:`integer` contains integer value if :member:`type` is
* either :enum:`sf_type.SF_TYPE_INTEGER` or
* :enum:`sf_type.SF_TYPE_DATE`.
* either :enum:`sfparse_type.SFPARSE_TYPE_INTEGER` or
* :enum:`sfparse_type.SFPARSE_TYPE_DATE`.
*/
int64_t integer;
/**
* :member:`decimal` contains decimal value if :member:`type` ==
* :enum:`sf_type.SF_TYPE_DECIMAL`.
* :enum:`sfparse_type.SFPARSE_TYPE_DECIMAL`.
*/
sf_decimal decimal;
sfparse_decimal decimal;
/**
* :member:`vec` contains sequence of bytes if :member:`type` is
* either :enum:`sf_type.SF_TYPE_STRING`,
* :enum:`sf_type.SF_TYPE_TOKEN`, or
* :enum:`sf_type.SF_TYPE_BYTESEQ`.
* either :enum:`sfparse_type.SFPARSE_TYPE_STRING`,
* :enum:`sfparse_type.SFPARSE_TYPE_TOKEN`,
* :enum:`sfparse_type.SFPARSE_TYPE_BYTESEQ`, or
* :enum:`sfparse_type.SFPARSE_TYPE_DISPSTRING`.
*
* For :enum:`sf_type.SF_TYPE_STRING`, this field contains one or
* more escaped characters if :member:`flags` has
* :macro:`SF_VALUE_FLAG_ESCAPED_STRING` set. To unescape the
* string, use `sf_unescape`.
* For :enum:`sfparse_type.SFPARSE_TYPE_STRING`, this field
* contains one or more escaped characters if :member:`flags` has
* :macro:`SFPARSE_VALUE_FLAG_ESCAPED_STRING` set. To unescape
* the string, use `sfparse_unescape`.
*
* For :enum:`sf_type.SF_TYPE_BYTESEQ`, this field contains base64
* encoded string. To decode this byte string, use
* `sf_base64decode`.
* For :enum:`sfparse_type.SFPARSE_TYPE_BYTESEQ`, this field
* contains base64 encoded string. To decode this byte string,
* use `sfparse_base64decode`.
*
* If :member:`vec.len <sf_vec.len>` == 0, :member:`vec.base
* <sf_vec.base>` is guaranteed to be NULL.
* For :enum:`sfparse_type.SFPARSE_TYPE_DISPSTRING`, this field
* may contain percent-encoded UTF-8 byte sequences. To decode
* it, use `sfparse_pctdecode`.
*
* If :member:`vec.len <sfparse_vec.len>` == 0, :member:`vec.base
* <sfparse_vec.base>` is guaranteed to be NULL.
*/
sf_vec vec;
sfparse_vec vec;
/**
* @anonunion_end
*/
};
} sf_value;
} sfparse_value;
/**
* @struct
*
* :type:`sf_parser` is the Structured Field Values parser. Use
* `sf_parser_init` to initialize it.
* :type:`sfparse_parser` is the Structured Field Values parser. Use
* `sfparse_parser_init` to initialize it.
*/
typedef struct sf_parser {
typedef struct sfparse_parser {
/* all fields are private */
const uint8_t *pos;
const uint8_t *end;
uint32_t state;
} sf_parser;
} sfparse_parser;
/**
* @function
*
* `sf_parser_init` initializes |sfp| with the given buffer pointed by
* |data| of length |datalen|.
* `sfparse_parser_init` initializes |sfp| with the given data encoded
* in Structured Field Values pointed by |data| of length |datalen|.
*/
void sf_parser_init(sf_parser *sfp, const uint8_t *data, size_t datalen);
void sfparse_parser_init(sfparse_parser *sfp, const uint8_t *data,
size_t datalen);
/**
* @function
*
* `sf_parser_param` reads a parameter. If this function returns 0,
* it stores parameter key and value in |dest_key| and |dest_value|
* `sfparse_parser_param` reads a parameter. If this function returns
* 0, it stores parameter key and value in |dest_key| and |dest_value|
* respectively, if they are not NULL.
*
* This function does no effort to find duplicated keys. Same key may
* be reported more than once.
*
* Caller should keep calling this function until it returns negative
* error code. If it returns :macro:`SF_ERR_EOF`, all parameters have
* read, and caller can continue to read rest of the values. If it
* returns :macro:`SF_ERR_PARSE_ERROR`, it encountered fatal error
* error code. If it returns :macro:`SFPARSE_ERR_EOF`, all parameters
* have read, and caller can continue to read rest of the values. If
* it returns :macro:`SFPARSE_ERR_PARSE`, it encountered fatal error
* while parsing field value.
*/
int sf_parser_param(sf_parser *sfp, sf_vec *dest_key, sf_value *dest_value);
int sfparse_parser_param(sfparse_parser *sfp, sfparse_vec *dest_key,
sfparse_value *dest_value);
/**
* @function
*
* `sf_parser_dict` reads the next dictionary key and value pair. If
* this function returns 0, it stores the key and value in |dest_key|
* and |dest_value| respectively, if they are not NULL.
* `sfparse_parser_dict` reads the next dictionary key and value pair.
* If this function returns 0, it stores the key and value in
* |dest_key| and |dest_value| respectively, if they are not NULL.
*
* Caller can optionally read parameters attached to the pair by
* calling `sf_parser_param`.
* calling `sfparse_parser_param`.
*
* This function does no effort to find duplicated keys. Same key may
* be reported more than once.
*
* Caller should keep calling this function until it returns negative
* error code. If it returns :macro:`SF_ERR_EOF`, all key and value
* pairs have been read, and there is nothing left to read.
* error code. If it returns :macro:`SFPARSE_ERR_EOF`, all key and
* value pairs have been read, and there is nothing left to read.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* :macro:`SF_ERR_EOF`
* :macro:`SFPARSE_ERR_EOF`
* All values in the dictionary have read.
* :macro:`SF_ERR_PARSE_ERROR`
* :macro:`SFPARSE_ERR_PARSE`
* It encountered fatal error while parsing field value.
*/
int sf_parser_dict(sf_parser *sfp, sf_vec *dest_key, sf_value *dest_value);
int sfparse_parser_dict(sfparse_parser *sfp, sfparse_vec *dest_key,
sfparse_value *dest_value);
/**
* @function
*
* `sf_parser_list` reads the next list item. If this function
* `sfparse_parser_list` reads the next list item. If this function
* returns 0, it stores the item in |dest| if it is not NULL.
*
* Caller can optionally read parameters attached to the item by
* calling `sf_parser_param`.
* calling `sfparse_parser_param`.
*
* Caller should keep calling this function until it returns negative
* error code. If it returns :macro:`SF_ERR_EOF`, all values in the
* list have been read, and there is nothing left to read.
* error code. If it returns :macro:`SFPARSE_ERR_EOF`, all values in
* the list have been read, and there is nothing left to read.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* :macro:`SF_ERR_EOF`
* :macro:`SFPARSE_ERR_EOF`
* All values in the list have read.
* :macro:`SF_ERR_PARSE_ERROR`
* :macro:`SFPARSE_ERR_PARSE`
* It encountered fatal error while parsing field value.
*/
int sf_parser_list(sf_parser *sfp, sf_value *dest);
int sfparse_parser_list(sfparse_parser *sfp, sfparse_value *dest);
/**
* @function
*
* `sf_parser_item` reads a single item. If this function returns 0,
* it stores the item in |dest| if it is not NULL.
* `sfparse_parser_item` reads a single item. If this function
* returns 0, it stores the item in |dest| if it is not NULL.
*
* This function is only used for the field value that consists of a
* single item.
*
* Caller can optionally read parameters attached to the item by
* calling `sf_parser_param`.
* calling `sfparse_parser_param`.
*
* Caller should call this function again to make sure that there is
* nothing left to read. If this 2nd function call returns
* :macro:`SF_ERR_EOF`, all data have been processed successfully.
* :macro:`SFPARSE_ERR_EOF`, all data have been processed
* successfully.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* :macro:`SF_ERR_EOF`
* :macro:`SFPARSE_ERR_EOF`
* There is nothing left to read.
* :macro:`SF_ERR_PARSE_ERROR`
* :macro:`SFPARSE_ERR_PARSE`
* It encountered fatal error while parsing field value.
*/
int sf_parser_item(sf_parser *sfp, sf_value *dest);
int sfparse_parser_item(sfparse_parser *sfp, sfparse_value *dest);
/**
* @function
*
* `sf_parser_inner_list` reads the next inner list item. If this
* function returns 0, it stores the item in |dest| if it is not NULL.
* `sfparse_parser_inner_list` reads the next inner list item. If
* this function returns 0, it stores the item in |dest| if it is not
* NULL.
*
* Caller can optionally read parameters attached to the item by
* calling `sf_parser_param`.
* calling `sfparse_parser_param`.
*
* Caller should keep calling this function until it returns negative
* error code. If it returns :macro:`SF_ERR_EOF`, all values in this
* inner list have been read, and caller can optionally read
* error code. If it returns :macro:`SFPARSE_ERR_EOF`, all values in
* this inner list have been read, and caller can optionally read
* parameters attached to this inner list by calling
* `sf_parser_param`. Then caller can continue to read rest of the
* values.
* `sfparse_parser_param`. Then caller can continue to read rest of
* the values.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* :macro:`SF_ERR_EOF`
* :macro:`SFPARSE_ERR_EOF`
* All values in the inner list have read.
* :macro:`SF_ERR_PARSE_ERROR`
* :macro:`SFPARSE_ERR_PARSE`
* It encountered fatal error while parsing field value.
*/
int sf_parser_inner_list(sf_parser *sfp, sf_value *dest);
int sfparse_parser_inner_list(sfparse_parser *sfp, sfparse_value *dest);
/**
* @function
*
* `sf_unescape` copies |src| to |dest| by removing escapes (``\``).
* |src| should be the pointer to :member:`sf_value.vec` of type
* :enum:`sf_type.SF_TYPE_STRING` produced by either `sf_parser_dict`,
* `sf_parser_list`, `sf_parser_inner_list`, `sf_parser_item`, or
* `sf_parser_param`, otherwise the behavior is undefined.
* `sfparse_unescape` copies |src| to |dest| by removing escapes
* (``\``). |src| should be the pointer to
* :member:`sfparse_value.vec` of type
* :enum:`sfparse_type.SFPARSE_TYPE_STRING` produced by either
* `sfparse_parser_dict`, `sfparse_parser_list`,
* `sfparse_parser_inner_list`, `sfparse_parser_item`, or
* `sfparse_parser_param`, otherwise the behavior is undefined.
*
* :member:`dest->base <sf_vec.base>` must point to the buffer that
* has sufficient space to store the unescaped string.
*
* If there is no escape character in |src|, |*src| is assigned to
* |*dest|. This includes the case that :member:`src->len
* <sf_vec.len>` == 0.
* :member:`dest->base <sfparse_vec.base>` must point to the buffer
* that has sufficient space to store the unescaped string. The
* memory areas pointed by :member:`dest->base <sfparse_vec.base>` and
* :member:`src->base <sfparse_vec.base>` must not overlap.
*
* This function sets the length of unescaped string to
* :member:`dest->len <sf_vec.len>`.
* :member:`dest->len <sfparse_vec.len>`.
*/
void sf_unescape(sf_vec *dest, const sf_vec *src);
void sfparse_unescape(sfparse_vec *dest, const sfparse_vec *src);
/**
* @function
*
* `sf_base64decode` decodes Base64 encoded string |src| and writes
* the result into |dest|. |src| should be the pointer to
* :member:`sf_value.vec` of type :enum:`sf_type.SF_TYPE_BYTESEQ`
* produced by either `sf_parser_dict`, `sf_parser_list`,
* `sf_parser_inner_list`, `sf_parser_item`, or `sf_parser_param`,
* otherwise the behavior is undefined.
* `sfparse_base64decode` decodes Base64 encoded string |src| and
* writes the result into |dest|. |src| should be the pointer to
* :member:`sfparse_value.vec` of type
* :enum:`sfparse_type.SFPARSE_TYPE_BYTESEQ` produced by either
* `sfparse_parser_dict`, `sfparse_parser_list`,
* `sfparse_parser_inner_list`, `sfparse_parser_item`, or
* `sfparse_parser_param`, otherwise the behavior is undefined.
*
* :member:`dest->base <sf_vec.base>` must point to the buffer that
* has sufficient space to store the decoded byte string.
*
* If :member:`src->len <sf_vec.len>` == 0, |*src| is assigned to
* |*dest|.
* :member:`dest->base <sfparse_vec.base>` must point to the buffer
* that has sufficient space to store the decoded byte string.
*
* This function sets the length of decoded byte string to
* :member:`dest->len <sf_vec.len>`.
* :member:`dest->len <sfparse_vec.len>`.
*/
void sf_base64decode(sf_vec *dest, const sf_vec *src);
void sfparse_base64decode(sfparse_vec *dest, const sfparse_vec *src);
/**
* @function
*
* `sfparse_pctdecode` decodes percent-encoded string |src| and writes
* the result into |dest|. |src| should be the pointer to
* :member:`sfparse_value.vec` of type
* :enum:`sfparse_type.SFPARSE_TYPE_DISPSTRING` produced by either
* `sfparse_parser_dict`, `sfparse_parser_list`,
* `sfparse_parser_inner_list`, `sfparse_parser_item`, or
* `sfparse_parser_param`, otherwise the behavior is undefined.
*
* :member:`dest->base <sfparse_vec.base>` must point to the buffer
* that has sufficient space to store the decoded byte string. The
* memory areas pointed by :member:`dest->base <sfparse_vec.base>` and
* :member:`src->base <sfparse_vec.base>` must not overlap.
*
* This function sets the length of decoded byte string to
* :member:`dest->len <sfparse_vec.len>`.
*/
void sfparse_pctdecode(sfparse_vec *dest, const sfparse_vec *src);
#ifdef __cplusplus
}
#endif
#endif /* defined(__cplusplus) */
#endif /* SFPARSE_H */
#endif /* !defined(SFPARSE_H) */