From 6da044cbb51d33c68c66c2f74eb6059b1f1c1b74 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Wed, 2 Jul 2014 21:20:40 +0900 Subject: [PATCH] Send WINDOW_UPDATE for ignored DATA bytes when manual flow control is enabled Since we do not call on_data_chunk_recv_callback for ignored DATA chunk, if nghttp2_option_set_no_auto_connection_window_update is used, application may not have a chance to send connection WINDOW_UPDATE. To fix this, we accumulate those received bytes, and if it exceeds certain number, we automatically send connection-level WINDOW_UPDATE. --- lib/nghttp2_session.c | 22 ++++++++++++++++++++++ lib/nghttp2_session.h | 9 +++++++++ lib/nghttp2_submit.c | 6 ++++++ 3 files changed, 37 insertions(+) diff --git a/lib/nghttp2_session.c b/lib/nghttp2_session.c index 014fcd6b..9ae6cff1 100644 --- a/lib/nghttp2_session.c +++ b/lib/nghttp2_session.c @@ -3889,6 +3889,10 @@ static int session_update_recv_connection_window_size session->recv_window_size); if(rv == 0) { session->recv_window_size = 0; + /* recv_ign_window_size keeps track of ignored DATA bytes + before any connection-level WINDOW_UPDATE therefore, we can + reset it here. */ + session->recv_ign_window_size = 0; } else { return rv; } @@ -5027,12 +5031,30 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, readlen, iframe->payloadleft)); if(readlen > 0) { + session->recv_ign_window_size += readlen; + /* Update connection-level flow control window for ignored DATA frame too */ rv = session_update_recv_connection_window_size(session, readlen); if(nghttp2_is_fatal(rv)) { return rv; } + + if((session->opt_flags & + NGHTTP2_OPTMASK_NO_AUTO_CONNECTION_WINDOW_UPDATE) && + nghttp2_should_send_window_update + (session->local_window_size, session->recv_ign_window_size)) { + + rv = nghttp2_session_add_window_update + (session, NGHTTP2_FLAG_NONE, 0, session->recv_ign_window_size); + + if(nghttp2_is_fatal(rv)) { + return rv; + } + + session->recv_window_size -= session->recv_ign_window_size; + session->recv_ign_window_size = 0; + } } if(iframe->payloadleft) { diff --git a/lib/nghttp2_session.h b/lib/nghttp2_session.h index 871fca6f..0cf28ffc 100644 --- a/lib/nghttp2_session.h +++ b/lib/nghttp2_session.h @@ -200,6 +200,15 @@ struct nghttp2_session { WINDOW_UPDATE. This could be negative after submitting negative value to WINDOW_UPDATE. */ int32_t recv_window_size; + /* The number of bytes in ignored DATA frame received without + connection-level WINDOW_UPDATE. Since we do not call + on_data_chunk_recv_callback for ignored DATA chunk, if + nghttp2_option_set_no_auto_connection_window_update is used, + application may not have a chance to send connection + WINDOW_UPDATE. To fix this, we accumulate those received bytes, + and if it exceeds certain number, we automatically send + connection-level WINDOW_UPDATE. */ + int32_t recv_ign_window_size; /* The amount of recv_window_size cut using submitting negative value to WINDOW_UPDATE */ int32_t recv_reduction; diff --git a/lib/nghttp2_submit.c b/lib/nghttp2_submit.c index 748d47d6..48a4b876 100644 --- a/lib/nghttp2_submit.c +++ b/lib/nghttp2_submit.c @@ -348,6 +348,11 @@ int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags, if(rv != 0) { return rv; } + + /* recv_ign_window_size keeps track of ignored DATA bytes before + any connection-level WINDOW_UPDATE therefore, we can reset it + here. */ + session->recv_ign_window_size = 0; } else { stream = nghttp2_session_get_stream(session, stream_id); if(stream) { @@ -362,6 +367,7 @@ int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags, return 0; } } + if(window_size_increment > 0) { return nghttp2_session_add_window_update(session, flags, stream_id, window_size_increment);