Fix stream stall when initial window size is decreased

Fix the bug that causes a stream to stall when a receiver, which
enables nghttp2_option_set_no_auto_window_update() option on, sends
SETTINGS_INITIAL_WINDOW_SIZE with the value that is less than or equal
to the amount of data received.  Previously, in this particular case,
when SETTINGS is acknowledged by the sender, the receiver does not try
to send WINDOW_UPDATE frame.  The sender is unable to send more data
because its stream-level window size is smaller than or equal to the
amount of data it has sent.
This commit is contained in:
Tatsuhiro Tsujikawa 2022-03-12 16:52:31 +09:00
parent 072784c9ee
commit 129daeff6f
2 changed files with 73 additions and 3 deletions

View File

@ -4259,9 +4259,16 @@ static int update_local_initial_window_size_func(void *entry, void *ptr) {
return nghttp2_session_add_rst_stream(arg->session, stream->stream_id,
NGHTTP2_FLOW_CONTROL_ERROR);
}
if (!(arg->session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) &&
stream->window_update_queued == 0 &&
nghttp2_should_send_window_update(stream->local_window_size,
if (stream->window_update_queued) {
return 0;
}
if (arg->session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) {
return session_update_stream_consumed_size(arg->session, stream, 0);
}
if (nghttp2_should_send_window_update(stream->local_window_size,
stream->recv_window_size)) {
rv = nghttp2_session_add_window_update(arg->session, NGHTTP2_FLAG_NONE,

View File

@ -3380,6 +3380,11 @@ void test_nghttp2_session_on_settings_received(void) {
nghttp2_nv nv = MAKE_NV(":authority", "example.org");
nghttp2_mem *mem;
nghttp2_option *option;
uint8_t data[2048];
nghttp2_frame_hd hd;
int rv;
ssize_t nread;
nghttp2_stream *stream;
mem = nghttp2_mem_default();
@ -3588,6 +3593,64 @@ void test_nghttp2_session_on_settings_received(void) {
CU_ASSERT(NGHTTP2_GOAWAY == item->frame.hd.type);
nghttp2_session_del(session);
/* Should send WINDOW_UPDATE with no_auto_window_update option on if
the initial window size is decreased and becomes smaller than or
equal to the amount of data that has already received. */
nghttp2_option_new(&option);
nghttp2_option_set_no_auto_window_update(option, 1);
nghttp2_session_server_new2(&session, &callbacks, NULL, option);
iv[0].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
iv[0].value = 1024;
rv = nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 1);
CU_ASSERT(0 == rv);
rv = nghttp2_session_send(session);
CU_ASSERT(0 == rv);
stream = open_recv_stream(session, 1);
memset(data, 0, sizeof(data));
hd.length = 1024;
hd.type = NGHTTP2_DATA;
hd.flags = NGHTTP2_FLAG_NONE;
hd.stream_id = 1;
nghttp2_frame_pack_frame_hd(data, &hd);
nread =
nghttp2_session_mem_recv(session, data, NGHTTP2_FRAME_HDLEN + hd.length);
CU_ASSERT((ssize_t)(NGHTTP2_FRAME_HDLEN + hd.length) == nread);
rv = nghttp2_session_consume(session, 1, hd.length);
CU_ASSERT(0 == rv);
CU_ASSERT((int32_t)hd.length == stream->recv_window_size);
CU_ASSERT((int32_t)hd.length == stream->consumed_size);
nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_ACK, NULL, 0);
rv = nghttp2_session_on_settings_received(session, &frame, 0);
CU_ASSERT(0 == rv);
CU_ASSERT(1024 == stream->local_window_size);
CU_ASSERT(0 == stream->recv_window_size);
CU_ASSERT(0 == stream->consumed_size);
item = nghttp2_session_get_next_ob_item(session);
CU_ASSERT(NULL != item);
CU_ASSERT(NGHTTP2_WINDOW_UPDATE == item->frame.hd.type);
CU_ASSERT((int32_t)hd.length ==
item->frame.window_update.window_size_increment);
nghttp2_session_del(session);
nghttp2_option_del(option);
}
void test_nghttp2_session_on_push_promise_received(void) {