From 5cf21ec187c9b7e99403c3999ca38a9e4f631265 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Sun, 21 Aug 2016 19:01:51 +0900 Subject: [PATCH] Add APIs to return the number of data that remote endpoint can send 2 APIs are added. nghttp2_session_get_local_window_size() returns the amount of data that the remote endpoint can send without receiving connection level WINDOW_UPDATE. nghttp2_session_get_stream_local_window_size() returns the amount of data that the remote endpoint can send without receiving stream level WINDOW_UPDATE. --- lib/includes/nghttp2/nghttp2.h | 43 ++++++++++++++++++++ lib/nghttp2_session.c | 24 ++++++++++++ tests/nghttp2_session_test.c | 72 ++++++++++++++++++++++++++++++++++ 3 files changed, 139 insertions(+) diff --git a/lib/includes/nghttp2/nghttp2.h b/lib/includes/nghttp2/nghttp2.h index 730b7410..887280f7 100644 --- a/lib/includes/nghttp2/nghttp2.h +++ b/lib/includes/nghttp2/nghttp2.h @@ -3007,12 +3007,35 @@ nghttp2_session_get_stream_effective_recv_data_length(nghttp2_session *session, * `nghttp2_submit_window_update()`. This function takes into account * that and returns effective window size. * + * This function does not take into account the amount of received + * data from the remote endpoint. Use + * `nghttp2_session_get_stream_local_window_size()` to know the amount + * of data the remote endpoint can send without receiving stream level + * WINDOW_UPDATE frame. Note that each stream is still subject to the + * connection level flow control. + * * This function returns -1 if it fails. */ NGHTTP2_EXTERN int32_t nghttp2_session_get_stream_effective_local_window_size(nghttp2_session *session, int32_t stream_id); +/** + * @function + * + * Returns the amount of data that the remote endpoint can send + * without receiving stream level WINDOW_UPDATE frame. It is also + * subject to the connection level flow control. So the actual amount + * of data to send is + * min(`nghttp2_session_get_stream_local_window_size()`, + * `nghttp2_session_get_local_window_size()`). + * + * This function returns -1 if it fails. + */ +NGHTTP2_EXTERN int32_t +nghttp2_session_get_stream_local_window_size(nghttp2_session *session, + int32_t stream_id); + /** * @function * @@ -3038,11 +3061,31 @@ nghttp2_session_get_effective_recv_data_length(nghttp2_session *session); * `nghttp2_submit_window_update()`. This function takes into account * that and returns effective window size. * + * This function does not take into account the amount of received + * data from the remote endpoint. Use + * `nghttp2_session_get_local_window_size()` to know the amount of + * data the remote endpoint can send without receiving + * connection-level WINDOW_UPDATE frame. Note that each stream is + * still subject to the stream level flow control. + * * This function returns -1 if it fails. */ NGHTTP2_EXTERN int32_t nghttp2_session_get_effective_local_window_size(nghttp2_session *session); +/** + * @function + * + * Returns the amount of data that the remote endpoint can send + * without receiving connection level WINDOW_UPDATE frame. Note that + * each stream is still subject to the stream level flow control (see + * `nghttp2_session_get_stream_local_window_size()`). + * + * This function returns -1 if it fails. + */ +NGHTTP2_EXTERN int32_t +nghttp2_session_get_local_window_size(nghttp2_session *session); + /** * @function * diff --git a/lib/nghttp2_session.c b/lib/nghttp2_session.c index 0d8734c4..ceef2877 100644 --- a/lib/nghttp2_session.c +++ b/lib/nghttp2_session.c @@ -7165,6 +7165,26 @@ nghttp2_session_get_stream_effective_local_window_size(nghttp2_session *session, return stream->local_window_size; } +int32_t nghttp2_session_get_stream_local_window_size(nghttp2_session *session, + int32_t stream_id) { + nghttp2_stream *stream; + int32_t size; + stream = nghttp2_session_get_stream(session, stream_id); + if (stream == NULL) { + return -1; + } + + size = stream->local_window_size - stream->recv_window_size; + + /* size could be negative if local endpoint reduced + SETTINGS_INITIAL_WINDOW_SIZE */ + if (size < 0) { + return 0; + } + + return size; +} + int32_t nghttp2_session_get_effective_recv_data_length(nghttp2_session *session) { return session->recv_window_size < 0 ? 0 : session->recv_window_size; @@ -7175,6 +7195,10 @@ nghttp2_session_get_effective_local_window_size(nghttp2_session *session) { return session->local_window_size; } +int32_t nghttp2_session_get_local_window_size(nghttp2_session *session) { + return session->local_window_size - session->recv_window_size; +} + int32_t nghttp2_session_get_stream_remote_window_size(nghttp2_session *session, int32_t stream_id) { nghttp2_stream *stream; diff --git a/tests/nghttp2_session_test.c b/tests/nghttp2_session_test.c index 95117092..7fe6e591 100644 --- a/tests/nghttp2_session_test.c +++ b/tests/nghttp2_session_test.c @@ -5134,6 +5134,7 @@ void test_nghttp2_submit_settings_update_local_window_size(void) { nghttp2_stream *stream; nghttp2_frame ack_frame; nghttp2_mem *mem; + nghttp2_option *option; mem = nghttp2_mem_default(); nghttp2_frame_settings_init(&ack_frame.settings, NGHTTP2_FLAG_ACK, NULL, 0); @@ -5169,6 +5170,32 @@ void test_nghttp2_submit_settings_update_local_window_size(void) { nghttp2_session_del(session); + /* Without auto-window update */ + nghttp2_option_new(&option); + nghttp2_option_set_no_auto_window_update(option, 1); + + nghttp2_session_server_new2(&session, &callbacks, NULL, option); + + nghttp2_option_del(option); + + stream = open_recv_stream(session, 1); + stream->local_window_size = NGHTTP2_INITIAL_WINDOW_SIZE + 100; + stream->recv_window_size = 32768; + + CU_ASSERT(0 == nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 1)); + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &ack_frame, 0)); + + stream = nghttp2_session_get_stream(session, 1); + + CU_ASSERT(32768 == stream->recv_window_size); + CU_ASSERT(16 * 1024 + 100 == stream->local_window_size); + /* Check that we can handle the case where local_window_size < + recv_window_size */ + CU_ASSERT(0 == nghttp2_session_get_stream_local_window_size(session, 1)); + + nghttp2_session_del(session); + /* Check overflow case */ iv[0].value = 128 * 1024; nghttp2_session_server_new(&session, &callbacks, NULL); @@ -6556,19 +6583,34 @@ void test_nghttp2_session_get_effective_local_window_size(void) { CU_ASSERT(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE + 1000 == nghttp2_session_get_effective_local_window_size(session)); + CU_ASSERT(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE + 1000 == + nghttp2_session_get_local_window_size(session)); CU_ASSERT(0 == nghttp2_session_get_effective_recv_data_length(session)); nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 0, -50); /* Now session->recv_window_size = -50 */ + CU_ASSERT(-50 == session->recv_window_size); + CU_ASSERT(50 == session->recv_reduction); CU_ASSERT(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE + 950 == nghttp2_session_get_effective_local_window_size(session)); + CU_ASSERT(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE + 1000 == + nghttp2_session_get_local_window_size(session)); CU_ASSERT(0 == nghttp2_session_get_effective_recv_data_length(session)); session->recv_window_size += 50; + /* Now session->recv_window_size = 0 */ + + CU_ASSERT(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE + 950 == + nghttp2_session_get_local_window_size(session)); + nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 0, 100); + CU_ASSERT(50 == session->recv_window_size); + CU_ASSERT(0 == session->recv_reduction); CU_ASSERT(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE + 1050 == nghttp2_session_get_effective_local_window_size(session)); + CU_ASSERT(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE + 1000 == + nghttp2_session_get_local_window_size(session)); CU_ASSERT(50 == nghttp2_session_get_effective_recv_data_length(session)); /* Check stream flow control */ @@ -6577,6 +6619,8 @@ void test_nghttp2_session_get_effective_local_window_size(void) { CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE + 1000 == nghttp2_session_get_stream_effective_local_window_size(session, 1)); + CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE + 1000 == + nghttp2_session_get_stream_local_window_size(session, 1)); CU_ASSERT(0 == nghttp2_session_get_stream_effective_recv_data_length(session, 1)); @@ -6584,6 +6628,8 @@ void test_nghttp2_session_get_effective_local_window_size(void) { /* Now stream->recv_window_size = -50 */ CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE + 950 == nghttp2_session_get_stream_effective_local_window_size(session, 1)); + CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE + 1000 == + nghttp2_session_get_stream_local_window_size(session, 1)); CU_ASSERT(0 == nghttp2_session_get_stream_effective_recv_data_length(session, 1)); @@ -6592,6 +6638,8 @@ void test_nghttp2_session_get_effective_local_window_size(void) { nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 1, 100); CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE + 1050 == nghttp2_session_get_stream_effective_local_window_size(session, 1)); + CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE + 1000 == + nghttp2_session_get_stream_local_window_size(session, 1)); CU_ASSERT(50 == nghttp2_session_get_stream_effective_recv_data_length(session, 1)); @@ -9586,7 +9634,11 @@ void test_nghttp2_session_set_local_window_size(void) { CU_ASSERT(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE + 1 == stream->local_window_size); CU_ASSERT(4096 == stream->recv_window_size); + CU_ASSERT(65536 - 4096 == + nghttp2_session_get_stream_local_window_size(session, 1)); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_WINDOW_UPDATE == item->frame.hd.type); CU_ASSERT(1 == item->frame.window_update.hd.stream_id); CU_ASSERT(1 == item->frame.window_update.window_size_increment); @@ -9599,7 +9651,11 @@ void test_nghttp2_session_set_local_window_size(void) { CU_ASSERT(32768 == stream->local_window_size); CU_ASSERT(-28672 == stream->recv_window_size); CU_ASSERT(32768 == stream->recv_reduction); + CU_ASSERT(65536 - 4096 == + nghttp2_session_get_stream_local_window_size(session, 1)); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(item == NULL); /* Increase local window size */ @@ -9608,6 +9664,8 @@ void test_nghttp2_session_set_local_window_size(void) { CU_ASSERT(49152 == stream->local_window_size); CU_ASSERT(-12288 == stream->recv_window_size); CU_ASSERT(16384 == stream->recv_reduction); + CU_ASSERT(65536 - 4096 == + nghttp2_session_get_stream_local_window_size(session, 1)); CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); /* Increase local window again */ @@ -9616,7 +9674,11 @@ void test_nghttp2_session_set_local_window_size(void) { CU_ASSERT(65537 == stream->local_window_size); CU_ASSERT(4096 == stream->recv_window_size); CU_ASSERT(0 == stream->recv_reduction); + CU_ASSERT(65537 - 4096 == + nghttp2_session_get_stream_local_window_size(session, 1)); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(1 == item->frame.window_update.window_size_increment); CU_ASSERT(0 == nghttp2_session_send(session)); @@ -9628,7 +9690,10 @@ void test_nghttp2_session_set_local_window_size(void) { CU_ASSERT(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE + 1 == session->local_window_size); CU_ASSERT(4096 == session->recv_window_size); + CU_ASSERT(65536 - 4096 == nghttp2_session_get_local_window_size(session)); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_WINDOW_UPDATE == item->frame.hd.type); CU_ASSERT(0 == item->frame.window_update.hd.stream_id); CU_ASSERT(1 == item->frame.window_update.window_size_increment); @@ -9641,7 +9706,10 @@ void test_nghttp2_session_set_local_window_size(void) { CU_ASSERT(32768 == session->local_window_size); CU_ASSERT(-28672 == session->recv_window_size); CU_ASSERT(32768 == session->recv_reduction); + CU_ASSERT(65536 - 4096 == nghttp2_session_get_local_window_size(session)); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(item == NULL); /* Increase local window size */ @@ -9650,6 +9718,7 @@ void test_nghttp2_session_set_local_window_size(void) { CU_ASSERT(49152 == session->local_window_size); CU_ASSERT(-12288 == session->recv_window_size); CU_ASSERT(16384 == session->recv_reduction); + CU_ASSERT(65536 - 4096 == nghttp2_session_get_local_window_size(session)); CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session)); /* Increase local window again */ @@ -9658,7 +9727,10 @@ void test_nghttp2_session_set_local_window_size(void) { CU_ASSERT(65537 == session->local_window_size); CU_ASSERT(4096 == session->recv_window_size); CU_ASSERT(0 == session->recv_reduction); + CU_ASSERT(65537 - 4096 == nghttp2_session_get_local_window_size(session)); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(1 == item->frame.window_update.window_size_increment); CU_ASSERT(0 == nghttp2_session_send(session));