Support increment/reduction of local window size by WINDOW_UPDATE
This commit is contained in:
parent
e67096fef3
commit
b979d2e8d2
|
@ -1615,6 +1615,10 @@ int nghttp2_submit_rst_stream(nghttp2_session *session, int32_t stream_id,
|
|||
* This function does not take ownership of the |iv|. This function
|
||||
* copies all the elements in the |iv|.
|
||||
*
|
||||
* While updating individual stream's local window size, if the window
|
||||
* size becomes strictly larger than NGHTTP2_MAX_WINDOW_SIZE,
|
||||
* RST_STREAM is issued against such a stream.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
|
@ -1711,21 +1715,29 @@ int nghttp2_submit_goaway(nghttp2_session *session,
|
|||
/**
|
||||
* @function
|
||||
*
|
||||
* Submits WINDOW_UPDATE frame. The effective range of the
|
||||
* |window_size_increment| is [1, (1 << 31)-1], inclusive. But the
|
||||
* application must be responsible to keep the resulting window size
|
||||
* <= (1 << 31)-1. If :enum:`NGHTTP2_FLAG_END_FLOW_CONTROL` bit set in
|
||||
* the |flags|, 0 can be specified in the |window_size_increment|. In
|
||||
* fact, if this flag is set, the value specified in the
|
||||
* |window_size_increment| is ignored.
|
||||
* Submits WINDOW_UPDATE frame.
|
||||
*
|
||||
* If the |window_size_increment| is positive, the WINDOW_UPDATE with
|
||||
* that value as window_size_increment is queued. If the
|
||||
* |window_size_increment| is larger than the received bytes from the
|
||||
* remote endpoint, the local window size is increased by that
|
||||
* difference.
|
||||
*
|
||||
* If the |window_size_increment| is negative, the local window size
|
||||
* is decreased by -|window_size_increment|. If
|
||||
* :enum:`NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE` is not set and the
|
||||
* library decided that the WINDOW_UPDATE should be submitted, then
|
||||
* WINDOW_UPDATE is queued with the current received bytes count.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
|
||||
* The |delta_window_size| is 0 or negative and
|
||||
* The |delta_window_size| is 0 and
|
||||
* :enum:`NGHTTP2_FLAG_END_FLOW_CONTROL` bit is not set in
|
||||
* |flags|.
|
||||
* :enum:`NGHTTP2_ERR_FLOW_CONTROL`
|
||||
* The local window size overflow or gets negative.
|
||||
* :enum:`NGHTTP2_ERR_STREAM_CLOSED`
|
||||
* The stream is already closed or does not exist.
|
||||
* :enum:`NGHTTP2_ERR_NOMEM`
|
||||
|
|
|
@ -91,6 +91,37 @@ void nghttp2_downcase(uint8_t *s, size_t len)
|
|||
}
|
||||
}
|
||||
|
||||
int nghttp2_adjust_local_window_size(int32_t *local_window_size_ptr,
|
||||
int32_t *recv_window_size_ptr,
|
||||
int32_t delta)
|
||||
{
|
||||
if(delta > 0) {
|
||||
int32_t new_recv_window_size = *recv_window_size_ptr - delta;
|
||||
if(new_recv_window_size < 0) {
|
||||
if(*local_window_size_ptr >
|
||||
NGHTTP2_MAX_WINDOW_SIZE + new_recv_window_size) {
|
||||
return NGHTTP2_ERR_FLOW_CONTROL;
|
||||
}
|
||||
*local_window_size_ptr -= new_recv_window_size;
|
||||
new_recv_window_size = 0;
|
||||
}
|
||||
*recv_window_size_ptr = new_recv_window_size;
|
||||
return 0;
|
||||
} else {
|
||||
if(*local_window_size_ptr + delta < 0) {
|
||||
return NGHTTP2_ERR_FLOW_CONTROL;
|
||||
}
|
||||
*local_window_size_ptr += delta;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nghttp2_should_send_window_update(int32_t local_window_size,
|
||||
int32_t recv_window_size)
|
||||
{
|
||||
return recv_window_size >= local_window_size / 2;
|
||||
}
|
||||
|
||||
const char* nghttp2_strerror(int error_code)
|
||||
{
|
||||
switch(error_code) {
|
||||
|
|
|
@ -91,4 +91,29 @@ void* nghttp2_memdup(const void* src, size_t n);
|
|||
|
||||
void nghttp2_downcase(uint8_t *s, size_t len);
|
||||
|
||||
/*
|
||||
* Adjusts |*local_window_size_ptr| and |*recv_window_size_ptr| with
|
||||
* |delta| which is the WINDOW_UPDATE's window_size_increment sent
|
||||
* from local side. If |delta| is strictly larger than
|
||||
* |*recv_window_size_ptr|, |*local_window_size_ptr| is increased by
|
||||
* delta - *recv_window_size_ptr. If |delta| is negative,
|
||||
* |*local_window_size_ptr| is decreased by delta.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_FLOW_CONTROL
|
||||
* local_window_size overflow or gets negative.
|
||||
*/
|
||||
int nghttp2_adjust_local_window_size(int32_t *local_window_size_ptr,
|
||||
int32_t *recv_window_size_ptr,
|
||||
int32_t delta);
|
||||
|
||||
/*
|
||||
* Returns non-zero if the function decided that WINDOW_UPDATE should
|
||||
* be sent.
|
||||
*/
|
||||
int nghttp2_should_send_window_update(int32_t local_window_size,
|
||||
int32_t recv_window_size);
|
||||
|
||||
#endif /* NGHTTP2_HELPER_H */
|
||||
|
|
|
@ -146,6 +146,7 @@ static int nghttp2_session_new(nghttp2_session **session_ptr,
|
|||
(*session_ptr)->local_flow_control = 1;
|
||||
(*session_ptr)->window_size = NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE;
|
||||
(*session_ptr)->recv_window_size = 0;
|
||||
(*session_ptr)->local_window_size = NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE;
|
||||
|
||||
(*session_ptr)->goaway_flags = NGHTTP2_GOAWAY_NONE;
|
||||
(*session_ptr)->last_stream_id = 0;
|
||||
|
@ -1856,7 +1857,8 @@ int nghttp2_session_on_rst_stream_received(nghttp2_session *session,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int nghttp2_update_initial_window_size_func(nghttp2_map_entry *entry,
|
||||
static int nghttp2_update_remote_initial_window_size_func
|
||||
(nghttp2_map_entry *entry,
|
||||
void *ptr)
|
||||
{
|
||||
int rv;
|
||||
|
@ -1868,7 +1870,8 @@ static int nghttp2_update_initial_window_size_func(nghttp2_map_entry *entry,
|
|||
arg->new_window_size,
|
||||
arg->old_window_size);
|
||||
if(rv != 0) {
|
||||
return NGHTTP2_ERR_FLOW_CONTROL;
|
||||
return nghttp2_session_add_rst_stream(arg->session, stream->stream_id,
|
||||
NGHTTP2_FLOW_CONTROL_ERROR);
|
||||
}
|
||||
/* If window size gets positive, push deferred DATA frame to
|
||||
outbound queue. */
|
||||
|
@ -1890,8 +1893,8 @@ static int nghttp2_update_initial_window_size_func(nghttp2_map_entry *entry,
|
|||
}
|
||||
|
||||
/*
|
||||
* Updates the initial window size of all active streams.
|
||||
* If error occurs, all streams may not be updated.
|
||||
* Updates the remote initial window size of all active streams. If
|
||||
* error occurs, all streams may not be updated.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
|
@ -1899,7 +1902,7 @@ static int nghttp2_update_initial_window_size_func(nghttp2_map_entry *entry,
|
|||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory.
|
||||
*/
|
||||
static int nghttp2_session_update_initial_window_size
|
||||
static int nghttp2_session_update_remote_initial_window_size
|
||||
(nghttp2_session *session,
|
||||
int32_t new_initial_window_size)
|
||||
{
|
||||
|
@ -1909,7 +1912,66 @@ static int nghttp2_session_update_initial_window_size
|
|||
arg.old_window_size =
|
||||
session->remote_settings[NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE];
|
||||
return nghttp2_map_each(&session->streams,
|
||||
nghttp2_update_initial_window_size_func,
|
||||
nghttp2_update_remote_initial_window_size_func,
|
||||
&arg);
|
||||
}
|
||||
|
||||
static int nghttp2_update_local_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;
|
||||
if(!stream->local_flow_control) {
|
||||
return 0;
|
||||
}
|
||||
rv = nghttp2_stream_update_local_initial_window_size(stream,
|
||||
arg->new_window_size,
|
||||
arg->old_window_size);
|
||||
if(rv != 0) {
|
||||
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)) {
|
||||
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,
|
||||
stream->stream_id,
|
||||
stream->recv_window_size);
|
||||
if(rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
stream->recv_window_size = 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Updates the local initial window size of all active streams. If
|
||||
* error occurs, all streams may not be updated.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory.
|
||||
*/
|
||||
static int nghttp2_session_update_local_initial_window_size
|
||||
(nghttp2_session *session,
|
||||
int32_t new_initial_window_size,
|
||||
int32_t old_initial_window_size)
|
||||
{
|
||||
nghttp2_update_window_size_arg arg;
|
||||
arg.session = session;
|
||||
arg.new_window_size = new_initial_window_size;
|
||||
arg.old_window_size = old_initial_window_size;
|
||||
return nghttp2_map_each(&session->streams,
|
||||
nghttp2_update_local_initial_window_size_func,
|
||||
&arg);
|
||||
}
|
||||
|
||||
|
@ -1973,14 +2035,32 @@ static void nghttp2_session_disable_local_flow_control
|
|||
assert(rv == 0);
|
||||
}
|
||||
|
||||
void nghttp2_session_update_local_settings(nghttp2_session *session,
|
||||
int nghttp2_session_update_local_settings(nghttp2_session *session,
|
||||
nghttp2_settings_entry *iv,
|
||||
size_t niv)
|
||||
{
|
||||
int rv;
|
||||
size_t i;
|
||||
uint8_t old_flow_control =
|
||||
session->local_settings[NGHTTP2_SETTINGS_FLOW_CONTROL_OPTIONS];
|
||||
|
||||
uint8_t new_flow_control = old_flow_control;
|
||||
int32_t new_initial_window_size = -1;
|
||||
for(i = 0; i < niv; ++i) {
|
||||
if(iv[i].settings_id == NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE) {
|
||||
new_initial_window_size = iv[i].value;
|
||||
} else if(iv[i].settings_id == NGHTTP2_SETTINGS_FLOW_CONTROL_OPTIONS) {
|
||||
new_flow_control = iv[i].value;
|
||||
}
|
||||
}
|
||||
if(!old_flow_control && !new_flow_control && new_initial_window_size != -1) {
|
||||
rv = nghttp2_session_update_local_initial_window_size
|
||||
(session,
|
||||
new_initial_window_size,
|
||||
session->local_settings[NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]);
|
||||
if(rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
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;
|
||||
|
@ -1989,6 +2069,7 @@ void nghttp2_session_update_local_settings(nghttp2_session *session,
|
|||
session->local_settings[NGHTTP2_SETTINGS_FLOW_CONTROL_OPTIONS]) {
|
||||
nghttp2_session_disable_local_flow_control(session);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nghttp2_session_on_settings_received(nghttp2_session *session,
|
||||
|
@ -2017,7 +2098,8 @@ int nghttp2_session_on_settings_received(nghttp2_session *session,
|
|||
/* Update the initial window size of the all active streams */
|
||||
/* Check that initial_window_size < (1u << 31) */
|
||||
if(entry->value <= NGHTTP2_MAX_WINDOW_SIZE) {
|
||||
rv = nghttp2_session_update_initial_window_size(session, entry->value);
|
||||
rv = nghttp2_session_update_remote_initial_window_size
|
||||
(session, entry->value);
|
||||
if(rv != 0) {
|
||||
if(nghttp2_is_fatal(rv)) {
|
||||
return rv;
|
||||
|
@ -2201,7 +2283,7 @@ int nghttp2_session_on_window_update_received(nghttp2_session *session,
|
|||
}
|
||||
return 0;
|
||||
}
|
||||
if(INT32_MAX - frame->window_update.window_size_increment <
|
||||
if(NGHTTP2_MAX_WINDOW_SIZE - frame->window_update.window_size_increment <
|
||||
session->window_size) {
|
||||
return nghttp2_session_handle_invalid_connection
|
||||
(session, frame, NGHTTP2_FLOW_CONTROL_ERROR);
|
||||
|
@ -2552,18 +2634,18 @@ static int nghttp2_session_process_data_frame(nghttp2_session *session)
|
|||
}
|
||||
}
|
||||
|
||||
static int32_t adjust_recv_window_size(int32_t recv_window_size, int32_t delta)
|
||||
/*
|
||||
* If the resulting recv_window_size is strictly larger than
|
||||
* NGHTTP2_MAX_WINDOW_SIZE, return NGHTTP2_ERR_FLOW_CONTROL.
|
||||
*/
|
||||
static int adjust_recv_window_size(int32_t *recv_window_size_ptr,
|
||||
int32_t delta)
|
||||
{
|
||||
/* If NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE is set and the application
|
||||
does not send WINDOW_UPDATE and the remote endpoint keeps
|
||||
sending data, stream->recv_window_size will eventually
|
||||
overflow. */
|
||||
if(recv_window_size > INT32_MAX - delta) {
|
||||
recv_window_size = INT32_MAX;
|
||||
} else {
|
||||
recv_window_size += delta;
|
||||
if(*recv_window_size_ptr > NGHTTP2_MAX_WINDOW_SIZE - delta) {
|
||||
return NGHTTP2_ERR_FLOW_CONTROL;
|
||||
}
|
||||
return recv_window_size;
|
||||
*recv_window_size_ptr += delta;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2583,23 +2665,25 @@ static int nghttp2_session_update_recv_stream_window_size
|
|||
nghttp2_stream *stream,
|
||||
int32_t delta_size)
|
||||
{
|
||||
stream->recv_window_size = adjust_recv_window_size
|
||||
(stream->recv_window_size, delta_size);
|
||||
int rv;
|
||||
rv = adjust_recv_window_size(&stream->recv_window_size, delta_size);
|
||||
if(rv != 0) {
|
||||
return nghttp2_session_add_rst_stream(session, stream->stream_id,
|
||||
NGHTTP2_ERR_FLOW_CONTROL);
|
||||
}
|
||||
if(!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE)) {
|
||||
/* This is just a heuristics. */
|
||||
/* We have to use local_settings here because it is the constraint
|
||||
the remote endpoint should honor. */
|
||||
if((size_t)stream->recv_window_size*2 >=
|
||||
session->local_settings[NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]) {
|
||||
int r;
|
||||
r = nghttp2_session_add_window_update(session,
|
||||
if(nghttp2_should_send_window_update(stream->local_window_size,
|
||||
stream->recv_window_size)) {
|
||||
rv = nghttp2_session_add_window_update(session,
|
||||
NGHTTP2_FLAG_NONE,
|
||||
stream->stream_id,
|
||||
stream->recv_window_size);
|
||||
if(r == 0) {
|
||||
if(rv == 0) {
|
||||
stream->recv_window_size = 0;
|
||||
} else {
|
||||
return r;
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2622,23 +2706,24 @@ static int nghttp2_session_update_recv_connection_window_size
|
|||
(nghttp2_session *session,
|
||||
int32_t delta_size)
|
||||
{
|
||||
session->recv_window_size = adjust_recv_window_size
|
||||
(session->recv_window_size, delta_size);
|
||||
int rv;
|
||||
rv = adjust_recv_window_size(&session->recv_window_size, delta_size);
|
||||
if(rv != 0) {
|
||||
return nghttp2_session_fail_session(session, NGHTTP2_ERR_FLOW_CONTROL);
|
||||
}
|
||||
if(!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE)) {
|
||||
/* Same heuristics above */
|
||||
if((size_t)session->recv_window_size*2 >=
|
||||
NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE) {
|
||||
int r;
|
||||
if(nghttp2_should_send_window_update(session->local_window_size,
|
||||
session->recv_window_size)) {
|
||||
/* Use stream ID 0 to update connection-level flow control
|
||||
window */
|
||||
r = nghttp2_session_add_window_update(session,
|
||||
rv = nghttp2_session_add_window_update(session,
|
||||
NGHTTP2_FLAG_NONE,
|
||||
0,
|
||||
session->recv_window_size);
|
||||
if(r == 0) {
|
||||
if(rv == 0) {
|
||||
session->recv_window_size = 0;
|
||||
} else {
|
||||
return r;
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -174,6 +174,11 @@ struct nghttp2_session {
|
|||
/* Keep track of the number of bytes received without
|
||||
WINDOW_UPDATE. */
|
||||
int32_t recv_window_size;
|
||||
/* window size for local flow control. It is initially set to
|
||||
NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE and could be
|
||||
increased/decreased by submitting WINDOW_UPDATE. See
|
||||
nghttp2_submit_window_update(). */
|
||||
int32_t local_window_size;
|
||||
|
||||
/* Settings value received from the remote endpoint. We just use ID
|
||||
as index. The index = 0 is unused. */
|
||||
|
@ -517,8 +522,18 @@ nghttp2_outbound_item* nghttp2_session_get_next_ob_item
|
|||
* array pointed by the |iv| is given by the |niv|. This function
|
||||
* assumes that the all settings_id member in |iv| are in range 1 to
|
||||
* NGHTTP2_SETTINGS_MAX, inclusive.
|
||||
*
|
||||
* While updating individual stream's local window size, if the window
|
||||
* size becomes strictly larger than NGHTTP2_MAX_WINDOW_SIZE,
|
||||
* RST_STREAM is issued against such a stream.
|
||||
*
|
||||
* This function returns 0 if it succeeds, or one of the following
|
||||
* negative error codes:
|
||||
*
|
||||
* NGHTTP2_ERR_NOMEM
|
||||
* Out of memory
|
||||
*/
|
||||
void nghttp2_session_update_local_settings(nghttp2_session *session,
|
||||
int nghttp2_session_update_local_settings(nghttp2_session *session,
|
||||
nghttp2_settings_entry *iv,
|
||||
size_t niv);
|
||||
|
||||
|
|
|
@ -77,22 +77,41 @@ void nghttp2_stream_detach_deferred_data(nghttp2_stream *stream)
|
|||
stream->deferred_flags = NGHTTP2_DEFERRED_NONE;
|
||||
}
|
||||
|
||||
int nghttp2_stream_update_remote_initial_window_size
|
||||
(nghttp2_stream *stream,
|
||||
static int update_initial_window_size
|
||||
(int32_t *window_size_ptr,
|
||||
int32_t new_initial_window_size,
|
||||
int32_t old_initial_window_size)
|
||||
{
|
||||
int64_t new_window_size = (int64_t)stream->remote_window_size +
|
||||
int64_t new_window_size = (int64_t)(*window_size_ptr) +
|
||||
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;
|
||||
*window_size_ptr += new_initial_window_size - old_initial_window_size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nghttp2_stream_update_remote_initial_window_size
|
||||
(nghttp2_stream *stream,
|
||||
int32_t new_initial_window_size,
|
||||
int32_t old_initial_window_size)
|
||||
{
|
||||
return update_initial_window_size(&stream->remote_window_size,
|
||||
new_initial_window_size,
|
||||
old_initial_window_size);
|
||||
}
|
||||
|
||||
int nghttp2_stream_update_local_initial_window_size
|
||||
(nghttp2_stream *stream,
|
||||
int32_t new_initial_window_size,
|
||||
int32_t old_initial_window_size)
|
||||
{
|
||||
return update_initial_window_size(&stream->local_window_size,
|
||||
new_initial_window_size,
|
||||
old_initial_window_size);
|
||||
}
|
||||
|
||||
void nghttp2_stream_promise_fulfilled(nghttp2_stream *stream)
|
||||
{
|
||||
stream->state = NGHTTP2_STREAM_OPENED;
|
||||
|
|
|
@ -115,7 +115,7 @@ typedef struct {
|
|||
/* 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
|
||||
/* window size for local flow 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;
|
||||
|
@ -166,6 +166,19 @@ int nghttp2_stream_update_remote_initial_window_size
|
|||
int32_t new_initial_window_size,
|
||||
int32_t old_initial_window_size);
|
||||
|
||||
/*
|
||||
* Updates the local 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.
|
||||
*/
|
||||
int nghttp2_stream_update_local_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
|
||||
* HEADERS. This function makes the state of the |stream| to
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "nghttp2_submit.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "nghttp2_session.h"
|
||||
#include "nghttp2_frame.h"
|
||||
|
@ -168,10 +169,17 @@ int nghttp2_submit_settings(nghttp2_session *session,
|
|||
}
|
||||
nghttp2_frame_iv_sort(iv_copy, niv);
|
||||
nghttp2_frame_settings_init(&frame->settings, iv_copy, niv);
|
||||
|
||||
r = nghttp2_session_update_local_settings(session, iv_copy, niv);
|
||||
if(r != 0) {
|
||||
nghttp2_frame_settings_free(&frame->settings);
|
||||
free(frame);
|
||||
return r;
|
||||
}
|
||||
r = nghttp2_session_add_frame(session, NGHTTP2_CAT_CTRL, frame, NULL);
|
||||
if(r == 0) {
|
||||
nghttp2_session_update_local_settings(session, iv_copy, niv);
|
||||
} else {
|
||||
if(r != 0) {
|
||||
/* The only expected error is fatal one */
|
||||
assert(r < NGHTTP2_ERR_FATAL);
|
||||
nghttp2_frame_settings_free(&frame->settings);
|
||||
free(frame);
|
||||
}
|
||||
|
@ -215,27 +223,59 @@ int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags,
|
|||
int32_t stream_id,
|
||||
int32_t window_size_increment)
|
||||
{
|
||||
int rv;
|
||||
nghttp2_stream *stream;
|
||||
flags &= NGHTTP2_FLAG_END_FLOW_CONTROL;
|
||||
if(flags & NGHTTP2_FLAG_END_FLOW_CONTROL) {
|
||||
window_size_increment = 0;
|
||||
} else if(window_size_increment <= 0) {
|
||||
} else if(window_size_increment == 0) {
|
||||
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
if(stream_id == 0) {
|
||||
return nghttp2_session_add_window_update(session, flags, stream_id,
|
||||
if(!session->local_flow_control) {
|
||||
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
rv = nghttp2_adjust_local_window_size(&session->local_window_size,
|
||||
&session->recv_window_size,
|
||||
window_size_increment);
|
||||
if(rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
if(!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) &&
|
||||
window_size_increment < 0 &&
|
||||
nghttp2_should_send_window_update(session->local_window_size,
|
||||
session->recv_window_size)) {
|
||||
window_size_increment = session->recv_window_size;
|
||||
session->recv_window_size = 0;
|
||||
}
|
||||
} else {
|
||||
stream = nghttp2_session_get_stream(session, stream_id);
|
||||
if(stream) {
|
||||
stream->recv_window_size -= nghttp2_min(window_size_increment,
|
||||
stream->recv_window_size);
|
||||
return nghttp2_session_add_window_update(session, flags, stream_id,
|
||||
if(!stream->local_flow_control) {
|
||||
return NGHTTP2_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
rv = nghttp2_adjust_local_window_size(&stream->local_window_size,
|
||||
&stream->recv_window_size,
|
||||
window_size_increment);
|
||||
if(rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
if(!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_WINDOW_UPDATE) &&
|
||||
window_size_increment < 0 &&
|
||||
nghttp2_should_send_window_update(stream->local_window_size,
|
||||
stream->recv_window_size)) {
|
||||
window_size_increment = stream->recv_window_size;
|
||||
stream->recv_window_size = 0;
|
||||
}
|
||||
} else {
|
||||
return NGHTTP2_ERR_STREAM_CLOSED;
|
||||
}
|
||||
}
|
||||
if(window_size_increment > 0 || (flags & NGHTTP2_FLAG_END_FLOW_CONTROL)) {
|
||||
return nghttp2_session_add_window_update(session, flags, stream_id,
|
||||
window_size_increment);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nghttp2_submit_request(nghttp2_session *session, int32_t pri,
|
||||
|
|
|
@ -35,12 +35,14 @@ OBJECTS = main.c nghttp2_pq_test.c nghttp2_map_test.c nghttp2_queue_test.c \
|
|||
nghttp2_session_test.c \
|
||||
nghttp2_hd_test.c \
|
||||
nghttp2_npn_test.c \
|
||||
nghttp2_gzip_test.c
|
||||
nghttp2_gzip_test.c \
|
||||
nghttp2_helper_test.c
|
||||
|
||||
HFILES = nghttp2_pq_test.h nghttp2_map_test.h nghttp2_queue_test.h \
|
||||
nghttp2_buffer_test.h nghttp2_session_test.h \
|
||||
nghttp2_frame_test.h nghttp2_stream_test.h nghttp2_hd_test.h \
|
||||
nghttp2_npn_test.h nghttp2_gzip_test.h nghttp2_test_helper.h
|
||||
nghttp2_npn_test.h nghttp2_gzip_test.h nghttp2_helper_test.h \
|
||||
nghttp2_test_helper.h
|
||||
|
||||
main_SOURCES = $(HFILES) $(OBJECTS)
|
||||
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include "nghttp2_hd_test.h"
|
||||
#include "nghttp2_npn_test.h"
|
||||
#include "nghttp2_gzip_test.h"
|
||||
#include "nghttp2_helper_test.h"
|
||||
|
||||
static int init_suite1(void)
|
||||
{
|
||||
|
@ -142,10 +143,14 @@ int main(int argc, char* argv[])
|
|||
!CU_add_test(pSuite, "submit_priority", test_nghttp2_submit_priority) ||
|
||||
!CU_add_test(pSuite, "session_submit_settings",
|
||||
test_nghttp2_submit_settings) ||
|
||||
!CU_add_test(pSuite, "session_submit_settings_update_local_window_size",
|
||||
test_nghttp2_submit_settings_update_local_window_size) ||
|
||||
!CU_add_test(pSuite, "session_submit_push_promise",
|
||||
test_nghttp2_submit_push_promise) ||
|
||||
!CU_add_test(pSuite, "submit_window_update",
|
||||
test_nghttp2_submit_window_update) ||
|
||||
!CU_add_test(pSuite, "submit_window_update_local_window_size",
|
||||
test_nghttp2_submit_window_update_local_window_size) ||
|
||||
!CU_add_test(pSuite, "submit_invalid_nv",
|
||||
test_nghttp2_submit_invalid_nv) ||
|
||||
!CU_add_test(pSuite, "session_open_stream",
|
||||
|
@ -231,7 +236,9 @@ int main(int argc, char* argv[])
|
|||
test_nghttp2_hd_inflate_newname_subst) ||
|
||||
!CU_add_test(pSuite, "hd_deflate_inflate",
|
||||
test_nghttp2_hd_deflate_inflate) ||
|
||||
!CU_add_test(pSuite, "gzip_inflate", test_nghttp2_gzip_inflate)
|
||||
!CU_add_test(pSuite, "gzip_inflate", test_nghttp2_gzip_inflate) ||
|
||||
!CU_add_test(pSuite, "adjust_local_window_size",
|
||||
test_nghttp2_adjust_local_window_size)
|
||||
) {
|
||||
CU_cleanup_registry();
|
||||
return CU_get_error();
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* nghttp2 - HTTP/2.0 C Library
|
||||
*
|
||||
* Copyright (c) 2013 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include "nghttp2_helper_test.h"
|
||||
|
||||
#include <CUnit/CUnit.h>
|
||||
|
||||
#include "nghttp2_helper.h"
|
||||
|
||||
void test_nghttp2_adjust_local_window_size(void)
|
||||
{
|
||||
int32_t local_window_size = 100;
|
||||
int32_t recv_window_size = 50;
|
||||
|
||||
CU_ASSERT(0 == nghttp2_adjust_local_window_size(&local_window_size,
|
||||
&recv_window_size, 0));
|
||||
CU_ASSERT(100 == local_window_size);
|
||||
CU_ASSERT(50 == recv_window_size);
|
||||
|
||||
CU_ASSERT(0 == nghttp2_adjust_local_window_size(&local_window_size,
|
||||
&recv_window_size, 49));
|
||||
CU_ASSERT(100 == local_window_size);
|
||||
CU_ASSERT(1 == recv_window_size);
|
||||
|
||||
CU_ASSERT(0 == nghttp2_adjust_local_window_size(&local_window_size,
|
||||
&recv_window_size, 1));
|
||||
CU_ASSERT(100 == local_window_size);
|
||||
CU_ASSERT(0 == recv_window_size);
|
||||
|
||||
CU_ASSERT(0 == nghttp2_adjust_local_window_size(&local_window_size,
|
||||
&recv_window_size, 1));
|
||||
CU_ASSERT(101 == local_window_size);
|
||||
CU_ASSERT(0 == recv_window_size);
|
||||
|
||||
CU_ASSERT(0 == nghttp2_adjust_local_window_size(&local_window_size,
|
||||
&recv_window_size, -1));
|
||||
CU_ASSERT(100 == local_window_size);
|
||||
CU_ASSERT(0 == recv_window_size);
|
||||
|
||||
recv_window_size = 50;
|
||||
CU_ASSERT(NGHTTP2_ERR_FLOW_CONTROL ==
|
||||
nghttp2_adjust_local_window_size(&local_window_size,
|
||||
&recv_window_size,
|
||||
INT32_MAX));
|
||||
CU_ASSERT(100 == local_window_size);
|
||||
CU_ASSERT(50 == recv_window_size);
|
||||
|
||||
CU_ASSERT(NGHTTP2_ERR_FLOW_CONTROL ==
|
||||
nghttp2_adjust_local_window_size(&local_window_size,
|
||||
&recv_window_size,
|
||||
INT32_MIN));
|
||||
CU_ASSERT(100 == local_window_size);
|
||||
CU_ASSERT(50 == recv_window_size);
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* nghttp2 - HTTP/2.0 C Library
|
||||
*
|
||||
* Copyright (c) 2013 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef NGHTTP2_HELPER_TEST_H
|
||||
#define NGHTTP2_HELPER_TEST_H
|
||||
|
||||
void test_nghttp2_adjust_local_window_size(void);
|
||||
|
||||
#endif /* NGHTTP2_HELPER_TEST_H */
|
|
@ -1927,6 +1927,80 @@ void test_nghttp2_submit_settings(void)
|
|||
nghttp2_session_del(session);
|
||||
}
|
||||
|
||||
void test_nghttp2_submit_settings_update_local_window_size(void)
|
||||
{
|
||||
nghttp2_session *session;
|
||||
nghttp2_session_callbacks callbacks;
|
||||
nghttp2_outbound_item *item;
|
||||
nghttp2_settings_entry iv[4];
|
||||
nghttp2_stream *stream;
|
||||
my_user_data ud;
|
||||
|
||||
iv[0].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
|
||||
iv[0].value = 16*1024;
|
||||
|
||||
iv[1].settings_id = NGHTTP2_SETTINGS_FLOW_CONTROL_OPTIONS;
|
||||
iv[1].value = 1;
|
||||
|
||||
memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
|
||||
callbacks.send_callback = block_count_send_callback;
|
||||
|
||||
nghttp2_session_server_new(&session, &callbacks, &ud);
|
||||
|
||||
stream = nghttp2_session_open_stream(session, 1, NGHTTP2_FLAG_NONE,
|
||||
NGHTTP2_PRI_DEFAULT,
|
||||
NGHTTP2_STREAM_OPENED, NULL);
|
||||
stream->local_window_size = NGHTTP2_INITIAL_WINDOW_SIZE + 100;
|
||||
stream->recv_window_size = 32768;
|
||||
|
||||
stream = nghttp2_session_open_stream(session, 3, NGHTTP2_FLAG_NONE,
|
||||
NGHTTP2_PRI_DEFAULT,
|
||||
NGHTTP2_STREAM_OPENED, NULL);
|
||||
stream->local_flow_control = 0;
|
||||
|
||||
CU_ASSERT(0 == nghttp2_submit_settings(session, iv, 1));
|
||||
|
||||
stream = nghttp2_session_get_stream(session, 1);
|
||||
CU_ASSERT(0 == stream->recv_window_size);
|
||||
CU_ASSERT(16*1024 + 100 == stream->local_window_size);
|
||||
|
||||
stream = nghttp2_session_get_stream(session, 3);
|
||||
CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE == stream->local_window_size);
|
||||
|
||||
/* Setting block_count = 0 will pop first entry SETTINGS */
|
||||
ud.block_count = 0;
|
||||
CU_ASSERT(0 == nghttp2_session_send(session));
|
||||
item = nghttp2_session_get_next_ob_item(session);
|
||||
CU_ASSERT(NGHTTP2_WINDOW_UPDATE == OB_CTRL_TYPE(item));
|
||||
CU_ASSERT(32768 == OB_CTRL(item)->window_update.window_size_increment);
|
||||
|
||||
nghttp2_session_del(session);
|
||||
|
||||
/* Check flow control disabled case */
|
||||
nghttp2_session_server_new(&session, &callbacks, &ud);
|
||||
stream = nghttp2_session_open_stream(session, 1, NGHTTP2_FLAG_NONE,
|
||||
NGHTTP2_PRI_DEFAULT,
|
||||
NGHTTP2_STREAM_OPENED, NULL);
|
||||
|
||||
CU_ASSERT(0 == nghttp2_submit_settings(session, iv, 2));
|
||||
CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE == stream->local_window_size);
|
||||
|
||||
nghttp2_session_del(session);
|
||||
|
||||
/* Check overflow case */
|
||||
iv[0].value = 128*1024;
|
||||
nghttp2_session_server_new(&session, &callbacks, &ud);
|
||||
stream = nghttp2_session_open_stream(session, 1, NGHTTP2_FLAG_NONE,
|
||||
NGHTTP2_PRI_DEFAULT,
|
||||
NGHTTP2_STREAM_OPENED, NULL);
|
||||
stream->local_window_size = NGHTTP2_MAX_WINDOW_SIZE;
|
||||
|
||||
CU_ASSERT(0 == nghttp2_submit_settings(session, iv, 1));
|
||||
CU_ASSERT(NGHTTP2_STREAM_CLOSING == stream->state);
|
||||
|
||||
nghttp2_session_del(session);
|
||||
}
|
||||
|
||||
void test_nghttp2_submit_push_promise(void)
|
||||
{
|
||||
nghttp2_session *session;
|
||||
|
@ -2025,6 +2099,83 @@ void test_nghttp2_submit_window_update(void)
|
|||
nghttp2_session_del(session);
|
||||
}
|
||||
|
||||
void test_nghttp2_submit_window_update_local_window_size(void)
|
||||
{
|
||||
nghttp2_session *session;
|
||||
nghttp2_session_callbacks callbacks;
|
||||
nghttp2_outbound_item *item;
|
||||
nghttp2_stream *stream;
|
||||
|
||||
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, 2,
|
||||
NGHTTP2_FLAG_NONE,
|
||||
NGHTTP2_PRI_DEFAULT,
|
||||
NGHTTP2_STREAM_OPENED, NULL);
|
||||
stream->recv_window_size = 4096;
|
||||
|
||||
CU_ASSERT(0 == nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 2,
|
||||
stream->recv_window_size + 1));
|
||||
CU_ASSERT(NGHTTP2_INITIAL_WINDOW_SIZE + 1 == stream->local_window_size);
|
||||
CU_ASSERT(0 == stream->recv_window_size);
|
||||
item = nghttp2_session_get_next_ob_item(session);
|
||||
CU_ASSERT(NGHTTP2_WINDOW_UPDATE == OB_CTRL_TYPE(item));
|
||||
CU_ASSERT(4097 == OB_CTRL(item)->window_update.window_size_increment);
|
||||
|
||||
CU_ASSERT(0 == nghttp2_session_send(session));
|
||||
|
||||
/* Let's decrement local window size */
|
||||
stream->recv_window_size = 32768;
|
||||
CU_ASSERT(0 == nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 2,
|
||||
-stream->local_window_size / 2));
|
||||
CU_ASSERT(32768 == stream->local_window_size);
|
||||
CU_ASSERT(0 == stream->recv_window_size);
|
||||
item = nghttp2_session_get_next_ob_item(session);
|
||||
CU_ASSERT(NGHTTP2_WINDOW_UPDATE == OB_CTRL_TYPE(item));
|
||||
CU_ASSERT(32768 == OB_CTRL(item)->window_update.window_size_increment);
|
||||
|
||||
CU_ASSERT(0 == nghttp2_session_send(session));
|
||||
|
||||
CU_ASSERT(NGHTTP2_ERR_FLOW_CONTROL ==
|
||||
nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 2,
|
||||
NGHTTP2_MAX_WINDOW_SIZE));
|
||||
|
||||
CU_ASSERT(0 == nghttp2_session_send(session));
|
||||
|
||||
/* Check connection-level flow control */
|
||||
session->recv_window_size = 4096;
|
||||
CU_ASSERT(0 == nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 0,
|
||||
session->recv_window_size + 1));
|
||||
CU_ASSERT(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE + 1 ==
|
||||
session->local_window_size);
|
||||
CU_ASSERT(0 == session->recv_window_size);
|
||||
item = nghttp2_session_get_next_ob_item(session);
|
||||
CU_ASSERT(NGHTTP2_WINDOW_UPDATE == OB_CTRL_TYPE(item));
|
||||
CU_ASSERT(4097 == OB_CTRL(item)->window_update.window_size_increment);
|
||||
|
||||
CU_ASSERT(0 == nghttp2_session_send(session));
|
||||
|
||||
/* Go decrement part */
|
||||
session->recv_window_size = 32768;
|
||||
CU_ASSERT(0 == nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 0,
|
||||
-session->local_window_size/2));
|
||||
CU_ASSERT(32768 == session->local_window_size);
|
||||
CU_ASSERT(0 == session->recv_window_size);
|
||||
item = nghttp2_session_get_next_ob_item(session);
|
||||
CU_ASSERT(NGHTTP2_WINDOW_UPDATE == OB_CTRL_TYPE(item));
|
||||
CU_ASSERT(32768 == OB_CTRL(item)->window_update.window_size_increment);
|
||||
|
||||
CU_ASSERT(0 == nghttp2_session_send(session));
|
||||
|
||||
CU_ASSERT(NGHTTP2_ERR_FLOW_CONTROL ==
|
||||
nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 0,
|
||||
NGHTTP2_MAX_WINDOW_SIZE));
|
||||
|
||||
nghttp2_session_del(session);
|
||||
}
|
||||
|
||||
void test_nghttp2_submit_invalid_nv(void)
|
||||
{
|
||||
nghttp2_session *session;
|
||||
|
|
|
@ -62,8 +62,10 @@ void test_nghttp2_submit_headers_push_reply(void);
|
|||
void test_nghttp2_submit_headers(void);
|
||||
void test_nghttp2_submit_priority(void);
|
||||
void test_nghttp2_submit_settings(void);
|
||||
void test_nghttp2_submit_settings_update_local_window_size(void);
|
||||
void test_nghttp2_submit_push_promise(void);
|
||||
void test_nghttp2_submit_window_update(void);
|
||||
void test_nghttp2_submit_window_update_local_window_size(void);
|
||||
void test_nghttp2_submit_invalid_nv(void);
|
||||
void test_nghttp2_session_open_stream(void);
|
||||
void test_nghttp2_session_get_next_ob_item(void);
|
||||
|
|
Loading…
Reference in New Issue