Allow server to override RFC 9218 stream priority

Allow server to override RFC 9218 stream priority with
nghttp2_session_change_extpri_stream_priority.
This commit is contained in:
Tatsuhiro Tsujikawa 2022-06-16 19:31:26 +09:00
parent 534284477e
commit 41955b3878
7 changed files with 157 additions and 0 deletions

View File

@ -107,6 +107,7 @@ APIDOCS= \
nghttp2_session_callbacks_set_send_callback.rst \
nghttp2_session_callbacks_set_send_data_callback.rst \
nghttp2_session_callbacks_set_unpack_extension_callback.rst \
nghttp2_session_change_extpri_stream_priority.rst \
nghttp2_session_change_stream_priority.rst \
nghttp2_session_check_request_allowed.rst \
nghttp2_session_check_server_session.rst \

View File

@ -4901,6 +4901,40 @@ NGHTTP2_EXTERN int nghttp2_submit_priority_update(nghttp2_session *session,
const uint8_t *field_value,
size_t field_value_len);
/**
* @function
*
* Changes the priority of the existing stream denoted by |stream_id|.
* The new priority is |extpri|. This function is meant to be used by
* server for :rfc:`9218` extensible prioritization scheme.
*
* If |session| is initialized as client, this function returns
* :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE`. For client, use
* `nghttp2_submit_priority_update()` instead.
*
* If :member:`extpri->urgency <nghttp2_extpri.urgency>` is out of
* bound, it is set to :macro:`NGHTTP2_EXTPRI_URGENCY_LOW`.
*
* If |ignore_client_signal| is nonzero, server starts to ignore
* client priority signals for this stream.
*
* If
* :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES`
* of value of 1 is not submitted via `nghttp2_submit_settings()`,
* this function does nothing and returns 0.
*
* :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM`
* Out of memory.
* :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE`
* The |session| is initialized as client.
* :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT`
* |stream_id| is zero; or a stream denoted by |stream_id| is not
* found.
*/
NGHTTP2_EXTERN int nghttp2_session_change_extpri_stream_priority(
nghttp2_session *session, int32_t stream_id, const nghttp2_extpri *extpri,
int ignore_client_signal);
/**
* @function
*

View File

@ -4084,6 +4084,7 @@ static int session_end_stream_headers_received(nghttp2_session *session,
if (session->server && session_enforce_http_messaging(session) &&
frame->headers.cat == NGHTTP2_HCAT_REQUEST &&
(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) &&
!(stream->flags & NGHTTP2_STREAM_FLAG_IGNORE_CLIENT_PRIORITIES) &&
(stream->http_flags & NGHTTP2_HTTP_FLAG_PRIORITY)) {
rv = session_update_stream_priority(session, stream, stream->http_extpri);
if (rv != 0) {
@ -5333,6 +5334,9 @@ int nghttp2_session_on_priority_update_received(nghttp2_session *session,
stream = nghttp2_session_get_stream_raw(session, priority_update->stream_id);
if (stream) {
/* Stream already exists. */
if (stream->flags & NGHTTP2_STREAM_FLAG_IGNORE_CLIENT_PRIORITIES) {
return session_call_on_frame_received(session, frame);
}
} else if (session_detect_idle_stream(session, priority_update->stream_id)) {
if (session->num_idle_streams + session->num_incoming_streams >=
session->local_settings.max_concurrent_streams) {
@ -8375,3 +8379,38 @@ nghttp2_session_get_hd_deflate_dynamic_table_size(nghttp2_session *session) {
void nghttp2_session_set_user_data(nghttp2_session *session, void *user_data) {
session->user_data = user_data;
}
int nghttp2_session_change_extpri_stream_priority(
nghttp2_session *session, int32_t stream_id,
const nghttp2_extpri *extpri_in, int ignore_client_signal) {
nghttp2_stream *stream;
nghttp2_extpri extpri = *extpri_in;
if (!session->server) {
return NGHTTP2_ERR_INVALID_STATE;
}
if (session->pending_no_rfc7540_priorities != 1) {
return 0;
}
if (stream_id == 0) {
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
stream = nghttp2_session_get_stream_raw(session, stream_id);
if (!stream) {
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
if (extpri.urgency > NGHTTP2_EXTPRI_URGENCY_LOW) {
extpri.urgency = NGHTTP2_EXTPRI_URGENCY_LOW;
}
if (ignore_client_signal) {
stream->flags |= NGHTTP2_STREAM_FLAG_IGNORE_CLIENT_PRIORITIES;
}
return session_update_stream_priority(session, stream,
nghttp2_extpri_to_uint8(&extpri));
}

View File

@ -94,6 +94,8 @@ typedef enum {
/* Indicates that this stream is not subject to RFC7540
priorities scheme. */
NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES = 0x10,
/* Ignore client RFC 9218 priority signal. */
NGHTTP2_STREAM_FLAG_IGNORE_CLIENT_PRIORITIES = 0x20,
} nghttp2_stream_flag;
/* HTTP related flags to enforce HTTP semantics */

View File

@ -317,6 +317,8 @@ int main(void) {
!CU_add_test(pSuite, "session_flooding", test_nghttp2_session_flooding) ||
!CU_add_test(pSuite, "session_change_stream_priority",
test_nghttp2_session_change_stream_priority) ||
!CU_add_test(pSuite, "session_change_extpri_stream_priority",
test_nghttp2_session_change_extpri_stream_priority) ||
!CU_add_test(pSuite, "session_create_idle_stream",
test_nghttp2_session_create_idle_stream) ||
!CU_add_test(pSuite, "session_repeated_priority_change",

View File

@ -10698,6 +10698,84 @@ void test_nghttp2_session_change_stream_priority(void) {
nghttp2_session_del(session);
}
void test_nghttp2_session_change_extpri_stream_priority(void) {
nghttp2_session *session;
nghttp2_session_callbacks callbacks;
nghttp2_bufs bufs;
nghttp2_buf *buf;
ssize_t rv;
nghttp2_option *option;
nghttp2_extension frame;
nghttp2_ext_priority_update priority_update;
nghttp2_extpri extpri;
nghttp2_stream *stream;
static const uint8_t field_value[] = "u=2";
memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
frame_pack_bufs_init(&bufs);
nghttp2_option_new(&option);
nghttp2_option_set_builtin_recv_extension_type(option,
NGHTTP2_PRIORITY_UPDATE);
nghttp2_session_server_new2(&session, &callbacks, NULL, option);
session->pending_no_rfc7540_priorities = 1;
open_recv_stream(session, 1);
extpri.urgency = NGHTTP2_EXTPRI_URGENCY_LOW + 1;
extpri.inc = 1;
rv = nghttp2_session_change_extpri_stream_priority(
session, 1, &extpri, /* ignore_client_signal = */ 0);
CU_ASSERT(0 == rv);
stream = nghttp2_session_get_stream(session, 1);
CU_ASSERT(NGHTTP2_EXTPRI_URGENCY_LOW ==
nghttp2_extpri_uint8_urgency(stream->extpri));
CU_ASSERT(1 == nghttp2_extpri_uint8_inc(stream->extpri));
/* Client can still update stream priority. */
frame.payload = &priority_update;
nghttp2_frame_priority_update_init(&frame, 1, (uint8_t *)field_value,
sizeof(field_value) - 1);
nghttp2_frame_pack_priority_update(&bufs, &frame);
buf = &bufs.head->buf;
rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf));
CU_ASSERT((ssize_t)nghttp2_buf_len(buf) == rv);
CU_ASSERT(2 == stream->extpri);
/* Start to ignore client priority signal for this stream. */
rv = nghttp2_session_change_extpri_stream_priority(
session, 1, &extpri, /* ignore_client_signal = */ 1);
CU_ASSERT(0 == rv);
stream = nghttp2_session_get_stream(session, 1);
CU_ASSERT(NGHTTP2_EXTPRI_URGENCY_LOW ==
nghttp2_extpri_uint8_urgency(stream->extpri));
CU_ASSERT(1 == nghttp2_extpri_uint8_inc(stream->extpri));
buf = &bufs.head->buf;
rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf));
CU_ASSERT((ssize_t)nghttp2_buf_len(buf) == rv);
CU_ASSERT(NGHTTP2_EXTPRI_URGENCY_LOW ==
nghttp2_extpri_uint8_urgency(stream->extpri));
CU_ASSERT(1 == nghttp2_extpri_uint8_inc(stream->extpri));
nghttp2_session_del(session);
nghttp2_option_del(option);
nghttp2_bufs_free(&bufs);
}
void test_nghttp2_session_create_idle_stream(void) {
nghttp2_session *session;
nghttp2_session_callbacks callbacks;

View File

@ -155,6 +155,7 @@ void test_nghttp2_session_defer_then_close(void);
void test_nghttp2_session_detach_item_from_closed_stream(void);
void test_nghttp2_session_flooding(void);
void test_nghttp2_session_change_stream_priority(void);
void test_nghttp2_session_change_extpri_stream_priority(void);
void test_nghttp2_session_create_idle_stream(void);
void test_nghttp2_session_repeated_priority_change(void);
void test_nghttp2_session_repeated_priority_submission(void);