Handle overflow in initial window update in stream
Rename window_size in nghttp2_stream as remote_window_size.
This commit is contained in:
parent
dcfa421d6f
commit
e67096fef3
|
@ -71,6 +71,13 @@ typedef struct nghttp2_session nghttp2_session;
|
|||
*/
|
||||
#define NGHTTP2_PRI_LOWEST ((1U << 31) - 1)
|
||||
|
||||
/**
|
||||
* @macro
|
||||
*
|
||||
* The maximum window size
|
||||
*/
|
||||
#define NGHTTP2_MAX_WINDOW_SIZE ((int32_t)((1U << 31) - 1))
|
||||
|
||||
/**
|
||||
* @macro
|
||||
*
|
||||
|
@ -207,6 +214,10 @@ typedef enum {
|
|||
* Header block inflate/deflate error.
|
||||
*/
|
||||
NGHTTP2_ERR_HEADER_COMP = -523,
|
||||
/**
|
||||
* Flow control error
|
||||
*/
|
||||
NGHTTP2_ERR_FLOW_CONTROL = -524,
|
||||
/**
|
||||
* The errors < :enum:`NGHTTP2_ERR_FATAL` mean that the library is
|
||||
* under unexpected condition and cannot process any further data
|
||||
|
|
|
@ -457,6 +457,8 @@ nghttp2_stream* nghttp2_session_open_stream(nghttp2_session *session,
|
|||
[NGHTTP2_SETTINGS_FLOW_CONTROL_OPTIONS],
|
||||
session->remote_settings
|
||||
[NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE],
|
||||
session->local_settings
|
||||
[NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE],
|
||||
stream_user_data);
|
||||
r = nghttp2_map_insert(&session->streams, &stream->map_entry);
|
||||
if(r != 0) {
|
||||
|
@ -818,7 +820,7 @@ static size_t nghttp2_session_next_data_read(nghttp2_session *session,
|
|||
int32_t session_window_size =
|
||||
session->remote_flow_control ? session->window_size : INT32_MAX;
|
||||
int32_t stream_window_size =
|
||||
stream->remote_flow_control ? stream->window_size : INT32_MAX;
|
||||
stream->remote_flow_control ? stream->remote_window_size : INT32_MAX;
|
||||
int32_t window_size = nghttp2_min(session_window_size,
|
||||
stream_window_size);
|
||||
if(window_size > 0) {
|
||||
|
@ -1509,7 +1511,7 @@ int nghttp2_session_send(nghttp2_session *session)
|
|||
frame = nghttp2_outbound_item_get_data_frame(session->aob.item);
|
||||
stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
|
||||
if(stream && stream->remote_flow_control) {
|
||||
stream->window_size -= len;
|
||||
stream->remote_window_size -= len;
|
||||
}
|
||||
if(session->remote_flow_control) {
|
||||
session->window_size -= len;
|
||||
|
@ -1857,21 +1859,24 @@ int nghttp2_session_on_rst_stream_received(nghttp2_session *session,
|
|||
static int nghttp2_update_initial_window_size_func(nghttp2_map_entry *entry,
|
||||
void *ptr)
|
||||
{
|
||||
int rv;
|
||||
nghttp2_update_window_size_arg *arg;
|
||||
nghttp2_stream *stream;
|
||||
arg = (nghttp2_update_window_size_arg*)ptr;
|
||||
stream = (nghttp2_stream*)entry;
|
||||
nghttp2_stream_update_initial_window_size(stream,
|
||||
arg->new_window_size,
|
||||
arg->old_window_size);
|
||||
rv = nghttp2_stream_update_remote_initial_window_size(stream,
|
||||
arg->new_window_size,
|
||||
arg->old_window_size);
|
||||
if(rv != 0) {
|
||||
return NGHTTP2_ERR_FLOW_CONTROL;
|
||||
}
|
||||
/* If window size gets positive, push deferred DATA frame to
|
||||
outbound queue. */
|
||||
if(stream->deferred_data &&
|
||||
(stream->deferred_flags & NGHTTP2_DEFERRED_FLOW_CONTROL) &&
|
||||
stream->window_size > 0 &&
|
||||
stream->remote_window_size > 0 &&
|
||||
(arg->session->remote_flow_control == 0 ||
|
||||
arg->session->window_size > 0)) {
|
||||
int rv;
|
||||
rv = nghttp2_pq_push(&arg->session->ob_pq, stream->deferred_data);
|
||||
if(rv == 0) {
|
||||
nghttp2_stream_detach_deferred_data(stream);
|
||||
|
@ -2011,10 +2016,15 @@ int nghttp2_session_on_settings_received(nghttp2_session *session,
|
|||
case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:
|
||||
/* Update the initial window size of the all active streams */
|
||||
/* Check that initial_window_size < (1u << 31) */
|
||||
if(entry->value < (1u << 31)) {
|
||||
if(entry->value <= NGHTTP2_MAX_WINDOW_SIZE) {
|
||||
rv = nghttp2_session_update_initial_window_size(session, entry->value);
|
||||
if(rv != 0) {
|
||||
return rv;
|
||||
if(nghttp2_is_fatal(rv)) {
|
||||
return rv;
|
||||
} else {
|
||||
return nghttp2_session_handle_invalid_connection
|
||||
(session, frame, NGHTTP2_FLOW_CONTROL_ERROR);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return nghttp2_session_handle_invalid_connection
|
||||
|
@ -2143,7 +2153,7 @@ static int nghttp2_push_back_deferred_data_func(nghttp2_map_entry *entry,
|
|||
outbound queue. */
|
||||
if(stream->deferred_data &&
|
||||
(stream->deferred_flags & NGHTTP2_DEFERRED_FLOW_CONTROL) &&
|
||||
(stream->remote_flow_control == 0 || stream->window_size > 0)) {
|
||||
(stream->remote_flow_control == 0 || stream->remote_window_size > 0)) {
|
||||
int rv;
|
||||
rv = nghttp2_pq_push(&session->ob_pq, stream->deferred_data);
|
||||
if(rv == 0) {
|
||||
|
@ -2232,15 +2242,16 @@ int nghttp2_session_on_window_update_received(nghttp2_session *session,
|
|||
nghttp2_session_call_on_frame_received(session, frame);
|
||||
return 0;
|
||||
}
|
||||
if(INT32_MAX - frame->window_update.window_size_increment <
|
||||
stream->window_size) {
|
||||
if(NGHTTP2_MAX_WINDOW_SIZE - frame->window_update.window_size_increment <
|
||||
stream->remote_window_size) {
|
||||
int r;
|
||||
r = nghttp2_session_handle_invalid_stream
|
||||
(session, frame, NGHTTP2_FLOW_CONTROL_ERROR);
|
||||
return r;
|
||||
} else {
|
||||
stream->window_size += frame->window_update.window_size_increment;
|
||||
if(stream->window_size > 0 &&
|
||||
stream->remote_window_size +=
|
||||
frame->window_update.window_size_increment;
|
||||
if(stream->remote_window_size > 0 &&
|
||||
(session->remote_flow_control == 0 ||
|
||||
session->window_size > 0) &&
|
||||
stream->deferred_data != NULL &&
|
||||
|
|
|
@ -31,7 +31,8 @@ void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id,
|
|||
nghttp2_stream_state initial_state,
|
||||
uint8_t remote_flow_control,
|
||||
uint8_t local_flow_control,
|
||||
int32_t initial_window_size,
|
||||
int32_t remote_initial_window_size,
|
||||
int32_t local_initial_window_size,
|
||||
void *stream_user_data)
|
||||
{
|
||||
nghttp2_map_entry_init(&stream->map_entry, stream_id);
|
||||
|
@ -45,7 +46,8 @@ void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id,
|
|||
stream->deferred_flags = NGHTTP2_DEFERRED_NONE;
|
||||
stream->remote_flow_control = remote_flow_control;
|
||||
stream->local_flow_control = local_flow_control;
|
||||
stream->window_size = initial_window_size;
|
||||
stream->remote_window_size = remote_initial_window_size;
|
||||
stream->local_window_size = local_initial_window_size;
|
||||
stream->recv_window_size = 0;
|
||||
}
|
||||
|
||||
|
@ -75,12 +77,20 @@ void nghttp2_stream_detach_deferred_data(nghttp2_stream *stream)
|
|||
stream->deferred_flags = NGHTTP2_DEFERRED_NONE;
|
||||
}
|
||||
|
||||
void nghttp2_stream_update_initial_window_size(nghttp2_stream *stream,
|
||||
int32_t new_initial_window_size,
|
||||
int32_t old_initial_window_size)
|
||||
int nghttp2_stream_update_remote_initial_window_size
|
||||
(nghttp2_stream *stream,
|
||||
int32_t new_initial_window_size,
|
||||
int32_t old_initial_window_size)
|
||||
{
|
||||
stream->window_size =
|
||||
new_initial_window_size-(old_initial_window_size-stream->window_size);
|
||||
int64_t new_window_size = (int64_t)stream->remote_window_size +
|
||||
new_initial_window_size - old_initial_window_size;
|
||||
if(INT32_MIN > new_window_size ||
|
||||
new_window_size > NGHTTP2_MAX_WINDOW_SIZE) {
|
||||
return -1;
|
||||
}
|
||||
stream->remote_window_size +=
|
||||
new_initial_window_size - old_initial_window_size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void nghttp2_stream_promise_fulfilled(nghttp2_stream *stream)
|
||||
|
|
|
@ -109,12 +109,16 @@ typedef struct {
|
|||
flow control options off or sending WINDOW_UPDATE with
|
||||
END_FLOW_CONTROL bit set. */
|
||||
uint8_t local_flow_control;
|
||||
/* Current sender window size. This value is computed against the
|
||||
/* Current remote window size. This value is computed against the
|
||||
current initial window size of remote endpoint. */
|
||||
int32_t window_size;
|
||||
int32_t remote_window_size;
|
||||
/* Keep track of the number of bytes received without
|
||||
WINDOW_UPDATE. */
|
||||
int32_t recv_window_size;
|
||||
/* window size for local window control. It is initially set to
|
||||
NGHTTP2_INITIAL_WINDOW_SIZE and could be increased/decreased by
|
||||
submitting WINDOW_UPDATE. See nghttp2_submit_window_update(). */
|
||||
int32_t local_window_size;
|
||||
} nghttp2_stream;
|
||||
|
||||
void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id,
|
||||
|
@ -122,7 +126,8 @@ void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id,
|
|||
nghttp2_stream_state initial_state,
|
||||
uint8_t remote_flow_control,
|
||||
uint8_t local_flow_control,
|
||||
int32_t initial_window_size,
|
||||
int32_t remote_initial_window_size,
|
||||
int32_t local_initial_window_size,
|
||||
void *stream_user_data);
|
||||
|
||||
void nghttp2_stream_free(nghttp2_stream *stream);
|
||||
|
@ -149,13 +154,17 @@ void nghttp2_stream_defer_data(nghttp2_stream *stream,
|
|||
void nghttp2_stream_detach_deferred_data(nghttp2_stream *stream);
|
||||
|
||||
/*
|
||||
* Updates the initial window size with the new value
|
||||
* Updates the remote window size with the new value
|
||||
* |new_initial_window_size|. The |old_initial_window_size| is used to
|
||||
* calculate the current window size.
|
||||
*
|
||||
* This function returns 0 if it succeeds or -1. The failure is due to
|
||||
* overflow.
|
||||
*/
|
||||
void nghttp2_stream_update_initial_window_size(nghttp2_stream *stream,
|
||||
int32_t new_initial_window_size,
|
||||
int32_t old_initial_window_size);
|
||||
int nghttp2_stream_update_remote_initial_window_size
|
||||
(nghttp2_stream *stream,
|
||||
int32_t new_initial_window_size,
|
||||
int32_t old_initial_window_size);
|
||||
|
||||
/*
|
||||
* Call this function if promised stream |stream| is replied with
|
||||
|
|
|
@ -906,8 +906,8 @@ void test_nghttp2_session_on_settings_received(void)
|
|||
NGHTTP2_STREAM_OPENING, NULL);
|
||||
/* Set window size for each streams and will see how settings
|
||||
updates these values */
|
||||
stream1->window_size = 16*1024;
|
||||
stream2->window_size = -48*1024;
|
||||
stream1->remote_window_size = 16*1024;
|
||||
stream2->remote_window_size = -48*1024;
|
||||
|
||||
nghttp2_frame_settings_init(&frame.settings, dup_iv(iv, niv), niv);
|
||||
|
||||
|
@ -919,15 +919,15 @@ void test_nghttp2_session_on_settings_received(void)
|
|||
CU_ASSERT(1 ==
|
||||
session->remote_settings[NGHTTP2_SETTINGS_FLOW_CONTROL_OPTIONS]);
|
||||
|
||||
CU_ASSERT(64*1024 == stream1->window_size);
|
||||
CU_ASSERT(0 == stream2->window_size);
|
||||
CU_ASSERT(64*1024 == stream1->remote_window_size);
|
||||
CU_ASSERT(0 == stream2->remote_window_size);
|
||||
|
||||
frame.settings.iv[2].value = 16*1024;
|
||||
|
||||
CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &frame));
|
||||
|
||||
CU_ASSERT(16*1024 == stream1->window_size);
|
||||
CU_ASSERT(-48*1024 == stream2->window_size);
|
||||
CU_ASSERT(16*1024 == stream1->remote_window_size);
|
||||
CU_ASSERT(-48*1024 == stream2->remote_window_size);
|
||||
|
||||
CU_ASSERT(0 == stream1->remote_flow_control);
|
||||
CU_ASSERT(0 == stream2->remote_flow_control);
|
||||
|
@ -1141,7 +1141,7 @@ void test_nghttp2_session_on_window_update_received(void)
|
|||
|
||||
CU_ASSERT(0 == nghttp2_session_on_window_update_received(session, &frame));
|
||||
CU_ASSERT(1 == user_data.frame_recv_cb_called);
|
||||
CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE+16*1024 == stream->window_size);
|
||||
CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE+16*1024 == stream->remote_window_size);
|
||||
|
||||
data_item = malloc(sizeof(nghttp2_outbound_item));
|
||||
memset(data_item, 0, sizeof(nghttp2_outbound_item));
|
||||
|
@ -1150,7 +1150,8 @@ void test_nghttp2_session_on_window_update_received(void)
|
|||
|
||||
CU_ASSERT(0 == nghttp2_session_on_window_update_received(session, &frame));
|
||||
CU_ASSERT(2 == user_data.frame_recv_cb_called);
|
||||
CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE+16*1024*2 == stream->window_size);
|
||||
CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE+16*1024*2 ==
|
||||
stream->remote_window_size);
|
||||
CU_ASSERT(NULL == stream->deferred_data);
|
||||
|
||||
nghttp2_frame_window_update_free(&frame.window_update);
|
||||
|
@ -2475,12 +2476,12 @@ void test_nghttp2_session_flow_control(void)
|
|||
/* Change initial window size to 16KiB. The window_size becomes
|
||||
negative. */
|
||||
new_initial_window_size = 16*1024;
|
||||
stream->window_size = new_initial_window_size-
|
||||
stream->remote_window_size = new_initial_window_size-
|
||||
(session->remote_settings[NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]
|
||||
-stream->window_size);
|
||||
- stream->remote_window_size);
|
||||
session->remote_settings[NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE] =
|
||||
new_initial_window_size;
|
||||
CU_ASSERT(-48*1024 == stream->window_size);
|
||||
CU_ASSERT(-48*1024 == stream->remote_window_size);
|
||||
|
||||
/* Back 48KiB to stream window */
|
||||
frame.hd.stream_id = 1;
|
||||
|
|
Loading…
Reference in New Issue