From 4098512b5dbeff301c67eed1bf4dcb3aa644c500 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Sat, 18 Aug 2018 17:38:58 +0900 Subject: [PATCH] Tweak nghttp2_session_set_stream_user_data nghttp2_session_set_stream_user_data now works for a stream which is not created yet, but the request which creates the stream is queued. --- lib/includes/nghttp2/nghttp2.h | 7 +++++-- lib/nghttp2_session.c | 36 +++++++++++++++++++++++++++++++--- tests/main.c | 2 ++ tests/nghttp2_session_test.c | 33 +++++++++++++++++++++++++++++++ tests/nghttp2_session_test.h | 1 + 5 files changed, 74 insertions(+), 5 deletions(-) diff --git a/lib/includes/nghttp2/nghttp2.h b/lib/includes/nghttp2/nghttp2.h index 4756ea64..ede96f66 100644 --- a/lib/includes/nghttp2/nghttp2.h +++ b/lib/includes/nghttp2/nghttp2.h @@ -3802,10 +3802,13 @@ nghttp2_priority_spec_check_default(const nghttp2_priority_spec *pri_spec); * .. warning:: * * This function returns assigned stream ID if it succeeds. But - * that stream is not opened yet. The application must not submit + * that stream is not created yet. The application must not submit * frame to that stream ID before * :type:`nghttp2_before_frame_send_callback` is called for this - * frame. + * frame. This means `nghttp2_session_get_stream_user_data()` does + * not work before the callback. But + * `nghttp2_session_set_stream_user_data()` handles this situation + * specially, and it can set data to a stream during this period. * */ NGHTTP2_EXTERN int32_t nghttp2_submit_request( diff --git a/lib/nghttp2_session.c b/lib/nghttp2_session.c index 7f3aa88f..418ad666 100644 --- a/lib/nghttp2_session.c +++ b/lib/nghttp2_session.c @@ -7208,12 +7208,42 @@ int nghttp2_session_set_stream_user_data(nghttp2_session *session, int32_t stream_id, void *stream_user_data) { nghttp2_stream *stream; + nghttp2_frame *frame; + nghttp2_outbound_item *item; + stream = nghttp2_session_get_stream(session, stream_id); - if (!stream) { + if (stream) { + stream->stream_user_data = stream_user_data; + return 0; + } + + if (session->server || !nghttp2_session_is_my_stream_id(session, stream_id) || + !nghttp2_outbound_queue_top(&session->ob_syn)) { return NGHTTP2_ERR_INVALID_ARGUMENT; } - stream->stream_user_data = stream_user_data; - return 0; + + frame = &nghttp2_outbound_queue_top(&session->ob_syn)->frame; + assert(frame->hd.type == NGHTTP2_HEADERS); + + if (frame->hd.stream_id > stream_id || + (uint32_t)stream_id >= session->next_stream_id) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + + for (item = session->ob_syn.head; item; item = item->qnext) { + if (item->frame.hd.stream_id < stream_id) { + continue; + } + + if (item->frame.hd.stream_id > stream_id) { + break; + } + + item->aux_data.headers.stream_user_data = stream_user_data; + return 0; + } + + return NGHTTP2_ERR_INVALID_ARGUMENT; } int nghttp2_session_resume_data(nghttp2_session *session, int32_t stream_id) { diff --git a/tests/main.c b/tests/main.c index aa584ad8..13865de7 100644 --- a/tests/main.c +++ b/tests/main.c @@ -319,6 +319,8 @@ int main() { test_nghttp2_session_pause_data) || !CU_add_test(pSuite, "session_no_closed_streams", test_nghttp2_session_no_closed_streams) || + !CU_add_test(pSuite, "session_set_stream_user_data", + test_nghttp2_session_set_stream_user_data) || !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 1394dce6..a7a5605b 100644 --- a/tests/nghttp2_session_test.c +++ b/tests/nghttp2_session_test.c @@ -10696,6 +10696,39 @@ void test_nghttp2_session_no_closed_streams(void) { nghttp2_option_del(option); } +void test_nghttp2_session_set_stream_user_data(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + int32_t stream_id; + int user_data1, user_data2; + int rv; + const uint8_t *datap; + ssize_t datalen; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + + nghttp2_session_client_new(&session, &callbacks, NULL); + + stream_id = nghttp2_submit_request(session, NULL, reqnv, ARRLEN(reqnv), NULL, + &user_data1); + + rv = nghttp2_session_set_stream_user_data(session, stream_id, &user_data2); + + CU_ASSERT(0 == rv); + + datalen = nghttp2_session_mem_send(session, &datap); + + CU_ASSERT(datalen > 0); + + CU_ASSERT(&user_data2 == + nghttp2_session_get_stream_user_data(session, stream_id)); + + CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == + nghttp2_session_set_stream_user_data(session, 2, NULL)); + + 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 c06ad9aa..35a48b82 100644 --- a/tests/nghttp2_session_test.h +++ b/tests/nghttp2_session_test.h @@ -158,6 +158,7 @@ void test_nghttp2_session_cancel_from_before_frame_send(void); void test_nghttp2_session_removed_closed_stream(void); void test_nghttp2_session_pause_data(void); void test_nghttp2_session_no_closed_streams(void); +void test_nghttp2_session_set_stream_user_data(void); void test_nghttp2_http_mandatory_headers(void); void test_nghttp2_http_content_length(void); void test_nghttp2_http_content_length_mismatch(void);