Commit 204f9a3e authored by Tatsuhiro Tsujikawa's avatar Tatsuhiro Tsujikawa

Add nghttp2_session_set_local_window_size() API function

parent f68dc02d
...@@ -4071,14 +4071,17 @@ nghttp2_session_check_server_session(nghttp2_session *session); ...@@ -4071,14 +4071,17 @@ nghttp2_session_check_server_session(nghttp2_session *session);
* that value as window_size_increment is queued. If the * that value as window_size_increment is queued. If the
* |window_size_increment| is larger than the received bytes from the * |window_size_increment| is larger than the received bytes from the
* remote endpoint, the local window size is increased by that * remote endpoint, the local window size is increased by that
* difference. * difference. If the sole intention is to increase the local window
* size, consider to use `nghttp2_session_set_local_window_size()`.
* *
* If the |window_size_increment| is negative, the local window size * If the |window_size_increment| is negative, the local window size
* is decreased by -|window_size_increment|. If automatic * is decreased by -|window_size_increment|. If automatic
* WINDOW_UPDATE is enabled * WINDOW_UPDATE is enabled
* (`nghttp2_option_set_no_auto_window_update()`), and the library * (`nghttp2_option_set_no_auto_window_update()`), and the library
* decided that the WINDOW_UPDATE should be submitted, then * decided that the WINDOW_UPDATE should be submitted, then
* WINDOW_UPDATE is queued with the current received bytes count. * WINDOW_UPDATE is queued with the current received bytes count. If
* the sole intention is to decrease the local window size, consider
* to use `nghttp2_session_set_local_window_size()`.
* *
* If the |window_size_increment| is 0, the function does nothing and * If the |window_size_increment| is 0, the function does nothing and
* returns 0. * returns 0.
...@@ -4096,6 +4099,44 @@ NGHTTP2_EXTERN int nghttp2_submit_window_update(nghttp2_session *session, ...@@ -4096,6 +4099,44 @@ NGHTTP2_EXTERN int nghttp2_submit_window_update(nghttp2_session *session,
int32_t stream_id, int32_t stream_id,
int32_t window_size_increment); int32_t window_size_increment);
/**
* @function
*
* Set local window size (local endpoints's window size) to the given
* |window_size| for the given stream denoted by |stream_id|. To
* change connection level window size, specify 0 to |stream_id|. To
* increase window size, this function may submit WINDOW_UPDATE frame
* to transmission queue.
*
* The |flags| is currently ignored and should be
* :enum:`NGHTTP2_FLAG_NONE`.
*
* This sounds similar to `nghttp2_submit_window_update()`, but there
* are 2 differences. The first difference is that this function
* takes the absolute value of window size to set, rather than the
* delta. To change the window size, this may be easier to use since
* the application just declares the intended window size, rather than
* calculating delta. The second difference is that
* `nghttp2_submit_window_update()` affects the received bytes count
* which has not acked yet. By the specification of
* `nghttp2_submit_window_update()`, to strictly increase the local
* window size, we have to submit delta including all received bytes
* count, which might not be desirable in some cases. On the other
* hand, this function does not affect the received bytes count. It
* just sets the local window size to the given value.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
* The |stream_id| is negative.
* :enum:`NGHTTP2_ERR_NOMEM`
* Out of memory.
*/
NGHTTP2_EXTERN int
nghttp2_session_set_local_window_size(nghttp2_session *session, uint8_t flags,
int32_t stream_id, int32_t window_size);
/** /**
* @function * @function
* *
......
...@@ -213,6 +213,38 @@ int nghttp2_adjust_local_window_size(int32_t *local_window_size_ptr, ...@@ -213,6 +213,38 @@ int nghttp2_adjust_local_window_size(int32_t *local_window_size_ptr,
return 0; return 0;
} }
int nghttp2_increase_local_window_size(int32_t *local_window_size_ptr,
int32_t *recv_window_size_ptr,
int32_t *recv_reduction_ptr,
int32_t *delta_ptr) {
int32_t recv_reduction_delta;
int32_t delta;
delta = *delta_ptr;
assert(delta >= 0);
/* The delta size is strictly more than received bytes. Increase
local_window_size by that difference |delta|. */
if (*local_window_size_ptr > NGHTTP2_MAX_WINDOW_SIZE - delta) {
return NGHTTP2_ERR_FLOW_CONTROL;
}
*local_window_size_ptr += delta;
/* If there is recv_reduction due to earlier window_size
reduction, we have to adjust it too. */
recv_reduction_delta = nghttp2_min(*recv_reduction_ptr, delta);
*recv_reduction_ptr -= recv_reduction_delta;
*recv_window_size_ptr += recv_reduction_delta;
/* recv_reduction_delta must be paied from *delta_ptr, since it was
added in window size reduction (see below). */
*delta_ptr -= recv_reduction_delta;
return 0;
}
int nghttp2_should_send_window_update(int32_t local_window_size, int nghttp2_should_send_window_update(int32_t local_window_size,
int32_t recv_window_size) { int32_t recv_window_size) {
return recv_window_size > 0 && recv_window_size >= local_window_size / 2; return recv_window_size > 0 && recv_window_size >= local_window_size / 2;
......
...@@ -89,6 +89,22 @@ int nghttp2_adjust_local_window_size(int32_t *local_window_size_ptr, ...@@ -89,6 +89,22 @@ int nghttp2_adjust_local_window_size(int32_t *local_window_size_ptr,
int32_t *recv_reduction_ptr, int32_t *recv_reduction_ptr,
int32_t *delta_ptr); int32_t *delta_ptr);
/*
* This function works like nghttp2_adjust_local_window_size(). The
* difference is that this function assumes *delta_ptr >= 0, and
* *recv_window_size_ptr is not decreased by *delta_ptr.
*
* 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_increase_local_window_size(int32_t *local_window_size_ptr,
int32_t *recv_window_size_ptr,
int32_t *recv_reduction_ptr,
int32_t *delta_ptr);
/* /*
* Returns non-zero if the function decided that WINDOW_UPDATE should * Returns non-zero if the function decided that WINDOW_UPDATE should
* be sent. * be sent.
......
...@@ -410,6 +410,75 @@ int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags, ...@@ -410,6 +410,75 @@ int nghttp2_submit_window_update(nghttp2_session *session, uint8_t flags,
return 0; return 0;
} }
int nghttp2_session_set_local_window_size(nghttp2_session *session,
uint8_t flags, int32_t stream_id,
int32_t window_size) {
int32_t window_size_increment;
nghttp2_stream *stream;
int rv;
if (window_size < 0) {
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
flags = 0;
if (stream_id == 0) {
window_size_increment = window_size - session->local_window_size;
if (window_size_increment == 0) {
return 0;
}
if (window_size_increment < 0) {
return nghttp2_adjust_local_window_size(
&session->local_window_size, &session->recv_window_size,
&session->recv_reduction, &window_size_increment);
}
rv = nghttp2_increase_local_window_size(
&session->local_window_size, &session->recv_window_size,
&session->recv_reduction, &window_size_increment);
if (rv != 0) {
return rv;
}
} else {
stream = nghttp2_session_get_stream(session, stream_id);
if (stream == NULL) {
return 0;
}
window_size_increment = window_size - stream->local_window_size;
if (window_size_increment == 0) {
return 0;
}
if (window_size_increment < 0) {
return nghttp2_adjust_local_window_size(
&stream->local_window_size, &stream->recv_window_size,
&stream->recv_reduction, &window_size_increment);
}
rv = nghttp2_increase_local_window_size(
&stream->local_window_size, &stream->recv_window_size,
&stream->recv_reduction, &window_size_increment);
if (rv != 0) {
return rv;
}
}
if (window_size_increment > 0) {
return nghttp2_session_add_window_update(session, flags, stream_id,
window_size_increment);
}
return 0;
}
int nghttp2_submit_altsvc(nghttp2_session *session, uint8_t flags _U_, int nghttp2_submit_altsvc(nghttp2_session *session, uint8_t flags _U_,
int32_t stream_id, const uint8_t *origin, int32_t stream_id, const uint8_t *origin,
size_t origin_len, const uint8_t *field_value, size_t origin_len, const uint8_t *field_value,
......
...@@ -302,6 +302,8 @@ int main(int argc _U_, char *argv[] _U_) { ...@@ -302,6 +302,8 @@ int main(int argc _U_, char *argv[] _U_) {
test_nghttp2_session_repeated_priority_change) || test_nghttp2_session_repeated_priority_change) ||
!CU_add_test(pSuite, "session_repeated_priority_submission", !CU_add_test(pSuite, "session_repeated_priority_submission",
test_nghttp2_session_repeated_priority_submission) || test_nghttp2_session_repeated_priority_submission) ||
!CU_add_test(pSuite, "session_set_local_window_size",
test_nghttp2_session_set_local_window_size) ||
!CU_add_test(pSuite, "http_mandatory_headers", !CU_add_test(pSuite, "http_mandatory_headers",
test_nghttp2_http_mandatory_headers) || test_nghttp2_http_mandatory_headers) ||
!CU_add_test(pSuite, "http_content_length", !CU_add_test(pSuite, "http_content_length",
......
...@@ -9405,6 +9405,104 @@ void test_nghttp2_session_repeated_priority_submission(void) { ...@@ -9405,6 +9405,104 @@ void test_nghttp2_session_repeated_priority_submission(void) {
nghttp2_session_del(session); nghttp2_session_del(session);
} }
void test_nghttp2_session_set_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 = open_sent_stream(session, 1);
stream->recv_window_size = 4096;
CU_ASSERT(0 == nghttp2_session_set_local_window_size(
session, NGHTTP2_FLAG_NONE, 1, 65536));
CU_ASSERT(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE + 1 ==
stream->local_window_size);
CU_ASSERT(4096 == stream->recv_window_size);
item = nghttp2_session_get_next_ob_item(session);
CU_ASSERT(NGHTTP2_WINDOW_UPDATE == item->frame.hd.type);
CU_ASSERT(1 == item->frame.window_update.hd.stream_id);
CU_ASSERT(1 == item->frame.window_update.window_size_increment);
CU_ASSERT(0 == nghttp2_session_send(session));
/* Go decrement part */
CU_ASSERT(0 == nghttp2_session_set_local_window_size(
session, NGHTTP2_FLAG_NONE, 1, 32768));
CU_ASSERT(32768 == stream->local_window_size);
CU_ASSERT(-28672 == stream->recv_window_size);
CU_ASSERT(32768 == stream->recv_reduction);
item = nghttp2_session_get_next_ob_item(session);
CU_ASSERT(item == NULL);
/* Increase local window size */
CU_ASSERT(0 == nghttp2_session_set_local_window_size(
session, NGHTTP2_FLAG_NONE, 1, 49152));
CU_ASSERT(49152 == stream->local_window_size);
CU_ASSERT(-12288 == stream->recv_window_size);
CU_ASSERT(16384 == stream->recv_reduction);
CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session));
/* Increase local window again */
CU_ASSERT(0 == nghttp2_session_set_local_window_size(
session, NGHTTP2_FLAG_NONE, 1, 65537));
CU_ASSERT(65537 == stream->local_window_size);
CU_ASSERT(4096 == stream->recv_window_size);
CU_ASSERT(0 == stream->recv_reduction);
item = nghttp2_session_get_next_ob_item(session);
CU_ASSERT(1 == item->frame.window_update.window_size_increment);
CU_ASSERT(0 == nghttp2_session_send(session));
/* Check connection-level flow control */
session->recv_window_size = 4096;
CU_ASSERT(0 == nghttp2_session_set_local_window_size(
session, NGHTTP2_FLAG_NONE, 0, 65536));
CU_ASSERT(NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE + 1 ==
session->local_window_size);
CU_ASSERT(4096 == session->recv_window_size);
item = nghttp2_session_get_next_ob_item(session);
CU_ASSERT(NGHTTP2_WINDOW_UPDATE == item->frame.hd.type);
CU_ASSERT(0 == item->frame.window_update.hd.stream_id);
CU_ASSERT(1 == item->frame.window_update.window_size_increment);
CU_ASSERT(0 == nghttp2_session_send(session));
/* Go decrement part */
CU_ASSERT(0 == nghttp2_session_set_local_window_size(
session, NGHTTP2_FLAG_NONE, 0, 32768));
CU_ASSERT(32768 == session->local_window_size);
CU_ASSERT(-28672 == session->recv_window_size);
CU_ASSERT(32768 == session->recv_reduction);
item = nghttp2_session_get_next_ob_item(session);
CU_ASSERT(item == NULL);
/* Increase local window size */
CU_ASSERT(0 == nghttp2_session_set_local_window_size(
session, NGHTTP2_FLAG_NONE, 0, 49152));
CU_ASSERT(49152 == session->local_window_size);
CU_ASSERT(-12288 == session->recv_window_size);
CU_ASSERT(16384 == session->recv_reduction);
CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(session));
/* Increase local window again */
CU_ASSERT(0 == nghttp2_session_set_local_window_size(
session, NGHTTP2_FLAG_NONE, 0, 65537));
CU_ASSERT(65537 == session->local_window_size);
CU_ASSERT(4096 == session->recv_window_size);
CU_ASSERT(0 == session->recv_reduction);
item = nghttp2_session_get_next_ob_item(session);
CU_ASSERT(1 == item->frame.window_update.window_size_increment);
CU_ASSERT(0 == nghttp2_session_send(session));
nghttp2_session_del(session);
}
static void check_nghttp2_http_recv_headers_fail( static void check_nghttp2_http_recv_headers_fail(
nghttp2_session *session, nghttp2_hd_deflater *deflater, int32_t stream_id, nghttp2_session *session, nghttp2_hd_deflater *deflater, int32_t stream_id,
int stream_state, const nghttp2_nv *nva, size_t nvlen) { int stream_state, const nghttp2_nv *nva, size_t nvlen) {
......
...@@ -149,6 +149,7 @@ void test_nghttp2_session_change_stream_priority(void); ...@@ -149,6 +149,7 @@ void test_nghttp2_session_change_stream_priority(void);
void test_nghttp2_session_create_idle_stream(void); void test_nghttp2_session_create_idle_stream(void);
void test_nghttp2_session_repeated_priority_change(void); void test_nghttp2_session_repeated_priority_change(void);
void test_nghttp2_session_repeated_priority_submission(void); void test_nghttp2_session_repeated_priority_submission(void);
void test_nghttp2_session_set_local_window_size(void);
void test_nghttp2_http_mandatory_headers(void); void test_nghttp2_http_mandatory_headers(void);
void test_nghttp2_http_content_length(void); void test_nghttp2_http_content_length(void);
void test_nghttp2_http_content_length_mismatch(void); void test_nghttp2_http_content_length_mismatch(void);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment