diff --git a/lib/nghttp2_session.c b/lib/nghttp2_session.c index 926a4002..c02bd702 100644 --- a/lib/nghttp2_session.c +++ b/lib/nghttp2_session.c @@ -2160,12 +2160,21 @@ static int session_after_frame_sent(nghttp2_session *session) } } + /* On EOF, we have already detached data if stream is not NULL. + If stream is NULL, we cannot detach data. Please note that + application may issue nghttp2_submit_data() in + on_frame_send_callback, which attach data to stream. We don't + want to detach it. */ + if(data_frame->eof) { + active_outbound_item_reset(aob); + + return 0; + } + /* If session is closed or RST_STREAM was queued, we won't send further data. */ - if(data_frame->eof || - nghttp2_session_predicate_data_send(session, + if(nghttp2_session_predicate_data_send(session, data_frame->hd.stream_id) != 0) { - if(stream) { rv = nghttp2_stream_detach_data(stream, &session->ob_pq, session->last_cycle); diff --git a/tests/main.c b/tests/main.c index cc6a066a..02a8895f 100644 --- a/tests/main.c +++ b/tests/main.c @@ -144,6 +144,8 @@ int main(int argc, char* argv[]) !CU_add_test(pSuite, "submit_data", test_nghttp2_submit_data) || !CU_add_test(pSuite, "submit_data_read_length_too_large", test_nghttp2_submit_data_read_length_too_large) || + !CU_add_test(pSuite, "submit_data_twice", + test_nghttp2_submit_data_twice) || !CU_add_test(pSuite, "submit_request_with_data", test_nghttp2_submit_request_with_data) || !CU_add_test(pSuite, "submit_request_without_data", diff --git a/tests/nghttp2_session_test.c b/tests/nghttp2_session_test.c index 31d447cc..73dce6b0 100644 --- a/tests/nghttp2_session_test.c +++ b/tests/nghttp2_session_test.c @@ -3207,6 +3207,72 @@ void test_nghttp2_submit_data_read_length_smallest(void) nghttp2_session_del(session); } +static ssize_t submit_data_twice_data_source_read_callback +(nghttp2_session *session, int32_t stream_id, + uint8_t *buf, size_t len, uint32_t *data_flags, + nghttp2_data_source *source, void *user_data) +{ + *data_flags |= NGHTTP2_DATA_FLAG_EOF; + return nghttp2_min(len, 16); +} + +static int submit_data_twice_on_frame_send_callback(nghttp2_session *session, + const nghttp2_frame *frame, + void *user_data) +{ + static int called = 0; + int rv; + nghttp2_data_provider data_prd; + + if(called == 0) { + called = 1; + + data_prd.read_callback = submit_data_twice_data_source_read_callback; + + rv = nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM, + frame->hd.stream_id, &data_prd); + CU_ASSERT(0 == rv); + } + + return 0; +} + +void test_nghttp2_submit_data_twice(void) +{ + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_data_provider data_prd; + my_user_data ud; + accumulator acc; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = accumulator_send_callback; + callbacks.on_frame_send_callback = submit_data_twice_on_frame_send_callback; + + data_prd.read_callback = submit_data_twice_data_source_read_callback; + + acc.length = 0; + ud.acc = &acc; + + CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &ud)); + + nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, + &pri_spec_default, NGHTTP2_STREAM_OPENING, + NULL); + + CU_ASSERT(0 == nghttp2_submit_data(session, + NGHTTP2_FLAG_NONE, 1, &data_prd)); + + CU_ASSERT(0 == nghttp2_session_send(session)); + + fprintf(stderr, "acc.length=%zu\n", acc.length); + + /* We should have sent 2 DATA frame with 16 bytes payload each */ + CU_ASSERT(NGHTTP2_FRAME_HDLEN * 2 + 16 * 2 == acc.length); + + nghttp2_session_del(session); +} + void test_nghttp2_submit_request_with_data(void) { nghttp2_session *session; diff --git a/tests/nghttp2_session_test.h b/tests/nghttp2_session_test.h index 81d32098..4b1be44b 100644 --- a/tests/nghttp2_session_test.h +++ b/tests/nghttp2_session_test.h @@ -64,6 +64,7 @@ void test_nghttp2_session_reprioritize_stream(void); void test_nghttp2_submit_data(void); void test_nghttp2_submit_data_read_length_too_large(void); void test_nghttp2_submit_data_read_length_smallest(void); +void test_nghttp2_submit_data_twice(void); void test_nghttp2_submit_request_with_data(void); void test_nghttp2_submit_request_without_data(void); void test_nghttp2_submit_response_with_data(void);