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)
|
#define NGHTTP2_PRI_LOWEST ((1U << 31) - 1)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @macro
|
||||||
|
*
|
||||||
|
* The maximum window size
|
||||||
|
*/
|
||||||
|
#define NGHTTP2_MAX_WINDOW_SIZE ((int32_t)((1U << 31) - 1))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @macro
|
* @macro
|
||||||
*
|
*
|
||||||
|
@ -207,6 +214,10 @@ typedef enum {
|
||||||
* Header block inflate/deflate error.
|
* Header block inflate/deflate error.
|
||||||
*/
|
*/
|
||||||
NGHTTP2_ERR_HEADER_COMP = -523,
|
NGHTTP2_ERR_HEADER_COMP = -523,
|
||||||
|
/**
|
||||||
|
* Flow control error
|
||||||
|
*/
|
||||||
|
NGHTTP2_ERR_FLOW_CONTROL = -524,
|
||||||
/**
|
/**
|
||||||
* The errors < :enum:`NGHTTP2_ERR_FATAL` mean that the library is
|
* The errors < :enum:`NGHTTP2_ERR_FATAL` mean that the library is
|
||||||
* under unexpected condition and cannot process any further data
|
* 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],
|
[NGHTTP2_SETTINGS_FLOW_CONTROL_OPTIONS],
|
||||||
session->remote_settings
|
session->remote_settings
|
||||||
[NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE],
|
[NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE],
|
||||||
|
session->local_settings
|
||||||
|
[NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE],
|
||||||
stream_user_data);
|
stream_user_data);
|
||||||
r = nghttp2_map_insert(&session->streams, &stream->map_entry);
|
r = nghttp2_map_insert(&session->streams, &stream->map_entry);
|
||||||
if(r != 0) {
|
if(r != 0) {
|
||||||
|
@ -818,7 +820,7 @@ static size_t nghttp2_session_next_data_read(nghttp2_session *session,
|
||||||
int32_t session_window_size =
|
int32_t session_window_size =
|
||||||
session->remote_flow_control ? session->window_size : INT32_MAX;
|
session->remote_flow_control ? session->window_size : INT32_MAX;
|
||||||
int32_t stream_window_size =
|
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,
|
int32_t window_size = nghttp2_min(session_window_size,
|
||||||
stream_window_size);
|
stream_window_size);
|
||||||
if(window_size > 0) {
|
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);
|
frame = nghttp2_outbound_item_get_data_frame(session->aob.item);
|
||||||
stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
|
stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
|
||||||
if(stream && stream->remote_flow_control) {
|
if(stream && stream->remote_flow_control) {
|
||||||
stream->window_size -= len;
|
stream->remote_window_size -= len;
|
||||||
}
|
}
|
||||||
if(session->remote_flow_control) {
|
if(session->remote_flow_control) {
|
||||||
session->window_size -= len;
|
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,
|
static int nghttp2_update_initial_window_size_func(nghttp2_map_entry *entry,
|
||||||
void *ptr)
|
void *ptr)
|
||||||
{
|
{
|
||||||
|
int rv;
|
||||||
nghttp2_update_window_size_arg *arg;
|
nghttp2_update_window_size_arg *arg;
|
||||||
nghttp2_stream *stream;
|
nghttp2_stream *stream;
|
||||||
arg = (nghttp2_update_window_size_arg*)ptr;
|
arg = (nghttp2_update_window_size_arg*)ptr;
|
||||||
stream = (nghttp2_stream*)entry;
|
stream = (nghttp2_stream*)entry;
|
||||||
nghttp2_stream_update_initial_window_size(stream,
|
rv = nghttp2_stream_update_remote_initial_window_size(stream,
|
||||||
arg->new_window_size,
|
arg->new_window_size,
|
||||||
arg->old_window_size);
|
arg->old_window_size);
|
||||||
|
if(rv != 0) {
|
||||||
|
return NGHTTP2_ERR_FLOW_CONTROL;
|
||||||
|
}
|
||||||
/* If window size gets positive, push deferred DATA frame to
|
/* If window size gets positive, push deferred DATA frame to
|
||||||
outbound queue. */
|
outbound queue. */
|
||||||
if(stream->deferred_data &&
|
if(stream->deferred_data &&
|
||||||
(stream->deferred_flags & NGHTTP2_DEFERRED_FLOW_CONTROL) &&
|
(stream->deferred_flags & NGHTTP2_DEFERRED_FLOW_CONTROL) &&
|
||||||
stream->window_size > 0 &&
|
stream->remote_window_size > 0 &&
|
||||||
(arg->session->remote_flow_control == 0 ||
|
(arg->session->remote_flow_control == 0 ||
|
||||||
arg->session->window_size > 0)) {
|
arg->session->window_size > 0)) {
|
||||||
int rv;
|
|
||||||
rv = nghttp2_pq_push(&arg->session->ob_pq, stream->deferred_data);
|
rv = nghttp2_pq_push(&arg->session->ob_pq, stream->deferred_data);
|
||||||
if(rv == 0) {
|
if(rv == 0) {
|
||||||
nghttp2_stream_detach_deferred_data(stream);
|
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:
|
case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:
|
||||||
/* Update the initial window size of the all active streams */
|
/* Update the initial window size of the all active streams */
|
||||||
/* Check that initial_window_size < (1u << 31) */
|
/* 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);
|
rv = nghttp2_session_update_initial_window_size(session, entry->value);
|
||||||
if(rv != 0) {
|
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 {
|
} else {
|
||||||
return nghttp2_session_handle_invalid_connection
|
return nghttp2_session_handle_invalid_connection
|
||||||
|
@ -2143,7 +2153,7 @@ static int nghttp2_push_back_deferred_data_func(nghttp2_map_entry *entry,
|
||||||
outbound queue. */
|
outbound queue. */
|
||||||
if(stream->deferred_data &&
|
if(stream->deferred_data &&
|
||||||
(stream->deferred_flags & NGHTTP2_DEFERRED_FLOW_CONTROL) &&
|
(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;
|
int rv;
|
||||||
rv = nghttp2_pq_push(&session->ob_pq, stream->deferred_data);
|
rv = nghttp2_pq_push(&session->ob_pq, stream->deferred_data);
|
||||||
if(rv == 0) {
|
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);
|
nghttp2_session_call_on_frame_received(session, frame);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if(INT32_MAX - frame->window_update.window_size_increment <
|
if(NGHTTP2_MAX_WINDOW_SIZE - frame->window_update.window_size_increment <
|
||||||
stream->window_size) {
|
stream->remote_window_size) {
|
||||||
int r;
|
int r;
|
||||||
r = nghttp2_session_handle_invalid_stream
|
r = nghttp2_session_handle_invalid_stream
|
||||||
(session, frame, NGHTTP2_FLOW_CONTROL_ERROR);
|
(session, frame, NGHTTP2_FLOW_CONTROL_ERROR);
|
||||||
return r;
|
return r;
|
||||||
} else {
|
} else {
|
||||||
stream->window_size += frame->window_update.window_size_increment;
|
stream->remote_window_size +=
|
||||||
if(stream->window_size > 0 &&
|
frame->window_update.window_size_increment;
|
||||||
|
if(stream->remote_window_size > 0 &&
|
||||||
(session->remote_flow_control == 0 ||
|
(session->remote_flow_control == 0 ||
|
||||||
session->window_size > 0) &&
|
session->window_size > 0) &&
|
||||||
stream->deferred_data != NULL &&
|
stream->deferred_data != NULL &&
|
||||||
|
|
|
@ -31,7 +31,8 @@ void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id,
|
||||||
nghttp2_stream_state initial_state,
|
nghttp2_stream_state initial_state,
|
||||||
uint8_t remote_flow_control,
|
uint8_t remote_flow_control,
|
||||||
uint8_t local_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 *stream_user_data)
|
||||||
{
|
{
|
||||||
nghttp2_map_entry_init(&stream->map_entry, stream_id);
|
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->deferred_flags = NGHTTP2_DEFERRED_NONE;
|
||||||
stream->remote_flow_control = remote_flow_control;
|
stream->remote_flow_control = remote_flow_control;
|
||||||
stream->local_flow_control = local_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;
|
stream->recv_window_size = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,12 +77,20 @@ void nghttp2_stream_detach_deferred_data(nghttp2_stream *stream)
|
||||||
stream->deferred_flags = NGHTTP2_DEFERRED_NONE;
|
stream->deferred_flags = NGHTTP2_DEFERRED_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nghttp2_stream_update_initial_window_size(nghttp2_stream *stream,
|
int nghttp2_stream_update_remote_initial_window_size
|
||||||
int32_t new_initial_window_size,
|
(nghttp2_stream *stream,
|
||||||
int32_t old_initial_window_size)
|
int32_t new_initial_window_size,
|
||||||
|
int32_t 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-stream->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)
|
void nghttp2_stream_promise_fulfilled(nghttp2_stream *stream)
|
||||||
|
|
|
@ -109,12 +109,16 @@ typedef struct {
|
||||||
flow control options off or sending WINDOW_UPDATE with
|
flow control options off or sending WINDOW_UPDATE with
|
||||||
END_FLOW_CONTROL bit set. */
|
END_FLOW_CONTROL bit set. */
|
||||||
uint8_t local_flow_control;
|
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. */
|
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
|
/* Keep track of the number of bytes received without
|
||||||
WINDOW_UPDATE. */
|
WINDOW_UPDATE. */
|
||||||
int32_t recv_window_size;
|
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;
|
} nghttp2_stream;
|
||||||
|
|
||||||
void nghttp2_stream_init(nghttp2_stream *stream, int32_t stream_id,
|
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,
|
nghttp2_stream_state initial_state,
|
||||||
uint8_t remote_flow_control,
|
uint8_t remote_flow_control,
|
||||||
uint8_t local_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 *stream_user_data);
|
||||||
|
|
||||||
void nghttp2_stream_free(nghttp2_stream *stream);
|
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);
|
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
|
* |new_initial_window_size|. The |old_initial_window_size| is used to
|
||||||
* calculate the current window size.
|
* 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,
|
int nghttp2_stream_update_remote_initial_window_size
|
||||||
int32_t new_initial_window_size,
|
(nghttp2_stream *stream,
|
||||||
int32_t old_initial_window_size);
|
int32_t new_initial_window_size,
|
||||||
|
int32_t old_initial_window_size);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Call this function if promised stream |stream| is replied with
|
* 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);
|
NGHTTP2_STREAM_OPENING, NULL);
|
||||||
/* Set window size for each streams and will see how settings
|
/* Set window size for each streams and will see how settings
|
||||||
updates these values */
|
updates these values */
|
||||||
stream1->window_size = 16*1024;
|
stream1->remote_window_size = 16*1024;
|
||||||
stream2->window_size = -48*1024;
|
stream2->remote_window_size = -48*1024;
|
||||||
|
|
||||||
nghttp2_frame_settings_init(&frame.settings, dup_iv(iv, niv), niv);
|
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 ==
|
CU_ASSERT(1 ==
|
||||||
session->remote_settings[NGHTTP2_SETTINGS_FLOW_CONTROL_OPTIONS]);
|
session->remote_settings[NGHTTP2_SETTINGS_FLOW_CONTROL_OPTIONS]);
|
||||||
|
|
||||||
CU_ASSERT(64*1024 == stream1->window_size);
|
CU_ASSERT(64*1024 == stream1->remote_window_size);
|
||||||
CU_ASSERT(0 == stream2->window_size);
|
CU_ASSERT(0 == stream2->remote_window_size);
|
||||||
|
|
||||||
frame.settings.iv[2].value = 16*1024;
|
frame.settings.iv[2].value = 16*1024;
|
||||||
|
|
||||||
CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &frame));
|
CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &frame));
|
||||||
|
|
||||||
CU_ASSERT(16*1024 == stream1->window_size);
|
CU_ASSERT(16*1024 == stream1->remote_window_size);
|
||||||
CU_ASSERT(-48*1024 == stream2->window_size);
|
CU_ASSERT(-48*1024 == stream2->remote_window_size);
|
||||||
|
|
||||||
CU_ASSERT(0 == stream1->remote_flow_control);
|
CU_ASSERT(0 == stream1->remote_flow_control);
|
||||||
CU_ASSERT(0 == stream2->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(0 == nghttp2_session_on_window_update_received(session, &frame));
|
||||||
CU_ASSERT(1 == user_data.frame_recv_cb_called);
|
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));
|
data_item = malloc(sizeof(nghttp2_outbound_item));
|
||||||
memset(data_item, 0, 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(0 == nghttp2_session_on_window_update_received(session, &frame));
|
||||||
CU_ASSERT(2 == user_data.frame_recv_cb_called);
|
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);
|
CU_ASSERT(NULL == stream->deferred_data);
|
||||||
|
|
||||||
nghttp2_frame_window_update_free(&frame.window_update);
|
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
|
/* Change initial window size to 16KiB. The window_size becomes
|
||||||
negative. */
|
negative. */
|
||||||
new_initial_window_size = 16*1024;
|
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]
|
(session->remote_settings[NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]
|
||||||
-stream->window_size);
|
- stream->remote_window_size);
|
||||||
session->remote_settings[NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE] =
|
session->remote_settings[NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE] =
|
||||||
new_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 */
|
/* Back 48KiB to stream window */
|
||||||
frame.hd.stream_id = 1;
|
frame.hd.stream_id = 1;
|
||||||
|
|
Loading…
Reference in New Issue