From aa317c89ea7e7d5424b70e563229d3332479aa02 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Sat, 21 Nov 2015 18:22:28 +0900 Subject: [PATCH 1/2] Add API to change stream priority without sending PRIORITY frame The added API is nghttp2_session_change_stream_priority(). This provides the same functionality to re-prioritize stream when PRIORITY frame. is received, but we do it without PRIORITY frame. This could be useful for server to change pushed stream's priority silently. --- doc/Makefile.am | 1 + lib/includes/nghttp2/nghttp2.h | 34 ++++++++++++++++++++++++++++++++++ lib/nghttp2_priority_spec.c | 8 ++++++++ lib/nghttp2_priority_spec.h | 8 ++++++++ lib/nghttp2_session.c | 21 +++++++++++++++++++++ lib/nghttp2_submit.c | 12 ++---------- 6 files changed, 74 insertions(+), 10 deletions(-) diff --git a/doc/Makefile.am b/doc/Makefile.am index 4a4c37ca..0bc4f66c 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -105,6 +105,7 @@ APIDOCS= \ nghttp2_session_mem_recv.rst \ nghttp2_session_mem_send.rst \ nghttp2_session_recv.rst \ + nghttp2_session_change_stream_priority.rst \ nghttp2_session_check_request_allowed.rst \ nghttp2_session_check_server_session.rst \ nghttp2_session_resume_data.rst \ diff --git a/lib/includes/nghttp2/nghttp2.h b/lib/includes/nghttp2/nghttp2.h index d4ba7e3d..94b07753 100644 --- a/lib/includes/nghttp2/nghttp2.h +++ b/lib/includes/nghttp2/nghttp2.h @@ -2853,6 +2853,40 @@ NGHTTP2_EXTERN int nghttp2_session_consume_stream(nghttp2_session *session, int32_t stream_id, size_t size); +/** + * @function + * + * Changes priority of existing stream denoted by |stream_id|. The + * new priority specification is |pri_spec|. + * + * The priority is changed silently and instantly, and no PRIORITY + * frame will be sent to notify the peer of this change. This + * function may be useful for server to change the priority of pushed + * stream. + * + * If |session| is initialized as server, and ``pri_spec->stream_id`` + * points to the idle stream, the idle stream is created if it does + * not exist. The created idle stream will depend on root stream + * (stream 0) with weight 16. + * + * If stream denoted by ``pri_spec->stream_id`` is not found, we use + * default priority instead of given |pri_spec|. That is make stream + * depend on root stream with weight 16. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT` + * Attempted to depend on itself; no stream exist for the given + * |stream_id|. + */ +NGHTTP2_EXTERN int +nghttp2_session_change_stream_priority(nghttp2_session *session, + int32_t stream_id, + const nghttp2_priority_spec *pri_spec); + /** * @function * diff --git a/lib/nghttp2_priority_spec.c b/lib/nghttp2_priority_spec.c index cd254b1f..c2196e30 100644 --- a/lib/nghttp2_priority_spec.c +++ b/lib/nghttp2_priority_spec.c @@ -42,3 +42,11 @@ int nghttp2_priority_spec_check_default(const nghttp2_priority_spec *pri_spec) { return pri_spec->stream_id == 0 && pri_spec->weight == NGHTTP2_DEFAULT_WEIGHT && pri_spec->exclusive == 0; } + +void nghttp2_priority_spec_normalize_weight(nghttp2_priority_spec *pri_spec) { + if (pri_spec->weight < NGHTTP2_MIN_WEIGHT) { + pri_spec->weight = NGHTTP2_MIN_WEIGHT; + } else if (pri_spec->weight > NGHTTP2_MAX_WEIGHT) { + pri_spec->weight = NGHTTP2_MAX_WEIGHT; + } +} diff --git a/lib/nghttp2_priority_spec.h b/lib/nghttp2_priority_spec.h index a325a581..98fac210 100644 --- a/lib/nghttp2_priority_spec.h +++ b/lib/nghttp2_priority_spec.h @@ -31,4 +31,12 @@ #include +/* + * This function normalizes pri_spec->weight if it is out of range. + * If pri_spec->weight is less than NGHTTP2_MIN_WEIGHT, it is set to + * NGHTTP2_MIN_WEIGHT. If pri_spec->weight is larger than + * NGHTTP2_MAX_WEIGHT, it is set to NGHTTP2_MAX_WEIGHT. + */ +void nghttp2_priority_spec_normalize_weight(nghttp2_priority_spec *pri_spec); + #endif /* NGHTTP2_PRIORITY_SPEC_H */ diff --git a/lib/nghttp2_session.c b/lib/nghttp2_session.c index 64e39da5..6e815ca2 100644 --- a/lib/nghttp2_session.c +++ b/lib/nghttp2_session.c @@ -6771,3 +6771,24 @@ nghttp2_stream *nghttp2_session_get_root_stream(nghttp2_session *session) { int nghttp2_session_check_server_session(nghttp2_session *session) { return session->server; } + +int nghttp2_session_change_stream_priority( + nghttp2_session *session, int32_t stream_id, + const nghttp2_priority_spec *pri_spec) { + nghttp2_stream *stream; + nghttp2_priority_spec pri_spec_copy; + + if (stream_id == pri_spec->stream_id) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + stream = nghttp2_session_get_stream_raw(session, stream_id); + if (!stream) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + pri_spec_copy = *pri_spec; + nghttp2_priority_spec_normalize_weight(&pri_spec_copy); + + return nghttp2_session_reprioritize_stream(session, stream, &pri_spec_copy); +} diff --git a/lib/nghttp2_submit.c b/lib/nghttp2_submit.c index 763b4038..65226363 100644 --- a/lib/nghttp2_submit.c +++ b/lib/nghttp2_submit.c @@ -117,14 +117,6 @@ fail2: return rv; } -static void adjust_priority_spec_weight(nghttp2_priority_spec *pri_spec) { - if (pri_spec->weight < NGHTTP2_MIN_WEIGHT) { - pri_spec->weight = NGHTTP2_MIN_WEIGHT; - } else if (pri_spec->weight > NGHTTP2_MAX_WEIGHT) { - pri_spec->weight = NGHTTP2_MAX_WEIGHT; - } -} - static int32_t submit_headers_shared_nva(nghttp2_session *session, uint8_t flags, int32_t stream_id, const nghttp2_priority_spec *pri_spec, @@ -141,7 +133,7 @@ static int32_t submit_headers_shared_nva(nghttp2_session *session, if (pri_spec) { copy_pri_spec = *pri_spec; - adjust_priority_spec_weight(©_pri_spec); + nghttp2_priority_spec_normalize_weight(©_pri_spec); } else { nghttp2_priority_spec_default_init(©_pri_spec); } @@ -206,7 +198,7 @@ int nghttp2_submit_priority(nghttp2_session *session, uint8_t flags _U_, copy_pri_spec = *pri_spec; - adjust_priority_spec_weight(©_pri_spec); + nghttp2_priority_spec_normalize_weight(©_pri_spec); item = nghttp2_mem_malloc(mem, sizeof(nghttp2_outbound_item)); From d62bc26b6212cf1b359b91bd1eb87831527db0f9 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Mon, 23 Nov 2015 19:48:51 +0900 Subject: [PATCH 2/2] Add test for nghttp2_session_change_stream_priority() --- tests/main.c | 2 ++ tests/nghttp2_session_test.c | 35 +++++++++++++++++++++++++++++++++++ tests/nghttp2_session_test.h | 1 + 3 files changed, 38 insertions(+) diff --git a/tests/main.c b/tests/main.c index 14b43dbf..9e0ab033 100644 --- a/tests/main.c +++ b/tests/main.c @@ -282,6 +282,8 @@ int main(int argc _U_, char *argv[] _U_) { !CU_add_test(pSuite, "session_detach_item_from_closed_stream", test_nghttp2_session_detach_item_from_closed_stream) || !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, "http_mandatory_headers", test_nghttp2_http_mandatory_headers) || !CU_add_test(pSuite, "http_content_length", diff --git a/tests/nghttp2_session_test.c b/tests/nghttp2_session_test.c index 32e27a0b..ee17e9e1 100644 --- a/tests/nghttp2_session_test.c +++ b/tests/nghttp2_session_test.c @@ -8319,6 +8319,41 @@ void test_nghttp2_session_flooding(void) { nghttp2_bufs_free(&bufs); } +void test_nghttp2_session_change_stream_priority(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_stream *stream1, *stream2, *stream3; + nghttp2_priority_spec pri_spec; + int rv; + + memset(&callbacks, 0, sizeof(callbacks)); + + nghttp2_session_server_new(&session, &callbacks, NULL); + + stream1 = open_stream(session, 1); + stream3 = open_stream_with_dep_weight(session, 3, 199, stream1); + stream2 = open_stream_with_dep_weight(session, 2, 101, stream3); + + nghttp2_priority_spec_init(&pri_spec, 1, 256, 0); + + rv = nghttp2_session_change_stream_priority(session, 2, &pri_spec); + + CU_ASSERT(0 == rv); + + CU_ASSERT(stream1 == stream2->dep_prev); + CU_ASSERT(256 == stream2->weight); + + /* Cannot change stream which does not exist */ + rv = nghttp2_session_change_stream_priority(session, 5, &pri_spec); + CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == rv); + + /* It is an error to depend on itself */ + rv = nghttp2_session_change_stream_priority(session, 1, &pri_spec); + CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == rv); + + nghttp2_session_del(session); +} + static void check_nghttp2_http_recv_headers_fail( nghttp2_session *session, nghttp2_hd_deflater *deflater, int32_t stream_id, int stream_state, const nghttp2_nv *nva, size_t nvlen) { diff --git a/tests/nghttp2_session_test.h b/tests/nghttp2_session_test.h index ce3954b3..3c14eb3c 100644 --- a/tests/nghttp2_session_test.h +++ b/tests/nghttp2_session_test.h @@ -133,6 +133,7 @@ void test_nghttp2_session_on_begin_headers_temporal_failure(void); 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_http_mandatory_headers(void); void test_nghttp2_http_content_length(void); void test_nghttp2_http_content_length_mismatch(void);