deps: update nghttp3 to 1.11.0

Signed-off-by: James M Snell <jasnell@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/59249
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
This commit is contained in:
James M Snell 2025-07-27 13:42:38 -07:00
parent dceb1fca40
commit ebfc28a037
36 changed files with 1649 additions and 2300 deletions

View file

@ -1116,11 +1116,43 @@ typedef struct nghttp3_qpack_encoder nghttp3_qpack_encoder;
*
* :macro:`NGHTTP3_ERR_NOMEM`
* Out of memory.
*
* See also `nghttp3_qpack_encoder_new2`. This function calls
* `nghttp3_qpack_encoder_new2` with the given parameters and 0 as
* seed.
*/
NGHTTP3_EXTERN int nghttp3_qpack_encoder_new(nghttp3_qpack_encoder **pencoder,
size_t hard_max_dtable_capacity,
const nghttp3_mem *mem);
/**
* @function
*
* `nghttp3_qpack_encoder_new2` initializes QPACK encoder. |pencoder|
* must be non-NULL pointer. |hard_max_dtable_capacity| is the upper
* bound of the dynamic table capacity. |seed| must be unpredictable
* value, and is used to seed the internal data structure. |mem| is a
* memory allocator. This function allocates memory for
* :type:`nghttp3_qpack_encoder` itself, and assigns its pointer to
* |*pencoder| if it succeeds.
*
* The maximum dynamic table capacity is still 0. In order to change
* the maximum dynamic table capacity, call
* `nghttp3_qpack_encoder_set_max_dtable_capacity`.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* :macro:`NGHTTP3_ERR_NOMEM`
* Out of memory.
*
* This function is available since v1.11.0.
*/
NGHTTP3_EXTERN int nghttp3_qpack_encoder_new2(nghttp3_qpack_encoder **pencoder,
size_t hard_max_dtable_capacity,
uint64_t seed,
const nghttp3_mem *mem);
/**
* @function
*
@ -1605,7 +1637,8 @@ NGHTTP3_EXTERN void nghttp3_set_debug_vprintf_callback(
typedef struct nghttp3_conn nghttp3_conn;
#define NGHTTP3_SETTINGS_V1 1
#define NGHTTP3_SETTINGS_VERSION NGHTTP3_SETTINGS_V1
#define NGHTTP3_SETTINGS_V2 2
#define NGHTTP3_SETTINGS_VERSION NGHTTP3_SETTINGS_V2
/**
* @struct
@ -1652,6 +1685,21 @@ typedef struct nghttp3_settings {
* Datagrams (see :rfc:`9297`).
*/
uint8_t h3_datagram;
/* The following fields have been added since NGHTTP3_SETTINGS_V2. */
/**
* :member:`origin_list`, if set, must contain a serialized HTTP/3
* ORIGIN frame (see :rfc:`9412`) payload. The ORIGIN frame payload
* is a sequence of zero or more of a length prefixed byte string.
* The length is encoded in 2 bytes in network byte order. If
* :member:`origin_list->len <nghttp3_vec.len>` is zero, an empty
* ORIGIN frame is sent. An application must keep the buffer
* pointed by :member:`origin_list->base <nghttp3_vec.base>` alive
* until the :type:`nghttp3_conn` to which this field was passed is
* freed by `nghttp3_conn_del`. The object pointed to by this field
* is copied internally, and does not need to be kept alive. Only
* server uses this field. This field is available since v1.11.0.
*/
const nghttp3_vec *origin_list;
} nghttp3_settings;
/**
@ -1891,8 +1939,47 @@ typedef int (*nghttp3_recv_settings)(nghttp3_conn *conn,
const nghttp3_settings *settings,
void *conn_user_data);
/**
* @functypedef
*
* :type:`nghttp3_recv_origin` is a callback function which is invoked
* when a single origin in ORIGIN frame is received. |origin| is a
* received origin of length |originlen|. |originlen| never be 0.
*
* The implementation of this callback must return 0 if it succeeds.
* Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the
* caller immediately. Any values other than 0 is treated as
* :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`.
*/
typedef int (*nghttp3_recv_origin)(nghttp3_conn *conn, const uint8_t *origin,
size_t originlen, void *conn_user_data);
/**
* @functypedef
*
* :type:`nghttp3_end_origin` is a callback function which is invoked
* when an ORIGIN frame has been completely processed.
*
* The implementation of this callback must return 0 if it succeeds.
* Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the
* caller immediately. Any values other than 0 is treated as
* :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`.
*/
typedef int (*nghttp3_end_origin)(nghttp3_conn *conn, void *conn_user_data);
/**
* @functypedef
*
* :type:`nghttp3_rand` is a callback function which is invoked when
* unpredictable data of |destlen| bytes are needed. The
* implementation must write unpredictable data of |destlen| bytes
* into the buffer pointed by |dest|.
*/
typedef void (*nghttp3_rand)(uint8_t *dest, size_t destlen);
#define NGHTTP3_CALLBACKS_V1 1
#define NGHTTP3_CALLBACKS_VERSION NGHTTP3_CALLBACKS_V1
#define NGHTTP3_CALLBACKS_V2 2
#define NGHTTP3_CALLBACKS_VERSION NGHTTP3_CALLBACKS_V2
/**
* @struct
@ -1986,6 +2073,28 @@ typedef struct nghttp3_callbacks {
* when SETTINGS frame is received.
*/
nghttp3_recv_settings recv_settings;
/* The following fields have been added since NGHTTP3_CALLBACKS_V2. */
/**
* :member:`recv_origin` is a callback function which is invoked
* when a single origin in an ORIGIN frame is received. This field
* is available since v1.11.0.
*/
nghttp3_recv_origin recv_origin;
/**
* :member:`end_origin` is a callback function which is invoked when
* an ORIGIN frame has been completely processed. This field is
* available since v1.11.0.
*/
nghttp3_end_origin end_origin;
/**
* :member:`rand` is a callback function which is invoked when
* unpredictable data are needed. Although this field is optional
* due to the backward compatibility, it is recommended to specify
* this field to harden the runtime behavior against suspicious
* activities of a remote endpoint. This field is available since
* v1.11.0.
*/
nghttp3_rand rand;
} nghttp3_callbacks;
/**
@ -2106,7 +2215,7 @@ NGHTTP3_EXTERN int nghttp3_conn_bind_qpack_streams(nghttp3_conn *conn,
* control credit (both stream and connection) of underlying QUIC
* connection by that amount. It does not include the amount of data
* carried by DATA frame which contains application data (excluding
* any control or QPACK unidirectional streams) . See
* any control or QPACK unidirectional streams). See
* :type:`nghttp3_recv_data` to handle those bytes. If |fin| is
* nonzero, this is the last data from remote endpoint in this stream.
*
@ -2480,8 +2589,6 @@ typedef struct nghttp3_data_reader {
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* :macro:`NGHTTP3_ERR_INVALID_ARGUMENT`
* |stream_id| identifies unidirectional stream.
* :macro:`NGHTTP3_ERR_CONN_CLOSING`
* Connection is shutting down, and no new stream is allowed.
* :macro:`NGHTTP3_ERR_STREAM_IN_USE`

View file

@ -31,7 +31,7 @@
*
* Version number of the nghttp3 library release.
*/
#define NGHTTP3_VERSION "1.6.0"
#define NGHTTP3_VERSION "1.11.0"
/**
* @macro
@ -41,6 +41,6 @@
* number, 8 bits for minor and 8 bits for patch. Version 1.2.3
* becomes 0x010203.
*/
#define NGHTTP3_VERSION_NUM 0x010600
#define NGHTTP3_VERSION_NUM 0x010b00
#endif /* !defined(NGHTTP3_VERSION_H) */

View file

@ -50,6 +50,10 @@ size_t nghttp3_buf_cap(const nghttp3_buf *buf) {
return (size_t)(buf->end - buf->begin);
}
size_t nghttp3_buf_offset(const nghttp3_buf *buf) {
return (size_t)(buf->pos - buf->begin);
}
void nghttp3_buf_reset(nghttp3_buf *buf) { buf->pos = buf->last = buf->begin; }
int nghttp3_buf_reserve(nghttp3_buf *buf, size_t size, const nghttp3_mem *mem) {
@ -87,4 +91,12 @@ void nghttp3_typed_buf_init(nghttp3_typed_buf *tbuf, const nghttp3_buf *buf,
nghttp3_buf_type type) {
tbuf->buf = *buf;
tbuf->type = type;
tbuf->buf.begin = tbuf->buf.pos;
}
void nghttp3_typed_buf_shared_init(nghttp3_typed_buf *tbuf,
const nghttp3_buf *chunk) {
tbuf->buf = *chunk;
tbuf->type = NGHTTP3_BUF_TYPE_SHARED;
tbuf->buf.begin = tbuf->buf.pos = tbuf->buf.last;
}

View file

@ -42,6 +42,12 @@ void nghttp3_buf_wrap_init(nghttp3_buf *buf, uint8_t *src, size_t len);
*/
size_t nghttp3_buf_cap(const nghttp3_buf *buf);
/*
* nghttp3_buf_offset returns the distance from tbuf->begin to
* tbuf->pos. In other words, it returns buf->pos - buf->begin.
*/
size_t nghttp3_buf_offset(const nghttp3_buf *buf);
int nghttp3_buf_reserve(nghttp3_buf *buf, size_t size, const nghttp3_mem *mem);
/*
@ -57,8 +63,12 @@ typedef enum nghttp3_buf_type {
memory. */
NGHTTP3_BUF_TYPE_SHARED,
/* NGHTTP3_BUF_TYPE_ALIEN indicates that the buffer points to a
memory which comes from outside of the library. */
memory which comes from outside of the library. When
acknowledged, acked_data callback is called. */
NGHTTP3_BUF_TYPE_ALIEN,
/* NGHTTP3_BUF_TYPE_ALIEN_NO_ACK is like NGHTTP3_BUF_TYPE_ALIEN, but
acked_data callback is not called. */
NGHTTP3_BUF_TYPE_ALIEN_NO_ACK,
} nghttp3_buf_type;
typedef struct nghttp3_typed_buf {
@ -69,6 +79,13 @@ typedef struct nghttp3_typed_buf {
void nghttp3_typed_buf_init(nghttp3_typed_buf *tbuf, const nghttp3_buf *buf,
nghttp3_buf_type type);
/*
* nghttp3_typed_buf_shared_init initializes |tbuf| of type
* NGHTTP3_BUF_TYPE_SHARED.
*/
void nghttp3_typed_buf_shared_init(nghttp3_typed_buf *tbuf,
const nghttp3_buf *chunk);
void nghttp3_typed_buf_free(nghttp3_typed_buf *tbuf);
#endif /* !defined(NGHTTP3_BUF_H) */

View file

@ -0,0 +1,75 @@
/*
* nghttp3
*
* Copyright (c) 2025 nghttp3 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 "nghttp3_callbacks.h"
#include <string.h>
#include <assert.h>
#include "nghttp3_unreachable.h"
static void callbacks_copy(nghttp3_callbacks *dest,
const nghttp3_callbacks *src,
int callbacks_version) {
assert(callbacks_version != NGHTTP3_CALLBACKS_VERSION);
memcpy(dest, src, nghttp3_callbackslen_version(callbacks_version));
}
const nghttp3_callbacks *
nghttp3_callbacks_convert_to_latest(nghttp3_callbacks *dest,
int callbacks_version,
const nghttp3_callbacks *src) {
if (callbacks_version == NGHTTP3_CALLBACKS_VERSION) {
return src;
}
memset(dest, 0, sizeof(*dest));
callbacks_copy(dest, src, callbacks_version);
return dest;
}
void nghttp3_callbacks_convert_to_old(int callbacks_version,
nghttp3_callbacks *dest,
const nghttp3_callbacks *src) {
assert(callbacks_version != NGHTTP3_CALLBACKS_VERSION);
callbacks_copy(dest, src, callbacks_version);
}
size_t nghttp3_callbackslen_version(int callbacks_version) {
nghttp3_callbacks callbacks;
switch (callbacks_version) {
case NGHTTP3_CALLBACKS_VERSION:
return sizeof(callbacks);
case NGHTTP3_CALLBACKS_V1:
return offsetof(nghttp3_callbacks, recv_settings) +
sizeof(callbacks.recv_settings);
default:
nghttp3_unreachable();
}
}

View file

@ -0,0 +1,74 @@
/*
* nghttp3
*
* Copyright (c) 2025 nghttp3 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 NGHTTP3_CALLBACKS_H
#define NGHTTP3_CALLBACKS_H
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif /* defined(HAVE_CONFIG_H) */
#include <nghttp3/nghttp3.h>
/*
* nghttp3_callbacks_convert_to_latest converts |src| of version
* |callbacks_version| to the latest version
* NGHTTP3_CALLBACKS_VERSION.
*
* |dest| must point to the latest version. |src| may be the older
* version, and if so, it may have fewer fields. Accessing those
* fields causes undefined behavior.
*
* If |callbacks_version| == NGHTTP3_CALLBACKS_VERSION, no conversion
* is made, and |src| is returned. Otherwise, first |dest| is
* zero-initialized, and then all valid fields in |src| are copied
* into |dest|. Finally, |dest| is returned.
*/
const nghttp3_callbacks *nghttp3_callbacks_convert_to_latest(
nghttp3_callbacks *dest, int callbacks_version, const nghttp3_callbacks *src);
/*
* nghttp3_callbacks_convert_to_old converts |src| of the latest
* version to |dest| of version |callbacks_version|.
*
* |callbacks_version| must not be the latest version
* NGHTTP3_CALLBACKS_VERSION.
*
* |dest| points to the older version, and it may have fewer fields.
* Accessing those fields causes undefined behavior.
*
* This function copies all valid fields in version
* |callbacks_version| from |src| to |dest|.
*/
void nghttp3_callbacks_convert_to_old(int callbacks_version,
nghttp3_callbacks *dest,
const nghttp3_callbacks *src);
/*
* nghttp3_callbackslen_version returns the effective length of
* nghttp3_callbacks at the version |callbacks_version|.
*/
size_t nghttp3_callbackslen_version(int callbacks_version);
#endif /* !defined(NGHTTP3_CALLBACKS_H) */

View file

@ -34,12 +34,10 @@
#include "nghttp3_conv.h"
#include "nghttp3_http.h"
#include "nghttp3_unreachable.h"
#include "nghttp3_settings.h"
#include "nghttp3_callbacks.h"
/* NGHTTP3_QPACK_ENCODER_MAX_DTABLE_CAPACITY is the upper bound of the
dynamic table capacity that QPACK encoder is willing to use. */
#define NGHTTP3_QPACK_ENCODER_MAX_DTABLE_CAPACITY 4096
nghttp3_objalloc_def(chunk, nghttp3_chunk, oplent);
nghttp3_objalloc_def(chunk, nghttp3_chunk, oplent)
/*
* conn_remote_stream_uni returns nonzero if |stream_id| is remote
@ -207,6 +205,32 @@ static int conn_call_recv_settings(nghttp3_conn *conn) {
return 0;
}
static int conn_call_recv_origin(nghttp3_conn *conn, const uint8_t *origin,
size_t originlen) {
if (!conn->callbacks.recv_origin) {
return 0;
}
if (conn->callbacks.recv_origin(conn, origin, originlen, conn->user_data) !=
0) {
return NGHTTP3_ERR_CALLBACK_FAILURE;
}
return 0;
}
static int conn_call_end_origin(nghttp3_conn *conn) {
if (!conn->callbacks.end_origin) {
return 0;
}
if (conn->callbacks.end_origin(conn, conn->user_data) != 0) {
return NGHTTP3_ERR_CALLBACK_FAILURE;
}
return 0;
}
static int ricnt_less(const nghttp3_pq_entry *lhsx,
const nghttp3_pq_entry *rhsx) {
nghttp3_stream *lhs =
@ -233,11 +257,22 @@ static int conn_new(nghttp3_conn **pconn, int server, int callbacks_version,
const nghttp3_callbacks *callbacks, int settings_version,
const nghttp3_settings *settings, const nghttp3_mem *mem,
void *user_data) {
int rv;
nghttp3_conn *conn;
nghttp3_settings settings_latest;
nghttp3_callbacks callbacks_latest;
uint64_t map_seed;
size_t i;
(void)callbacks_version;
(void)settings_version;
settings = nghttp3_settings_convert_to_latest(&settings_latest,
settings_version, settings);
callbacks = nghttp3_callbacks_convert_to_latest(&callbacks_latest,
callbacks_version, callbacks);
assert(settings->max_field_section_size <= NGHTTP3_VARINT_MAX);
assert(settings->qpack_max_dtable_capacity <= NGHTTP3_VARINT_MAX);
assert(settings->qpack_encoder_max_dtable_capacity <= NGHTTP3_VARINT_MAX);
assert(settings->qpack_blocked_streams <= NGHTTP3_VARINT_MAX);
if (mem == NULL) {
mem = nghttp3_mem_default();
@ -252,20 +287,19 @@ static int conn_new(nghttp3_conn **pconn, int server, int callbacks_version,
NGHTTP3_STREAM_MIN_CHUNK_SIZE * 16, mem);
nghttp3_objalloc_stream_init(&conn->stream_objalloc, 8, mem);
nghttp3_map_init(&conn->streams, mem);
rv =
nghttp3_qpack_decoder_init(&conn->qdec, settings->qpack_max_dtable_capacity,
settings->qpack_blocked_streams, mem);
if (rv != 0) {
goto qdec_init_fail;
if (callbacks->rand) {
callbacks->rand((uint8_t *)&map_seed, sizeof(map_seed));
} else {
map_seed = 0;
}
rv = nghttp3_qpack_encoder_init(
&conn->qenc, settings->qpack_encoder_max_dtable_capacity, mem);
if (rv != 0) {
goto qenc_init_fail;
}
nghttp3_map_init(&conn->streams, map_seed, mem);
nghttp3_qpack_decoder_init(&conn->qdec, settings->qpack_max_dtable_capacity,
settings->qpack_blocked_streams, mem);
nghttp3_qpack_encoder_init(
&conn->qenc, settings->qpack_encoder_max_dtable_capacity, ++map_seed, mem);
nghttp3_pq_init(&conn->qpack_blocked_streams, ricnt_less, mem);
@ -277,8 +311,14 @@ static int conn_new(nghttp3_conn **pconn, int server, int callbacks_version,
conn->callbacks = *callbacks;
conn->local.settings = *settings;
if (!server) {
if (server) {
if (settings->origin_list) {
conn->local.settings.origin_list = &conn->local.origin_list;
conn->local.origin_list = *settings->origin_list;
}
} else {
conn->local.settings.enable_connect_protocol = 0;
conn->local.settings.origin_list = NULL;
}
nghttp3_settings_default(&conn->remote.settings);
conn->mem = mem;
@ -291,16 +331,6 @@ static int conn_new(nghttp3_conn **pconn, int server, int callbacks_version,
*pconn = conn;
return 0;
qenc_init_fail:
nghttp3_qpack_decoder_free(&conn->qdec);
qdec_init_fail:
nghttp3_map_free(&conn->streams);
nghttp3_objalloc_free(&conn->stream_objalloc);
nghttp3_objalloc_free(&conn->out_chunk_objalloc);
nghttp3_mem_free(mem, conn);
return rv;
}
int nghttp3_conn_client_new_versioned(nghttp3_conn **pconn,
@ -374,6 +404,8 @@ void nghttp3_conn_del(nghttp3_conn *conn) {
nghttp3_objalloc_free(&conn->stream_objalloc);
nghttp3_objalloc_free(&conn->out_chunk_objalloc);
nghttp3_mem_free(conn->mem, conn->rx.originbuf);
nghttp3_mem_free(conn->mem, conn);
}
@ -399,6 +431,9 @@ nghttp3_ssize nghttp3_conn_read_stream(nghttp3_conn *conn, int64_t stream_id,
size_t bidi_nproc;
int rv;
assert(stream_id >= 0);
assert(stream_id <= (int64_t)NGHTTP3_MAX_VARINT);
stream = nghttp3_conn_find_stream(conn, stream_id);
if (stream == NULL) {
/* TODO Assert idtr */
@ -434,6 +469,10 @@ nghttp3_ssize nghttp3_conn_read_stream(nghttp3_conn *conn, int64_t stream_id,
return rv;
}
}
} else if (!nghttp3_client_stream_uni(stream_id)) {
/* server does not expect to receive new server initiated
bidirectional or unidirectional stream from client. */
return NGHTTP3_ERR_H3_STREAM_CREATION_ERROR;
} else {
/* unidirectional stream */
if (srclen == 0 && fin) {
@ -448,7 +487,7 @@ nghttp3_ssize nghttp3_conn_read_stream(nghttp3_conn *conn, int64_t stream_id,
stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_INITIAL;
stream->tx.hstate = NGHTTP3_HTTP_STATE_REQ_INITIAL;
} else if (nghttp3_stream_uni(stream_id)) {
} else if (nghttp3_server_stream_uni(stream_id)) {
if (srclen == 0 && fin) {
return 0;
}
@ -461,17 +500,16 @@ nghttp3_ssize nghttp3_conn_read_stream(nghttp3_conn *conn, int64_t stream_id,
stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_INITIAL;
stream->tx.hstate = NGHTTP3_HTTP_STATE_RESP_INITIAL;
} else {
/* client doesn't expect to receive new bidirectional stream
from server. */
/* client doesn't expect to receive new bidirectional stream or
client initiated unidirectional stream from server. */
return NGHTTP3_ERR_H3_STREAM_CREATION_ERROR;
}
} else if (conn->server) {
if (nghttp3_client_stream_bidi(stream_id)) {
if (stream->rx.hstate == NGHTTP3_HTTP_STATE_NONE) {
stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_INITIAL;
stream->tx.hstate = NGHTTP3_HTTP_STATE_REQ_INITIAL;
}
}
assert(nghttp3_client_stream_bidi(stream_id) ||
nghttp3_client_stream_uni(stream_id));
} else {
assert(nghttp3_client_stream_bidi(stream_id) ||
nghttp3_server_stream_uni(stream_id));
}
if (srclen == 0 && !fin) {
@ -608,6 +646,9 @@ nghttp3_ssize nghttp3_conn_read_uni(nghttp3_conn *conn, nghttp3_stream *stream,
break;
case NGHTTP3_STREAM_TYPE_UNKNOWN:
nconsumed = (nghttp3_ssize)srclen;
if (fin) {
break;
}
rv = conn_call_stop_sending(conn, stream, NGHTTP3_H3_STREAM_CREATION_ERROR);
if (rv != 0) {
@ -625,6 +666,11 @@ nghttp3_ssize nghttp3_conn_read_uni(nghttp3_conn *conn, nghttp3_stream *stream,
return nread + nconsumed;
}
static void conn_reset_rx_originlen(nghttp3_conn *conn) {
conn->rx.originlen_offset = 0;
conn->rx.originlen = 0;
}
static int frame_fin(nghttp3_stream_read_state *rstate, size_t len) {
return (int64_t)len >= rstate->left;
}
@ -661,11 +707,11 @@ nghttp3_ssize nghttp3_conn_read_control(nghttp3_conn *conn,
return (nghttp3_ssize)nconsumed;
}
rstate->fr.hd.type = rvint->acc;
rstate->fr.type = rvint->acc;
nghttp3_varint_read_state_reset(rvint);
rstate->state = NGHTTP3_CTRL_STREAM_STATE_FRAME_LENGTH;
if (p == end) {
break;
return (nghttp3_ssize)nconsumed;
}
/* Fall through */
case NGHTTP3_CTRL_STREAM_STATE_FRAME_LENGTH:
@ -681,19 +727,19 @@ nghttp3_ssize nghttp3_conn_read_control(nghttp3_conn *conn,
return (nghttp3_ssize)nconsumed;
}
rstate->left = rstate->fr.hd.length = rvint->acc;
rstate->left = rvint->acc;
nghttp3_varint_read_state_reset(rvint);
if (!(conn->flags & NGHTTP3_CONN_FLAG_SETTINGS_RECVED)) {
if (rstate->fr.hd.type != NGHTTP3_FRAME_SETTINGS) {
if (rstate->fr.type != NGHTTP3_FRAME_SETTINGS) {
return NGHTTP3_ERR_H3_MISSING_SETTINGS;
}
conn->flags |= NGHTTP3_CONN_FLAG_SETTINGS_RECVED;
} else if (rstate->fr.hd.type == NGHTTP3_FRAME_SETTINGS) {
} else if (rstate->fr.type == NGHTTP3_FRAME_SETTINGS) {
return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
}
switch (rstate->fr.hd.type) {
switch (rstate->fr.type) {
case NGHTTP3_FRAME_SETTINGS:
/* SETTINGS frame might be empty. */
if (rstate->left == 0) {
@ -734,6 +780,31 @@ nghttp3_ssize nghttp3_conn_read_control(nghttp3_conn *conn,
case NGHTTP3_FRAME_PRIORITY_UPDATE_PUSH_ID:
/* We do not support push */
return NGHTTP3_ERR_H3_ID_ERROR;
case NGHTTP3_FRAME_ORIGIN:
if (conn->server ||
(!conn->callbacks.recv_origin && !conn->callbacks.end_origin)) {
busy = 1;
rstate->state = NGHTTP3_CTRL_STREAM_STATE_IGN_FRAME;
break;
}
/* ORIGIN frame might be empty */
if (rstate->left == 0) {
rv = conn_call_end_origin(conn);
if (rv != 0) {
return rv;
}
nghttp3_stream_read_state_reset(rstate);
break;
}
conn_reset_rx_originlen(conn);
rstate->state = NGHTTP3_CTRL_STREAM_STATE_ORIGIN_ORIGIN_LEN;
break;
case NGHTTP3_FRAME_CANCEL_PUSH: /* We do not support push */
case NGHTTP3_FRAME_DATA:
case NGHTTP3_FRAME_HEADERS:
@ -971,8 +1042,14 @@ nghttp3_ssize nghttp3_conn_read_control(nghttp3_conn *conn,
break;
}
conn->rx.pri_fieldbuflen = 0;
rstate->state = NGHTTP3_CTRL_STREAM_STATE_PRIORITY_UPDATE;
if (p == end) {
return (nghttp3_ssize)nconsumed;
}
/* Fall through */
case NGHTTP3_CTRL_STREAM_STATE_PRIORITY_UPDATE:
/* We need to buffer Priority Field Value because it might be
@ -1027,9 +1104,134 @@ nghttp3_ssize nghttp3_conn_read_control(nghttp3_conn *conn,
return rv;
}
conn->rx.pri_fieldbuflen = 0;
nghttp3_stream_read_state_reset(rstate);
break;
case NGHTTP3_CTRL_STREAM_STATE_ORIGIN_ORIGIN_LEN:
/* client side only */
len = (size_t)nghttp3_min_int64(rstate->left, (int64_t)(end - p));
assert(len > 0);
for (;;) {
nread = 0;
for (; conn->rx.originlen_offset < sizeof(conn->rx.originlen) &&
(size_t)nread < len;
++conn->rx.originlen_offset, ++nread) {
conn->rx.originlen <<= 8;
conn->rx.originlen += *p++;
}
nconsumed += (size_t)nread;
rstate->left -= nread;
len -= (size_t)nread;
if (conn->rx.originlen_offset < sizeof(conn->rx.originlen)) {
/* Needs another byte to parse Origin-Len */
if (rstate->left == 0) {
return NGHTTP3_ERR_H3_FRAME_ERROR;
}
return (nghttp3_ssize)nconsumed;
}
if (conn->rx.originlen == 0 || rstate->left < conn->rx.originlen) {
/* While this seems OK in very loose RFC, allowing this
sounds like an atack vector. */
return NGHTTP3_ERR_H3_FRAME_ERROR;
}
if (len < conn->rx.originlen) {
/* ASCII-Origin does not fit into this buffer. Needs
buffering. */
if (conn->rx.originbuf == NULL) {
conn->rx.originbuf = nghttp3_mem_malloc(conn->mem, UINT16_MAX);
if (conn->rx.originbuf == NULL) {
return NGHTTP3_ERR_NOMEM;
}
}
memcpy(conn->rx.originbuf, p, len);
/* No need to update p because we will return very soon. */
nconsumed += len;
rstate->left -= (int64_t)len;
conn->rx.originbuflen = len;
rstate->state = NGHTTP3_CTRL_STREAM_STATE_ORIGIN_ASCII_ORIGIN;
return (nghttp3_ssize)nconsumed;
}
rv = conn_call_recv_origin(conn, p, conn->rx.originlen);
if (rv != 0) {
return rv;
}
p += conn->rx.originlen;
nconsumed += conn->rx.originlen;
rstate->left -= conn->rx.originlen;
if (rstate->left == 0) {
rv = conn_call_end_origin(conn);
if (rv != 0) {
return rv;
}
nghttp3_stream_read_state_reset(rstate);
break;
}
len -= conn->rx.originlen;
conn_reset_rx_originlen(conn);
if (p == end) {
return (nghttp3_ssize)nconsumed;
}
}
break;
case NGHTTP3_CTRL_STREAM_STATE_ORIGIN_ASCII_ORIGIN:
/* client side only */
len = nghttp3_min_size(conn->rx.originlen - conn->rx.originbuflen,
(size_t)(end - p));
assert(len > 0);
memcpy(conn->rx.originbuf + conn->rx.originbuflen, p, len);
conn->rx.originbuflen += len;
p += len;
nconsumed += len;
rstate->left -= (int64_t)len;
if (conn->rx.originbuflen < conn->rx.originlen) {
return (nghttp3_ssize)nconsumed;
}
rv = conn_call_recv_origin(conn, conn->rx.originbuf, conn->rx.originlen);
if (rv != 0) {
return rv;
}
if (rstate->left) {
conn_reset_rx_originlen(conn);
rstate->state = NGHTTP3_CTRL_STREAM_STATE_ORIGIN_ORIGIN_LEN;
break;
}
rv = conn_call_end_origin(conn);
if (rv != 0) {
return rv;
}
nghttp3_stream_read_state_reset(rstate);
break;
case NGHTTP3_CTRL_STREAM_STATE_IGN_FRAME:
len = (size_t)nghttp3_min_int64(rstate->left, (int64_t)(end - p));
@ -1251,7 +1453,7 @@ nghttp3_ssize nghttp3_conn_read_bidi(nghttp3_conn *conn, size_t *pnproc,
goto almost_done;
}
rstate->fr.hd.type = rvint->acc;
rstate->fr.type = rvint->acc;
nghttp3_varint_read_state_reset(rvint);
rstate->state = NGHTTP3_REQ_STREAM_STATE_FRAME_LENGTH;
if (p == end) {
@ -1271,10 +1473,10 @@ nghttp3_ssize nghttp3_conn_read_bidi(nghttp3_conn *conn, size_t *pnproc,
goto almost_done;
}
rstate->left = rstate->fr.hd.length = rvint->acc;
rstate->left = rvint->acc;
nghttp3_varint_read_state_reset(rvint);
switch (rstate->fr.hd.type) {
switch (rstate->fr.type) {
case NGHTTP3_FRAME_DATA:
rv = nghttp3_stream_transit_rx_http_state(
stream, NGHTTP3_HTTP_EVENT_DATA_BEGIN);
@ -1374,10 +1576,6 @@ nghttp3_ssize nghttp3_conn_read_bidi(nghttp3_conn *conn, size_t *pnproc,
nread = nghttp3_conn_on_headers(conn, stream, p, len,
(int64_t)len == rstate->left);
if (nread < 0) {
if (nread == NGHTTP3_ERR_MALFORMED_HTTP_HEADER) {
goto http_header_error;
}
return nread;
}
@ -1416,10 +1614,6 @@ nghttp3_ssize nghttp3_conn_read_bidi(nghttp3_conn *conn, size_t *pnproc,
}
if (rv != 0) {
if (rv == NGHTTP3_ERR_MALFORMED_HTTP_HEADER) {
goto http_header_error;
}
return rv;
}
@ -1458,24 +1652,6 @@ nghttp3_ssize nghttp3_conn_read_bidi(nghttp3_conn *conn, size_t *pnproc,
nghttp3_stream_read_state_reset(rstate);
break;
http_header_error:
stream->flags |= NGHTTP3_STREAM_FLAG_HTTP_ERROR;
busy = 1;
rstate->state = NGHTTP3_REQ_STREAM_STATE_IGN_REST;
rv = conn_call_stop_sending(conn, stream, NGHTTP3_H3_MESSAGE_ERROR);
if (rv != 0) {
return rv;
}
rv = conn_call_reset_stream(conn, stream, NGHTTP3_H3_MESSAGE_ERROR);
if (rv != 0) {
return rv;
}
break;
case NGHTTP3_REQ_STREAM_STATE_IGN_FRAME:
len = (size_t)nghttp3_min_int64(rstate->left, (int64_t)(end - p));
@ -1792,6 +1968,8 @@ conn_on_priority_update_stream(nghttp3_conn *conn,
stream->node.pri = fr->pri;
stream->flags |= NGHTTP3_STREAM_FLAG_PRIORITY_UPDATE_RECVED;
stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_INITIAL;
stream->tx.hstate = NGHTTP3_HTTP_STATE_REQ_INITIAL;
return 0;
}
@ -1808,7 +1986,7 @@ conn_on_priority_update_stream(nghttp3_conn *conn,
int nghttp3_conn_on_priority_update(nghttp3_conn *conn,
const nghttp3_frame_priority_update *fr) {
assert(conn->server);
assert(fr->hd.type == NGHTTP3_FRAME_PRIORITY_UPDATE);
assert(fr->type == NGHTTP3_FRAME_PRIORITY_UPDATE);
return conn_on_priority_update_stream(conn, fr);
}
@ -1836,7 +2014,7 @@ int nghttp3_conn_create_stream(nghttp3_conn *conn, nghttp3_stream **pstream,
nghttp3_stream *stream;
int rv;
nghttp3_stream_callbacks callbacks = {
conn_stream_acked_data,
.acked_data = conn_stream_acked_data,
};
rv = nghttp3_stream_new(&stream, stream_id, &callbacks,
@ -1874,6 +2052,8 @@ int nghttp3_conn_bind_control_stream(nghttp3_conn *conn, int64_t stream_id) {
nghttp3_frame_entry frent;
int rv;
assert(stream_id >= 0);
assert(stream_id <= (int64_t)NGHTTP3_MAX_VARINT);
assert(!conn->server || nghttp3_server_stream_uni(stream_id));
assert(conn->server || nghttp3_client_stream_uni(stream_id));
@ -1895,10 +2075,27 @@ int nghttp3_conn_bind_control_stream(nghttp3_conn *conn, int64_t stream_id) {
return rv;
}
frent.fr.hd.type = NGHTTP3_FRAME_SETTINGS;
frent.fr.type = NGHTTP3_FRAME_SETTINGS;
frent.aux.settings.local_settings = &conn->local.settings;
return nghttp3_stream_frq_add(stream, &frent);
rv = nghttp3_stream_frq_add(stream, &frent);
if (rv != 0) {
return rv;
}
if (conn->local.settings.origin_list) {
assert(conn->server);
frent.fr.origin.type = NGHTTP3_FRAME_ORIGIN;
frent.fr.origin.origin_list = *conn->local.settings.origin_list;
rv = nghttp3_stream_frq_add(stream, &frent);
if (rv != 0) {
return rv;
}
}
return 0;
}
int nghttp3_conn_bind_qpack_streams(nghttp3_conn *conn, int64_t qenc_stream_id,
@ -1906,6 +2103,10 @@ int nghttp3_conn_bind_qpack_streams(nghttp3_conn *conn, int64_t qenc_stream_id,
nghttp3_stream *stream;
int rv;
assert(qenc_stream_id >= 0);
assert(qenc_stream_id <= (int64_t)NGHTTP3_MAX_VARINT);
assert(qdec_stream_id >= 0);
assert(qdec_stream_id <= (int64_t)NGHTTP3_MAX_VARINT);
assert(!conn->server || nghttp3_server_stream_uni(qenc_stream_id));
assert(!conn->server || nghttp3_server_stream_uni(qdec_stream_id));
assert(conn->server || nghttp3_client_stream_uni(qenc_stream_id));
@ -2118,14 +2319,14 @@ static int conn_submit_headers_data(nghttp3_conn *conn, nghttp3_stream *stream,
const nghttp3_data_reader *dr) {
int rv;
nghttp3_nv *nnva;
nghttp3_frame_entry frent = {0};
nghttp3_frame_entry frent;
rv = nghttp3_nva_copy(&nnva, nva, nvlen, conn->mem);
if (rv != 0) {
return rv;
}
frent.fr.hd.type = NGHTTP3_FRAME_HEADERS;
frent.fr.type = NGHTTP3_FRAME_HEADERS;
frent.fr.headers.nva = nnva;
frent.fr.headers.nvlen = nvlen;
@ -2136,7 +2337,7 @@ static int conn_submit_headers_data(nghttp3_conn *conn, nghttp3_stream *stream,
}
if (dr) {
frent.fr.hd.type = NGHTTP3_FRAME_DATA;
frent.fr.type = NGHTTP3_FRAME_DATA;
frent.aux.data.dr = *dr;
rv = nghttp3_stream_frq_add(stream, &frent);
@ -2194,13 +2395,11 @@ int nghttp3_conn_submit_request(nghttp3_conn *conn, int64_t stream_id,
assert(!conn->server);
assert(conn->tx.qenc);
assert(stream_id >= 0);
assert(stream_id <= (int64_t)NGHTTP3_MAX_VARINT);
assert(nghttp3_client_stream_bidi(stream_id));
/* TODO Should we check that stream_id is client stream_id? */
/* TODO Check GOAWAY last stream ID */
if (nghttp3_stream_uni(stream_id)) {
return NGHTTP3_ERR_INVALID_ARGUMENT;
}
if (conn->flags & NGHTTP3_CONN_FLAG_GOAWAY_RECVED) {
return NGHTTP3_ERR_CONN_CLOSING;
@ -2288,12 +2487,12 @@ int nghttp3_conn_submit_trailers(nghttp3_conn *conn, int64_t stream_id,
}
int nghttp3_conn_submit_shutdown_notice(nghttp3_conn *conn) {
nghttp3_frame_entry frent = {0};
nghttp3_frame_entry frent;
int rv;
assert(conn->tx.ctrl);
frent.fr.hd.type = NGHTTP3_FRAME_GOAWAY;
frent.fr.type = NGHTTP3_FRAME_GOAWAY;
frent.fr.goaway.id = conn->server ? NGHTTP3_SHUTDOWN_NOTICE_STREAM_ID
: NGHTTP3_SHUTDOWN_NOTICE_PUSH_ID;
@ -2311,12 +2510,12 @@ int nghttp3_conn_submit_shutdown_notice(nghttp3_conn *conn) {
}
int nghttp3_conn_shutdown(nghttp3_conn *conn) {
nghttp3_frame_entry frent = {0};
nghttp3_frame_entry frent;
int rv;
assert(conn->tx.ctrl);
frent.fr.hd.type = NGHTTP3_FRAME_GOAWAY;
frent.fr.type = NGHTTP3_FRAME_GOAWAY;
if (conn->server) {
frent.fr.goaway.id =
nghttp3_min_int64((1ll << 62) - 4, conn->rx.max_stream_id_bidi + 4);
@ -2454,6 +2653,9 @@ int nghttp3_conn_close_stream(nghttp3_conn *conn, int64_t stream_id,
int nghttp3_conn_shutdown_stream_read(nghttp3_conn *conn, int64_t stream_id) {
nghttp3_stream *stream;
assert(stream_id >= 0);
assert(stream_id <= (int64_t)NGHTTP3_MAX_VARINT);
if (!nghttp3_client_stream_bidi(stream_id)) {
return 0;
}
@ -2515,6 +2717,9 @@ uint64_t nghttp3_conn_get_frame_payload_left(nghttp3_conn *conn,
nghttp3_stream *stream;
int uni = 0;
assert(stream_id >= 0);
assert(stream_id <= (int64_t)NGHTTP3_MAX_VARINT);
if (!nghttp3_client_stream_bidi(stream_id)) {
uni = conn_remote_stream_uni(conn, stream_id);
if (!uni) {
@ -2542,6 +2747,8 @@ int nghttp3_conn_get_stream_priority_versioned(nghttp3_conn *conn,
(void)pri_version;
assert(conn->server);
assert(stream_id >= 0);
assert(stream_id <= (int64_t)NGHTTP3_MAX_VARINT);
if (!nghttp3_client_stream_bidi(stream_id)) {
return NGHTTP3_ERR_INVALID_ARGUMENT;
@ -2562,10 +2769,12 @@ int nghttp3_conn_set_client_stream_priority(nghttp3_conn *conn,
const uint8_t *data,
size_t datalen) {
nghttp3_stream *stream;
nghttp3_frame_entry frent = {0};
nghttp3_frame_entry frent;
uint8_t *buf = NULL;
assert(!conn->server);
assert(stream_id >= 0);
assert(stream_id <= (int64_t)NGHTTP3_MAX_VARINT);
if (!nghttp3_client_stream_bidi(stream_id)) {
return NGHTTP3_ERR_INVALID_ARGUMENT;
@ -2585,7 +2794,7 @@ int nghttp3_conn_set_client_stream_priority(nghttp3_conn *conn,
memcpy(buf, data, datalen);
}
frent.fr.hd.type = NGHTTP3_FRAME_PRIORITY_UPDATE;
frent.fr.type = NGHTTP3_FRAME_PRIORITY_UPDATE;
frent.fr.priority_update.pri_elem_id = stream_id;
frent.fr.priority_update.data = buf;
frent.fr.priority_update.datalen = datalen;
@ -2603,6 +2812,8 @@ int nghttp3_conn_set_server_stream_priority_versioned(nghttp3_conn *conn,
assert(conn->server);
assert(pri->urgency < NGHTTP3_URGENCY_LEVELS);
assert(pri->inc == 0 || pri->inc == 1);
assert(stream_id >= 0);
assert(stream_id <= (int64_t)NGHTTP3_MAX_VARINT);
if (!nghttp3_client_stream_bidi(stream_id)) {
return NGHTTP3_ERR_INVALID_ARGUMENT;
@ -2626,13 +2837,3 @@ int nghttp3_conn_is_drained(nghttp3_conn *conn) {
nghttp3_stream_outq_write_done(conn->tx.ctrl) &&
nghttp3_ringbuf_len(&conn->tx.ctrl->frq) == 0;
}
void nghttp3_settings_default_versioned(int settings_version,
nghttp3_settings *settings) {
(void)settings_version;
memset(settings, 0, sizeof(nghttp3_settings));
settings->max_field_section_size = NGHTTP3_VARINT_MAX;
settings->qpack_encoder_max_dtable_capacity =
NGHTTP3_QPACK_ENCODER_MAX_DTABLE_CAPACITY;
}

View file

@ -38,8 +38,6 @@
#include "nghttp3_idtr.h"
#include "nghttp3_gaptr.h"
#define NGHTTP3_VARINT_MAX ((1ull << 62) - 1)
/* NGHTTP3_QPACK_ENCODER_MAX_TABLE_CAPACITY is the maximum dynamic
table size for QPACK encoder. */
#define NGHTTP3_QPACK_ENCODER_MAX_TABLE_CAPACITY 16384
@ -76,7 +74,7 @@ typedef struct nghttp3_chunk {
nghttp3_opl_entry oplent;
} nghttp3_chunk;
nghttp3_objalloc_decl(chunk, nghttp3_chunk, oplent);
nghttp3_objalloc_decl(chunk, nghttp3_chunk, oplent)
struct nghttp3_conn {
nghttp3_objalloc out_chunk_objalloc;
@ -95,6 +93,11 @@ struct nghttp3_conn {
uint16_t flags;
struct {
/* origin_list contains the shallow copy of
nghttp3_settings.origin_list passed from an application if this
object is initialized as server. settings.origin_list may
point to the address of this field. */
nghttp3_vec origin_list;
nghttp3_settings settings;
struct {
/* max_pushes is the number of push IDs that local endpoint can
@ -125,12 +128,36 @@ struct nghttp3_conn {
int64_t max_stream_id_bidi;
/* pri_fieldbuf is a buffer to store incoming Priority Field Value
in PRIORITY_UPDATE frame. */
uint8_t pri_fieldbuf[8];
/* pri_fieldlen is the number of bytes written into
pri_fieldbuf. */
size_t pri_fieldbuflen;
union {
struct {
/* pri_fieldbuf is a buffer to store incoming Priority Field Value
in PRIORITY_UPDATE frame. */
uint8_t pri_fieldbuf[8];
/* pri_fieldlen is the number of bytes written into
pri_fieldbuf. */
size_t pri_fieldbuflen;
};
/* ORIGIN frame */
struct {
/* originlen_offset is the offset to Origin-Len that is going
to be added to originlen. If this value equals
sizeof(originlen), Origin-Len is fully read, and the length
of ASCII-Origin is determined. */
size_t originlen_offset;
/* originlen is Origin-Len of ASCII-Origin currently read. */
uint16_t originlen;
};
};
/* originbuf points to the buffer that contains ASCII-Origin that
is not fully available in a single input buffer. If it is
fully available in the input buffer, it is emitted to an
application without using this field. Otherwise, partial
ASCII-Origin is copied to this field, and the complete
ASCII-Origin is emitted when the assembly finishes. */
uint8_t *originbuf;
/* originbuflen is the length of bytes written to originbuf. */
size_t originbuflen;
} rx;
struct {

View file

@ -66,8 +66,6 @@ const uint8_t *nghttp3_get_varint(int64_t *dest, const uint8_t *p) {
}
}
int64_t nghttp3_get_varint_fb(const uint8_t *p) { return *p & 0x3f; }
size_t nghttp3_get_varintlen(const uint8_t *p) {
return (size_t)(1u << (*p >> 6));
}

View file

@ -56,6 +56,8 @@
#include <nghttp3/nghttp3.h>
#define NGHTTP3_VARINT_MAX ((1ull << 62) - 1)
#if HAVE_DECL_BE64TOH
# define nghttp3_ntohl64(N) be64toh(N)
# define nghttp3_htonl64(N) htobe64(N)
@ -135,12 +137,6 @@ STIN uint16_t ntohs(uint16_t netshort) {
*/
const uint8_t *nghttp3_get_varint(int64_t *dest, const uint8_t *p);
/*
* nghttp3_get_varint_fb reads first byte of encoded variable-length
* integer from |p|.
*/
int64_t nghttp3_get_varint_fb(const uint8_t *p);
/*
* nghttp3_get_varintlen returns the required number of bytes to read
* variable-length integer starting at |p|.

View file

@ -31,21 +31,22 @@
#include "nghttp3_conv.h"
#include "nghttp3_str.h"
uint8_t *nghttp3_frame_write_hd(uint8_t *p, const nghttp3_frame_hd *hd) {
p = nghttp3_put_varint(p, hd->type);
p = nghttp3_put_varint(p, hd->length);
uint8_t *nghttp3_frame_write_hd(uint8_t *p, int64_t type, int64_t payloadlen) {
p = nghttp3_put_varint(p, type);
p = nghttp3_put_varint(p, payloadlen);
return p;
}
size_t nghttp3_frame_write_hd_len(const nghttp3_frame_hd *hd) {
return nghttp3_put_varintlen(hd->type) + nghttp3_put_varintlen(hd->length);
size_t nghttp3_frame_write_hd_len(int64_t type, int64_t payloadlen) {
return nghttp3_put_varintlen(type) + nghttp3_put_varintlen(payloadlen);
}
uint8_t *nghttp3_frame_write_settings(uint8_t *p,
const nghttp3_frame_settings *fr) {
const nghttp3_frame_settings *fr,
int64_t payloadlen) {
size_t i;
p = nghttp3_frame_write_hd(p, &fr->hd);
p = nghttp3_frame_write_hd(p, fr->type, payloadlen);
for (i = 0; i < fr->niv; ++i) {
p = nghttp3_put_varint(p, (int64_t)fr->iv[i].id);
@ -71,9 +72,9 @@ size_t nghttp3_frame_write_settings_len(int64_t *ppayloadlen,
nghttp3_put_varintlen((int64_t)payloadlen) + payloadlen;
}
uint8_t *nghttp3_frame_write_goaway(uint8_t *p,
const nghttp3_frame_goaway *fr) {
p = nghttp3_frame_write_hd(p, &fr->hd);
uint8_t *nghttp3_frame_write_goaway(uint8_t *p, const nghttp3_frame_goaway *fr,
int64_t payloadlen) {
p = nghttp3_frame_write_hd(p, fr->type, payloadlen);
p = nghttp3_put_varint(p, fr->id);
return p;
@ -89,10 +90,9 @@ size_t nghttp3_frame_write_goaway_len(int64_t *ppayloadlen,
nghttp3_put_varintlen((int64_t)payloadlen) + payloadlen;
}
uint8_t *
nghttp3_frame_write_priority_update(uint8_t *p,
const nghttp3_frame_priority_update *fr) {
p = nghttp3_frame_write_hd(p, &fr->hd);
uint8_t *nghttp3_frame_write_priority_update(
uint8_t *p, const nghttp3_frame_priority_update *fr, int64_t payloadlen) {
p = nghttp3_frame_write_hd(p, fr->type, payloadlen);
p = nghttp3_put_varint(p, fr->pri_elem_id);
if (fr->datalen) {
p = nghttp3_cpymem(p, fr->data, fr->datalen);
@ -107,7 +107,27 @@ size_t nghttp3_frame_write_priority_update_len(
*ppayloadlen = (int64_t)payloadlen;
return nghttp3_put_varintlen(fr->hd.type) +
return nghttp3_put_varintlen(fr->type) +
nghttp3_put_varintlen((int64_t)payloadlen) + payloadlen;
}
uint8_t *nghttp3_frame_write_origin(uint8_t *p, const nghttp3_frame_origin *fr,
int64_t payloadlen) {
p = nghttp3_frame_write_hd(p, fr->type, payloadlen);
if (fr->origin_list.len) {
p = nghttp3_cpymem(p, fr->origin_list.base, fr->origin_list.len);
}
return p;
}
size_t nghttp3_frame_write_origin_len(int64_t *ppayloadlen,
const nghttp3_frame_origin *fr) {
size_t payloadlen = fr->origin_list.len;
*ppayloadlen = (int64_t)payloadlen;
return nghttp3_put_varintlen(fr->type) +
nghttp3_put_varintlen((int64_t)payloadlen) + payloadlen;
}

View file

@ -44,6 +44,8 @@
/* PRIORITY_UPDATE: https://datatracker.ietf.org/doc/html/rfc9218 */
#define NGHTTP3_FRAME_PRIORITY_UPDATE 0x0f0700
#define NGHTTP3_FRAME_PRIORITY_UPDATE_PUSH_ID 0x0f0701
/* ORIGIN: https://datatracker.ietf.org/doc/html/rfc9412 */
#define NGHTTP3_FRAME_ORIGIN 0x0c
/* Frame types that are reserved for HTTP/2, and must not be used in
HTTP/3. */
@ -52,17 +54,12 @@
#define NGHTTP3_H2_FRAME_WINDOW_UPDATE 0x08
#define NGHTTP3_H2_FRAME_CONTINUATION 0x9
typedef struct nghttp3_frame_hd {
int64_t type;
int64_t length;
} nghttp3_frame_hd;
typedef struct nghttp3_frame_data {
nghttp3_frame_hd hd;
int64_t type;
} nghttp3_frame_data;
typedef struct nghttp3_frame_headers {
nghttp3_frame_hd hd;
int64_t type;
nghttp3_nv *nva;
size_t nvlen;
} nghttp3_frame_headers;
@ -84,20 +81,20 @@ typedef struct nghttp3_settings_entry {
} nghttp3_settings_entry;
typedef struct nghttp3_frame_settings {
nghttp3_frame_hd hd;
int64_t type;
size_t niv;
nghttp3_settings_entry iv[1];
} nghttp3_frame_settings;
typedef struct nghttp3_frame_goaway {
nghttp3_frame_hd hd;
int64_t type;
int64_t id;
} nghttp3_frame_goaway;
typedef struct nghttp3_frame_priority_update {
nghttp3_frame_hd hd;
/* pri_elem_id is stream ID if hd.type ==
NGHTTP3_FRAME_PRIORITY_UPDATE. It is push ID if hd.type ==
int64_t type;
/* pri_elem_id is stream ID if type ==
NGHTTP3_FRAME_PRIORITY_UPDATE. It is push ID if type ==
NGHTTP3_FRAME_PRIORITY_UPDATE_PUSH_ID. It is undefined
otherwise. */
int64_t pri_elem_id;
@ -114,42 +111,54 @@ typedef struct nghttp3_frame_priority_update {
};
} nghttp3_frame_priority_update;
typedef struct nghttp3_frame_origin {
int64_t type;
/* These fields are only used by server to send ORIGIN frame.
Client never use them. */
nghttp3_vec origin_list;
} nghttp3_frame_origin;
typedef union nghttp3_frame {
nghttp3_frame_hd hd;
int64_t type;
nghttp3_frame_data data;
nghttp3_frame_headers headers;
nghttp3_frame_settings settings;
nghttp3_frame_goaway goaway;
nghttp3_frame_priority_update priority_update;
nghttp3_frame_origin origin;
} nghttp3_frame;
/*
* nghttp3_frame_write_hd writes frame header |hd| to |dest|. This
* function assumes that |dest| has enough space to write |hd|.
* nghttp3_frame_write_hd writes frame header consisting of |type| and
* |payloadlen| to |dest|. This function assumes that |dest| has
* enough space to write the frame header.
*
* This function returns |dest| plus the number of bytes written.
*/
uint8_t *nghttp3_frame_write_hd(uint8_t *dest, const nghttp3_frame_hd *hd);
uint8_t *nghttp3_frame_write_hd(uint8_t *dest, int64_t type,
int64_t payloadlen);
/*
* nghttp3_frame_write_hd_len returns the number of bytes required to
* write |hd|. hd->length must be set.
* write a frame header consisting of |type| and |payloadlen|.
*/
size_t nghttp3_frame_write_hd_len(const nghttp3_frame_hd *hd);
size_t nghttp3_frame_write_hd_len(int64_t type, int64_t payloadlen);
/*
* nghttp3_frame_write_settings writes SETTINGS frame |fr| to |dest|.
* This function assumes that |dest| has enough space to write |fr|.
* |payloadlen| is the length of the frame payload.
*
* This function returns |dest| plus the number of bytes written.
*/
uint8_t *nghttp3_frame_write_settings(uint8_t *dest,
const nghttp3_frame_settings *fr);
const nghttp3_frame_settings *fr,
int64_t payloadlen);
/*
* nghttp3_frame_write_settings_len returns the number of bytes
* required to write |fr|. fr->hd.length is ignored. This function
* stores payload length in |*ppayloadlen|.
* required to write |fr|. This function stores the frame payload
* length in |*ppayloadlen|.
*/
size_t nghttp3_frame_write_settings_len(int64_t *pppayloadlen,
const nghttp3_frame_settings *fr);
@ -157,16 +166,18 @@ size_t nghttp3_frame_write_settings_len(int64_t *pppayloadlen,
/*
* nghttp3_frame_write_goaway writes GOAWAY frame |fr| to |dest|.
* This function assumes that |dest| has enough space to write |fr|.
* |payloadlen| is the length of the frame payload.
*
* This function returns |dest| plus the number of bytes written.
*/
uint8_t *nghttp3_frame_write_goaway(uint8_t *dest,
const nghttp3_frame_goaway *fr);
const nghttp3_frame_goaway *fr,
int64_t payloadlen);
/*
* nghttp3_frame_write_goaway_len returns the number of bytes required
* to write |fr|. fr->hd.length is ignored. This function stores
* payload length in |*ppayloadlen|.
* to write |fr|. This function stores the frame payload length in
* |*ppayloadlen|.
*/
size_t nghttp3_frame_write_goaway_len(int64_t *ppayloadlen,
const nghttp3_frame_goaway *fr);
@ -174,22 +185,40 @@ size_t nghttp3_frame_write_goaway_len(int64_t *ppayloadlen,
/*
* nghttp3_frame_write_priority_update writes PRIORITY_UPDATE frame
* |fr| to |dest|. This function assumes that |dest| has enough space
* to write |fr|.
* to write |fr|. |payloadlen| is the length of the frame payload.
*
* This function returns |dest| plus the number of bytes written;
*/
uint8_t *
nghttp3_frame_write_priority_update(uint8_t *dest,
const nghttp3_frame_priority_update *fr);
uint8_t *nghttp3_frame_write_priority_update(
uint8_t *dest, const nghttp3_frame_priority_update *fr, int64_t payloadlen);
/*
* nghttp3_frame_write_priority_update_len returns the number of bytes
* required to write |fr|. fr->hd.length is ignored. This function
* stores payload length in |*ppayloadlen|.
* required to write |fr|. This function stores the frame payload
* length in |*ppayloadlen|.
*/
size_t nghttp3_frame_write_priority_update_len(
int64_t *ppayloadlen, const nghttp3_frame_priority_update *fr);
/*
* nghttp3_frame_write_origin writes ORIGIN frame |fr| to |dest|.
* This function assumes that |dest| has enough space to write |fr|.
* |payloadlen| is the length of the frame payload.
*
* This function returns |dest| plus the number of bytes written;
*/
uint8_t *nghttp3_frame_write_origin(uint8_t *dest,
const nghttp3_frame_origin *fr,
int64_t payloadlen);
/*
* nghttp3_frame_write_origin_len returns the number of bytes required
* to write |fr|. This function stores the frame payload length in
* |*ppayloadlen|.
*/
size_t nghttp3_frame_write_origin_len(int64_t *ppayloadlen,
const nghttp3_frame_origin *fr);
/*
* nghttp3_nva_copy copies name/value pairs from |nva|, which contains
* |nvlen| pairs, to |*nva_ptr|, which is dynamically allocated so

View file

@ -29,14 +29,16 @@
#include <assert.h>
void nghttp3_gaptr_init(nghttp3_gaptr *gaptr, const nghttp3_mem *mem) {
nghttp3_ksl_init(&gaptr->gap, nghttp3_ksl_range_compar, sizeof(nghttp3_range),
mem);
nghttp3_ksl_init(&gaptr->gap, nghttp3_ksl_range_compar,
nghttp3_ksl_range_search, sizeof(nghttp3_range), mem);
gaptr->mem = mem;
}
static int gaptr_gap_init(nghttp3_gaptr *gaptr) {
nghttp3_range range = {0, UINT64_MAX};
nghttp3_range range = {
.end = UINT64_MAX,
};
return nghttp3_ksl_insert(&gaptr->gap, NULL, &range, NULL);
}
@ -52,7 +54,11 @@ void nghttp3_gaptr_free(nghttp3_gaptr *gaptr) {
int nghttp3_gaptr_push(nghttp3_gaptr *gaptr, uint64_t offset,
uint64_t datalen) {
int rv;
nghttp3_range k, m, l, r, q = {offset, offset + datalen};
nghttp3_range k, m, l, r;
nghttp3_range q = {
.begin = offset,
.end = offset + datalen,
};
nghttp3_ksl_it it;
if (nghttp3_ksl_len(&gaptr->gap) == 0) {
@ -62,8 +68,8 @@ int nghttp3_gaptr_push(nghttp3_gaptr *gaptr, uint64_t offset,
}
}
it = nghttp3_ksl_lower_bound_compar(&gaptr->gap, &q,
nghttp3_ksl_range_exclusive_compar);
it = nghttp3_ksl_lower_bound_search(&gaptr->gap, &q,
nghttp3_ksl_range_exclusive_search);
for (; !nghttp3_ksl_it_end(&it);) {
k = *(nghttp3_range *)nghttp3_ksl_it_key(&it);
@ -112,7 +118,10 @@ uint64_t nghttp3_gaptr_first_gap_offset(nghttp3_gaptr *gaptr) {
nghttp3_range nghttp3_gaptr_get_first_gap_after(nghttp3_gaptr *gaptr,
uint64_t offset) {
nghttp3_range q = {offset, offset + 1};
nghttp3_range q = {
.begin = offset,
.end = offset + 1,
};
nghttp3_ksl_it it;
if (nghttp3_ksl_len(&gaptr->gap) == 0) {
@ -120,8 +129,8 @@ nghttp3_range nghttp3_gaptr_get_first_gap_after(nghttp3_gaptr *gaptr,
return r;
}
it = nghttp3_ksl_lower_bound_compar(&gaptr->gap, &q,
nghttp3_ksl_range_exclusive_compar);
it = nghttp3_ksl_lower_bound_search(&gaptr->gap, &q,
nghttp3_ksl_range_exclusive_search);
assert(!nghttp3_ksl_it_end(&it));
@ -130,7 +139,10 @@ nghttp3_range nghttp3_gaptr_get_first_gap_after(nghttp3_gaptr *gaptr,
int nghttp3_gaptr_is_pushed(nghttp3_gaptr *gaptr, uint64_t offset,
uint64_t datalen) {
nghttp3_range q = {offset, offset + datalen};
nghttp3_range q = {
.begin = offset,
.end = offset + datalen,
};
nghttp3_ksl_it it;
nghttp3_range m;
@ -138,8 +150,11 @@ int nghttp3_gaptr_is_pushed(nghttp3_gaptr *gaptr, uint64_t offset,
return 0;
}
it = nghttp3_ksl_lower_bound_compar(&gaptr->gap, &q,
nghttp3_ksl_range_exclusive_compar);
it = nghttp3_ksl_lower_bound_search(&gaptr->gap, &q,
nghttp3_ksl_range_exclusive_search);
assert(!nghttp3_ksl_it_end(&it));
m = nghttp3_range_intersect(&q, (nghttp3_range *)nghttp3_ksl_it_key(&it));
return nghttp3_range_len(&m) == 0;

View file

@ -69,11 +69,11 @@ static int64_t parse_uint(const uint8_t *s, size_t len) {
}
for (i = 0; i < len; ++i) {
if ('0' <= s[i] && s[i] <= '9') {
if (n > INT64_MAX / 10) {
if (n > (int64_t)NGHTTP3_MAX_VARINT / 10) {
return -1;
}
n *= 10;
if (n > INT64_MAX - (s[i] - '0')) {
if (n > (int64_t)NGHTTP3_MAX_VARINT - (s[i] - '0')) {
return -1;
}
n += s[i] - '0';
@ -124,17 +124,17 @@ static int is_ws(uint8_t c) {
int nghttp3_http_parse_priority(nghttp3_pri *dest, const uint8_t *value,
size_t valuelen) {
nghttp3_pri 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;
}
@ -147,7 +147,7 @@ int nghttp3_http_parse_priority(nghttp3_pri *dest, const uint8_t *value,
switch (key.base[0]) {
case 'i':
if (val.type != SF_TYPE_BOOLEAN) {
if (val.type != SFPARSE_TYPE_BOOLEAN) {
return NGHTTP3_ERR_INVALID_ARGUMENT;
}
@ -155,7 +155,8 @@ int nghttp3_http_parse_priority(nghttp3_pri *dest, const uint8_t *value,
break;
case 'u':
if (val.type != SF_TYPE_INTEGER || val.integer < NGHTTP3_URGENCY_HIGH ||
if (val.type != SFPARSE_TYPE_INTEGER ||
val.integer < NGHTTP3_URGENCY_HIGH ||
NGHTTP3_URGENCY_LOW < val.integer) {
return NGHTTP3_ERR_INVALID_ARGUMENT;
}
@ -197,7 +198,7 @@ static char VALID_AUTHORITY_CHARS[] = {
1 /* 4 */, 1 /* 5 */, 1 /* 6 */, 1 /* 7 */,
1 /* 8 */, 1 /* 9 */, 1 /* : */, 1 /* ; */,
0 /* < */, 1 /* = */, 0 /* > */, 0 /* ? */,
1 /* @ */, 1 /* A */, 1 /* B */, 1 /* C */,
0 /* @ */, 1 /* A */, 1 /* B */, 1 /* C */,
1 /* D */, 1 /* E */, 1 /* F */, 1 /* G */,
1 /* H */, 1 /* I */, 1 /* J */, 1 /* K */,
1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */,
@ -548,6 +549,9 @@ static int http_request_on_header(nghttp3_http_state *http,
break;
case NGHTTP3_QPACK_TOKEN_PRIORITY:
if (!nghttp3_check_header_value(nv->value->base, nv->value->len)) {
http->flags &= ~NGHTTP3_HTTP_FLAG_PRIORITY;
http->flags |= NGHTTP3_HTTP_FLAG_BAD_PRIORITY;
return NGHTTP3_ERR_REMOVE_HTTP_HEADER;
}
@ -991,7 +995,11 @@ int nghttp3_check_header_value(const uint8_t *value, size_t len) {
case 0:
return 1;
case 1:
return !is_ws(*value);
if (is_ws(*value)) {
return 0;
}
break;
default:
if (is_ws(*value) || is_ws(*(value + len - 1))) {
return 0;

View file

@ -34,9 +34,9 @@
#include "nghttp3_mem.h"
#include "nghttp3_range.h"
static nghttp3_ksl_blk null_blk = {{{NULL, NULL, 0, 0, {0}}}};
static nghttp3_ksl_blk null_blk;
nghttp3_objalloc_def(ksl_blk, nghttp3_ksl_blk, oplent);
nghttp3_objalloc_def(ksl_blk, nghttp3_ksl_blk, oplent)
static size_t ksl_nodelen(size_t keylen) {
assert(keylen >= sizeof(uint64_t));
@ -59,7 +59,8 @@ static void ksl_node_set_key(nghttp3_ksl *ksl, nghttp3_ksl_node *node,
}
void nghttp3_ksl_init(nghttp3_ksl *ksl, nghttp3_ksl_compar compar,
size_t keylen, const nghttp3_mem *mem) {
nghttp3_ksl_search search, size_t keylen,
const nghttp3_mem *mem) {
size_t nodelen = ksl_nodelen(keylen);
nghttp3_objalloc_init(&ksl->blkalloc,
@ -68,6 +69,7 @@ void nghttp3_ksl_init(nghttp3_ksl *ksl, nghttp3_ksl_compar compar,
ksl->head = NULL;
ksl->front = ksl->back = NULL;
ksl->compar = compar;
ksl->search = search;
ksl->n = 0;
ksl->keylen = keylen;
ksl->nodelen = nodelen;
@ -274,20 +276,6 @@ static void ksl_insert_node(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, size_t i,
++blk->n;
}
static size_t ksl_search(const nghttp3_ksl *ksl, nghttp3_ksl_blk *blk,
const nghttp3_ksl_key *key,
nghttp3_ksl_compar compar) {
size_t i;
nghttp3_ksl_node *node;
for (i = 0, node = (nghttp3_ksl_node *)(void *)blk->nodes;
i < blk->n && compar((nghttp3_ksl_key *)node->key, key);
++i, node = (nghttp3_ksl_node *)(void *)((uint8_t *)node + ksl->nodelen))
;
return i;
}
int nghttp3_ksl_insert(nghttp3_ksl *ksl, nghttp3_ksl_it *it,
const nghttp3_ksl_key *key, void *data) {
nghttp3_ksl_blk *blk;
@ -312,7 +300,7 @@ int nghttp3_ksl_insert(nghttp3_ksl *ksl, nghttp3_ksl_it *it,
blk = ksl->head;
for (;;) {
i = ksl_search(ksl, blk, key, ksl->compar);
i = ksl->search(ksl, blk, key);
if (blk->leaf) {
if (i < blk->n &&
@ -571,7 +559,7 @@ int nghttp3_ksl_remove(nghttp3_ksl *ksl, nghttp3_ksl_it *it,
}
for (;;) {
i = ksl_search(ksl, blk, key, ksl->compar);
i = ksl->search(ksl, blk, key);
if (i == blk->n) {
if (it) {
@ -642,12 +630,12 @@ int nghttp3_ksl_remove(nghttp3_ksl *ksl, nghttp3_ksl_it *it,
nghttp3_ksl_it nghttp3_ksl_lower_bound(const nghttp3_ksl *ksl,
const nghttp3_ksl_key *key) {
return nghttp3_ksl_lower_bound_compar(ksl, key, ksl->compar);
return nghttp3_ksl_lower_bound_search(ksl, key, ksl->search);
}
nghttp3_ksl_it nghttp3_ksl_lower_bound_compar(const nghttp3_ksl *ksl,
nghttp3_ksl_it nghttp3_ksl_lower_bound_search(const nghttp3_ksl *ksl,
const nghttp3_ksl_key *key,
nghttp3_ksl_compar compar) {
nghttp3_ksl_search search) {
nghttp3_ksl_blk *blk = ksl->head;
nghttp3_ksl_it it;
size_t i;
@ -658,7 +646,7 @@ nghttp3_ksl_it nghttp3_ksl_lower_bound_compar(const nghttp3_ksl *ksl,
}
for (;;) {
i = ksl_search(ksl, blk, key, compar);
i = search(ksl, blk, key);
if (blk->leaf) {
if (i == blk->n && blk->next) {
@ -702,7 +690,7 @@ void nghttp3_ksl_update_key(nghttp3_ksl *ksl, const nghttp3_ksl_key *old_key,
assert(ksl->head);
for (;;) {
i = ksl_search(ksl, blk, old_key, ksl->compar);
i = ksl->search(ksl, blk, old_key);
assert(i < blk->n);
node = nghttp3_ksl_nth_node(ksl, blk, i);
@ -825,9 +813,50 @@ int nghttp3_ksl_range_compar(const nghttp3_ksl_key *lhs,
return a->begin < b->begin;
}
nghttp3_ksl_search_def(range, nghttp3_ksl_range_compar)
size_t nghttp3_ksl_range_search(const nghttp3_ksl *ksl, nghttp3_ksl_blk *blk,
const nghttp3_ksl_key *key) {
return ksl_range_search(ksl, blk, key);
}
int nghttp3_ksl_range_exclusive_compar(const nghttp3_ksl_key *lhs,
const nghttp3_ksl_key *rhs) {
const nghttp3_range *a = lhs, *b = rhs;
return a->begin < b->begin && !(nghttp3_max_uint64(a->begin, b->begin) <
nghttp3_min_uint64(a->end, b->end));
}
nghttp3_ksl_search_def(range_exclusive, nghttp3_ksl_range_exclusive_compar)
size_t nghttp3_ksl_range_exclusive_search(const nghttp3_ksl *ksl,
nghttp3_ksl_blk *blk,
const nghttp3_ksl_key *key) {
return ksl_range_exclusive_search(ksl, blk, key);
}
int nghttp3_ksl_uint64_less(const nghttp3_ksl_key *lhs,
const nghttp3_ksl_key *rhs) {
return *(uint64_t *)lhs < *(uint64_t *)rhs;
}
nghttp3_ksl_search_def(uint64_less, nghttp3_ksl_uint64_less)
size_t nghttp3_ksl_uint64_less_search(const nghttp3_ksl *ksl,
nghttp3_ksl_blk *blk,
const nghttp3_ksl_key *key) {
return ksl_uint64_less_search(ksl, blk, key);
}
int nghttp3_ksl_int64_greater(const nghttp3_ksl_key *lhs,
const nghttp3_ksl_key *rhs) {
return *(int64_t *)lhs > *(int64_t *)rhs;
}
nghttp3_ksl_search_def(int64_greater, nghttp3_ksl_int64_greater)
size_t nghttp3_ksl_int64_greater_search(const nghttp3_ksl *ksl,
nghttp3_ksl_blk *blk,
const nghttp3_ksl_key *key) {
return ksl_int64_greater_search(ksl, blk, key);
}

View file

@ -104,7 +104,7 @@ struct nghttp3_ksl_blk {
};
};
nghttp3_objalloc_decl(ksl_blk, nghttp3_ksl_blk, oplent);
nghttp3_objalloc_decl(ksl_blk, nghttp3_ksl_blk, oplent)
/*
* nghttp3_ksl_compar is a function type which returns nonzero if key
@ -115,6 +115,35 @@ typedef int (*nghttp3_ksl_compar)(const nghttp3_ksl_key *lhs,
typedef struct nghttp3_ksl nghttp3_ksl;
/*
* nghttp3_ksl_search is a function to search for the first element in
* |blk|->nodes which is not ordered before |key|. It returns the
* index of such element. It returns |blk|->n if there is no such
* element.
*/
typedef size_t (*nghttp3_ksl_search)(const nghttp3_ksl *ksl,
nghttp3_ksl_blk *blk,
const nghttp3_ksl_key *key);
/*
* nghttp3_ksl_search_def is a macro to implement nghttp3_ksl_search
* with COMPAR which is supposed to be nghttp3_ksl_compar.
*/
#define nghttp3_ksl_search_def(NAME, COMPAR) \
static size_t ksl_##NAME##_search(const nghttp3_ksl *ksl, \
nghttp3_ksl_blk *blk, \
const nghttp3_ksl_key *key) { \
size_t i; \
nghttp3_ksl_node *node; \
\
for (i = 0, node = (nghttp3_ksl_node *)(void *)blk->nodes; \
i < blk->n && COMPAR((nghttp3_ksl_key *)node->key, key); ++i, \
node = (nghttp3_ksl_node *)(void *)((uint8_t *)node + ksl->nodelen)) \
; \
\
return i; \
}
typedef struct nghttp3_ksl_it nghttp3_ksl_it;
/*
@ -138,6 +167,7 @@ struct nghttp3_ksl {
/* back points to the last leaf block. */
nghttp3_ksl_blk *back;
nghttp3_ksl_compar compar;
nghttp3_ksl_search search;
/* n is the number of elements stored. */
size_t n;
/* keylen is the size of key */
@ -149,11 +179,13 @@ struct nghttp3_ksl {
/*
* nghttp3_ksl_init initializes |ksl|. |compar| specifies compare
* function. |keylen| is the length of key and must be at least
* function. |search| is a search function which must use |compar|.
* |keylen| is the length of key and must be at least
* sizeof(uint64_t).
*/
void nghttp3_ksl_init(nghttp3_ksl *ksl, nghttp3_ksl_compar compar,
size_t keylen, const nghttp3_mem *mem);
nghttp3_ksl_search search, size_t keylen,
const nghttp3_mem *mem);
/*
* nghttp3_ksl_free frees resources allocated for |ksl|. If |ksl| is
@ -218,12 +250,12 @@ nghttp3_ksl_it nghttp3_ksl_lower_bound(const nghttp3_ksl *ksl,
const nghttp3_ksl_key *key);
/*
* nghttp3_ksl_lower_bound_compar works like nghttp3_ksl_lower_bound,
* but it takes custom function |compar| to do lower bound search.
* nghttp3_ksl_lower_bound_search works like nghttp3_ksl_lower_bound,
* but it takes custom function |search| to do lower bound search.
*/
nghttp3_ksl_it nghttp3_ksl_lower_bound_compar(const nghttp3_ksl *ksl,
nghttp3_ksl_it nghttp3_ksl_lower_bound_search(const nghttp3_ksl *ksl,
const nghttp3_ksl_key *key,
nghttp3_ksl_compar compar);
nghttp3_ksl_search search);
/*
* nghttp3_ksl_update_key replaces the key of nodes which has
@ -263,8 +295,11 @@ void nghttp3_ksl_clear(nghttp3_ksl *ksl);
/*
* nghttp3_ksl_nth_node returns the |n|th node under |blk|.
*/
#define nghttp3_ksl_nth_node(KSL, BLK, N) \
((nghttp3_ksl_node *)(void *)((BLK)->nodes + (KSL)->nodelen * (N)))
static inline nghttp3_ksl_node *nghttp3_ksl_nth_node(const nghttp3_ksl *ksl,
const nghttp3_ksl_blk *blk,
size_t n) {
return (nghttp3_ksl_node *)(void *)(blk->nodes + ksl->nodelen * n);
}
#ifndef WIN32
/*
@ -286,18 +321,21 @@ void nghttp3_ksl_it_init(nghttp3_ksl_it *it, const nghttp3_ksl *ksl,
* |it| points to. It is undefined to call this function when
* nghttp3_ksl_it_end(it) returns nonzero.
*/
#define nghttp3_ksl_it_get(IT) \
nghttp3_ksl_nth_node((IT)->ksl, (IT)->blk, (IT)->i)->data
static inline void *nghttp3_ksl_it_get(const nghttp3_ksl_it *it) {
return nghttp3_ksl_nth_node(it->ksl, it->blk, it->i)->data;
}
/*
* nghttp3_ksl_it_next advances the iterator by one. It is undefined
* if this function is called when nghttp3_ksl_it_end(it) returns
* nonzero.
*/
#define nghttp3_ksl_it_next(IT) \
(++(IT)->i == (IT)->blk->n && (IT)->blk->next \
? ((IT)->blk = (IT)->blk->next, (IT)->i = 0) \
: 0)
static inline void nghttp3_ksl_it_next(nghttp3_ksl_it *it) {
if (++it->i == it->blk->n && it->blk->next) {
it->blk = it->blk->next;
it->i = 0;
}
}
/*
* nghttp3_ksl_it_prev moves backward the iterator by one. It is
@ -310,8 +348,9 @@ void nghttp3_ksl_it_prev(nghttp3_ksl_it *it);
* nghttp3_ksl_it_end returns nonzero if |it| points to the one beyond
* the last node.
*/
#define nghttp3_ksl_it_end(IT) \
((IT)->blk->n == (IT)->i && (IT)->blk->next == NULL)
static inline int nghttp3_ksl_it_end(const nghttp3_ksl_it *it) {
return it->blk->n == it->i && it->blk->next == NULL;
}
/*
* nghttp3_ksl_it_begin returns nonzero if |it| points to the first
@ -325,27 +364,75 @@ int nghttp3_ksl_it_begin(const nghttp3_ksl_it *it);
* It is undefined to call this function when nghttp3_ksl_it_end(it)
* returns nonzero.
*/
#define nghttp3_ksl_it_key(IT) \
((nghttp3_ksl_key *)nghttp3_ksl_nth_node((IT)->ksl, (IT)->blk, (IT)->i)->key)
static inline nghttp3_ksl_key *nghttp3_ksl_it_key(const nghttp3_ksl_it *it) {
return (nghttp3_ksl_key *)nghttp3_ksl_nth_node(it->ksl, it->blk, it->i)->key;
}
/*
* nghttp3_ksl_range_compar is an implementation of
* nghttp3_ksl_compar. lhs->ptr and rhs->ptr must point to
* nghttp3_range object and the function returns nonzero if (const
* nghttp3_range *)(lhs->ptr)->begin < (const nghttp3_range
* *)(rhs->ptr)->begin.
* nghttp3_ksl_compar. |lhs| and |rhs| must point to nghttp3_range
* object, and the function returns nonzero if ((const nghttp3_range
* *)lhs)->begin < ((const nghttp3_range *)rhs)->begin.
*/
int nghttp3_ksl_range_compar(const nghttp3_ksl_key *lhs,
const nghttp3_ksl_key *rhs);
/*
* nghttp3_ksl_range_search is an implementation of nghttp3_ksl_search
* that uses nghttp3_ksl_range_compar.
*/
size_t nghttp3_ksl_range_search(const nghttp3_ksl *ksl, nghttp3_ksl_blk *blk,
const nghttp3_ksl_key *key);
/*
* nghttp3_ksl_range_exclusive_compar is an implementation of
* nghttp3_ksl_compar. lhs->ptr and rhs->ptr must point to
* nghttp3_range object and the function returns nonzero if (const
* nghttp3_range *)(lhs->ptr)->begin < (const nghttp3_range
* *)(rhs->ptr)->begin and the 2 ranges do not intersect.
* nghttp3_ksl_compar. |lhs| and |rhs| must point to nghttp3_range
* object, and the function returns nonzero if ((const nghttp3_range
* *)lhs)->begin < ((const nghttp3_range *)rhs)->begin, and the 2
* ranges do not intersect.
*/
int nghttp3_ksl_range_exclusive_compar(const nghttp3_ksl_key *lhs,
const nghttp3_ksl_key *rhs);
/*
* nghttp3_ksl_range_exclusive_search is an implementation of
* nghttp3_ksl_search that uses nghttp3_ksl_range_exclusive_compar.
*/
size_t nghttp3_ksl_range_exclusive_search(const nghttp3_ksl *ksl,
nghttp3_ksl_blk *blk,
const nghttp3_ksl_key *key);
/*
* nghttp3_ksl_uint64_less is an implementation of nghttp3_ksl_compar.
* |lhs| and |rhs| must point to uint64_t objects, and the function
* returns nonzero if *(uint64_t *)|lhs| < *(uint64_t *)|rhs|.
*/
int nghttp3_ksl_uint64_less(const nghttp3_ksl_key *lhs,
const nghttp3_ksl_key *rhs);
/*
* nghttp3_ksl_uint64_less_search is an implementation of
* nghttp3_ksl_search that uses nghttp3_ksl_uint64_less.
*/
size_t nghttp3_ksl_uint64_less_search(const nghttp3_ksl *ksl,
nghttp3_ksl_blk *blk,
const nghttp3_ksl_key *key);
/*
* nghttp3_ksl_int64_greater is an implementation of
* nghttp3_ksl_compar. |lhs| and |rhs| must point to int64_t objects,
* and the function returns nonzero if *(int64_t *)|lhs| > *(int64_t
* *)|rhs|.
*/
int nghttp3_ksl_int64_greater(const nghttp3_ksl_key *lhs,
const nghttp3_ksl_key *rhs);
/*
* nghttp3_ksl_int64_greater_search is an implementation of
* nghttp3_ksl_search that uses nghttp3_ksl_int64_greater.
*/
size_t nghttp3_ksl_int64_greater_search(const nghttp3_ksl *ksl,
nghttp3_ksl_blk *blk,
const nghttp3_ksl_key *key);
#endif /* !defined(NGHTTP3_KSL_H) */

View file

@ -48,27 +48,27 @@
#define nghttp3_max_def(SUFFIX, T) \
static inline T nghttp3_max_##SUFFIX(T a, T b) { return a < b ? b : a; }
nghttp3_max_def(int8, int8_t);
nghttp3_max_def(int16, int16_t);
nghttp3_max_def(int32, int32_t);
nghttp3_max_def(int64, int64_t);
nghttp3_max_def(uint8, uint8_t);
nghttp3_max_def(uint16, uint16_t);
nghttp3_max_def(uint32, uint32_t);
nghttp3_max_def(uint64, uint64_t);
nghttp3_max_def(size, size_t);
nghttp3_max_def(int8, int8_t)
nghttp3_max_def(int16, int16_t)
nghttp3_max_def(int32, int32_t)
nghttp3_max_def(int64, int64_t)
nghttp3_max_def(uint8, uint8_t)
nghttp3_max_def(uint16, uint16_t)
nghttp3_max_def(uint32, uint32_t)
nghttp3_max_def(uint64, uint64_t)
nghttp3_max_def(size, size_t)
#define nghttp3_min_def(SUFFIX, T) \
static inline T nghttp3_min_##SUFFIX(T a, T b) { return a < b ? a : b; }
nghttp3_min_def(int8, int8_t);
nghttp3_min_def(int16, int16_t);
nghttp3_min_def(int32, int32_t);
nghttp3_min_def(int64, int64_t);
nghttp3_min_def(uint8, uint8_t);
nghttp3_min_def(uint16, uint16_t);
nghttp3_min_def(uint32, uint32_t);
nghttp3_min_def(uint64, uint64_t);
nghttp3_min_def(size, size_t);
nghttp3_min_def(int8, int8_t)
nghttp3_min_def(int16, int16_t)
nghttp3_min_def(int32, int32_t)
nghttp3_min_def(int64, int64_t)
nghttp3_min_def(uint8, uint8_t)
nghttp3_min_def(uint16, uint16_t)
nghttp3_min_def(uint32, uint32_t)
nghttp3_min_def(uint64, uint64_t)
nghttp3_min_def(size, size_t)
#endif /* !defined(NGHTTP3_MACRO_H) */

View file

@ -32,12 +32,13 @@
#include "nghttp3_conv.h"
#define NGHTTP3_INITIAL_TABLE_LENBITS 4
#define NGHTTP3_INITIAL_HASHBITS 4
void nghttp3_map_init(nghttp3_map *map, const nghttp3_mem *mem) {
void nghttp3_map_init(nghttp3_map *map, uint64_t seed, const nghttp3_mem *mem) {
map->mem = mem;
map->hashbits = 0;
map->table = NULL;
map->seed = seed;
map->size = 0;
}
@ -78,8 +79,14 @@ int nghttp3_map_each(const nghttp3_map *map, int (*func)(void *data, void *ptr),
return 0;
}
static size_t hash(nghttp3_map_key_type key, size_t bits) {
return (size_t)((key * 11400714819323198485llu) >> (64 - bits));
static size_t map_hash(const nghttp3_map *map, nghttp3_map_key_type key) {
/* hasher from
https://github.com/rust-lang/rustc-hash/blob/dc5c33f1283de2da64d8d7a06401d91aded03ad4/src/lib.rs
We do not perform finalization here because we use top bits
anyway. */
key += map->seed;
key *= 0xf1357aea2e62a9c5ull;
return (size_t)((key * 11400714819323198485llu) >> (64 - map->hashbits));
}
static void map_bucket_swap(nghttp3_map_bucket *a, nghttp3_map_bucket *b) {
@ -110,24 +117,28 @@ void nghttp3_map_print_distance(const nghttp3_map *map) {
continue;
}
idx = hash(bkt->key, map->hashbits);
idx = map_hash(map, bkt->key);
fprintf(stderr, "@%zu hash=%zu key=%" PRIu64 " base=%zu distance=%u\n", i,
hash(bkt->key, map->hashbits), bkt->key, idx, bkt->psl);
map_hash(map, bkt->key), bkt->key, idx, bkt->psl);
}
}
#endif /* !defined(WIN32) */
static int insert(nghttp3_map_bucket *table, size_t hashbits,
nghttp3_map_key_type key, void *data) {
size_t idx = hash(key, hashbits);
nghttp3_map_bucket b = {0, key, data}, *bkt;
size_t mask = (1u << hashbits) - 1;
static int map_insert(nghttp3_map *map, nghttp3_map_key_type key, void *data) {
size_t idx = map_hash(map, key);
nghttp3_map_bucket b = {
.key = key,
.data = data,
};
nghttp3_map_bucket *bkt;
size_t mask = (1u << map->hashbits) - 1;
for (;;) {
bkt = &table[idx];
bkt = &map->table[idx];
if (bkt->data == NULL) {
*bkt = b;
++map->size;
return 0;
}
@ -148,15 +159,19 @@ static int insert(nghttp3_map_bucket *table, size_t hashbits,
static int map_resize(nghttp3_map *map, size_t new_hashbits) {
size_t i;
nghttp3_map_bucket *new_table;
nghttp3_map_bucket *bkt;
size_t tablelen;
int rv;
nghttp3_map new_map = {
.table = nghttp3_mem_calloc(map->mem, 1u << new_hashbits,
sizeof(nghttp3_map_bucket)),
.mem = map->mem,
.seed = map->seed,
.hashbits = new_hashbits,
};
(void)rv;
new_table = nghttp3_mem_calloc(map->mem, 1u << new_hashbits,
sizeof(nghttp3_map_bucket));
if (new_table == NULL) {
if (new_map.table == NULL) {
return NGHTTP3_ERR_NOMEM;
}
@ -169,15 +184,15 @@ static int map_resize(nghttp3_map *map, size_t new_hashbits) {
continue;
}
rv = insert(new_table, new_hashbits, bkt->key, bkt->data);
rv = map_insert(&new_map, bkt->key, bkt->data);
assert(0 == rv);
}
}
nghttp3_mem_free(map->mem, map->table);
map->table = new_map.table;
map->hashbits = new_hashbits;
map->table = new_table;
return 0;
}
@ -187,30 +202,28 @@ int nghttp3_map_insert(nghttp3_map *map, nghttp3_map_key_type key, void *data) {
assert(data);
/* Load factor is 0.75 */
/* Load factor is 7/8 */
/* Under the very initial condition, that is map->size == 0 and
map->hashbits == 0, 4 > 3 still holds nicely. */
if ((map->size + 1) * 4 > (1u << map->hashbits) * 3) {
map->hashbits == 0, 8 > 7 still holds nicely. */
if ((map->size + 1) * 8 > (1u << map->hashbits) * 7) {
if (map->hashbits) {
rv = map_resize(map, map->hashbits + 1);
if (rv != 0) {
return rv;
}
} else {
rv = map_resize(map, NGHTTP3_INITIAL_TABLE_LENBITS);
rv = map_resize(map, NGHTTP3_INITIAL_HASHBITS);
if (rv != 0) {
return rv;
}
}
}
rv = insert(map->table, map->hashbits, key, data);
rv = map_insert(map, key, data);
if (rv != 0) {
return rv;
}
++map->size;
return 0;
}
@ -224,7 +237,7 @@ void *nghttp3_map_find(const nghttp3_map *map, nghttp3_map_key_type key) {
return NULL;
}
idx = hash(key, map->hashbits);
idx = map_hash(map, key);
mask = (1u << map->hashbits) - 1;
for (;;) {
@ -253,7 +266,7 @@ int nghttp3_map_remove(nghttp3_map *map, nghttp3_map_key_type key) {
return NGHTTP3_ERR_INVALID_ARGUMENT;
}
idx = hash(key, map->hashbits);
idx = map_hash(map, key);
mask = (1u << map->hashbits) - 1;
for (;;) {

View file

@ -48,6 +48,7 @@ typedef struct nghttp3_map_bucket {
typedef struct nghttp3_map {
nghttp3_map_bucket *table;
const nghttp3_mem *mem;
uint64_t seed;
size_t size;
size_t hashbits;
} nghttp3_map;
@ -55,7 +56,7 @@ typedef struct nghttp3_map {
/*
* nghttp3_map_init initializes the map |map|.
*/
void nghttp3_map_init(nghttp3_map *map, const nghttp3_mem *mem);
void nghttp3_map_init(nghttp3_map *map, uint64_t seed, const nghttp3_mem *mem);
/*
* nghttp3_map_free deallocates any resources allocated for |map|.

View file

@ -164,20 +164,4 @@ int nghttp3_pq_empty(const nghttp3_pq *pq) { return pq->length == 0; }
size_t nghttp3_pq_size(const nghttp3_pq *pq) { return pq->length; }
int nghttp3_pq_each(const nghttp3_pq *pq, nghttp3_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;
}
void nghttp3_pq_clear(nghttp3_pq *pq) { pq->length = 0; }

View file

@ -112,17 +112,6 @@ int nghttp3_pq_empty(const nghttp3_pq *pq);
*/
size_t nghttp3_pq_size(const nghttp3_pq *pq);
typedef int (*nghttp3_pq_item_cb)(nghttp3_pq_entry *item, void *arg);
/*
* nghttp3_pq_each applies |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 nghttp3_pq_each(const nghttp3_pq *pq, nghttp3_pq_item_cb fun, void *arg);
/*
* nghttp3_pq_remove removes |item| from |pq|. |pq| must contain
* |item| otherwise the behavior is undefined.

View file

@ -41,7 +41,12 @@
#define NGHTTP3_QPACK_MAX_QPACK_STREAMS 2000
/* Make scalar initialization form of nghttp3_qpack_static_entry */
#define MAKE_STATIC_ENT(I, T, H) {I, T, H}
#define MAKE_STATIC_ENT(I, T, H) \
{ \
.absidx = I, \
.token = T, \
.hash = H, \
}
/* Generated by mkstatichdtbl.py */
static nghttp3_qpack_static_entry token_stable[] = {
@ -166,9 +171,19 @@ static nghttp3_qpack_static_entry token_stable[] = {
/* Make scalar initialization form of nghttp3_qpack_static_entry */
#define MAKE_STATIC_HD(N, V, T) \
{ \
{NULL, (uint8_t *)(N), sizeof((N)) - 1, -1}, \
{NULL, (uint8_t *)(V), sizeof((V)) - 1, -1}, \
T, \
.name = \
{ \
.base = (uint8_t *)(N), \
.len = sizeof((N)) - 1, \
.ref = -1, \
}, \
.value = \
{ \
.base = (uint8_t *)(V), \
.len = sizeof((V)) - 1, \
.ref = -1, \
}, \
.token = T, \
}
static nghttp3_qpack_static_header stable[] = {
@ -828,29 +843,12 @@ static void encoder_qpack_map_find(nghttp3_qpack_encoder *encoder,
* ctx->max_dtable_capacity and it is initialized to 0.
* |hard_max_dtable_capacity| is the upper bound of
* ctx->max_dtable_capacity.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* NGHTTP3_ERR_NOMEM
* Out of memory.
*/
static int qpack_context_init(nghttp3_qpack_context *ctx,
size_t hard_max_dtable_capacity,
size_t max_blocked_streams,
const nghttp3_mem *mem) {
int rv;
size_t len = 4096 / NGHTTP3_QPACK_ENTRY_OVERHEAD;
size_t len2;
for (len2 = 1; len2 < len; len2 <<= 1)
;
rv = nghttp3_ringbuf_init(&ctx->dtable, len2, sizeof(nghttp3_qpack_entry *),
mem);
if (rv != 0) {
return rv;
}
static void qpack_context_init(nghttp3_qpack_context *ctx,
size_t hard_max_dtable_capacity,
size_t max_blocked_streams,
const nghttp3_mem *mem) {
nghttp3_ringbuf_init(&ctx->dtable, 0, sizeof(nghttp3_qpack_entry *), mem);
ctx->mem = mem;
ctx->dtable_size = 0;
@ -860,8 +858,6 @@ static int qpack_context_init(nghttp3_qpack_context *ctx,
ctx->max_blocked_streams = max_blocked_streams;
ctx->next_absidx = 0;
ctx->bad = 0;
return 0;
}
static void qpack_context_free(nghttp3_qpack_context *ctx) {
@ -898,19 +894,17 @@ static int max_cnt_greater(const nghttp3_ksl_key *lhs,
return a->max_cnt > b->max_cnt || (a->max_cnt == b->max_cnt && a->id < b->id);
}
int nghttp3_qpack_encoder_init(nghttp3_qpack_encoder *encoder,
size_t hard_max_dtable_capacity,
const nghttp3_mem *mem) {
int rv;
nghttp3_ksl_search_def(max_cnt_greater, max_cnt_greater)
rv = qpack_context_init(&encoder->ctx, hard_max_dtable_capacity, 0, mem);
if (rv != 0) {
return rv;
}
void nghttp3_qpack_encoder_init(nghttp3_qpack_encoder *encoder,
size_t hard_max_dtable_capacity, uint64_t seed,
const nghttp3_mem *mem) {
qpack_context_init(&encoder->ctx, hard_max_dtable_capacity, 0, mem);
nghttp3_map_init(&encoder->streams, mem);
nghttp3_map_init(&encoder->streams, seed, mem);
nghttp3_ksl_init(&encoder->blocked_streams, max_cnt_greater,
ksl_max_cnt_greater_search,
sizeof(nghttp3_blocked_streams_key), mem);
qpack_map_init(&encoder->dtable_map);
@ -925,8 +919,6 @@ int nghttp3_qpack_encoder_init(nghttp3_qpack_encoder *encoder,
encoder->flags = NGHTTP3_QPACK_ENCODER_FLAG_NONE;
nghttp3_qpack_read_state_reset(&encoder->rstate);
return 0;
}
static int map_stream_free(void *data, void *ptr) {
@ -1149,6 +1141,9 @@ int nghttp3_qpack_encoder_encode(nghttp3_qpack_encoder *encoder,
int blocked_stream;
nghttp3_qpack_stream *stream;
assert(stream_id >= 0);
assert(stream_id <= (int64_t)NGHTTP3_MAX_VARINT);
if (encoder->ctx.bad) {
return NGHTTP3_ERR_QPACK_FATAL;
}
@ -1444,7 +1439,14 @@ int nghttp3_qpack_encoder_encode_nv(nghttp3_qpack_encoder *encoder,
uint32_t hash = 0;
int32_t token;
nghttp3_qpack_indexing_mode indexing_mode;
nghttp3_qpack_lookup_result sres = {-1, 0, -1}, dres = {-1, 0, -1};
nghttp3_qpack_lookup_result sres = {
.index = -1,
.pb_index = -1,
};
nghttp3_qpack_lookup_result dres = {
.index = -1,
.pb_index = -1,
};
nghttp3_qpack_entry *new_ent = NULL;
int static_entry;
int just_index = 0;
@ -1608,8 +1610,10 @@ int nghttp3_qpack_encoder_encode_nv(nghttp3_qpack_encoder *encoder,
nghttp3_qpack_lookup_result
nghttp3_qpack_lookup_stable(const nghttp3_nv *nv, int32_t token,
nghttp3_qpack_indexing_mode indexing_mode) {
nghttp3_qpack_lookup_result res = {(nghttp3_ssize)token_stable[token].absidx,
0, -1};
nghttp3_qpack_lookup_result res = {
.index = (nghttp3_ssize)token_stable[token].absidx,
.pb_index = -1,
};
nghttp3_qpack_static_entry *ent;
nghttp3_qpack_static_header *hdr;
size_t i;
@ -1639,7 +1643,10 @@ nghttp3_qpack_lookup_result nghttp3_qpack_encoder_lookup_dtable(
nghttp3_qpack_encoder *encoder, const nghttp3_nv *nv, int32_t token,
uint32_t hash, nghttp3_qpack_indexing_mode indexing_mode, uint64_t krcnt,
int allow_blocking) {
nghttp3_qpack_lookup_result res = {-1, 0, -1};
nghttp3_qpack_lookup_result res = {
.index = -1,
.pb_index = -1,
};
int exact_match = 0;
nghttp3_qpack_entry *match, *pb_match;
@ -1694,7 +1701,6 @@ static int ref_max_cnt_greater(const nghttp3_pq_entry *lhsx,
int nghttp3_qpack_stream_new(nghttp3_qpack_stream **pstream, int64_t stream_id,
const nghttp3_mem *mem) {
int rv;
nghttp3_qpack_stream *stream;
stream = nghttp3_mem_malloc(mem, sizeof(nghttp3_qpack_stream));
@ -1702,12 +1708,8 @@ int nghttp3_qpack_stream_new(nghttp3_qpack_stream **pstream, int64_t stream_id,
return NGHTTP3_ERR_NOMEM;
}
rv = nghttp3_ringbuf_init(&stream->refs, 4,
sizeof(nghttp3_qpack_header_block_ref *), mem);
if (rv != 0) {
nghttp3_mem_free(mem, stream);
return rv;
}
nghttp3_ringbuf_init(&stream->refs, 0,
sizeof(nghttp3_qpack_header_block_ref *), mem);
nghttp3_pq_init(&stream->max_cnts, ref_max_cnt_greater, mem);
@ -1759,17 +1761,23 @@ int nghttp3_qpack_stream_add_ref(nghttp3_qpack_stream *stream,
int rv;
if (nghttp3_ringbuf_full(&stream->refs)) {
rv = nghttp3_ringbuf_reserve(&stream->refs,
nghttp3_ringbuf_len(&stream->refs) * 2);
rv = nghttp3_ringbuf_reserve(
&stream->refs,
nghttp3_max_size(4, nghttp3_ringbuf_len(&stream->refs) * 2));
if (rv != 0) {
return rv;
}
}
rv = nghttp3_pq_push(&stream->max_cnts, &ref->max_cnts_pe);
if (rv != 0) {
return rv;
}
dest = nghttp3_ringbuf_push_back(&stream->refs);
*dest = ref;
return nghttp3_pq_push(&stream->max_cnts, &ref->max_cnts_pe);
return 0;
}
void nghttp3_qpack_stream_pop_ref(nghttp3_qpack_stream *stream) {
@ -1834,7 +1842,7 @@ static int qpack_encoder_write_indexed_name(nghttp3_qpack_encoder *encoder,
int h = 0;
hlen = nghttp3_qpack_huffman_encode_count(nv->value, nv->valuelen);
if (hlen * 4 < nv->valuelen * 3) {
if (hlen < nv->valuelen) {
h = 1;
len += nghttp3_qpack_put_varint_len(hlen, 7) + hlen;
} else {
@ -1925,7 +1933,7 @@ static int qpack_encoder_write_literal(nghttp3_qpack_encoder *encoder,
int nh = 0, vh = 0;
nhlen = nghttp3_qpack_huffman_encode_count(nv->name, nv->namelen);
if (nhlen * 4 < nv->namelen * 3) {
if (nhlen < nv->namelen) {
nh = 1;
len = nghttp3_qpack_put_varint_len(nhlen, prefix) + nhlen;
} else {
@ -1933,7 +1941,7 @@ static int qpack_encoder_write_literal(nghttp3_qpack_encoder *encoder,
}
vhlen = nghttp3_qpack_huffman_encode_count(nv->value, nv->valuelen);
if (vhlen * 4 < nv->valuelen * 3) {
if (vhlen < nv->valuelen) {
vh = 1;
len += nghttp3_qpack_put_varint_len(vhlen, 7) + vhlen;
} else {
@ -2083,8 +2091,9 @@ int nghttp3_qpack_context_dtable_add(nghttp3_qpack_context *ctx,
hash);
if (nghttp3_ringbuf_full(&ctx->dtable)) {
rv = nghttp3_ringbuf_reserve(&ctx->dtable,
nghttp3_ringbuf_len(&ctx->dtable) * 2);
rv = nghttp3_ringbuf_reserve(
&ctx->dtable,
nghttp3_max_size(128, nghttp3_ringbuf_len(&ctx->dtable) * 2));
if (rv != 0) {
goto fail;
}
@ -2259,10 +2268,11 @@ void nghttp3_qpack_entry_free(nghttp3_qpack_entry *ent) {
int nghttp3_qpack_encoder_block_stream(nghttp3_qpack_encoder *encoder,
nghttp3_qpack_stream *stream) {
nghttp3_blocked_streams_key bsk = {
nghttp3_struct_of(nghttp3_pq_top(&stream->max_cnts),
nghttp3_qpack_header_block_ref, max_cnts_pe)
->max_cnt,
(uint64_t)stream->stream_id};
.max_cnt = nghttp3_struct_of(nghttp3_pq_top(&stream->max_cnts),
nghttp3_qpack_header_block_ref, max_cnts_pe)
->max_cnt,
.id = (uint64_t)stream->stream_id,
};
return nghttp3_ksl_insert(&encoder->blocked_streams, NULL, &bsk, stream);
}
@ -2270,10 +2280,11 @@ int nghttp3_qpack_encoder_block_stream(nghttp3_qpack_encoder *encoder,
void nghttp3_qpack_encoder_unblock_stream(nghttp3_qpack_encoder *encoder,
nghttp3_qpack_stream *stream) {
nghttp3_blocked_streams_key bsk = {
nghttp3_struct_of(nghttp3_pq_top(&stream->max_cnts),
nghttp3_qpack_header_block_ref, max_cnts_pe)
->max_cnt,
(uint64_t)stream->stream_id};
.max_cnt = nghttp3_struct_of(nghttp3_pq_top(&stream->max_cnts),
nghttp3_qpack_header_block_ref, max_cnts_pe)
->max_cnt,
.id = (uint64_t)stream->stream_id,
};
nghttp3_ksl_it it;
/* This is purely debugging purpose only */
@ -2287,7 +2298,9 @@ void nghttp3_qpack_encoder_unblock_stream(nghttp3_qpack_encoder *encoder,
void nghttp3_qpack_encoder_unblock(nghttp3_qpack_encoder *encoder,
uint64_t max_cnt) {
nghttp3_blocked_streams_key bsk = {max_cnt, 0};
nghttp3_blocked_streams_key bsk = {
.max_cnt = max_cnt,
};
nghttp3_ksl_it it;
it = nghttp3_ksl_lower_bound(&encoder->blocked_streams, &bsk);
@ -2667,17 +2680,12 @@ void nghttp3_qpack_read_state_reset(nghttp3_qpack_read_state *rstate) {
rstate->huffman_encoded = 0;
}
int nghttp3_qpack_decoder_init(nghttp3_qpack_decoder *decoder,
size_t hard_max_dtable_capacity,
size_t max_blocked_streams,
const nghttp3_mem *mem) {
int rv;
rv = qpack_context_init(&decoder->ctx, hard_max_dtable_capacity,
max_blocked_streams, mem);
if (rv != 0) {
return rv;
}
void nghttp3_qpack_decoder_init(nghttp3_qpack_decoder *decoder,
size_t hard_max_dtable_capacity,
size_t max_blocked_streams,
const nghttp3_mem *mem) {
qpack_context_init(&decoder->ctx, hard_max_dtable_capacity,
max_blocked_streams, mem);
decoder->state = NGHTTP3_QPACK_ES_STATE_OPCODE;
decoder->opcode = 0;
@ -2687,8 +2695,6 @@ int nghttp3_qpack_decoder_init(nghttp3_qpack_decoder *decoder,
nghttp3_qpack_read_state_reset(&decoder->rstate);
nghttp3_buf_init(&decoder->dbuf);
return 0;
}
void nghttp3_qpack_decoder_free(nghttp3_qpack_decoder *decoder) {
@ -3171,6 +3177,7 @@ int nghttp3_qpack_decoder_dtable_static_add(nghttp3_qpack_decoder *decoder) {
rv = nghttp3_qpack_context_dtable_add(&decoder->ctx, &qnv, NULL, 0);
nghttp3_rcbuf_decref(qnv.value);
decoder->rstate.value = NULL;
return rv;
}
@ -3197,6 +3204,7 @@ int nghttp3_qpack_decoder_dtable_dynamic_add(nghttp3_qpack_decoder *decoder) {
rv = nghttp3_qpack_context_dtable_add(&decoder->ctx, &qnv, NULL, 0);
nghttp3_rcbuf_decref(qnv.value);
decoder->rstate.value = NULL;
nghttp3_rcbuf_decref(qnv.name);
return rv;
@ -3250,7 +3258,9 @@ int nghttp3_qpack_decoder_dtable_literal_add(nghttp3_qpack_decoder *decoder) {
rv = nghttp3_qpack_context_dtable_add(&decoder->ctx, &qnv, NULL, 0);
nghttp3_rcbuf_decref(qnv.value);
decoder->rstate.value = NULL;
nghttp3_rcbuf_decref(qnv.name);
decoder->rstate.name = NULL;
return rv;
}
@ -3818,6 +3828,9 @@ int nghttp3_qpack_decoder_cancel_stream(nghttp3_qpack_decoder *decoder,
uint8_t *p;
int rv;
assert(stream_id >= 0);
assert(stream_id <= (int64_t)NGHTTP3_MAX_VARINT);
if (qpack_decoder_dbuf_overflow(decoder)) {
return NGHTTP3_ERR_QPACK_FATAL;
}
@ -4059,10 +4072,9 @@ void nghttp3_qpack_decoder_emit_literal(nghttp3_qpack_decoder *decoder,
sctx->rstate.value = NULL;
}
int nghttp3_qpack_encoder_new(nghttp3_qpack_encoder **pencoder,
size_t hard_max_dtable_capacity,
const nghttp3_mem *mem) {
int rv;
int nghttp3_qpack_encoder_new2(nghttp3_qpack_encoder **pencoder,
size_t hard_max_dtable_capacity, uint64_t seed,
const nghttp3_mem *mem) {
nghttp3_qpack_encoder *p;
p = nghttp3_mem_malloc(mem, sizeof(nghttp3_qpack_encoder));
@ -4070,16 +4082,19 @@ int nghttp3_qpack_encoder_new(nghttp3_qpack_encoder **pencoder,
return NGHTTP3_ERR_NOMEM;
}
rv = nghttp3_qpack_encoder_init(p, hard_max_dtable_capacity, mem);
if (rv != 0) {
return rv;
}
nghttp3_qpack_encoder_init(p, hard_max_dtable_capacity, seed, mem);
*pencoder = p;
return 0;
}
int nghttp3_qpack_encoder_new(nghttp3_qpack_encoder **pencoder,
size_t hard_max_dtable_capacity,
const nghttp3_mem *mem) {
return nghttp3_qpack_encoder_new2(pencoder, hard_max_dtable_capacity, 0, mem);
}
void nghttp3_qpack_encoder_del(nghttp3_qpack_encoder *encoder) {
const nghttp3_mem *mem;
@ -4098,6 +4113,9 @@ int nghttp3_qpack_stream_context_new(nghttp3_qpack_stream_context **psctx,
const nghttp3_mem *mem) {
nghttp3_qpack_stream_context *p;
assert(stream_id >= 0);
assert(stream_id <= (int64_t)NGHTTP3_MAX_VARINT);
p = nghttp3_mem_malloc(mem, sizeof(nghttp3_qpack_stream_context));
if (p == NULL) {
return NGHTTP3_ERR_NOMEM;
@ -4127,7 +4145,6 @@ int nghttp3_qpack_decoder_new(nghttp3_qpack_decoder **pdecoder,
size_t hard_max_dtable_capacity,
size_t max_blocked_streams,
const nghttp3_mem *mem) {
int rv;
nghttp3_qpack_decoder *p;
p = nghttp3_mem_malloc(mem, sizeof(nghttp3_qpack_decoder));
@ -4135,11 +4152,8 @@ int nghttp3_qpack_decoder_new(nghttp3_qpack_decoder **pdecoder,
return NGHTTP3_ERR_NOMEM;
}
rv = nghttp3_qpack_decoder_init(p, hard_max_dtable_capacity,
max_blocked_streams, mem);
if (rv != 0) {
return rv;
}
nghttp3_qpack_decoder_init(p, hard_max_dtable_capacity, max_blocked_streams,
mem);
*pdecoder = p;

View file

@ -269,17 +269,12 @@ struct nghttp3_qpack_encoder {
/*
* nghttp3_qpack_encoder_init initializes |encoder|.
* |hard_max_dtable_capacity| is the upper bound of the dynamic table
* capacity. |mem| is a memory allocator.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* NGHTTP3_ERR_NOMEM
* Out of memory.
* capacity. |seed| is used to initialize nghttp3_map. |mem| is a
* memory allocator.
*/
int nghttp3_qpack_encoder_init(nghttp3_qpack_encoder *encoder,
size_t hard_max_dtable_capacity,
const nghttp3_mem *mem);
void nghttp3_qpack_encoder_init(nghttp3_qpack_encoder *encoder,
size_t hard_max_dtable_capacity, uint64_t seed,
const nghttp3_mem *mem);
/*
* nghttp3_qpack_encoder_free frees memory allocated for |encoder|.
@ -800,17 +795,11 @@ struct nghttp3_qpack_decoder {
* |hard_max_dtable_capacity| is the upper bound of the dynamic table
* capacity. |max_blocked_streams| is the maximum number of stream
* which can be blocked. |mem| is a memory allocator.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* NGHTTP3_ERR_NOMEM
* Out of memory.
*/
int nghttp3_qpack_decoder_init(nghttp3_qpack_decoder *decoder,
size_t hard_max_dtable_capacity,
size_t max_blocked_streams,
const nghttp3_mem *mem);
void nghttp3_qpack_decoder_init(nghttp3_qpack_decoder *decoder,
size_t hard_max_dtable_capacity,
size_t max_blocked_streams,
const nghttp3_mem *mem);
/*
* nghttp3_qpack_decoder_free frees memory allocated for |decoder|.

View file

@ -88,7 +88,9 @@ nghttp3_qpack_huffman_decode(nghttp3_qpack_huffman_decode_context *ctx,
int fin) {
uint8_t *p = dest;
const uint8_t *end = src + srclen;
nghttp3_qpack_huffman_decode_node node = {ctx->fstate, 0};
nghttp3_qpack_huffman_decode_node node = {
.fstate = ctx->fstate,
};
const nghttp3_qpack_huffman_decode_node *t = &node;
uint8_t c;

View file

@ -33,7 +33,7 @@ void nghttp3_range_init(nghttp3_range *r, uint64_t begin, uint64_t end) {
nghttp3_range nghttp3_range_intersect(const nghttp3_range *a,
const nghttp3_range *b) {
nghttp3_range r = {0, 0};
nghttp3_range r = {0};
uint64_t begin = nghttp3_max_uint64(a->begin, b->begin);
uint64_t end = nghttp3_min_uint64(a->end, b->end);

View file

@ -33,18 +33,21 @@
#include "nghttp3_macro.h"
#ifndef NDEBUG
static int ispow2(size_t n) {
#if defined(_MSC_VER) && !defined(__clang__) && \
(defined(_M_ARM) || (defined(_M_ARM64) && _MSC_VER < 1941))
# if defined(DISABLE_POPCNT) || \
(defined(_MSC_VER) && !defined(__clang__) && \
(defined(_M_ARM) || (defined(_M_ARM64) && _MSC_VER < 1941)))
return n && !(n & (n - 1));
#elif defined(WIN32)
# elif defined(WIN32)
return 1 == __popcnt((unsigned int)n);
#else /* !((defined(_MSC_VER) && !defined(__clang__) && (defined(_M_ARM) || \
(defined(_M_ARM64) && _MSC_VER < 1941))) || defined(WIN32)) */
# else /* !((defined(_MSC_VER) && !defined(__clang__) && (defined(_M_ARM) || \
(defined(_M_ARM64) && _MSC_VER < 1941))) || defined(WIN32)) */
return 1 == __builtin_popcount((unsigned int)n);
#endif /* !((defined(_MSC_VER) && !defined(__clang__) && (defined(_M_ARM) || \
(defined(_M_ARM64) && _MSC_VER < 1941))) || defined(WIN32)) */
# endif /* !((defined(_MSC_VER) && !defined(__clang__) && (defined(_M_ARM) || \
(defined(_M_ARM64) && _MSC_VER < 1941))) || defined(WIN32)) */
}
#endif /* !defined(NDEBUG) */
int nghttp3_ringbuf_init(nghttp3_ringbuf *rb, size_t nmemb, size_t size,
const nghttp3_mem *mem) {

View file

@ -103,7 +103,9 @@ void nghttp3_ringbuf_resize(nghttp3_ringbuf *rb, size_t len);
void *nghttp3_ringbuf_get(nghttp3_ringbuf *rb, size_t offset);
/* nghttp3_ringbuf_len returns the number of elements stored. */
#define nghttp3_ringbuf_len(RB) ((RB)->len)
static inline size_t nghttp3_ringbuf_len(const nghttp3_ringbuf *rb) {
return rb->len;
}
/* nghttp3_ringbuf_full returns nonzero if |rb| is full. */
int nghttp3_ringbuf_full(nghttp3_ringbuf *rb);

View file

@ -0,0 +1,95 @@
/*
* nghttp3
*
* Copyright (c) 2025 nghttp3 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 "nghttp3_settings.h"
#include <string.h>
#include <assert.h>
#include "nghttp3_conv.h"
#include "nghttp3_unreachable.h"
/* NGHTTP3_QPACK_ENCODER_MAX_DTABLE_CAPACITY is the upper bound of the
dynamic table capacity that QPACK encoder is willing to use. */
#define NGHTTP3_QPACK_ENCODER_MAX_DTABLE_CAPACITY 4096
void nghttp3_settings_default_versioned(int settings_version,
nghttp3_settings *settings) {
size_t len = nghttp3_settingslen_version(settings_version);
memset(settings, 0, len);
switch (settings_version) {
case NGHTTP3_SETTINGS_VERSION:
case NGHTTP3_SETTINGS_V1:
settings->max_field_section_size = NGHTTP3_VARINT_MAX;
settings->qpack_encoder_max_dtable_capacity =
NGHTTP3_QPACK_ENCODER_MAX_DTABLE_CAPACITY;
break;
}
}
static void settings_copy(nghttp3_settings *dest, const nghttp3_settings *src,
int settings_version) {
assert(settings_version != NGHTTP3_SETTINGS_VERSION);
memcpy(dest, src, nghttp3_settingslen_version(settings_version));
}
const nghttp3_settings *
nghttp3_settings_convert_to_latest(nghttp3_settings *dest, int settings_version,
const nghttp3_settings *src) {
if (settings_version == NGHTTP3_SETTINGS_VERSION) {
return src;
}
nghttp3_settings_default(dest);
settings_copy(dest, src, settings_version);
return dest;
}
void nghttp3_settings_convert_to_old(int settings_version,
nghttp3_settings *dest,
const nghttp3_settings *src) {
assert(settings_version != NGHTTP3_SETTINGS_VERSION);
settings_copy(dest, src, settings_version);
}
size_t nghttp3_settingslen_version(int settings_version) {
nghttp3_settings settings;
switch (settings_version) {
case NGHTTP3_SETTINGS_VERSION:
return sizeof(settings);
case NGHTTP3_SETTINGS_V1:
return offsetof(nghttp3_settings, h3_datagram) +
sizeof(settings.h3_datagram);
default:
nghttp3_unreachable();
}
}

View file

@ -0,0 +1,74 @@
/*
* nghttp3
*
* Copyright (c) 2025 nghttp3 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 NGHTTP3_SETTINGS_H
#define NGHTTP3_SETTINGS_H
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif /* defined(HAVE_CONFIG_H) */
#include <nghttp3/nghttp3.h>
/*
* nghttp3_settings_convert_to_latest converts |src| of version
* |settings_version| to the latest version NGHTTP3_SETTINGS_VERSION.
*
* |dest| must point to the latest version. |src| may be the older
* version, and if so, it may have fewer fields. Accessing those
* fields causes undefined behavior.
*
* If |settings_version| == NGHTTP3_SETTINGS_VERSION, no conversion is
* made, and |src| is returned. Otherwise, first |dest| is
* initialized via nghttp3_settings_default, and then all valid fields
* in |src| are copied into |dest|. Finally, |dest| is returned.
*/
const nghttp3_settings *
nghttp3_settings_convert_to_latest(nghttp3_settings *dest, int settings_version,
const nghttp3_settings *src);
/*
* nghttp3_settings_convert_to_old converts |src| of the latest
* version to |dest| of version |settings_version|.
*
* |settings_version| must not be the latest version
* NGHTTP3_SETTINGS_VERSION.
*
* |dest| points to the older version, and it may have fewer fields.
* Accessing those fields causes undefined behavior.
*
* This function copies all valid fields in version |settings_version|
* from |src| to |dest|.
*/
void nghttp3_settings_convert_to_old(int settings_version,
nghttp3_settings *dest,
const nghttp3_settings *src);
/*
* nghttp3_settingslen_version returns the effective length of
* nghttp3_settings at the version |settings_version|.
*/
size_t nghttp3_settingslen_version(int settings_version);
#endif /* !defined(NGHTTP3_SETTINGS_H) */

View file

@ -44,7 +44,7 @@
/* NGHTTP3_MIN_RBLEN is the minimum length of nghttp3_ringbuf */
#define NGHTTP3_MIN_RBLEN 4
nghttp3_objalloc_def(stream, nghttp3_stream, oplent);
nghttp3_objalloc_def(stream, nghttp3_stream, oplent)
int nghttp3_stream_new(nghttp3_stream **pstream, int64_t stream_id,
const nghttp3_stream_callbacks *callbacks,
@ -57,10 +57,19 @@ int nghttp3_stream_new(nghttp3_stream **pstream, int64_t stream_id,
return NGHTTP3_ERR_NOMEM;
}
memset(stream, 0, sizeof(*stream));
stream->out_chunk_objalloc = out_chunk_objalloc;
stream->stream_objalloc = stream_objalloc;
*stream = (nghttp3_stream){
.out_chunk_objalloc = out_chunk_objalloc,
.stream_objalloc = stream_objalloc,
.qpack_blocked_pe.index = NGHTTP3_PQ_BAD_INDEX,
.mem = mem,
.rx =
{
.http.status_code = -1,
.http.content_length = -1,
.http.pri.urgency = NGHTTP3_DEFAULT_URGENCY,
},
.error_code = NGHTTP3_H3_NO_ERROR,
};
nghttp3_tnode_init(&stream->node, stream_id);
@ -71,13 +80,6 @@ int nghttp3_stream_new(nghttp3_stream **pstream, int64_t stream_id,
nghttp3_qpack_stream_context_init(&stream->qpack_sctx, stream_id, mem);
stream->qpack_blocked_pe.index = NGHTTP3_PQ_BAD_INDEX;
stream->mem = mem;
stream->rx.http.status_code = -1;
stream->rx.http.content_length = -1;
stream->rx.http.pri.urgency = NGHTTP3_DEFAULT_URGENCY;
stream->error_code = NGHTTP3_H3_NO_ERROR;
if (callbacks) {
stream->callbacks = *callbacks;
}
@ -140,7 +142,7 @@ static void delete_frq(nghttp3_ringbuf *frq, const nghttp3_mem *mem) {
for (i = 0; i < len; ++i) {
frent = nghttp3_ringbuf_get(frq, i);
switch (frent->fr.hd.type) {
switch (frent->fr.type) {
case NGHTTP3_FRAME_HEADERS:
nghttp3_frame_headers_free(&frent->fr.headers, mem);
break;
@ -181,42 +183,45 @@ void nghttp3_stream_read_state_reset(nghttp3_stream_read_state *rstate) {
nghttp3_ssize nghttp3_read_varint(nghttp3_varint_read_state *rvint,
const uint8_t *begin, const uint8_t *end,
int fin) {
const uint8_t *orig_begin = begin;
size_t len;
size_t len, vlen;
uint8_t *p;
assert(begin != end);
if (rvint->left == 0) {
assert(rvint->acc == 0);
len = nghttp3_get_varintlen(begin);
if (len <= (size_t)(end - begin)) {
vlen = nghttp3_get_varintlen(begin);
len = nghttp3_min_size(vlen, (size_t)(end - begin));
if (vlen <= len) {
nghttp3_get_varint(&rvint->acc, begin);
return (nghttp3_ssize)len;
return (nghttp3_ssize)vlen;
}
if (fin) {
return NGHTTP3_ERR_INVALID_ARGUMENT;
}
rvint->acc = nghttp3_get_varint_fb(begin++);
rvint->left = len - 1;
p = (uint8_t *)&rvint->acc + (sizeof(rvint->acc) - vlen);
memcpy(p, begin, len);
*p &= 0x3f;
rvint->left = vlen - len;
return (nghttp3_ssize)len;
}
len = nghttp3_min_size(rvint->left, (size_t)(end - begin));
end = begin + len;
for (; begin != end;) {
rvint->acc = (rvint->acc << 8) + *begin++;
}
p = (uint8_t *)&rvint->acc + (sizeof(rvint->acc) - rvint->left);
memcpy(p, begin, len);
rvint->left -= len;
if (fin && rvint->left) {
if (rvint->left == 0) {
rvint->acc = (int64_t)nghttp3_ntohl64((uint64_t)rvint->acc);
} else if (fin) {
return NGHTTP3_ERR_INVALID_ARGUMENT;
}
return (nghttp3_ssize)(begin - orig_begin);
return (nghttp3_ssize)len;
}
int nghttp3_stream_frq_add(nghttp3_stream *stream,
@ -250,7 +255,7 @@ int nghttp3_stream_fill_outq(nghttp3_stream *stream) {
stream->unsent_bytes < NGHTTP3_MIN_UNSENT_BYTES;) {
frent = nghttp3_ringbuf_get(frq, 0);
switch (frent->fr.hd.type) {
switch (frent->fr.type) {
case NGHTTP3_FRAME_SETTINGS:
rv = nghttp3_stream_write_settings(stream, frent);
if (rv != 0) {
@ -290,6 +295,13 @@ int nghttp3_stream_fill_outq(nghttp3_stream *stream) {
nghttp3_frame_priority_update_free(&frent->fr.priority_update,
stream->mem);
break;
case NGHTTP3_FRAME_ORIGIN:
rv = nghttp3_stream_write_origin(stream, frent);
if (rv != 0) {
return rv;
}
break;
default:
/* TODO Not implemented */
break;
@ -301,12 +313,6 @@ int nghttp3_stream_fill_outq(nghttp3_stream *stream) {
return 0;
}
static void typed_buf_shared_init(nghttp3_typed_buf *tbuf,
const nghttp3_buf *chunk) {
nghttp3_typed_buf_init(tbuf, chunk, NGHTTP3_BUF_TYPE_SHARED);
tbuf->buf.pos = tbuf->buf.last;
}
int nghttp3_stream_write_stream_type(nghttp3_stream *stream) {
size_t len = nghttp3_put_varintlen((int64_t)stream->type);
nghttp3_buf *chunk;
@ -319,7 +325,7 @@ int nghttp3_stream_write_stream_type(nghttp3_stream *stream) {
}
chunk = nghttp3_stream_get_chunk(stream);
typed_buf_shared_init(&tbuf, chunk);
nghttp3_typed_buf_shared_init(&tbuf, chunk);
chunk->last = nghttp3_put_varint(chunk->last, (int64_t)stream->type);
tbuf.buf.last = chunk->last;
@ -336,12 +342,17 @@ int nghttp3_stream_write_settings(nghttp3_stream *stream,
struct {
nghttp3_frame_settings settings;
nghttp3_settings_entry iv[15];
} fr;
} fr = {
.settings =
{
.type = NGHTTP3_FRAME_SETTINGS,
.niv = 3,
},
};
nghttp3_settings_entry *iv;
nghttp3_settings *local_settings = frent->aux.settings.local_settings;
int64_t payloadlen;
fr.settings.hd.type = NGHTTP3_FRAME_SETTINGS;
fr.settings.niv = 3;
iv = &fr.settings.iv[0];
iv[0].id = NGHTTP3_SETTINGS_ID_MAX_FIELD_SECTION_SIZE;
@ -365,7 +376,7 @@ int nghttp3_stream_write_settings(nghttp3_stream *stream,
++fr.settings.niv;
}
len = nghttp3_frame_write_settings_len(&fr.settings.hd.length, &fr.settings);
len = nghttp3_frame_write_settings_len(&payloadlen, &fr.settings);
rv = nghttp3_stream_ensure_chunk(stream, len);
if (rv != 0) {
@ -373,9 +384,10 @@ int nghttp3_stream_write_settings(nghttp3_stream *stream,
}
chunk = nghttp3_stream_get_chunk(stream);
typed_buf_shared_init(&tbuf, chunk);
nghttp3_typed_buf_shared_init(&tbuf, chunk);
chunk->last = nghttp3_frame_write_settings(chunk->last, &fr.settings);
chunk->last =
nghttp3_frame_write_settings(chunk->last, &fr.settings, payloadlen);
tbuf.buf.last = chunk->last;
@ -389,8 +401,9 @@ int nghttp3_stream_write_goaway(nghttp3_stream *stream,
int rv;
nghttp3_buf *chunk;
nghttp3_typed_buf tbuf;
int64_t payloadlen;
len = nghttp3_frame_write_goaway_len(&fr->hd.length, fr);
len = nghttp3_frame_write_goaway_len(&payloadlen, fr);
rv = nghttp3_stream_ensure_chunk(stream, len);
if (rv != 0) {
@ -398,9 +411,9 @@ int nghttp3_stream_write_goaway(nghttp3_stream *stream,
}
chunk = nghttp3_stream_get_chunk(stream);
typed_buf_shared_init(&tbuf, chunk);
nghttp3_typed_buf_shared_init(&tbuf, chunk);
chunk->last = nghttp3_frame_write_goaway(chunk->last, fr);
chunk->last = nghttp3_frame_write_goaway(chunk->last, fr, payloadlen);
tbuf.buf.last = chunk->last;
@ -414,8 +427,9 @@ int nghttp3_stream_write_priority_update(nghttp3_stream *stream,
int rv;
nghttp3_buf *chunk;
nghttp3_typed_buf tbuf;
int64_t payloadlen;
len = nghttp3_frame_write_priority_update_len(&fr->hd.length, fr);
len = nghttp3_frame_write_priority_update_len(&payloadlen, fr);
rv = nghttp3_stream_ensure_chunk(stream, len);
if (rv != 0) {
@ -423,15 +437,55 @@ int nghttp3_stream_write_priority_update(nghttp3_stream *stream,
}
chunk = nghttp3_stream_get_chunk(stream);
typed_buf_shared_init(&tbuf, chunk);
nghttp3_typed_buf_shared_init(&tbuf, chunk);
chunk->last = nghttp3_frame_write_priority_update(chunk->last, fr);
chunk->last =
nghttp3_frame_write_priority_update(chunk->last, fr, payloadlen);
tbuf.buf.last = chunk->last;
return nghttp3_stream_outq_add(stream, &tbuf);
}
int nghttp3_stream_write_origin(nghttp3_stream *stream,
nghttp3_frame_entry *frent) {
nghttp3_frame_origin *fr = &frent->fr.origin;
nghttp3_buf *chunk;
nghttp3_buf buf;
nghttp3_typed_buf tbuf;
int rv;
rv = nghttp3_stream_ensure_chunk(
stream, nghttp3_frame_write_hd_len(fr->type, (int64_t)fr->origin_list.len));
if (rv != 0) {
return rv;
}
chunk = nghttp3_stream_get_chunk(stream);
nghttp3_typed_buf_shared_init(&tbuf, chunk);
chunk->last =
nghttp3_frame_write_hd(chunk->last, fr->type, (int64_t)fr->origin_list.len);
tbuf.buf.last = chunk->last;
rv = nghttp3_stream_outq_add(stream, &tbuf);
if (rv != 0) {
return rv;
}
if (fr->origin_list.len == 0) {
return 0;
}
nghttp3_buf_wrap_init(&buf, (uint8_t *)fr->origin_list.base,
fr->origin_list.len);
buf.last = buf.end;
nghttp3_typed_buf_init(&tbuf, &buf, NGHTTP3_BUF_TYPE_ALIEN_NO_ACK);
return nghttp3_stream_outq_add(stream, &tbuf);
}
int nghttp3_stream_write_headers(nghttp3_stream *stream,
nghttp3_frame_entry *frent) {
nghttp3_frame_headers *fr = &frent->fr.headers;
@ -455,26 +509,25 @@ int nghttp3_stream_write_header_block(nghttp3_stream *stream,
size_t len;
nghttp3_buf *chunk;
nghttp3_typed_buf tbuf;
nghttp3_frame_hd hd;
uint8_t raw_pbuf[16];
size_t pbuflen, rbuflen, ebuflen;
int64_t payloadlen;
nghttp3_buf_wrap_init(&pbuf, raw_pbuf, sizeof(raw_pbuf));
rv = nghttp3_qpack_encoder_encode(qenc, &pbuf, rbuf, ebuf, stream->node.id,
nva, nvlen);
if (rv != 0) {
goto fail;
return rv;
}
pbuflen = nghttp3_buf_len(&pbuf);
rbuflen = nghttp3_buf_len(rbuf);
ebuflen = nghttp3_buf_len(ebuf);
hd.type = frame_type;
hd.length = (int64_t)(pbuflen + rbuflen);
payloadlen = (int64_t)(pbuflen + rbuflen);
len = nghttp3_frame_write_hd_len(&hd) + pbuflen;
len = nghttp3_frame_write_hd_len(frame_type, payloadlen) + pbuflen;
if (rbuflen <= NGHTTP3_STREAM_MAX_COPY_THRES) {
len += rbuflen;
@ -482,13 +535,13 @@ int nghttp3_stream_write_header_block(nghttp3_stream *stream,
rv = nghttp3_stream_ensure_chunk(stream, len);
if (rv != 0) {
goto fail;
return rv;
}
chunk = nghttp3_stream_get_chunk(stream);
typed_buf_shared_init(&tbuf, chunk);
nghttp3_typed_buf_shared_init(&tbuf, chunk);
chunk->last = nghttp3_frame_write_hd(chunk->last, &hd);
chunk->last = nghttp3_frame_write_hd(chunk->last, frame_type, payloadlen);
chunk->last = nghttp3_cpymem(chunk->last, pbuf.pos, pbuflen);
nghttp3_buf_init(&pbuf);
@ -498,13 +551,13 @@ int nghttp3_stream_write_header_block(nghttp3_stream *stream,
rv = nghttp3_stream_outq_add(stream, &tbuf);
if (rv != 0) {
goto fail;
return rv;
}
nghttp3_typed_buf_init(&tbuf, rbuf, NGHTTP3_BUF_TYPE_PRIVATE);
rv = nghttp3_stream_outq_add(stream, &tbuf);
if (rv != 0) {
goto fail;
return rv;
}
nghttp3_buf_init(rbuf);
} else if (rbuflen) {
@ -513,7 +566,7 @@ int nghttp3_stream_write_header_block(nghttp3_stream *stream,
rv = nghttp3_stream_outq_add(stream, &tbuf);
if (rv != 0) {
goto fail;
return rv;
}
nghttp3_buf_reset(rbuf);
}
@ -532,18 +585,18 @@ int nghttp3_stream_write_header_block(nghttp3_stream *stream,
rv = nghttp3_stream_ensure_chunk(qenc_stream, ebuflen);
if (rv != 0) {
goto fail;
return rv;
}
chunk = nghttp3_stream_get_chunk(qenc_stream);
typed_buf_shared_init(&tbuf, chunk);
nghttp3_typed_buf_shared_init(&tbuf, chunk);
chunk->last = nghttp3_cpymem(chunk->last, ebuf->pos, ebuflen);
tbuf.buf.last = chunk->last;
rv = nghttp3_stream_outq_add(qenc_stream, &tbuf);
if (rv != 0) {
goto fail;
return rv;
}
nghttp3_buf_reset(ebuf);
}
@ -553,10 +606,6 @@ int nghttp3_stream_write_header_block(nghttp3_stream *stream,
assert(0 == nghttp3_buf_len(ebuf));
return 0;
fail:
return rv;
}
int nghttp3_stream_write_data(nghttp3_stream *stream, int *peof,
@ -570,7 +619,6 @@ int nghttp3_stream_write_data(nghttp3_stream *stream, int *peof,
nghttp3_conn *conn = stream->conn;
int64_t datalen;
uint32_t flags = 0;
nghttp3_frame_hd hd;
nghttp3_vec vec[8];
nghttp3_vec *v;
nghttp3_ssize sveccnt;
@ -624,10 +672,7 @@ int nghttp3_stream_write_data(nghttp3_stream *stream, int *peof,
}
}
hd.type = NGHTTP3_FRAME_DATA;
hd.length = datalen;
len = nghttp3_frame_write_hd_len(&hd);
len = nghttp3_frame_write_hd_len(NGHTTP3_FRAME_DATA, datalen);
rv = nghttp3_stream_ensure_chunk(stream, len);
if (rv != 0) {
@ -635,9 +680,10 @@ int nghttp3_stream_write_data(nghttp3_stream *stream, int *peof,
}
chunk = nghttp3_stream_get_chunk(stream);
typed_buf_shared_init(&tbuf, chunk);
nghttp3_typed_buf_shared_init(&tbuf, chunk);
chunk->last = nghttp3_frame_write_hd(chunk->last, &hd);
chunk->last =
nghttp3_frame_write_hd(chunk->last, NGHTTP3_FRAME_DATA, datalen);
tbuf.buf.last = chunk->last;
@ -690,7 +736,7 @@ int nghttp3_stream_write_qpack_decoder_stream(nghttp3_stream *stream) {
}
chunk = nghttp3_stream_get_chunk(stream);
typed_buf_shared_init(&tbuf, chunk);
nghttp3_typed_buf_shared_init(&tbuf, chunk);
nghttp3_qpack_decoder_write_decoder(qdec, chunk);
@ -717,17 +763,16 @@ int nghttp3_stream_outq_add(nghttp3_stream *stream,
if (len) {
dest = nghttp3_ringbuf_get(outq, len - 1);
if (dest->type == tbuf->type && dest->type == NGHTTP3_BUF_TYPE_SHARED &&
dest->buf.begin == tbuf->buf.begin && dest->buf.last == tbuf->buf.pos) {
dest->buf.end == tbuf->buf.end && dest->buf.last == tbuf->buf.pos) {
/* If we have already written last entry, adjust outq_idx and
offset so that this entry is eligible to send. */
if (len == stream->outq_idx) {
--stream->outq_idx;
stream->outq_offset = nghttp3_buf_len(&dest->buf);
}
dest->buf.last = tbuf->buf.last;
/* TODO Is this required? */
dest->buf.end = tbuf->buf.end;
assert(dest->buf.end == tbuf->buf.end);
return 0;
}
@ -817,34 +862,24 @@ size_t nghttp3_stream_writev(nghttp3_stream *stream, int *pfin,
nghttp3_ringbuf *outq = &stream->outq;
size_t len = nghttp3_ringbuf_len(outq);
size_t i = stream->outq_idx;
uint64_t offset = stream->outq_offset;
size_t buflen;
nghttp3_vec *vbegin = vec, *vend = vec + veccnt;
nghttp3_typed_buf *tbuf;
assert(veccnt > 0);
if (i < len) {
for (; i < len && vec != vend; ++i) {
tbuf = nghttp3_ringbuf_get(outq, i);
buflen = nghttp3_buf_len(&tbuf->buf);
if (offset < buflen) {
vec->base = tbuf->buf.pos + offset;
vec->len = (size_t)(buflen - offset);
++vec;
} else {
/* This is the only case that satisfies offset >= buflen */
assert(0 == offset);
assert(0 == buflen);
if (buflen == 0) {
continue;
}
++i;
vec->base = tbuf->buf.pos;
vec->len = buflen;
for (; i < len && vec != vend; ++i, ++vec) {
tbuf = nghttp3_ringbuf_get(outq, i);
vec->base = tbuf->buf.pos;
vec->len = nghttp3_buf_len(&tbuf->buf);
}
++vec;
}
/* TODO Rework this if we have finished implementing HTTP
@ -859,26 +894,27 @@ void nghttp3_stream_add_outq_offset(nghttp3_stream *stream, size_t n) {
nghttp3_ringbuf *outq = &stream->outq;
size_t i;
size_t len = nghttp3_ringbuf_len(outq);
uint64_t offset = stream->outq_offset + n;
size_t buflen;
nghttp3_typed_buf *tbuf;
stream->unsent_bytes -= n;
for (i = stream->outq_idx; i < len; ++i) {
tbuf = nghttp3_ringbuf_get(outq, i);
buflen = nghttp3_buf_len(&tbuf->buf);
if (offset >= buflen) {
offset -= buflen;
continue;
if (n < buflen) {
tbuf->buf.pos += n;
break;
}
break;
tbuf->buf.pos = tbuf->buf.last;
n -= buflen;
}
assert(i < len || offset == 0);
assert(i < len || n == 0);
stream->unsent_bytes -= n;
stream->outq_idx = i;
stream->outq_offset = offset;
}
int nghttp3_stream_outq_write_done(nghttp3_stream *stream) {
@ -898,13 +934,13 @@ static void stream_pop_outq_entry(nghttp3_stream *stream,
nghttp3_buf_free(&tbuf->buf, stream->mem);
break;
case NGHTTP3_BUF_TYPE_ALIEN:
case NGHTTP3_BUF_TYPE_ALIEN_NO_ACK:
break;
case NGHTTP3_BUF_TYPE_SHARED:
assert(nghttp3_ringbuf_len(chunks));
chunk = nghttp3_ringbuf_get(chunks, 0);
assert(chunk->begin == tbuf->buf.begin);
assert(chunk->end == tbuf->buf.end);
if (chunk->last == tbuf->buf.last) {
@ -927,14 +963,13 @@ static void stream_pop_outq_entry(nghttp3_stream *stream,
int nghttp3_stream_update_ack_offset(nghttp3_stream *stream, uint64_t offset) {
nghttp3_ringbuf *outq = &stream->outq;
size_t buflen;
size_t npopped = 0;
uint64_t nack;
nghttp3_typed_buf *tbuf;
int rv;
for (; nghttp3_ringbuf_len(outq);) {
tbuf = nghttp3_ringbuf_get(outq, 0);
buflen = nghttp3_buf_len(&tbuf->buf);
buflen = (size_t)(tbuf->buf.last - tbuf->buf.begin);
/* For NGHTTP3_BUF_TYPE_ALIEN, we never add 0 length buffer. */
if (tbuf->type == NGHTTP3_BUF_TYPE_ALIEN && stream->ack_offset < offset &&
@ -949,17 +984,13 @@ int nghttp3_stream_update_ack_offset(nghttp3_stream *stream, uint64_t offset) {
}
}
if (offset >= stream->ack_base + buflen) {
if (stream->outq_idx > 0 && offset >= stream->ack_base + buflen) {
stream_pop_outq_entry(stream, tbuf);
stream->ack_base += buflen;
stream->ack_offset = stream->ack_base;
++npopped;
if (stream->outq_idx + 1 == npopped) {
stream->outq_offset = 0;
break;
}
--stream->outq_idx;
continue;
}
@ -967,13 +998,6 @@ int nghttp3_stream_update_ack_offset(nghttp3_stream *stream, uint64_t offset) {
break;
}
assert(stream->outq_idx + 1 >= npopped);
if (stream->outq_idx >= npopped) {
stream->outq_idx -= npopped;
} else {
stream->outq_idx = 0;
}
stream->ack_offset = offset;
return 0;
@ -1045,19 +1069,17 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream,
switch (stream->rx.hstate) {
case NGHTTP3_HTTP_STATE_NONE:
return NGHTTP3_ERR_H3_INTERNAL_ERROR;
nghttp3_unreachable();
case NGHTTP3_HTTP_STATE_REQ_INITIAL:
switch (event) {
case NGHTTP3_HTTP_EVENT_HEADERS_BEGIN:
stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN;
return 0;
default:
if (event != NGHTTP3_HTTP_EVENT_HEADERS_BEGIN) {
return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
}
stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN;
return 0;
case NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN:
if (event != NGHTTP3_HTTP_EVENT_HEADERS_END) {
return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING;
}
assert(NGHTTP3_HTTP_EVENT_HEADERS_END == event);
stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_HEADERS_END;
return 0;
case NGHTTP3_HTTP_STATE_REQ_HEADERS_END:
@ -1080,12 +1102,10 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream,
stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_END;
return 0;
default:
return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
nghttp3_unreachable();
}
case NGHTTP3_HTTP_STATE_REQ_DATA_BEGIN:
if (event != NGHTTP3_HTTP_EVENT_DATA_END) {
return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING;
}
assert(NGHTTP3_HTTP_EVENT_DATA_END == event);
stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_DATA_END;
return 0;
case NGHTTP3_HTTP_STATE_REQ_DATA_END:
@ -1108,12 +1128,10 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream,
stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_END;
return 0;
default:
return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
nghttp3_unreachable();
}
case NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN:
if (event != NGHTTP3_HTTP_EVENT_HEADERS_END) {
return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING;
}
assert(NGHTTP3_HTTP_EVENT_HEADERS_END == event);
stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_TRAILERS_END;
return 0;
case NGHTTP3_HTTP_STATE_REQ_TRAILERS_END:
@ -1129,7 +1147,7 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream,
stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_END;
return 0;
case NGHTTP3_HTTP_STATE_REQ_END:
return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING;
return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
case NGHTTP3_HTTP_STATE_RESP_INITIAL:
if (event != NGHTTP3_HTTP_EVENT_HEADERS_BEGIN) {
return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
@ -1137,9 +1155,7 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream,
stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN;
return 0;
case NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN:
if (event != NGHTTP3_HTTP_EVENT_HEADERS_END) {
return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING;
}
assert(NGHTTP3_HTTP_EVENT_HEADERS_END == event);
stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_HEADERS_END;
return 0;
case NGHTTP3_HTTP_STATE_RESP_HEADERS_END:
@ -1169,12 +1185,10 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream,
stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_END;
return 0;
default:
return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
nghttp3_unreachable();
}
case NGHTTP3_HTTP_STATE_RESP_DATA_BEGIN:
if (event != NGHTTP3_HTTP_EVENT_DATA_END) {
return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING;
}
assert(NGHTTP3_HTTP_EVENT_DATA_END == event);
stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_DATA_END;
return 0;
case NGHTTP3_HTTP_STATE_RESP_DATA_END:
@ -1197,17 +1211,15 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream,
stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_END;
return 0;
default:
return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
nghttp3_unreachable();
}
case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN:
if (event != NGHTTP3_HTTP_EVENT_HEADERS_END) {
return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING;
}
assert(NGHTTP3_HTTP_EVENT_HEADERS_END == event);
stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_TRAILERS_END;
return 0;
case NGHTTP3_HTTP_STATE_RESP_TRAILERS_END:
if (event != NGHTTP3_HTTP_EVENT_MSG_END) {
return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING;
return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
}
rv = nghttp3_http_on_remote_end_stream(stream);
if (rv != 0) {
@ -1216,7 +1228,7 @@ int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream,
stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_END;
return 0;
case NGHTTP3_HTTP_STATE_RESP_END:
return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING;
return NGHTTP3_ERR_H3_FRAME_UNEXPECTED;
default:
nghttp3_unreachable();
}

View file

@ -69,6 +69,8 @@ typedef enum nghttp3_ctrl_stream_state {
NGHTTP3_CTRL_STREAM_STATE_SETTINGS_VALUE,
NGHTTP3_CTRL_STREAM_STATE_PRIORITY_UPDATE_PRI_ELEM_ID,
NGHTTP3_CTRL_STREAM_STATE_PRIORITY_UPDATE,
NGHTTP3_CTRL_STREAM_STATE_ORIGIN_ORIGIN_LEN,
NGHTTP3_CTRL_STREAM_STATE_ORIGIN_ASCII_ORIGIN,
} nghttp3_ctrl_stream_state;
typedef enum nghttp3_req_stream_state {
@ -129,10 +131,6 @@ typedef struct nghttp3_stream_read_state {
/* NGHTTP3_STREAM_FLAG_PRIORITY_UPDATE_RECVED indicates that server
received PRIORITY_UPDATE frame for this stream. */
#define NGHTTP3_STREAM_FLAG_PRIORITY_UPDATE_RECVED 0x0800u
/* NGHTTP3_STREAM_FLAG_HTTP_ERROR indicates that
NGHTTP3_ERR_MALFORMED_HTTP_HEADER error is encountered while
processing incoming HTTP fields. */
#define NGHTTP3_STREAM_FLAG_HTTP_ERROR 0x1000u
typedef enum nghttp3_stream_http_state {
NGHTTP3_HTTP_STATE_NONE,
@ -223,9 +221,6 @@ struct nghttp3_stream {
uint64_t unsent_bytes;
/* outq_idx is an index into outq where next write is made. */
size_t outq_idx;
/* outq_offset is write offset relative to the element at outq_idx
in outq. */
uint64_t outq_offset;
/* ack_base is the number of bytes acknowledged by a remote
endpoint where the first element in outq is positioned at. */
uint64_t ack_base;
@ -255,7 +250,7 @@ struct nghttp3_stream {
};
};
nghttp3_objalloc_decl(stream, nghttp3_stream, oplent);
nghttp3_objalloc_decl(stream, nghttp3_stream, oplent)
typedef struct nghttp3_frame_entry {
nghttp3_frame fr;
@ -322,6 +317,9 @@ int nghttp3_stream_write_goaway(nghttp3_stream *stream,
int nghttp3_stream_write_priority_update(nghttp3_stream *stream,
nghttp3_frame_entry *frent);
int nghttp3_stream_write_origin(nghttp3_stream *stream,
nghttp3_frame_entry *frent);
int nghttp3_stream_ensure_chunk(nghttp3_stream *stream, size_t need);
nghttp3_buf *nghttp3_stream_get_chunk(nghttp3_stream *stream);

View file

@ -28,8 +28,11 @@
#include <nghttp3/nghttp3.h>
static nghttp3_info version = {NGHTTP3_VERSION_AGE, NGHTTP3_VERSION_NUM,
NGHTTP3_VERSION};
static nghttp3_info version = {
.age = NGHTTP3_VERSION_AGE,
.version_num = NGHTTP3_VERSION_NUM,
.version_str = NGHTTP3_VERSION,
};
const nghttp3_info *nghttp3_version(int least_version) {
if (least_version > NGHTTP3_VERSION_NUM) {

View file

@ -1,22 +0,0 @@
The MIT License
Copyright (c) 2023 sfparse 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.

File diff suppressed because it is too large Load diff

View file

@ -31,90 +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,
SFPARSE_TYPE_DATE,
/**
* :enum:`SF_TYPE_DISPSTRING` indicates display string type.
* :enum:`SFPARSE_TYPE_DISPSTRING` indicates display string type.
*/
SF_TYPE_DISPSTRING
} sf_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.
*/
@ -123,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.
*/
@ -154,275 +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`, :enum:`sf_type.SF_TYPE_BYTESEQ`,
* or :enum:`sf_type.SF_TYPE_DISPSTRING`.
* 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`.
*
* For :enum:`sf_type.SF_TYPE_DISPSTRING`, this field may contain
* percent-encoded UTF-8 byte sequences. To decode it, use
* `sf_pctdecode`.
* 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 <sf_vec.len>` == 0, :member:`vec.base
* <sf_vec.base>` is guaranteed to be NULL.
* 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.
* :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
* `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 <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 <sfparse_vec.len>`.
*/
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:`sf_value.vec` of type :enum:`sf_type.SF_TYPE_BYTESEQ`
* produced by either `sf_parser_dict`, `sf_parser_list`,
* `sf_parser_inner_list`, `sf_parser_item`, or `sf_parser_param`,
* otherwise the behavior is undefined.
* :member:`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 <sf_vec.base>` must point to the buffer that
* has sufficient space to store the decoded byte string.
* :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 <sf_vec.len>`.
* :member:`dest->len <sfparse_vec.len>`.
*/
void sf_base64decode(sf_vec *dest, const sf_vec *src);
/**
* @function
*
* `sf_pctdecode` decodes percent-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_DISPSTRING`
* produced by either `sf_parser_dict`, `sf_parser_list`,
* `sf_parser_inner_list`, `sf_parser_item`, or `sf_parser_param`,
* otherwise the behavior is undefined.
*
* :member:`dest->base <sf_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>`.
*/
void sf_pctdecode(sf_vec *dest, const sf_vec *src);
void sfparse_pctdecode(sfparse_vec *dest, const sfparse_vec *src);
#ifdef __cplusplus
}
#endif
#endif /* defined(__cplusplus) */
#endif /* SFPARSE_H */
#endif /* !defined(SFPARSE_H) */

View file

@ -57,6 +57,7 @@
'nghttp3_sources': [
'nghttp3/lib/nghttp3_balloc.c',
'nghttp3/lib/nghttp3_buf.c',
'nghttp3/lib/nghttp3_callbacks.c',
'nghttp3/lib/nghttp3_conn.c',
'nghttp3/lib/nghttp3_conv.c',
'nghttp3/lib/nghttp3_debug.c',
@ -77,14 +78,13 @@
'nghttp3/lib/nghttp3_range.c',
'nghttp3/lib/nghttp3_rcbuf.c',
'nghttp3/lib/nghttp3_ringbuf.c',
'nghttp3/lib/nghttp3_settings.c',
'nghttp3/lib/nghttp3_str.c',
'nghttp3/lib/nghttp3_stream.c',
'nghttp3/lib/nghttp3_tnode.c',
'nghttp3/lib/nghttp3_unreachable.c',
'nghttp3/lib/nghttp3_vec.c',
'nghttp3/lib/nghttp3_version.c',
# sfparse is also used by nghttp2 and is included by nghttp2.gyp
# 'nghttp3/lib/sfparse.c'
]
},
'targets': [