Commit b979d2e8 authored by Tatsuhiro Tsujikawa's avatar Tatsuhiro Tsujikawa

Support increment/reduction of local window size by WINDOW_UPDATE

parent e67096fe
......@@ -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();
......
/*
* 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);
}
/*
* 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);
......
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