diff --git a/lib/nghttp2_session.c b/lib/nghttp2_session.c index fbf4ecc3..4ecc5260 100644 --- a/lib/nghttp2_session.c +++ b/lib/nghttp2_session.c @@ -1908,8 +1908,8 @@ static int nghttp2_session_update_initial_window_size &arg); } -static int nghttp2_disable_flow_control_func(nghttp2_map_entry *entry, - void *ptr) +static int nghttp2_disable_remote_flow_control_func(nghttp2_map_entry *entry, + void *ptr) { nghttp2_session *session; nghttp2_stream *stream; @@ -1934,14 +1934,38 @@ static int nghttp2_disable_flow_control_func(nghttp2_map_entry *entry, } /* - * Disable connection-level flow control and stream-level flow control - * of existing streams. + * Disable remote side connection-level flow control and stream-level + * flow control of existing streams. */ -static int nghttp2_session_disable_flow_control(nghttp2_session *session) +static int nghttp2_session_disable_remote_flow_control +(nghttp2_session *session) { session->remote_flow_control = 0; return nghttp2_map_each(&session->streams, - nghttp2_disable_flow_control_func, session); + nghttp2_disable_remote_flow_control_func, session); +} + +static int nghttp2_disable_local_flow_control_func(nghttp2_map_entry *entry, + void *ptr) +{ + nghttp2_stream *stream; + stream = (nghttp2_stream*)entry; + stream->local_flow_control = 0; + return 0; +} + +/* + * Disable stream-level flow control in local side of existing + * streams. + */ +static void nghttp2_session_disable_local_flow_control +(nghttp2_session *session) +{ + int rv; + session->local_flow_control = 0; + rv = nghttp2_map_each(&session->streams, + nghttp2_disable_local_flow_control_func, NULL); + assert(rv == 0); } void nghttp2_session_update_local_settings(nghttp2_session *session, @@ -1949,10 +1973,17 @@ void nghttp2_session_update_local_settings(nghttp2_session *session, size_t niv) { size_t i; + uint8_t old_flow_control = + session->local_settings[NGHTTP2_SETTINGS_FLOW_CONTROL_OPTIONS]; + for(i = 0; i < niv; ++i) { assert(iv[i].settings_id > 0 && iv[i].settings_id <= NGHTTP2_SETTINGS_MAX); session->local_settings[iv[i].settings_id] = iv[i].value; } + if(old_flow_control == 0 && + session->local_settings[NGHTTP2_SETTINGS_FLOW_CONTROL_OPTIONS]) { + nghttp2_session_disable_local_flow_control(session); + } } int nghttp2_session_on_settings_received(nghttp2_session *session, @@ -1993,7 +2024,7 @@ int nghttp2_session_on_settings_received(nghttp2_session *session, case NGHTTP2_SETTINGS_FLOW_CONTROL_OPTIONS: if(entry->value & 0x1) { if(session->remote_settings[entry->settings_id] == 0) { - rv = nghttp2_session_disable_flow_control(session); + rv = nghttp2_session_disable_remote_flow_control(session); if(rv != 0) { return rv; } diff --git a/tests/main.c b/tests/main.c index ca87916f..16c56fb8 100644 --- a/tests/main.c +++ b/tests/main.c @@ -165,8 +165,10 @@ int main(int argc, char* argv[]) test_nghttp2_session_defer_data) || !CU_add_test(pSuite, "session_flow_control", test_nghttp2_session_flow_control) || - !CU_add_test(pSuite, "session_flow_control_disable", - test_nghttp2_session_flow_control_disable) || + !CU_add_test(pSuite, "session_flow_control_disable_remote", + test_nghttp2_session_flow_control_disable_remote) || + !CU_add_test(pSuite, "session_flow_control_disable_local", + test_nghttp2_session_flow_control_disable_local) || !CU_add_test(pSuite, "session_data_read_temporal_failure", test_nghttp2_session_data_read_temporal_failure) || !CU_add_test(pSuite, "session_on_request_recv_callback", diff --git a/tests/nghttp2_session_test.c b/tests/nghttp2_session_test.c index e570440c..170190af 100644 --- a/tests/nghttp2_session_test.c +++ b/tests/nghttp2_session_test.c @@ -1770,7 +1770,7 @@ void test_nghttp2_submit_settings(void) my_user_data ud; nghttp2_outbound_item *item; nghttp2_frame *frame; - nghttp2_settings_entry iv[3]; + nghttp2_settings_entry iv[4]; iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; iv[0].value = 50; @@ -1778,9 +1778,12 @@ void test_nghttp2_submit_settings(void) iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; iv[1].value = 16*1024; + iv[2].settings_id = NGHTTP2_SETTINGS_FLOW_CONTROL_OPTIONS; + iv[2].value = 1; + /* This is duplicate entry */ - iv[2].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; - iv[2].value = 150; + iv[3].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; + iv[3].value = 150; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback = null_send_callback; @@ -1788,29 +1791,34 @@ void test_nghttp2_submit_settings(void) nghttp2_session_server_new(&session, &callbacks, &ud); CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT == - nghttp2_submit_settings(session, iv, 3)); + nghttp2_submit_settings(session, iv, 4)); /* Make sure that local settings are not changed */ CU_ASSERT(NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS == session->local_settings[NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS]); CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE == session->local_settings[NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]); + CU_ASSERT(0 == + session->local_settings[NGHTTP2_SETTINGS_FLOW_CONTROL_OPTIONS]); - /* Now sends without 3rd one */ - CU_ASSERT(0 == nghttp2_submit_settings(session, iv, 2)); + /* Now sends without 4th one */ + CU_ASSERT(0 == nghttp2_submit_settings(session, iv, 3)); /* Make sure that local settings are changed */ CU_ASSERT(50 == session->local_settings[NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS]); CU_ASSERT(16*1024 == session->local_settings[NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]); + CU_ASSERT(1 == + session->local_settings[NGHTTP2_SETTINGS_FLOW_CONTROL_OPTIONS]); + CU_ASSERT(0 == session->local_flow_control); item = nghttp2_session_get_next_ob_item(session); CU_ASSERT(NGHTTP2_SETTINGS == OB_CTRL_TYPE(item)); frame = item->frame; - CU_ASSERT(2 == frame->settings.niv); + CU_ASSERT(3 == frame->settings.niv); CU_ASSERT(50 == frame->settings.iv[0].value); CU_ASSERT(NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS == frame->settings.iv[0].settings_id); @@ -2433,7 +2441,7 @@ void test_nghttp2_session_flow_control(void) nghttp2_session_del(session); } -void test_nghttp2_session_flow_control_disable(void) +void test_nghttp2_session_flow_control_disable_remote(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; @@ -2487,6 +2495,33 @@ void test_nghttp2_session_flow_control_disable(void) nghttp2_session_del(session); } +void test_nghttp2_session_flow_control_disable_local(void) +{ + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_stream *stream; + nghttp2_settings_entry iv[1]; + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + + nghttp2_session_client_new(&session, &callbacks, NULL); + + stream = nghttp2_session_open_stream(session, 1, NGHTTP2_FLAG_NONE, + NGHTTP2_PRI_DEFAULT, + NGHTTP2_STREAM_OPENING, NULL); + + iv[0].settings_id = NGHTTP2_SETTINGS_FLOW_CONTROL_OPTIONS; + iv[0].value = 1; + + CU_ASSERT(0 == nghttp2_submit_settings(session, iv, + sizeof(iv)/sizeof(iv[0]))); + CU_ASSERT(0 == stream->local_flow_control); + CU_ASSERT(0 == session->local_flow_control); + + nghttp2_session_del(session); +} + void test_nghttp2_session_data_read_temporal_failure(void) { nghttp2_session *session; diff --git a/tests/nghttp2_session_test.h b/tests/nghttp2_session_test.h index ed3a6843..deb9dccb 100644 --- a/tests/nghttp2_session_test.h +++ b/tests/nghttp2_session_test.h @@ -73,7 +73,8 @@ void test_nghttp2_session_stream_close_on_headers_push(void); void test_nghttp2_session_stop_data_with_rst_stream(void); void test_nghttp2_session_defer_data(void); void test_nghttp2_session_flow_control(void); -void test_nghttp2_session_flow_control_disable(void); +void test_nghttp2_session_flow_control_disable_remote(void); +void test_nghttp2_session_flow_control_disable_local(void); void test_nghttp2_session_data_read_temporal_failure(void); void test_nghttp2_session_on_request_recv_callback(void); void test_nghttp2_session_on_stream_close(void);