Add nghttp2_session_create_idle_stream() API

See GH-436
This commit is contained in:
Tatsuhiro Tsujikawa 2015-11-28 15:04:39 +09:00
parent aacac613af
commit 12b2e0a2b3
5 changed files with 147 additions and 0 deletions

View File

@ -86,6 +86,7 @@ APIDOCS= \
nghttp2_session_consume.rst \
nghttp2_session_consume_connection.rst \
nghttp2_session_consume_stream.rst \
nghttp2_session_create_idle_stream.rst \
nghttp2_session_del.rst \
nghttp2_session_find_stream.rst \
nghttp2_session_get_effective_local_window_size.rst \

View File

@ -2887,6 +2887,51 @@ nghttp2_session_change_stream_priority(nghttp2_session *session,
int32_t stream_id,
const nghttp2_priority_spec *pri_spec);
/**
* @function
*
* Creates idle stream with the given |stream_id|, and priority
* |pri_spec|.
*
* The stream creation is done without sending PRIORITY frame, which
* means that peer does not know about the existence of this idle
* stream in the local endpoint.
*
* RFC 7540 does not disallow the use of creation of idle stream with
* odd or even stream ID regardless of client or server. So this
* function can create odd or even stream ID regardless of client or
* server. But probably it is a bit safer to use the stream ID the
* local endpoint can initiate (in other words, use odd stream ID for
* client, and even stream ID for server), to avoid potential
* collision from peer's instruction. Also we can use
* `nghttp2_session_set_next_stream_id()` to avoid to open created
* idle streams accidentally if we follow this recommendation.
*
* 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.
*
* Otherwise, 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; or stream denoted by |stream_id|
* already exists; or |stream_id| cannot be used to create idle
* stream (in other words, local endpoint has already opened
* stream ID greater than or equal to the given stream ID; or
* |stream_id| is 0
*/
NGHTTP2_EXTERN int
nghttp2_session_create_idle_stream(nghttp2_session *session, int32_t stream_id,
const nghttp2_priority_spec *pri_spec);
/**
* @function
*

View File

@ -6731,3 +6731,32 @@ int nghttp2_session_change_stream_priority(
return nghttp2_session_reprioritize_stream(session, stream, &pri_spec_copy);
}
int nghttp2_session_create_idle_stream(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 == 0 || stream_id == pri_spec->stream_id ||
!session_detect_idle_stream(session, 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);
stream =
nghttp2_session_open_stream(session, stream_id, NGHTTP2_STREAM_FLAG_NONE,
&pri_spec_copy, NGHTTP2_STREAM_IDLE, NULL);
if (!stream) {
return NGHTTP2_ERR_NOMEM;
}
return 0;
}

View File

@ -8464,6 +8464,77 @@ void test_nghttp2_session_change_stream_priority(void) {
nghttp2_session_del(session);
}
void test_nghttp2_session_create_idle_stream(void) {
nghttp2_session *session;
nghttp2_session_callbacks callbacks;
nghttp2_stream *stream2, *stream4, *stream8, *stream10;
nghttp2_priority_spec pri_spec;
int rv;
memset(&callbacks, 0, sizeof(callbacks));
nghttp2_session_server_new(&session, &callbacks, NULL);
stream2 = open_stream(session, 2);
nghttp2_priority_spec_init(&pri_spec, 2, 111, 1);
rv = nghttp2_session_create_idle_stream(session, 4, &pri_spec);
CU_ASSERT(0 == rv);
stream4 = nghttp2_session_get_stream_raw(session, 4);
CU_ASSERT(4 == stream4->stream_id);
CU_ASSERT(111 == stream4->weight);
CU_ASSERT(stream2 == stream4->dep_prev);
CU_ASSERT(stream4 == stream2->dep_next);
/* If pri_spec->stream_id does not exist, and it is idle stream, it
is created too */
nghttp2_priority_spec_init(&pri_spec, 8, 109, 0);
rv = nghttp2_session_create_idle_stream(session, 8, &pri_spec);
CU_ASSERT(0 == rv);
stream8 = nghttp2_session_get_stream_raw(session, 8);
stream10 = nghttp2_session_get_stream_raw(session, 10);
CU_ASSERT(8 == stream8->stream_id);
CU_ASSERT(109 == stream8->weight);
CU_ASSERT(10 == stream10->stream_id);
CU_ASSERT(16 == stream10->weight);
CU_ASSERT(stream10 == stream8->dep_prev);
CU_ASSERT(&session->root == stream10->dep_prev);
/* It is an error to attempt to create already existing idle
stream */
rv = nghttp2_session_create_idle_stream(session, 4, &pri_spec);
CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == rv);
/* It is an error to depend on itself */
pri_spec.stream_id = 6;
rv = nghttp2_session_create_idle_stream(session, 6, &pri_spec);
CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == rv);
/* It is an error to create root stream (0) as idle stream */
rv = nghttp2_session_create_idle_stream(session, 0, &pri_spec);
CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == rv);
/* It is an error to create non-idle stream */
session->next_stream_id = 20;
pri_spec.stream_id = 2;
rv = nghttp2_session_create_idle_stream(session, 18, &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) {

View File

@ -136,6 +136,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_create_idle_stream(void);
void test_nghttp2_http_mandatory_headers(void);
void test_nghttp2_http_content_length(void);
void test_nghttp2_http_content_length_mismatch(void);