Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions doc/source/programmers-guide.rst
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ effectively required to do HTTP/3 transaction below:
had been blocked for synchronization between streams. Application
has to tell QUIC stack the number of bytes consumed which affects
flow control. We will discuss more about this callback later when
explaining `nghttp3_conn_read_stream`.
explaining `nghttp3_conn_read_stream2`.
* :member:`recv_header <nghttp3_callbacks.recv_header>`: It is called
when an HTTP header field is received.
* :member:`send_stop_sending <nghttp3_callbacks.send_stop_sending>`:
Expand All @@ -69,6 +69,7 @@ effectively required to do HTTP/3 transaction below:
The initialization functions also takes :type:`nghttp3_settings` which
is a set of options to tweak HTTP3/ connection settings.
`nghttp3_settings_default` fills the default values.
`nghttp3_settings.initial_ts` should be set to the current timestamp.

The *user_data* parameter to the initialization function is an opaque
pointer and it is passed to callback functions.
Expand All @@ -89,7 +90,7 @@ Use the following functions to bind those streams to their purposes:
Reading HTTP stream data
------------------------

`nghttp3_conn_read_stream` reads HTTP stream data from a particular
`nghttp3_conn_read_stream2` reads HTTP stream data from a particular
stream. It returns the number of bytes "consumed". "Consumed" means
that the those bytes are completely processed and QUIC stack can
increase the flow control credit of both stream and connection by that
Expand Down
105 changes: 104 additions & 1 deletion lib/includes/nghttp3/nghttp3.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,59 @@ extern "C" {
*/
typedef ptrdiff_t nghttp3_ssize;

/**
* @typedef
*
* :type:`nghttp3_tstamp` is a timestamp with nanosecond resolution.
* ``UINT64_MAX`` is an invalid value, and it is often used to
* indicate that no value is set. This type is available since
* v1.12.0.
*/
typedef uint64_t nghttp3_tstamp;

/**
* @typedef
*
* :type:`nghttp3_duration` is a period of time in nanosecond
* resolution. ``UINT64_MAX`` is an invalid value, and it is often
* used to indicate that no value is set. This type is available
* since v1.12.0.
*/
typedef uint64_t nghttp3_duration;

/**
* @macro
*
* :macro:`NGHTTP3_NANOSECONDS` is a count of tick which corresponds
* to 1 nanosecond. This macro is available since v1.12.0.
*/
#define NGHTTP3_NANOSECONDS ((nghttp3_duration)1ULL)

/**
* @macro
*
* :macro:`NGHTTP3_MICROSECONDS` is a count of tick which corresponds
* to 1 microsecond. This macro is available since v1.12.0.
*/
#define NGHTTP3_MICROSECONDS ((nghttp3_duration)(1000ULL * NGHTTP3_NANOSECONDS))

/**
* @macro
*
* :macro:`NGHTTP3_MILLISECONDS` is a count of tick which corresponds
* to 1 millisecond. This macro is available since v1.12.0.
*/
#define NGHTTP3_MILLISECONDS \
((nghttp3_duration)(1000ULL * NGHTTP3_MICROSECONDS))

/**
* @macro
*
* :macro:`NGHTTP3_SECONDS` is a count of tick which corresponds to 1
* second. This macro is available since v1.12.0.
*/
#define NGHTTP3_SECONDS ((nghttp3_duration)(1000ULL * NGHTTP3_MILLISECONDS))

/**
* @macro
*
Expand Down Expand Up @@ -1638,7 +1691,8 @@ typedef struct nghttp3_conn nghttp3_conn;

#define NGHTTP3_SETTINGS_V1 1
#define NGHTTP3_SETTINGS_V2 2
#define NGHTTP3_SETTINGS_VERSION NGHTTP3_SETTINGS_V2
#define NGHTTP3_SETTINGS_V3 3
#define NGHTTP3_SETTINGS_VERSION NGHTTP3_SETTINGS_V3

/**
* @struct
Expand Down Expand Up @@ -1700,6 +1754,12 @@ typedef struct nghttp3_settings {
* server uses this field. This field is available since v1.11.0.
*/
const nghttp3_vec *origin_list;
/* The following fields have been added since NGHTTP3_SETTINGS_V3. */
/**
* :member:`initial_ts` is an initial timestamp given to the
* library. This field is available since v1.12.0.
*/
nghttp3_tstamp initial_ts;
} nghttp3_settings;

/**
Expand Down Expand Up @@ -2113,6 +2173,8 @@ typedef struct nghttp3_callbacks {
* <nghttp3_settings.qpack_blocked_streams>` = 0
* - :member:`enable_connect_protocol
* <nghttp3_settings.enable_connect_protocol>` = 0
* - :member:`initial_ts <nghttp3_settings.initial_ts>` =
* ``UINT64_MAX``
*/
NGHTTP3_EXTERN void
nghttp3_settings_default_versioned(int settings_version,
Expand Down Expand Up @@ -2209,6 +2271,11 @@ NGHTTP3_EXTERN int nghttp3_conn_bind_qpack_streams(nghttp3_conn *conn,
/**
* @function
*
* .. warning::
*
* Deprecated since v1.12.0. Use `nghttp3_conn_read_stream2`
* instead.
*
* `nghttp3_conn_read_stream` reads data |src| of length |srclen| on
* stream identified by |stream_id|. It returns the number of bytes
* consumed. The "consumed" means that application can increase flow
Expand Down Expand Up @@ -2237,6 +2304,42 @@ NGHTTP3_EXTERN nghttp3_ssize nghttp3_conn_read_stream(nghttp3_conn *conn,
const uint8_t *src,
size_t srclen, int fin);

/**
* @function
*
* `nghttp3_conn_read_stream2` reads data |src| of length |srclen| on
* stream identified by |stream_id|. It returns the number of bytes
* consumed. The "consumed" means that application can increase flow
* 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
* :type:`nghttp3_recv_data` to handle those bytes. If |fin| is
* nonzero, this is the last data from remote endpoint in this stream.
* |ts| is the current timestamp, and must be non-decreasing. It
* should be obtained from the clock that is steadily increasing.
*
* This function returns the number of bytes consumed, or one of the
* following negative error codes:
*
* :macro:`NGHTTP3_ERR_NOMEM`
* Out of memory.
* :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`
* User callback failed.
*
* It may return the other error codes. The negative error code means
* that |conn| encountered a connection error, and the connection must
* be closed. Calling nghttp3 API other than `nghttp3_conn_del`
* causes undefined behavior.
*
* This function is available since v1.12.0.
*/
NGHTTP3_EXTERN nghttp3_ssize nghttp3_conn_read_stream2(nghttp3_conn *conn,
int64_t stream_id,
const uint8_t *src,
size_t srclen, int fin,
nghttp3_tstamp ts);

/**
* @function
*
Expand Down
14 changes: 14 additions & 0 deletions lib/nghttp3_conn.c
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,7 @@ static int conn_new(nghttp3_conn **pconn, int server, int callbacks_version,
}
nghttp3_settings_default(&conn->remote.settings);
conn->mem = mem;
conn->ts = settings->initial_ts;
conn->user_data = user_data;
conn->server = server;
conn->rx.goaway_id = NGHTTP3_VARINT_MAX + 1;
Expand Down Expand Up @@ -427,12 +428,25 @@ static int conn_bidi_idtr_open(nghttp3_conn *conn, int64_t stream_id) {
nghttp3_ssize nghttp3_conn_read_stream(nghttp3_conn *conn, int64_t stream_id,
const uint8_t *src, size_t srclen,
int fin) {
return nghttp3_conn_read_stream2(conn, stream_id, src, srclen, fin, conn->ts);
}

nghttp3_ssize nghttp3_conn_read_stream2(nghttp3_conn *conn, int64_t stream_id,
const uint8_t *src, size_t srclen,
int fin, nghttp3_tstamp ts) {
nghttp3_stream *stream;
size_t bidi_nproc;
int rv;

assert(stream_id >= 0);
assert(stream_id <= (int64_t)NGHTTP3_MAX_VARINT);
assert(conn->ts <= ts);

/* Guard against the case that nghttp3_settings.initial_ts is not
set. */
if (ts != UINT64_MAX) {
conn->ts = ts;
}

stream = nghttp3_conn_find_stream(conn, stream_id);
if (stream == NULL) {
Expand Down
1 change: 1 addition & 0 deletions lib/nghttp3_conn.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ struct nghttp3_conn {
nghttp3_pq spq;
} sched[NGHTTP3_URGENCY_LEVELS];
const nghttp3_mem *mem;
nghttp3_tstamp ts;
void *user_data;
int server;
uint16_t flags;
Expand Down
6 changes: 6 additions & 0 deletions lib/nghttp3_settings.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ void nghttp3_settings_default_versioned(int settings_version,

switch (settings_version) {
case NGHTTP3_SETTINGS_VERSION:
settings->initial_ts = UINT64_MAX;
/* fall through */
case NGHTTP3_SETTINGS_V2:
case NGHTTP3_SETTINGS_V1:
settings->max_field_section_size = NGHTTP3_VARINT_MAX;
settings->qpack_encoder_max_dtable_capacity =
Expand Down Expand Up @@ -86,6 +89,9 @@ size_t nghttp3_settingslen_version(int settings_version) {
switch (settings_version) {
case NGHTTP3_SETTINGS_VERSION:
return sizeof(settings);
case NGHTTP3_SETTINGS_V2:
return offsetof(nghttp3_settings, origin_list) +
sizeof(settings.origin_list);
Comment on lines +93 to +94

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using offsetof with the first field of the next version is a more robust way to determine the size of a versioned struct, as it correctly handles potential compiler padding between members. The current approach of offsetof(last_field) + sizeof(last_field) can be incorrect if there's padding after last_field.

    return offsetof(nghttp3_settings, initial_ts);

case NGHTTP3_SETTINGS_V1:
return offsetof(nghttp3_settings, h3_datagram) +
sizeof(settings.h3_datagram);
Expand Down
35 changes: 22 additions & 13 deletions tests/nghttp3_settings_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,25 +43,31 @@ const MunitSuite settings_suite = {
void test_nghttp3_settings_convert_to_latest(void) {
nghttp3_settings *src, srcbuf, settingsbuf;
const nghttp3_settings *dest;
size_t v1len;
const uint8_t origins[] = "foo";
nghttp3_vec origin_list = {
.base = (uint8_t *)origins,
.len = strsize(origins),
};
size_t v2len;

nghttp3_settings_default_versioned(NGHTTP3_SETTINGS_V1, &srcbuf);
nghttp3_settings_default_versioned(NGHTTP3_SETTINGS_V2, &srcbuf);

srcbuf.max_field_section_size = 1000000007;
srcbuf.qpack_max_dtable_capacity = 1000000009;
srcbuf.qpack_encoder_max_dtable_capacity = 781506803;
srcbuf.qpack_blocked_streams = 478324193;
srcbuf.enable_connect_protocol = 1;
srcbuf.h3_datagram = 1;
srcbuf.origin_list = &origin_list;

v1len = nghttp3_settingslen_version(NGHTTP3_SETTINGS_V1);
v2len = nghttp3_settingslen_version(NGHTTP3_SETTINGS_V2);

src = malloc(v1len);
src = malloc(v2len);

memcpy(src, &srcbuf, v1len);
memcpy(src, &srcbuf, v2len);

dest =
nghttp3_settings_convert_to_latest(&settingsbuf, NGHTTP3_SETTINGS_V1, src);
nghttp3_settings_convert_to_latest(&settingsbuf, NGHTTP3_SETTINGS_V2, src);

free(src);

Expand All @@ -76,7 +82,8 @@ void test_nghttp3_settings_convert_to_latest(void) {
assert_uint8(srcbuf.enable_connect_protocol, ==,
dest->enable_connect_protocol);
assert_uint8(srcbuf.h3_datagram, ==, dest->h3_datagram);
assert_null(dest->origin_list);
assert_ptr_equal(srcbuf.origin_list, dest->origin_list);
assert_uint64(UINT64_MAX, ==, dest->initial_ts);
}

void test_nghttp3_settings_convert_to_old(void) {
Expand All @@ -86,11 +93,11 @@ void test_nghttp3_settings_convert_to_old(void) {
.base = (uint8_t *)origins,
.len = strsize(origins),
};
size_t v1len;
size_t v2len;

v1len = nghttp3_settingslen_version(NGHTTP3_SETTINGS_V1);
v2len = nghttp3_settingslen_version(NGHTTP3_SETTINGS_V2);

dest = malloc(v1len);
dest = malloc(v2len);

nghttp3_settings_default(&src);
src.max_field_section_size = 1000000007;
Expand All @@ -100,11 +107,12 @@ void test_nghttp3_settings_convert_to_old(void) {
src.enable_connect_protocol = 1;
src.h3_datagram = 1;
src.origin_list = &origin_list;
src.initial_ts = 6398888;

nghttp3_settings_convert_to_old(NGHTTP3_SETTINGS_V1, dest, &src);
nghttp3_settings_convert_to_old(NGHTTP3_SETTINGS_V2, dest, &src);

memset(&destbuf, 0, sizeof(destbuf));
memcpy(&destbuf, dest, v1len);
memcpy(&destbuf, dest, v2len);

free(dest);

Expand All @@ -117,5 +125,6 @@ void test_nghttp3_settings_convert_to_old(void) {
assert_uint8(src.enable_connect_protocol, ==,
destbuf.enable_connect_protocol);
assert_uint8(src.h3_datagram, ==, destbuf.h3_datagram);
assert_null(destbuf.origin_list);
assert_ptr_equal(src.origin_list, destbuf.origin_list);
assert_uint64(0, ==, destbuf.initial_ts);
}
Loading