Add SETTINGS_NO_RFC7540_PRIORITIES

Add SETTINGS_NO_RFC7540_PRIORITIES to disable RFC7540 priorities.  If
disabled, streams are served in FIFO.
This commit is contained in:
Tatsuhiro Tsujikawa 2022-06-07 18:06:30 +09:00
parent 8d48686cec
commit 9812a0bc81
16 changed files with 548 additions and 40 deletions

View File

@ -703,7 +703,12 @@ typedef enum {
* SETTINGS_ENABLE_CONNECT_PROTOCOL * SETTINGS_ENABLE_CONNECT_PROTOCOL
* (`RFC 8441 <https://tools.ietf.org/html/rfc8441>`_) * (`RFC 8441 <https://tools.ietf.org/html/rfc8441>`_)
*/ */
NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL = 0x08 NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL = 0x08,
/**
* SETTINGS_NO_RFC7540_PRIORITIES (`RFC 9218
* <https://datatracker.ietf.org/doc/html/rfc9218>`_)
*/
NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES = 0x09
} nghttp2_settings_id; } nghttp2_settings_id;
/* Note: If we add SETTINGS, update the capacity of /* Note: If we add SETTINGS, update the capacity of
NGHTTP2_INBOUND_NUM_IV as well */ NGHTTP2_INBOUND_NUM_IV as well */
@ -2693,6 +2698,11 @@ nghttp2_option_set_max_deflate_dynamic_table_size(nghttp2_option *option,
* This option prevents the library from retaining closed streams to * This option prevents the library from retaining closed streams to
* maintain the priority tree. If this option is set to nonzero, * maintain the priority tree. If this option is set to nonzero,
* applications can discard closed stream completely to save memory. * applications can discard closed stream completely to save memory.
*
* If
* :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES`
* of value of 1 is submitted via `nghttp2_submit_settings()`, any
* closed streams are not retained regardless of this option.
*/ */
NGHTTP2_EXTERN void nghttp2_option_set_no_closed_streams(nghttp2_option *option, NGHTTP2_EXTERN void nghttp2_option_set_no_closed_streams(nghttp2_option *option,
int val); int val);
@ -3589,6 +3599,11 @@ NGHTTP2_EXTERN int nghttp2_session_consume_stream(nghttp2_session *session,
* found, we use default priority instead of given |pri_spec|. That * found, we use default priority instead of given |pri_spec|. That
* is make stream depend on root stream with weight 16. * is make stream depend on root stream with weight 16.
* *
* If
* :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES`
* of value of 1 is submitted via `nghttp2_submit_settings()`, this
* function does nothing and returns 0.
*
* This function returns 0 if it succeeds, or one of the following * This function returns 0 if it succeeds, or one of the following
* negative error codes: * negative error codes:
* *
@ -3632,6 +3647,11 @@ nghttp2_session_change_stream_priority(nghttp2_session *session,
* found, we use default priority instead of given |pri_spec|. That * found, we use default priority instead of given |pri_spec|. That
* is make stream depend on root stream with weight 16. * is make stream depend on root stream with weight 16.
* *
* If
* :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES`
* of value of 1 is submitted via `nghttp2_submit_settings()`, this
* function does nothing and returns 0.
*
* This function returns 0 if it succeeds, or one of the following * This function returns 0 if it succeeds, or one of the following
* negative error codes: * negative error codes:
* *
@ -3837,6 +3857,11 @@ nghttp2_priority_spec_check_default(const nghttp2_priority_spec *pri_spec);
* :macro:`NGHTTP2_MAX_WEIGHT`, it becomes * :macro:`NGHTTP2_MAX_WEIGHT`, it becomes
* :macro:`NGHTTP2_MAX_WEIGHT`. * :macro:`NGHTTP2_MAX_WEIGHT`.
* *
* If
* :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES`
* of value of 1 is received by a remote endpoint, |pri_spec| is
* ignored, and treated as if ``NULL`` is specified.
*
* The |nva| is an array of name/value pair :type:`nghttp2_nv` with * The |nva| is an array of name/value pair :type:`nghttp2_nv` with
* |nvlen| elements. The application is responsible to include * |nvlen| elements. The application is responsible to include
* required pseudo-header fields (header field whose name starts with * required pseudo-header fields (header field whose name starts with
@ -4057,6 +4082,11 @@ NGHTTP2_EXTERN int nghttp2_submit_trailer(nghttp2_session *session,
* :macro:`NGHTTP2_MIN_WEIGHT`. If it is strictly greater than * :macro:`NGHTTP2_MIN_WEIGHT`. If it is strictly greater than
* :macro:`NGHTTP2_MAX_WEIGHT`, it becomes :macro:`NGHTTP2_MAX_WEIGHT`. * :macro:`NGHTTP2_MAX_WEIGHT`, it becomes :macro:`NGHTTP2_MAX_WEIGHT`.
* *
* If
* :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES`
* of value of 1 is received by a remote endpoint, |pri_spec| is
* ignored, and treated as if ``NULL`` is specified.
*
* The |nva| is an array of name/value pair :type:`nghttp2_nv` with * The |nva| is an array of name/value pair :type:`nghttp2_nv` with
* |nvlen| elements. The application is responsible to include * |nvlen| elements. The application is responsible to include
* required pseudo-header fields (header field whose name starts with * required pseudo-header fields (header field whose name starts with
@ -4184,6 +4214,11 @@ NGHTTP2_EXTERN int nghttp2_submit_data(nghttp2_session *session, uint8_t flags,
* :macro:`NGHTTP2_MAX_WEIGHT`, it becomes * :macro:`NGHTTP2_MAX_WEIGHT`, it becomes
* :macro:`NGHTTP2_MAX_WEIGHT`. * :macro:`NGHTTP2_MAX_WEIGHT`.
* *
* If
* :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES`
* of value of 1 is received by a remote endpoint, this function does
* nothing and returns 0.
*
* This function returns 0 if it succeeds, or one of the following * This function returns 0 if it succeeds, or one of the following
* negative error codes: * negative error codes:
* *

View File

@ -1071,6 +1071,11 @@ int nghttp2_iv_check(const nghttp2_settings_entry *iv, size_t niv) {
return 0; return 0;
} }
break; break;
case NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES:
if (iv[i].value != 0 && iv[i].value != 1) {
return 0;
}
break;
} }
} }
return 1; return 1;

View File

@ -385,6 +385,7 @@ static void init_settings(nghttp2_settings_storage *settings) {
settings->initial_window_size = NGHTTP2_INITIAL_WINDOW_SIZE; settings->initial_window_size = NGHTTP2_INITIAL_WINDOW_SIZE;
settings->max_frame_size = NGHTTP2_MAX_FRAME_SIZE_MIN; settings->max_frame_size = NGHTTP2_MAX_FRAME_SIZE_MIN;
settings->max_header_list_size = UINT32_MAX; settings->max_header_list_size = UINT32_MAX;
settings->no_rfc7540_priorities = UINT32_MAX;
} }
static void active_outbound_item_reset(nghttp2_active_outbound_item *aob, static void active_outbound_item_reset(nghttp2_active_outbound_item *aob,
@ -398,6 +399,15 @@ static void active_outbound_item_reset(nghttp2_active_outbound_item *aob,
aob->state = NGHTTP2_OB_POP_ITEM; aob->state = NGHTTP2_OB_POP_ITEM;
} }
static int stream_less(const void *lhsx, const void *rhsx) {
const nghttp2_stream *lhs, *rhs;
lhs = nghttp2_struct_of(lhsx, nghttp2_stream, pq_entry);
rhs = nghttp2_struct_of(rhsx, nghttp2_stream, pq_entry);
return lhs->seq < rhs->seq;
}
int nghttp2_enable_strict_preface = 1; int nghttp2_enable_strict_preface = 1;
static int session_new(nghttp2_session **session_ptr, static int session_new(nghttp2_session **session_ptr,
@ -442,6 +452,7 @@ static int session_new(nghttp2_session **session_ptr,
(*session_ptr)->pending_local_max_concurrent_stream = (*session_ptr)->pending_local_max_concurrent_stream =
NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS; NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS;
(*session_ptr)->pending_enable_push = 1; (*session_ptr)->pending_enable_push = 1;
(*session_ptr)->pending_no_rfc7540_priorities = UINT8_MAX;
if (server) { if (server) {
(*session_ptr)->server = 1; (*session_ptr)->server = 1;
@ -584,6 +595,12 @@ static int session_new(nghttp2_session **session_ptr,
} }
} }
rv = nghttp2_pq_init(&(*session_ptr)->ob_data, stream_less, mem);
if (rv != 0) {
assert(0);
abort();
}
return 0; return 0;
fail_aob_framebuf: fail_aob_framebuf:
@ -748,6 +765,7 @@ void nghttp2_session_del(nghttp2_session *session) {
settings = next; settings = next;
} }
nghttp2_pq_free(&session->ob_data);
nghttp2_stream_free(&session->root); nghttp2_stream_free(&session->root);
/* Have to free streams first, so that we can check /* Have to free streams first, so that we can check
@ -775,6 +793,7 @@ int nghttp2_session_reprioritize_stream(
nghttp2_priority_spec pri_spec_default; nghttp2_priority_spec pri_spec_default;
const nghttp2_priority_spec *pri_spec = pri_spec_in; const nghttp2_priority_spec *pri_spec = pri_spec_in;
assert(session->pending_no_rfc7540_priorities != 1);
assert(pri_spec->stream_id != stream->stream_id); assert(pri_spec->stream_id != stream->stream_id);
if (!nghttp2_stream_in_dep_tree(stream)) { if (!nghttp2_stream_in_dep_tree(stream)) {
@ -842,6 +861,104 @@ int nghttp2_session_reprioritize_stream(
return 0; return 0;
} }
static int session_ob_data_push(nghttp2_session *session,
nghttp2_stream *stream) {
int rv;
assert(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES);
assert(stream->queued == 0);
rv = nghttp2_pq_push(&session->ob_data, &stream->pq_entry);
if (rv != 0) {
return rv;
}
stream->queued = 1;
return 0;
}
static int session_ob_data_remove(nghttp2_session *session,
nghttp2_stream *stream) {
assert(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES);
assert(stream->queued == 1);
nghttp2_pq_remove(&session->ob_data, &stream->pq_entry);
stream->queued = 0;
return 0;
}
static int session_attach_stream_item(nghttp2_session *session,
nghttp2_stream *stream,
nghttp2_outbound_item *item) {
int rv;
rv = nghttp2_stream_attach_item(stream, item);
if (rv != 0) {
return rv;
}
if (!(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES)) {
return 0;
}
return session_ob_data_push(session, stream);
}
static int session_detach_stream_item(nghttp2_session *session,
nghttp2_stream *stream) {
int rv;
rv = nghttp2_stream_detach_item(stream);
if (rv != 0) {
return rv;
}
if (!(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) ||
!stream->queued) {
return 0;
}
return session_ob_data_remove(session, stream);
}
static int session_defer_stream_item(nghttp2_session *session,
nghttp2_stream *stream, uint8_t flags) {
int rv;
rv = nghttp2_stream_defer_item(stream, flags);
if (rv != 0) {
return rv;
}
if (!(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) ||
!stream->queued) {
return 0;
}
return session_ob_data_remove(session, stream);
}
static int session_resume_deferred_stream_item(nghttp2_session *session,
nghttp2_stream *stream,
uint8_t flags) {
int rv;
rv = nghttp2_stream_resume_deferred_item(stream, flags);
if (rv != 0) {
return rv;
}
if (!(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) ||
(stream->flags & NGHTTP2_STREAM_FLAG_DEFERRED_ALL)) {
return 0;
}
return session_ob_data_push(session, stream);
}
int nghttp2_session_add_item(nghttp2_session *session, int nghttp2_session_add_item(nghttp2_session *session,
nghttp2_outbound_item *item) { nghttp2_outbound_item *item) {
/* TODO Return error if stream is not found for the frame requiring /* TODO Return error if stream is not found for the frame requiring
@ -863,7 +980,7 @@ int nghttp2_session_add_item(nghttp2_session *session,
return NGHTTP2_ERR_DATA_EXIST; return NGHTTP2_ERR_DATA_EXIST;
} }
rv = nghttp2_stream_attach_item(stream, item); rv = session_attach_stream_item(session, stream, item);
if (rv != 0) { if (rv != 0) {
return rv; return rv;
@ -1041,11 +1158,20 @@ nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session,
if (stream) { if (stream) {
assert(stream->state == NGHTTP2_STREAM_IDLE); assert(stream->state == NGHTTP2_STREAM_IDLE);
assert(nghttp2_stream_in_dep_tree(stream)); assert((stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) ||
nghttp2_session_detach_idle_stream(session, stream); nghttp2_stream_in_dep_tree(stream));
rv = nghttp2_stream_dep_remove(stream);
if (rv != 0) { if (nghttp2_stream_in_dep_tree(stream)) {
return NULL; assert(!(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES));
nghttp2_session_detach_idle_stream(session, stream);
rv = nghttp2_stream_dep_remove(stream);
if (rv != 0) {
return NULL;
}
if (session->pending_no_rfc7540_priorities == 1) {
stream->flags |= NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES;
}
} }
} else { } else {
stream = nghttp2_mem_malloc(mem, sizeof(nghttp2_stream)); stream = nghttp2_mem_malloc(mem, sizeof(nghttp2_stream));
@ -1056,7 +1182,21 @@ nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session,
stream_alloc = 1; stream_alloc = 1;
} }
if (pri_spec->stream_id != 0) { if (session->pending_no_rfc7540_priorities == 1 ||
session->remote_settings.no_rfc7540_priorities == 1) {
/* For client which has not received server
SETTINGS_NO_RFC7540_PRIORITIES = 1, send a priority signal
opportunistically. */
if (session->server ||
session->remote_settings.no_rfc7540_priorities == 1) {
nghttp2_priority_spec_default_init(&pri_spec_default);
pri_spec = &pri_spec_default;
}
if (session->pending_no_rfc7540_priorities == 1) {
flags |= NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES;
}
} else if (pri_spec->stream_id != 0) {
dep_stream = nghttp2_session_get_stream_raw(session, pri_spec->stream_id); dep_stream = nghttp2_session_get_stream_raw(session, pri_spec->stream_id);
if (!dep_stream && if (!dep_stream &&
@ -1102,6 +1242,10 @@ nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session,
(int32_t)session->local_settings.initial_window_size, (int32_t)session->local_settings.initial_window_size,
stream_user_data, mem); stream_user_data, mem);
if (session->pending_no_rfc7540_priorities == 1) {
stream->seq = session->stream_seq++;
}
rv = nghttp2_map_insert(&session->streams, stream_id, stream); rv = nghttp2_map_insert(&session->streams, stream_id, stream);
if (rv != 0) { if (rv != 0) {
nghttp2_stream_free(stream); nghttp2_stream_free(stream);
@ -1141,6 +1285,10 @@ nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session,
} }
} }
if (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) {
return stream;
}
if (pri_spec->stream_id == 0) { if (pri_spec->stream_id == 0) {
dep_stream = &session->root; dep_stream = &session->root;
} }
@ -1180,7 +1328,7 @@ int nghttp2_session_close_stream(nghttp2_session *session, int32_t stream_id,
item = stream->item; item = stream->item;
rv = nghttp2_stream_detach_item(stream); rv = session_detach_stream_item(session, stream);
if (rv != 0) { if (rv != 0) {
return rv; return rv;
@ -1230,6 +1378,10 @@ int nghttp2_session_close_stream(nghttp2_session *session, int32_t stream_id,
/* Closes both directions just in case they are not closed yet */ /* Closes both directions just in case they are not closed yet */
stream->flags |= NGHTTP2_STREAM_FLAG_CLOSED; stream->flags |= NGHTTP2_STREAM_FLAG_CLOSED;
if (session->pending_no_rfc7540_priorities == 1) {
return nghttp2_session_destroy_stream(session, stream);
}
if ((session->opt_flags & NGHTTP2_OPTMASK_NO_CLOSED_STREAMS) == 0 && if ((session->opt_flags & NGHTTP2_OPTMASK_NO_CLOSED_STREAMS) == 0 &&
session->server && !is_my_stream_id && session->server && !is_my_stream_id &&
nghttp2_stream_in_dep_tree(stream)) { nghttp2_stream_in_dep_tree(stream)) {
@ -2013,7 +2165,7 @@ static int session_prep_frame(nghttp2_session *session,
if (stream) { if (stream) {
int rv2; int rv2;
rv2 = nghttp2_stream_detach_item(stream); rv2 = session_detach_stream_item(session, stream);
if (nghttp2_is_fatal(rv2)) { if (nghttp2_is_fatal(rv2)) {
return rv2; return rv2;
@ -2032,7 +2184,7 @@ static int session_prep_frame(nghttp2_session *session,
queue when session->remote_window_size > 0 */ queue when session->remote_window_size > 0 */
assert(session->remote_window_size > 0); assert(session->remote_window_size > 0);
rv = nghttp2_stream_defer_item(stream, rv = session_defer_stream_item(session, stream,
NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL); NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL);
if (nghttp2_is_fatal(rv)) { if (nghttp2_is_fatal(rv)) {
@ -2051,7 +2203,8 @@ static int session_prep_frame(nghttp2_session *session,
return rv; return rv;
} }
if (rv == NGHTTP2_ERR_DEFERRED) { if (rv == NGHTTP2_ERR_DEFERRED) {
rv = nghttp2_stream_defer_item(stream, NGHTTP2_STREAM_FLAG_DEFERRED_USER); rv = session_defer_stream_item(session, stream,
NGHTTP2_STREAM_FLAG_DEFERRED_USER);
if (nghttp2_is_fatal(rv)) { if (nghttp2_is_fatal(rv)) {
return rv; return rv;
@ -2062,7 +2215,7 @@ static int session_prep_frame(nghttp2_session *session,
return NGHTTP2_ERR_DEFERRED; return NGHTTP2_ERR_DEFERRED;
} }
if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
rv = nghttp2_stream_detach_item(stream); rv = session_detach_stream_item(session, stream);
if (nghttp2_is_fatal(rv)) { if (nghttp2_is_fatal(rv)) {
return rv; return rv;
@ -2078,7 +2231,7 @@ static int session_prep_frame(nghttp2_session *session,
if (rv != 0) { if (rv != 0) {
int rv2; int rv2;
rv2 = nghttp2_stream_detach_item(stream); rv2 = session_detach_stream_item(session, stream);
if (nghttp2_is_fatal(rv2)) { if (nghttp2_is_fatal(rv2)) {
return rv2; return rv2;
@ -2339,6 +2492,10 @@ static int session_prep_frame(nghttp2_session *session,
nghttp2_outbound_item * nghttp2_outbound_item *
nghttp2_session_get_next_ob_item(nghttp2_session *session) { nghttp2_session_get_next_ob_item(nghttp2_session *session) {
nghttp2_outbound_item *item;
nghttp2_pq_entry *ent;
nghttp2_stream *stream;
if (nghttp2_outbound_queue_top(&session->ob_urgent)) { if (nghttp2_outbound_queue_top(&session->ob_urgent)) {
return nghttp2_outbound_queue_top(&session->ob_urgent); return nghttp2_outbound_queue_top(&session->ob_urgent);
} }
@ -2354,7 +2511,18 @@ nghttp2_session_get_next_ob_item(nghttp2_session *session) {
} }
if (session->remote_window_size > 0) { if (session->remote_window_size > 0) {
return nghttp2_stream_next_outbound_item(&session->root); item = nghttp2_stream_next_outbound_item(&session->root);
if (item) {
return item;
}
ent = nghttp2_pq_top(&session->ob_data);
if (!ent) {
return NULL;
}
stream = nghttp2_struct_of(ent, nghttp2_stream, pq_entry);
return stream->item;
} }
return NULL; return NULL;
@ -2363,6 +2531,8 @@ nghttp2_session_get_next_ob_item(nghttp2_session *session) {
nghttp2_outbound_item * nghttp2_outbound_item *
nghttp2_session_pop_next_ob_item(nghttp2_session *session) { nghttp2_session_pop_next_ob_item(nghttp2_session *session) {
nghttp2_outbound_item *item; nghttp2_outbound_item *item;
nghttp2_pq_entry *ent;
nghttp2_stream *stream;
item = nghttp2_outbound_queue_top(&session->ob_urgent); item = nghttp2_outbound_queue_top(&session->ob_urgent);
if (item) { if (item) {
@ -2388,7 +2558,18 @@ nghttp2_session_pop_next_ob_item(nghttp2_session *session) {
} }
if (session->remote_window_size > 0) { if (session->remote_window_size > 0) {
return nghttp2_stream_next_outbound_item(&session->root); item = nghttp2_stream_next_outbound_item(&session->root);
if (item) {
return item;
}
ent = nghttp2_pq_top(&session->ob_data);
if (!ent) {
return NULL;
}
stream = nghttp2_struct_of(ent, nghttp2_stream, pq_entry);
return stream->item;
} }
return NULL; return NULL;
@ -2550,7 +2731,7 @@ static int session_after_frame_sent1(nghttp2_session *session) {
} }
if (stream && aux_data->eof) { if (stream && aux_data->eof) {
rv = nghttp2_stream_detach_item(stream); rv = session_detach_stream_item(session, stream);
if (nghttp2_is_fatal(rv)) { if (nghttp2_is_fatal(rv)) {
return rv; return rv;
} }
@ -2675,9 +2856,8 @@ static int session_after_frame_sent1(nghttp2_session *session) {
} }
} }
case NGHTTP2_PRIORITY: case NGHTTP2_PRIORITY:
if (session->server) { if (session->server || session->pending_no_rfc7540_priorities == 1) {
return 0; return 0;
;
} }
stream = nghttp2_session_get_stream_raw(session, frame->hd.stream_id); stream = nghttp2_session_get_stream_raw(session, frame->hd.stream_id);
@ -2852,7 +3032,7 @@ static int session_after_frame_sent2(nghttp2_session *session) {
further data. */ further data. */
if (nghttp2_session_predicate_data_send(session, stream) != 0) { if (nghttp2_session_predicate_data_send(session, stream) != 0) {
if (stream) { if (stream) {
rv = nghttp2_stream_detach_item(stream); rv = session_detach_stream_item(session, stream);
if (nghttp2_is_fatal(rv)) { if (nghttp2_is_fatal(rv)) {
return rv; return rv;
@ -3150,7 +3330,7 @@ static ssize_t nghttp2_session_mem_send_internal(nghttp2_session *session,
} }
if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { if (rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
rv = nghttp2_stream_detach_item(stream); rv = session_detach_stream_item(session, stream);
if (nghttp2_is_fatal(rv)) { if (nghttp2_is_fatal(rv)) {
return rv; return rv;
@ -4091,6 +4271,8 @@ int nghttp2_session_on_priority_received(nghttp2_session *session,
int rv; int rv;
nghttp2_stream *stream; nghttp2_stream *stream;
assert(session->pending_no_rfc7540_priorities != 1);
if (frame->hd.stream_id == 0) { if (frame->hd.stream_id == 0) {
return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO, return session_handle_invalid_connection(session, frame, NGHTTP2_ERR_PROTO,
"PRIORITY: stream_id == 0"); "PRIORITY: stream_id == 0");
@ -4148,6 +4330,8 @@ static int session_process_priority_frame(nghttp2_session *session) {
nghttp2_inbound_frame *iframe = &session->iframe; nghttp2_inbound_frame *iframe = &session->iframe;
nghttp2_frame *frame = &iframe->frame; nghttp2_frame *frame = &iframe->frame;
assert(session->pending_no_rfc7540_priorities != 1);
nghttp2_frame_unpack_priority_payload(&frame->priority, iframe->sbuf.pos); nghttp2_frame_unpack_priority_payload(&frame->priority, iframe->sbuf.pos);
return nghttp2_session_on_priority_received(session, frame); return nghttp2_session_on_priority_received(session, frame);
@ -4214,8 +4398,8 @@ static int update_remote_initial_window_size_func(void *entry, void *ptr) {
if (stream->remote_window_size > 0 && if (stream->remote_window_size > 0 &&
nghttp2_stream_check_deferred_by_flow_control(stream)) { nghttp2_stream_check_deferred_by_flow_control(stream)) {
rv = nghttp2_stream_resume_deferred_item( rv = session_resume_deferred_stream_item(
stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL); arg->session, stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL);
if (nghttp2_is_fatal(rv)) { if (nghttp2_is_fatal(rv)) {
return rv; return rv;
@ -4389,6 +4573,9 @@ int nghttp2_session_update_local_settings(nghttp2_session *session,
case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL: case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL:
session->local_settings.enable_connect_protocol = iv[i].value; session->local_settings.enable_connect_protocol = iv[i].value;
break; break;
case NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES:
session->local_settings.no_rfc7540_priorities = iv[i].value;
break;
} }
} }
@ -4547,10 +4734,32 @@ int nghttp2_session_on_settings_received(nghttp2_session *session,
session->remote_settings.enable_connect_protocol = entry->value; session->remote_settings.enable_connect_protocol = entry->value;
break;
case NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES:
if (entry->value != 0 && entry->value != 1) {
return session_handle_invalid_connection(
session, frame, NGHTTP2_ERR_PROTO,
"SETTINGS: invalid SETTINGS_NO_RFC7540_PRIORITIES");
}
if (session->remote_settings.no_rfc7540_priorities != UINT32_MAX &&
session->remote_settings.no_rfc7540_priorities != entry->value) {
return session_handle_invalid_connection(
session, frame, NGHTTP2_ERR_PROTO,
"SETTINGS: SETTINGS_NO_RFC7540_PRIORITIES cannot be changed");
}
session->remote_settings.no_rfc7540_priorities = entry->value;
break; break;
} }
} }
if (session->remote_settings.no_rfc7540_priorities == UINT32_MAX) {
session->remote_settings.no_rfc7540_priorities = 0;
}
if (!noack && !session_is_closing(session)) { if (!noack && !session_is_closing(session)) {
rv = nghttp2_session_add_settings(session, NGHTTP2_FLAG_ACK, NULL, 0); rv = nghttp2_session_add_settings(session, NGHTTP2_FLAG_ACK, NULL, 0);
@ -4833,8 +5042,8 @@ static int session_on_stream_window_update_received(nghttp2_session *session,
if (stream->remote_window_size > 0 && if (stream->remote_window_size > 0 &&
nghttp2_stream_check_deferred_by_flow_control(stream)) { nghttp2_stream_check_deferred_by_flow_control(stream)) {
rv = nghttp2_stream_resume_deferred_item( rv = session_resume_deferred_stream_item(
stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL); session, stream, NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL);
if (nghttp2_is_fatal(rv)) { if (nghttp2_is_fatal(rv)) {
return rv; return rv;
@ -5276,6 +5485,7 @@ static void inbound_frame_set_settings_entry(nghttp2_inbound_frame *iframe) {
case NGHTTP2_SETTINGS_MAX_FRAME_SIZE: case NGHTTP2_SETTINGS_MAX_FRAME_SIZE:
case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE: case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL: case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL:
case NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES:
break; break;
default: default:
DEBUGF("recv: unknown settings id=0x%02x\n", iv.settings_id); DEBUGF("recv: unknown settings id=0x%02x\n", iv.settings_id);
@ -5995,13 +6205,16 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in,
break; break;
case NGHTTP2_PRIORITY: case NGHTTP2_PRIORITY:
rv = session_process_priority_frame(session); if (session->pending_no_rfc7540_priorities != 1 &&
if (nghttp2_is_fatal(rv)) { session->remote_settings.no_rfc7540_priorities != 1) {
return rv; rv = session_process_priority_frame(session);
} if (nghttp2_is_fatal(rv)) {
return rv;
}
if (iframe->state == NGHTTP2_IB_IGN_ALL) { if (iframe->state == NGHTTP2_IB_IGN_ALL) {
return (ssize_t)inlen; return (ssize_t)inlen;
}
} }
session_inbound_frame_reset(session); session_inbound_frame_reset(session);
@ -6870,7 +7083,8 @@ int nghttp2_session_want_write(nghttp2_session *session) {
*/ */
return session->aob.item || nghttp2_outbound_queue_top(&session->ob_urgent) || return session->aob.item || nghttp2_outbound_queue_top(&session->ob_urgent) ||
nghttp2_outbound_queue_top(&session->ob_reg) || nghttp2_outbound_queue_top(&session->ob_reg) ||
(!nghttp2_pq_empty(&session->root.obq) && ((!nghttp2_pq_empty(&session->root.obq) ||
!nghttp2_pq_empty(&session->ob_data)) &&
session->remote_window_size > 0) || session->remote_window_size > 0) ||
(nghttp2_outbound_queue_top(&session->ob_syn) && (nghttp2_outbound_queue_top(&session->ob_syn) &&
!session_is_outgoing_concurrent_streams_max(session)); !session_is_outgoing_concurrent_streams_max(session));
@ -7023,6 +7237,7 @@ int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags,
int rv; int rv;
nghttp2_mem *mem; nghttp2_mem *mem;
nghttp2_inflight_settings *inflight_settings = NULL; nghttp2_inflight_settings *inflight_settings = NULL;
uint8_t no_rfc7540_pri = session->pending_no_rfc7540_priorities;
mem = &session->mem; mem = &session->mem;
@ -7040,6 +7255,21 @@ int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags,
return NGHTTP2_ERR_INVALID_ARGUMENT; return NGHTTP2_ERR_INVALID_ARGUMENT;
} }
for (i = 0; i < niv; ++i) {
if (iv[i].settings_id != NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES) {
continue;
}
if (no_rfc7540_pri == UINT8_MAX) {
no_rfc7540_pri = (uint8_t)iv[i].value;
continue;
}
if (iv[i].value != (uint32_t)no_rfc7540_pri) {
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
}
item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item));
if (item == NULL) { if (item == NULL) {
return NGHTTP2_ERR_NOMEM; return NGHTTP2_ERR_NOMEM;
@ -7114,6 +7344,12 @@ int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags,
} }
} }
if (no_rfc7540_pri == UINT8_MAX) {
session->pending_no_rfc7540_priorities = 0;
} else {
session->pending_no_rfc7540_priorities = no_rfc7540_pri;
}
return 0; return 0;
} }
@ -7242,7 +7478,9 @@ int nghttp2_session_pack_data(nghttp2_session *session, nghttp2_bufs *bufs,
return rv; return rv;
} }
reschedule_stream(stream); if (!(stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES)) {
reschedule_stream(stream);
}
if (frame->hd.length == 0 && (data_flags & NGHTTP2_DATA_FLAG_EOF) && if (frame->hd.length == 0 && (data_flags & NGHTTP2_DATA_FLAG_EOF) &&
(data_flags & NGHTTP2_DATA_FLAG_NO_END_STREAM)) { (data_flags & NGHTTP2_DATA_FLAG_NO_END_STREAM)) {
@ -7316,7 +7554,7 @@ int nghttp2_session_resume_data(nghttp2_session *session, int32_t stream_id) {
return NGHTTP2_ERR_INVALID_ARGUMENT; return NGHTTP2_ERR_INVALID_ARGUMENT;
} }
rv = nghttp2_stream_resume_deferred_item(stream, rv = session_resume_deferred_stream_item(session, stream,
NGHTTP2_STREAM_FLAG_DEFERRED_USER); NGHTTP2_STREAM_FLAG_DEFERRED_USER);
if (nghttp2_is_fatal(rv)) { if (nghttp2_is_fatal(rv)) {
@ -7424,6 +7662,8 @@ uint32_t nghttp2_session_get_remote_settings(nghttp2_session *session,
return session->remote_settings.max_header_list_size; return session->remote_settings.max_header_list_size;
case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL: case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL:
return session->remote_settings.enable_connect_protocol; return session->remote_settings.enable_connect_protocol;
case NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES:
return session->remote_settings.no_rfc7540_priorities;
} }
assert(0); assert(0);
@ -7447,6 +7687,8 @@ uint32_t nghttp2_session_get_local_settings(nghttp2_session *session,
return session->local_settings.max_header_list_size; return session->local_settings.max_header_list_size;
case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL: case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL:
return session->local_settings.enable_connect_protocol; return session->local_settings.enable_connect_protocol;
case NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES:
return session->local_settings.no_rfc7540_priorities;
} }
assert(0); assert(0);
@ -7730,6 +7972,10 @@ int nghttp2_session_change_stream_priority(
nghttp2_stream *stream; nghttp2_stream *stream;
nghttp2_priority_spec pri_spec_copy; nghttp2_priority_spec pri_spec_copy;
if (session->pending_no_rfc7540_priorities == 1) {
return 0;
}
if (stream_id == 0 || stream_id == pri_spec->stream_id) { if (stream_id == 0 || stream_id == pri_spec->stream_id) {
return NGHTTP2_ERR_INVALID_ARGUMENT; return NGHTTP2_ERR_INVALID_ARGUMENT;
} }
@ -7762,6 +8008,10 @@ int nghttp2_session_create_idle_stream(nghttp2_session *session,
nghttp2_stream *stream; nghttp2_stream *stream;
nghttp2_priority_spec pri_spec_copy; nghttp2_priority_spec pri_spec_copy;
if (session->pending_no_rfc7540_priorities == 1) {
return 0;
}
if (stream_id == 0 || stream_id == pri_spec->stream_id || if (stream_id == 0 || stream_id == pri_spec->stream_id ||
!session_detect_idle_stream(session, stream_id)) { !session_detect_idle_stream(session, stream_id)) {
return NGHTTP2_ERR_INVALID_ARGUMENT; return NGHTTP2_ERR_INVALID_ARGUMENT;

View File

@ -165,6 +165,7 @@ typedef struct {
uint32_t max_frame_size; uint32_t max_frame_size;
uint32_t max_header_list_size; uint32_t max_header_list_size;
uint32_t enable_connect_protocol; uint32_t enable_connect_protocol;
uint32_t no_rfc7540_priorities;
} nghttp2_settings_storage; } nghttp2_settings_storage;
typedef enum { typedef enum {
@ -202,6 +203,9 @@ struct nghttp2_session {
response) frame, which are subject to response) frame, which are subject to
SETTINGS_MAX_CONCURRENT_STREAMS limit. */ SETTINGS_MAX_CONCURRENT_STREAMS limit. */
nghttp2_outbound_queue ob_syn; nghttp2_outbound_queue ob_syn;
/* Queue for DATA frames which is used when
SETTINGS_NO_RFC7540_PRIORITIES is enabled. */
nghttp2_pq ob_data;
nghttp2_active_outbound_item aob; nghttp2_active_outbound_item aob;
nghttp2_inbound_frame iframe; nghttp2_inbound_frame iframe;
nghttp2_hd_deflater hd_deflater; nghttp2_hd_deflater hd_deflater;
@ -227,6 +231,9 @@ struct nghttp2_session {
/* Queue of In-flight SETTINGS values. SETTINGS bearing ACK is not /* Queue of In-flight SETTINGS values. SETTINGS bearing ACK is not
considered as in-flight. */ considered as in-flight. */
nghttp2_inflight_settings *inflight_settings_head; nghttp2_inflight_settings *inflight_settings_head;
/* Sequential number across all streams to process streams in
FIFO. */
uint64_t stream_seq;
/* The number of outgoing streams. This will be capped by /* The number of outgoing streams. This will be capped by
remote_settings.max_concurrent_streams. */ remote_settings.max_concurrent_streams. */
size_t num_outgoing_streams; size_t num_outgoing_streams;
@ -328,6 +335,9 @@ struct nghttp2_session {
/* Unacked local ENABLE_CONNECT_PROTOCOL value. We use this to /* Unacked local ENABLE_CONNECT_PROTOCOL value. We use this to
accept :protocol header field before SETTINGS_ACK is received. */ accept :protocol header field before SETTINGS_ACK is received. */
uint8_t pending_enable_connect_protocol; uint8_t pending_enable_connect_protocol;
/* Unacked local SETTINGS_NO_RFC7540_PRIORITIES value, which is
effective before it is acknowledged. */
uint8_t pending_no_rfc7540_priorities;
/* Nonzero if the session is server side. */ /* Nonzero if the session is server side. */
uint8_t server; uint8_t server;
/* Flags indicating GOAWAY is sent and/or received. The flags are /* Flags indicating GOAWAY is sent and/or received. The flags are

View File

@ -484,6 +484,10 @@ int nghttp2_stream_attach_item(nghttp2_stream *stream,
stream->item = item; stream->item = item;
if (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) {
return 0;
}
rv = stream_update_dep_on_attach_item(stream); rv = stream_update_dep_on_attach_item(stream);
if (rv != 0) { if (rv != 0) {
/* This may relave stream->queued == 1, but stream->item == NULL. /* This may relave stream->queued == 1, but stream->item == NULL.
@ -503,6 +507,10 @@ int nghttp2_stream_detach_item(nghttp2_stream *stream) {
stream->item = NULL; stream->item = NULL;
stream->flags = (uint8_t)(stream->flags & ~NGHTTP2_STREAM_FLAG_DEFERRED_ALL); stream->flags = (uint8_t)(stream->flags & ~NGHTTP2_STREAM_FLAG_DEFERRED_ALL);
if (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) {
return 0;
}
return stream_update_dep_on_detach_item(stream); return stream_update_dep_on_detach_item(stream);
} }
@ -514,6 +522,10 @@ int nghttp2_stream_defer_item(nghttp2_stream *stream, uint8_t flags) {
stream->flags |= flags; stream->flags |= flags;
if (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) {
return 0;
}
return stream_update_dep_on_detach_item(stream); return stream_update_dep_on_detach_item(stream);
} }
@ -529,6 +541,10 @@ int nghttp2_stream_resume_deferred_item(nghttp2_stream *stream, uint8_t flags) {
return 0; return 0;
} }
if (stream->flags & NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES) {
return 0;
}
return stream_update_dep_on_attach_item(stream); return stream_update_dep_on_attach_item(stream);
} }

View File

@ -90,8 +90,10 @@ typedef enum {
NGHTTP2_STREAM_FLAG_DEFERRED_USER = 0x08, NGHTTP2_STREAM_FLAG_DEFERRED_USER = 0x08,
/* bitwise OR of NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL and /* bitwise OR of NGHTTP2_STREAM_FLAG_DEFERRED_FLOW_CONTROL and
NGHTTP2_STREAM_FLAG_DEFERRED_USER. */ NGHTTP2_STREAM_FLAG_DEFERRED_USER. */
NGHTTP2_STREAM_FLAG_DEFERRED_ALL = 0x0c NGHTTP2_STREAM_FLAG_DEFERRED_ALL = 0x0c,
/* Indicates that this stream is not subject to RFC7540
priorities scheme. */
NGHTTP2_STREAM_FLAG_NO_RFC7540_PRIORITIES = 0x10,
} nghttp2_stream_flag; } nghttp2_stream_flag;
/* HTTP related flags to enforce HTTP semantics */ /* HTTP related flags to enforce HTTP semantics */

View File

@ -196,7 +196,8 @@ int32_t nghttp2_submit_headers(nghttp2_session *session, uint8_t flags,
flags &= NGHTTP2_FLAG_END_STREAM; flags &= NGHTTP2_FLAG_END_STREAM;
if (pri_spec && !nghttp2_priority_spec_check_default(pri_spec)) { if (pri_spec && !nghttp2_priority_spec_check_default(pri_spec) &&
session->remote_settings.no_rfc7540_priorities != 1) {
rv = detect_self_dependency(session, stream_id, pri_spec); rv = detect_self_dependency(session, stream_id, pri_spec);
if (rv != 0) { if (rv != 0) {
return rv; return rv;
@ -229,6 +230,10 @@ int nghttp2_submit_priority(nghttp2_session *session, uint8_t flags,
mem = &session->mem; mem = &session->mem;
if (session->remote_settings.no_rfc7540_priorities == 1) {
return 0;
}
if (stream_id == 0 || pri_spec == NULL) { if (stream_id == 0 || pri_spec == NULL) {
return NGHTTP2_ERR_INVALID_ARGUMENT; return NGHTTP2_ERR_INVALID_ARGUMENT;
} }
@ -688,7 +693,8 @@ int32_t nghttp2_submit_request(nghttp2_session *session,
return NGHTTP2_ERR_PROTO; return NGHTTP2_ERR_PROTO;
} }
if (pri_spec && !nghttp2_priority_spec_check_default(pri_spec)) { if (pri_spec && !nghttp2_priority_spec_check_default(pri_spec) &&
session->remote_settings.no_rfc7540_priorities != 1) {
rv = detect_self_dependency(session, -1, pri_spec); rv = detect_self_dependency(session, -1, pri_spec);
if (rv != 0) { if (rv != 0) {
return rv; return rv;

View File

@ -114,7 +114,8 @@ Config::Config()
hexdump(false), hexdump(false),
echo_upload(false), echo_upload(false),
no_content_length(false), no_content_length(false),
ktls(false) {} ktls(false),
no_rfc7540_pri(false) {}
Config::~Config() {} Config::~Config() {}
@ -864,6 +865,12 @@ int Http2Handler::connection_made() {
++niv; ++niv;
} }
if (config->no_rfc7540_pri) {
entry[niv].settings_id = NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES;
entry[niv].value = 1;
++niv;
}
r = nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, entry.data(), niv); r = nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, entry.data(), niv);
if (r != 0) { if (r != 0) {
return r; return r;

View File

@ -83,6 +83,7 @@ struct Config {
bool echo_upload; bool echo_upload;
bool no_content_length; bool no_content_length;
bool ktls; bool ktls;
bool no_rfc7540_pri;
Config(); Config();
~Config(); ~Config();
}; };

View File

@ -79,6 +79,8 @@ const char *strsettingsid(int32_t id) {
return "SETTINGS_MAX_HEADER_LIST_SIZE"; return "SETTINGS_MAX_HEADER_LIST_SIZE";
case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL: case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL:
return "SETTINGS_ENABLE_CONNECT_PROTOCOL"; return "SETTINGS_ENABLE_CONNECT_PROTOCOL";
case NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES:
return "SETTINGS_NO_RFC7540_PRIORITIES";
default: default:
return "UNKNOWN"; return "UNKNOWN";
} }

View File

@ -123,7 +123,8 @@ Config::Config()
no_push(false), no_push(false),
expect_continue(false), expect_continue(false),
verify_peer(true), verify_peer(true),
ktls(false) { ktls(false),
no_rfc7540_pri(false) {
nghttp2_option_new(&http2_option); nghttp2_option_new(&http2_option);
nghttp2_option_set_peer_max_concurrent_streams(http2_option, nghttp2_option_set_peer_max_concurrent_streams(http2_option,
peer_max_concurrent_streams); peer_max_concurrent_streams);
@ -935,6 +936,12 @@ size_t populate_settings(nghttp2_settings_entry *iv) {
++niv; ++niv;
} }
if (config.no_rfc7540_pri) {
iv[niv].settings_id = NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES;
iv[niv].value = 1;
++niv;
}
return niv; return niv;
} }
} // namespace } // namespace
@ -2757,6 +2764,8 @@ Options:
Suppress warning on server certificate verification Suppress warning on server certificate verification
failure. failure.
--ktls Enable ktls. --ktls Enable ktls.
--no-rfc7540-pri
Disable RFC7540 priorities.
--version Display version information and exit. --version Display version information and exit.
-h, --help Display this help and exit. -h, --help Display this help and exit.
@ -2813,6 +2822,7 @@ int main(int argc, char **argv) {
{"expect-continue", no_argument, &flag, 13}, {"expect-continue", no_argument, &flag, 13},
{"encoder-header-table-size", required_argument, &flag, 14}, {"encoder-header-table-size", required_argument, &flag, 14},
{"ktls", no_argument, &flag, 15}, {"ktls", no_argument, &flag, 15},
{"no-rfc7540-pri", no_argument, &flag, 16},
{nullptr, 0, nullptr, 0}}; {nullptr, 0, nullptr, 0}};
int option_index = 0; int option_index = 0;
int c = int c =
@ -3044,6 +3054,10 @@ int main(int argc, char **argv) {
// ktls option // ktls option
config.ktls = true; config.ktls = true;
break; break;
case 16:
// no-rfc7540-pri option
config.no_rfc7540_pri = true;
break;
} }
break; break;
default: default:

View File

@ -98,6 +98,7 @@ struct Config {
bool expect_continue; bool expect_continue;
bool verify_peer; bool verify_peer;
bool ktls; bool ktls;
bool no_rfc7540_pri;
}; };
enum class RequestState { INITIAL, ON_REQUEST, ON_RESPONSE, ON_COMPLETE }; enum class RequestState { INITIAL, ON_REQUEST, ON_RESPONSE, ON_COMPLETE };

View File

@ -179,6 +179,8 @@ Options:
--no-content-length --no-content-length
Don't send content-length header field. Don't send content-length header field.
--ktls Enable ktls. --ktls Enable ktls.
--no-rfc7540-pri
Disable RFC7540 priorities.
--version Display version information and exit. --version Display version information and exit.
-h, --help Display this help and exit. -h, --help Display this help and exit.
@ -230,6 +232,7 @@ int main(int argc, char **argv) {
{"no-content-length", no_argument, &flag, 10}, {"no-content-length", no_argument, &flag, 10},
{"encoder-header-table-size", required_argument, &flag, 11}, {"encoder-header-table-size", required_argument, &flag, 11},
{"ktls", no_argument, &flag, 12}, {"ktls", no_argument, &flag, 12},
{"no-rfc7540-pri", no_argument, &flag, 13},
{nullptr, 0, nullptr, 0}}; {nullptr, 0, nullptr, 0}};
int option_index = 0; int option_index = 0;
int c = getopt_long(argc, argv, "DVb:c:d:ehm:n:p:va:w:W:", long_options, int c = getopt_long(argc, argv, "DVb:c:d:ehm:n:p:va:w:W:", long_options,
@ -413,6 +416,10 @@ int main(int argc, char **argv) {
// tls option // tls option
config.ktls = true; config.ktls = true;
break; break;
case 13:
// no-rfc7540-pri option
config.no_rfc7540_pri = true;
break;
} }
break; break;
default: default:

View File

@ -329,6 +329,8 @@ int main(void) {
test_nghttp2_session_no_closed_streams) || test_nghttp2_session_no_closed_streams) ||
!CU_add_test(pSuite, "session_set_stream_user_data", !CU_add_test(pSuite, "session_set_stream_user_data",
test_nghttp2_session_set_stream_user_data) || test_nghttp2_session_set_stream_user_data) ||
!CU_add_test(pSuite, "session_no_rfc7540_priorities",
test_nghttp2_session_no_rfc7540_priorities) ||
!CU_add_test(pSuite, "http_mandatory_headers", !CU_add_test(pSuite, "http_mandatory_headers",
test_nghttp2_http_mandatory_headers) || test_nghttp2_http_mandatory_headers) ||
!CU_add_test(pSuite, "http_content_length", !CU_add_test(pSuite, "http_content_length",

View File

@ -3651,6 +3651,29 @@ void test_nghttp2_session_on_settings_received(void) {
nghttp2_session_del(session); nghttp2_session_del(session);
nghttp2_option_del(option); nghttp2_option_del(option);
/* It is invalid to change SETTINGS_NO_RFC7540_PRIORITIES in the
following SETTINGS. */
nghttp2_session_client_new(&session, &callbacks, NULL);
session->remote_settings.no_rfc7540_priorities = 1;
iv[0].settings_id = NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES;
iv[0].value = 0;
nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE, dup_iv(iv, 1),
1);
CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &frame, 0));
nghttp2_frame_settings_free(&frame.settings, mem);
item = nghttp2_session_get_next_ob_item(session);
CU_ASSERT(NULL != item);
CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type);
nghttp2_session_del(session);
} }
void test_nghttp2_session_on_push_promise_received(void) { void test_nghttp2_session_on_push_promise_received(void) {
@ -5796,6 +5819,37 @@ void test_nghttp2_submit_settings(void) {
CU_ASSERT(50 == session->pending_local_max_concurrent_stream); CU_ASSERT(50 == session->pending_local_max_concurrent_stream);
nghttp2_session_del(session); nghttp2_session_del(session);
/* Bail out if there are contradicting
SETTINGS_NO_RFC7540_PRIORITIES in one SETTINGS. */
nghttp2_session_server_new(&session, &callbacks, &ud);
iv[0].settings_id = NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES;
iv[0].value = 1;
iv[1].settings_id = NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES;
iv[1].value = 0;
CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT ==
nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 2));
nghttp2_session_del(session);
/* Attempt to change SETTINGS_NO_RFC7540_PRIORITIES in the 2nd
SETTINGS. */
nghttp2_session_server_new(&session, &callbacks, &ud);
iv[0].settings_id = NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES;
iv[0].value = 1;
CU_ASSERT(0 == nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 1));
iv[0].settings_id = NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES;
iv[0].value = 0;
CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT ==
nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 1));
nghttp2_session_del(session);
} }
void test_nghttp2_submit_settings_update_local_window_size(void) { void test_nghttp2_submit_settings_update_local_window_size(void) {
@ -11118,6 +11172,101 @@ void test_nghttp2_session_set_stream_user_data(void) {
nghttp2_session_del(session); nghttp2_session_del(session);
} }
void test_nghttp2_session_no_rfc7540_priorities(void) {
nghttp2_session *session;
nghttp2_session_callbacks callbacks;
nghttp2_data_provider data_prd;
my_user_data ud;
nghttp2_outbound_item *item;
nghttp2_mem *mem;
nghttp2_settings_entry iv;
nghttp2_priority_spec pri_spec;
mem = nghttp2_mem_default();
memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
callbacks.send_callback = null_send_callback;
/* Do not use a dependency tree if SETTINGS_NO_RFC7540_PRIORITIES =
1. */
data_prd.read_callback = fixed_length_data_source_read_callback;
ud.data_source_length = 128 * 1024;
CU_ASSERT(0 == nghttp2_session_server_new(&session, &callbacks, &ud));
iv.settings_id = NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES;
iv.value = 1;
CU_ASSERT(0 == nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, &iv, 1));
CU_ASSERT(0 == nghttp2_session_send(session));
open_recv_stream2(session, 1, NGHTTP2_STREAM_OPENING);
CU_ASSERT(0 == nghttp2_submit_response(session, 1, resnv, ARRLEN(resnv),
&data_prd));
item = nghttp2_session_get_next_ob_item(session);
CU_ASSERT(ARRLEN(resnv) == item->frame.headers.nvlen);
assert_nv_equal(resnv, item->frame.headers.nva, item->frame.headers.nvlen,
mem);
CU_ASSERT(0 == nghttp2_session_send(session));
CU_ASSERT(1 == nghttp2_pq_size(&session->ob_data));
CU_ASSERT(nghttp2_pq_empty(&session->root.obq));
nghttp2_session_del(session);
/* Priorities are sent as is before client receives
SETTINGS_NO_RFC7540_PRIORITIES = 1 from server. */
CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, NULL));
iv.settings_id = NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES;
iv.value = 1;
CU_ASSERT(0 == nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, &iv, 1));
pri_spec.stream_id = 5;
pri_spec.weight = 111;
pri_spec.exclusive = 1;
CU_ASSERT(1 == nghttp2_submit_request(session, &pri_spec, reqnv,
ARRLEN(reqnv), NULL, NULL));
item = nghttp2_outbound_queue_top(&session->ob_syn);
CU_ASSERT(NULL != item);
CU_ASSERT(NGHTTP2_HEADERS == item->frame.hd.type);
CU_ASSERT(pri_spec.stream_id == item->frame.headers.pri_spec.stream_id);
CU_ASSERT(pri_spec.weight == item->frame.headers.pri_spec.weight);
CU_ASSERT(pri_spec.exclusive == item->frame.headers.pri_spec.exclusive);
nghttp2_session_del(session);
/* Priorities are defaulted if client received
SETTINGS_NO_RFC7540_PRIORITIES = 1 from server. */
CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, NULL));
iv.settings_id = NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES;
iv.value = 1;
CU_ASSERT(0 == nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, &iv, 1));
session->remote_settings.no_rfc7540_priorities = 1;
pri_spec.stream_id = 5;
pri_spec.weight = 111;
pri_spec.exclusive = 1;
CU_ASSERT(1 == nghttp2_submit_request(session, &pri_spec, reqnv,
ARRLEN(reqnv), NULL, NULL));
item = nghttp2_outbound_queue_top(&session->ob_syn);
CU_ASSERT(NULL != item);
CU_ASSERT(NGHTTP2_HEADERS == item->frame.hd.type);
CU_ASSERT(nghttp2_priority_spec_check_default(&item->frame.headers.pri_spec));
nghttp2_session_del(session);
}
static void check_nghttp2_http_recv_headers_fail( static void check_nghttp2_http_recv_headers_fail(
nghttp2_session *session, nghttp2_hd_deflater *deflater, int32_t stream_id, nghttp2_session *session, nghttp2_hd_deflater *deflater, int32_t stream_id,
int stream_state, const nghttp2_nv *nva, size_t nvlen) { int stream_state, const nghttp2_nv *nva, size_t nvlen) {

View File

@ -162,6 +162,7 @@ void test_nghttp2_session_removed_closed_stream(void);
void test_nghttp2_session_pause_data(void); void test_nghttp2_session_pause_data(void);
void test_nghttp2_session_no_closed_streams(void); void test_nghttp2_session_no_closed_streams(void);
void test_nghttp2_session_set_stream_user_data(void); void test_nghttp2_session_set_stream_user_data(void);
void test_nghttp2_session_no_rfc7540_priorities(void);
void test_nghttp2_http_mandatory_headers(void); void test_nghttp2_http_mandatory_headers(void);
void test_nghttp2_http_content_length(void); void test_nghttp2_http_content_length(void);
void test_nghttp2_http_content_length_mismatch(void); void test_nghttp2_http_content_length_mismatch(void);