diff --git a/lib/nghttp2_session.c b/lib/nghttp2_session.c index f99dfb7c..88b5fb65 100644 --- a/lib/nghttp2_session.c +++ b/lib/nghttp2_session.c @@ -1780,6 +1780,12 @@ static int session_prep_frame(nghttp2_session *session, rv = session_predicate_headers_send(session, stream); if (rv != 0) { + // If stream was alreay closed, + // nghttp2_session_get_stream() returns NULL, but item is + // still attached to the stream. Search stream including + // closed again. + stream = + nghttp2_session_get_stream_raw(session, frame->hd.stream_id); if (stream && stream->item == item) { int rv2; @@ -1938,6 +1944,10 @@ static int session_prep_frame(nghttp2_session *session, rv = nghttp2_session_predicate_data_send(session, stream); if (rv != 0) { + // If stream was alreay closed, nghttp2_session_get_stream() + // returns NULL, but item is still attached to the stream. + // Search stream including closed again. + stream = nghttp2_session_get_stream_raw(session, frame->hd.stream_id); if (stream) { int rv2; diff --git a/tests/main.c b/tests/main.c index 3bd70f7c..f83a3e36 100644 --- a/tests/main.c +++ b/tests/main.c @@ -270,6 +270,8 @@ int main(int argc _U_, char *argv[] _U_) { test_nghttp2_session_on_begin_headers_temporal_failure) || !CU_add_test(pSuite, "session_defer_then_close", test_nghttp2_session_defer_then_close) || + !CU_add_test(pSuite, "session_detach_item_from_closed_stream", + test_nghttp2_session_detach_item_from_closed_stream) || !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 5b05b88e..97ca582f 100644 --- a/tests/nghttp2_session_test.c +++ b/tests/nghttp2_session_test.c @@ -7640,6 +7640,53 @@ void test_nghttp2_session_defer_then_close(void) { nghttp2_session_del(session); } +static int submit_response_on_stream_close(nghttp2_session *session, + int32_t stream_id, + uint32_t error_code _U_, + void *user_data _U_) { + nghttp2_data_provider data_prd; + data_prd.read_callback = temporal_failure_data_source_read_callback; + + // Attempt to submit response or data to the stream being closed + switch (stream_id) { + case 1: + CU_ASSERT(0 == nghttp2_submit_response(session, stream_id, resnv, + ARRLEN(resnv), &data_prd)); + break; + case 3: + CU_ASSERT(0 == nghttp2_submit_data(session, NGHTTP2_FLAG_NONE, stream_id, + &data_prd)); + break; + } + + return 0; +} + +void test_nghttp2_session_detach_item_from_closed_stream(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_data_provider data_prd; + + memset(&callbacks, 0, sizeof(callbacks)); + + callbacks.send_callback = null_send_callback; + callbacks.on_stream_close_callback = submit_response_on_stream_close; + + data_prd.read_callback = temporal_failure_data_source_read_callback; + + nghttp2_session_server_new(&session, &callbacks, NULL); + + open_stream(session, 1); + open_stream(session, 3); + + nghttp2_session_close_stream(session, 1, NGHTTP2_NO_ERROR); + nghttp2_session_close_stream(session, 3, NGHTTP2_NO_ERROR); + + CU_ASSERT(0 == nghttp2_session_send(session)); + + 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 fc626ce0..6702f46d 100644 --- a/tests/nghttp2_session_test.h +++ b/tests/nghttp2_session_test.h @@ -128,6 +128,7 @@ void test_nghttp2_session_reset_pending_headers(void); void test_nghttp2_session_send_data_callback(void); 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_http_mandatory_headers(void); void test_nghttp2_http_content_length(void); void test_nghttp2_http_content_length_mismatch(void);