Added functions to process received SETTINGS frame.

Now remote and local settings are stored separately.
The initial window size is included in SETTINGS frame, all active stream's
window sizes are now updated.
Removed the initial_window_size member from spdylay_stream because
it is the same as remote_settings's initial window size.
This commit is contained in:
Tatsuhiro Tsujikawa 2012-03-10 00:10:11 +09:00
parent 6c9e79e8ca
commit 82e20192d8
10 changed files with 193 additions and 37 deletions

View File

@ -31,6 +31,9 @@
#include <spdylay/spdylay.h> #include <spdylay/spdylay.h>
#define spdylay_min(A, B) ((A) < (B) ? (A) : (B))
#define spdylay_max(A, B) ((A) > (B) ? (A) : (B))
/* /*
* Copies 2 byte unsigned integer |n| in host byte order to |buf| in * Copies 2 byte unsigned integer |n| in host byte order to |buf| in
* network byte order. * network byte order.

View File

@ -197,16 +197,19 @@ size_t spdylay_map_size(spdylay_map *map)
} }
static void for_each(spdylay_map_entry *entry, static void for_each(spdylay_map_entry *entry,
void (*func)(key_type key, void *val)) void (*func)(key_type key, void *val, void *ptr),
void *ptr)
{ {
if(entry != NULL) { if(entry != NULL) {
for_each(entry->left, func); for_each(entry->left, func, ptr);
func(entry->key, entry->val); func(entry->key, entry->val, ptr);
for_each(entry->right, func); for_each(entry->right, func, ptr);
} }
} }
void spdylay_map_each(spdylay_map *map, void (*func)(key_type key, void *val)) void spdylay_map_each(spdylay_map *map,
void (*func)(key_type key, void *val, void *ptr),
void *ptr)
{ {
for_each(map->root, func); for_each(map->root, func, ptr);
} }

View File

@ -99,9 +99,12 @@ void spdylay_map_erase(spdylay_map *map, key_type key);
size_t spdylay_map_size(spdylay_map *map); size_t spdylay_map_size(spdylay_map *map);
/* /*
* Applies the function |func| to each key/item pair in the map |map|. * Applies the function |func| to each key/item pair in the map |map|
* This function is useful to free item in the map. * with the optional user supplied pointer |ptr|. This function is
* useful to free item in the map.
*/ */
void spdylay_map_each(spdylay_map *map, void (*func)(key_type key, void *val)); void spdylay_map_each(spdylay_map *map,
void (*func)(key_type key, void *val, void *ptr),
void *ptr);
#endif /* SPDYLAY_MAP_H */ #endif /* SPDYLAY_MAP_H */

View File

@ -39,7 +39,11 @@
static int spdylay_session_get_max_concurrent_streams_reached static int spdylay_session_get_max_concurrent_streams_reached
(spdylay_session *session) (spdylay_session *session)
{ {
return session->settings[SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS] uint32_t local_max, remote_max;
local_max = session->local_settings[SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS];
remote_max =
session->remote_settings[SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS];
return spdylay_min(local_max, remote_max)
<= spdylay_map_size(&session->streams); <= spdylay_map_size(&session->streams);
} }
@ -157,11 +161,18 @@ static int spdylay_session_new(spdylay_session **session_ptr,
spdylay_buffer_init(&(*session_ptr)->inflatebuf, 4096); spdylay_buffer_init(&(*session_ptr)->inflatebuf, 4096);
memset((*session_ptr)->settings, 0, sizeof((*session_ptr)->settings)); memset((*session_ptr)->remote_settings, 0,
(*session_ptr)->settings[SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS] = sizeof((*session_ptr)->remote_settings));
(*session_ptr)->remote_settings[SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS] =
SPDYLAY_CONCURRENT_STREAMS_MAX; SPDYLAY_CONCURRENT_STREAMS_MAX;
(*session_ptr)->remote_settings[SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE] =
SPDYLAY_INITIAL_WINDOW_SIZE;
(*session_ptr)->settings[SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE] = memset((*session_ptr)->local_settings, 0,
sizeof((*session_ptr)->local_settings));
(*session_ptr)->local_settings[SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS] =
SPDYLAY_CONCURRENT_STREAMS_MAX;
(*session_ptr)->local_settings[SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE] =
SPDYLAY_INITIAL_WINDOW_SIZE; SPDYLAY_INITIAL_WINDOW_SIZE;
(*session_ptr)->callbacks = *callbacks; (*session_ptr)->callbacks = *callbacks;
@ -232,7 +243,7 @@ int spdylay_session_server_new(spdylay_session **session_ptr,
return r; return r;
} }
static void spdylay_free_streams(key_type key, void *val) static void spdylay_free_streams(key_type key, void *val, void *ptr)
{ {
spdylay_stream_free((spdylay_stream*)val); spdylay_stream_free((spdylay_stream*)val);
free(val); free(val);
@ -263,7 +274,7 @@ void spdylay_session_del(spdylay_session *session)
if(session == NULL) { if(session == NULL) {
return; return;
} }
spdylay_map_each(&session->streams, spdylay_free_streams); spdylay_map_each(&session->streams, spdylay_free_streams, NULL);
spdylay_map_free(&session->streams); spdylay_map_free(&session->streams);
spdylay_session_ob_pq_free(&session->ob_pq); spdylay_session_ob_pq_free(&session->ob_pq);
spdylay_session_ob_pq_free(&session->ob_ss_pq); spdylay_session_ob_pq_free(&session->ob_ss_pq);
@ -398,7 +409,8 @@ spdylay_stream* spdylay_session_open_stream(spdylay_session *session,
return NULL; return NULL;
} }
spdylay_stream_init(stream, stream_id, flags, pri, initial_state, spdylay_stream_init(stream, stream_id, flags, pri, initial_state,
session->settings[SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE], session->remote_settings
[SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE],
stream_user_data); stream_user_data);
r = spdylay_map_insert(&session->streams, stream_id, stream); r = spdylay_map_insert(&session->streams, stream_id, stream);
if(r != 0) { if(r != 0) {
@ -1390,7 +1402,6 @@ static int spdylay_session_handle_invalid_stream
int spdylay_session_on_syn_stream_received(spdylay_session *session, int spdylay_session_on_syn_stream_received(spdylay_session *session,
spdylay_frame *frame) spdylay_frame *frame)
{ {
/* TODO Check SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS */
int r = 0; int r = 0;
int status_code; int status_code;
if(session->goaway_flags) { if(session->goaway_flags) {
@ -1507,13 +1518,58 @@ int spdylay_session_on_rst_stream_received(spdylay_session *session,
return 0; return 0;
} }
static void spdylay_update_initial_window_size_func(key_type key, void *value,
void *ptr)
{
int32_t *vals;
vals = (int32_t*)ptr;
spdylay_stream_update_initial_window_size((spdylay_stream*)value,
vals[0], vals[1]);
}
/*
* Updates the initial window size of all active streams.
*/
static void spdylay_session_update_initial_window_size
(spdylay_session *session,
int32_t new_initial_window_size)
{
int32_t vals[2];
vals[0] = new_initial_window_size;
vals[1] = session->remote_settings[SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE];
spdylay_map_each(&session->streams,
spdylay_update_initial_window_size_func,
vals);
}
int spdylay_session_on_settings_received(spdylay_session *session, int spdylay_session_on_settings_received(spdylay_session *session,
spdylay_frame *frame) spdylay_frame *frame)
{ {
int i, check[SPDYLAY_SETTINGS_MAX+1];
if(!spdylay_session_check_version(session, frame->settings.hd.version)) { if(!spdylay_session_check_version(session, frame->settings.hd.version)) {
return 0; return 0;
} }
/* TODO Check ID/value pairs and persist them if necessary. */ /* Check ID/value pairs and persist them if necessary. */
memset(check, 0, sizeof(check));
for(i = 0; i < frame->settings.niv; ++i) {
const spdylay_settings_entry *entry = &frame->settings.iv[i];
/* SPDY/3 spec says if the multiple values for the same ID were
found, use the first one and ignore the rest. */
if(entry->settings_id > SPDYLAY_SETTINGS_MAX || entry->settings_id == 0 ||
check[entry->settings_id] == 1) {
continue;
}
check[entry->settings_id] = 1;
if(entry->settings_id == SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE &&
session->flow_control) {
/* Update the initial window size of the all active streams */
/* Check that initial_window_size < (1u << 31) */
if(entry->value < (1u << 31)) {
spdylay_session_update_initial_window_size(session, entry->value);
}
}
session->remote_settings[entry->settings_id] = entry->value;
}
spdylay_session_call_on_ctrl_frame_received(session, SPDYLAY_SETTINGS, frame); spdylay_session_call_on_ctrl_frame_received(session, SPDYLAY_SETTINGS, frame);
return 0; return 0;
} }
@ -1901,7 +1957,7 @@ static int spdylay_session_update_recv_window_size(spdylay_session *session,
stream->recv_window_size += delta_size; stream->recv_window_size += delta_size;
/* This is just a heuristics. */ /* This is just a heuristics. */
if(stream->recv_window_size*2 >= if(stream->recv_window_size*2 >=
session->settings[SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE]) { session->remote_settings[SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE]) {
int r; int r;
r = spdylay_session_add_window_update(session, stream_id, r = spdylay_session_add_window_update(session, stream_id,
stream->recv_window_size); stream->recv_window_size);

View File

@ -156,9 +156,11 @@ struct spdylay_session {
control. Nonzero for flow control enabled. */ control. Nonzero for flow control enabled. */
uint8_t flow_control; uint8_t flow_control;
/* Settings value store. We just use ID as index. The index = 0 is /* Settings value received from the remote endpoint. We just use ID
unused. */ as index. The index = 0 is unused. */
uint32_t settings[SPDYLAY_SETTINGS_MAX+1]; uint32_t remote_settings[SPDYLAY_SETTINGS_MAX+1];
/* Settings value of the local endpoint. */
uint32_t local_settings[SPDYLAY_SETTINGS_MAX+1];
spdylay_session_callbacks callbacks; spdylay_session_callbacks callbacks;
void *user_data; void *user_data;

View File

@ -43,7 +43,7 @@ void spdylay_stream_init(spdylay_stream *stream, int32_t stream_id,
stream->stream_user_data = stream_user_data; stream->stream_user_data = stream_user_data;
stream->deferred_data = NULL; stream->deferred_data = NULL;
stream->deferred_flags = SPDYLAY_DEFERRED_NONE; stream->deferred_flags = SPDYLAY_DEFERRED_NONE;
stream->initial_window_size = stream->window_size = initial_window_size; stream->window_size = initial_window_size;
stream->recv_window_size = 0; stream->recv_window_size = 0;
} }
@ -90,3 +90,11 @@ void spdylay_stream_detach_deferred_data(spdylay_stream *stream)
stream->deferred_data = NULL; stream->deferred_data = NULL;
stream->deferred_flags = SPDYLAY_DEFERRED_NONE; stream->deferred_flags = SPDYLAY_DEFERRED_NONE;
} }
void spdylay_stream_update_initial_window_size(spdylay_stream *stream,
int32_t new_initial_window_size,
int32_t old_initial_window_size)
{
stream->window_size =
new_initial_window_size-(old_initial_window_size-stream->window_size);
}

View File

@ -99,14 +99,8 @@ typedef struct {
/* The flags for defered DATA. Bitwise OR of zero or more /* The flags for defered DATA. Bitwise OR of zero or more
spdylay_deferred_flag values */ spdylay_deferred_flag values */
uint8_t deferred_flags; uint8_t deferred_flags;
/* Initial window size where window_size is compuated /* Current sender window size. This value is computed against the
against. Initially, window_size = initial_window_size. When N current initial window size of remote endpoint. */
bytes are sent, window_size -= N. After that, when the initial
window size is changed, say, new_initial_window_size, then
window_size becomes
new_initial_window_size-(initial_window_size-window_size) */
int32_t initial_window_size;
/* Current sender window size */
int32_t window_size; int32_t window_size;
/* Keep track of the number of bytes received without /* Keep track of the number of bytes received without
WINDOW_UPDATE. */ WINDOW_UPDATE. */
@ -155,4 +149,13 @@ void spdylay_stream_defer_data(spdylay_stream *stream,
*/ */
void spdylay_stream_detach_deferred_data(spdylay_stream *stream); void spdylay_stream_detach_deferred_data(spdylay_stream *stream);
/*
* Updates the initial window size with the new value
* |new_initial_window_size|. The |old_initial_window_size| is used to
* calculate the current window size.
*/
void spdylay_stream_update_initial_window_size(spdylay_stream *stream,
int32_t new_initial_window_size,
int32_t old_initial_window_size);
#endif /* SPDYLAY_STREAM */ #endif /* SPDYLAY_STREAM */

View File

@ -138,6 +138,8 @@ int main(int argc, char* argv[])
test_spdylay_session_flow_control) || test_spdylay_session_flow_control) ||
!CU_add_test(pSuite, "session_on_ctrl_not_send", !CU_add_test(pSuite, "session_on_ctrl_not_send",
test_spdylay_session_on_ctrl_not_send) || test_spdylay_session_on_ctrl_not_send) ||
!CU_add_test(pSuite, "session_on_settings_received",
test_spdylay_session_on_settings_received) ||
!CU_add_test(pSuite, "frame_unpack_nv_spdy2", !CU_add_test(pSuite, "frame_unpack_nv_spdy2",
test_spdylay_frame_unpack_nv_spdy2) || test_spdylay_frame_unpack_nv_spdy2) ||
!CU_add_test(pSuite, "frame_unpack_nv_spdy3", !CU_add_test(pSuite, "frame_unpack_nv_spdy3",

View File

@ -194,6 +194,12 @@ static char** dup_nv(const char **src)
return spdylay_frame_nv_copy(src); return spdylay_frame_nv_copy(src);
} }
static spdylay_settings_entry* dup_iv(const spdylay_settings_entry *iv,
size_t niv)
{
return spdylay_frame_iv_copy(iv, niv);
}
void test_spdylay_session_recv() void test_spdylay_session_recv()
{ {
spdylay_session *session; spdylay_session *session;
@ -1171,7 +1177,7 @@ void test_spdylay_session_get_next_ob_item()
callbacks.send_callback = null_send_callback; callbacks.send_callback = null_send_callback;
spdylay_session_server_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks, NULL); spdylay_session_server_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks, NULL);
session->settings[SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS] = 2; session->local_settings[SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS] = 2;
CU_ASSERT(NULL == spdylay_session_get_next_ob_item(session)); CU_ASSERT(NULL == spdylay_session_get_next_ob_item(session));
spdylay_submit_ping(session); spdylay_submit_ping(session);
@ -1195,7 +1201,7 @@ void test_spdylay_session_get_next_ob_item()
CU_ASSERT(SPDYLAY_SYN_REPLY == CU_ASSERT(SPDYLAY_SYN_REPLY ==
spdylay_session_get_next_ob_item(session)->frame_type); spdylay_session_get_next_ob_item(session)->frame_type);
session->settings[SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS] = 3; session->local_settings[SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS] = 3;
CU_ASSERT(SPDYLAY_SYN_STREAM == CU_ASSERT(SPDYLAY_SYN_STREAM ==
spdylay_session_get_next_ob_item(session)->frame_type); spdylay_session_get_next_ob_item(session)->frame_type);
@ -1213,7 +1219,7 @@ void test_spdylay_session_pop_next_ob_item()
callbacks.send_callback = null_send_callback; callbacks.send_callback = null_send_callback;
spdylay_session_server_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks, NULL); spdylay_session_server_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks, NULL);
session->settings[SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS] = 1; session->local_settings[SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS] = 1;
CU_ASSERT(NULL == spdylay_session_pop_next_ob_item(session)); CU_ASSERT(NULL == spdylay_session_pop_next_ob_item(session));
spdylay_submit_ping(session); spdylay_submit_ping(session);
@ -1245,7 +1251,7 @@ void test_spdylay_session_pop_next_ob_item()
CU_ASSERT(NULL == spdylay_session_pop_next_ob_item(session)); CU_ASSERT(NULL == spdylay_session_pop_next_ob_item(session));
spdylay_submit_response(session, 1, nv, NULL); spdylay_submit_response(session, 1, nv, NULL);
session->settings[SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS] = 2; session->local_settings[SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS] = 2;
item = spdylay_session_pop_next_ob_item(session); item = spdylay_session_pop_next_ob_item(session);
CU_ASSERT(SPDYLAY_SYN_STREAM == item->frame_type); CU_ASSERT(SPDYLAY_SYN_STREAM == item->frame_type);
@ -1360,7 +1366,7 @@ void test_spdylay_session_max_concurrent_streams()
SPDYLAY_STREAM_OPENED, NULL); SPDYLAY_STREAM_OPENED, NULL);
spdylay_frame_syn_stream_init(&frame.syn_stream, SPDYLAY_PROTO_SPDY2, spdylay_frame_syn_stream_init(&frame.syn_stream, SPDYLAY_PROTO_SPDY2,
SPDYLAY_CTRL_FLAG_NONE, 3, 0, 3, dup_nv(nv)); SPDYLAY_CTRL_FLAG_NONE, 3, 0, 3, dup_nv(nv));
session->settings[SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS] = 1; session->local_settings[SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS] = 1;
CU_ASSERT(0 == spdylay_session_on_syn_stream_received(session, &frame)); CU_ASSERT(0 == spdylay_session_on_syn_stream_received(session, &frame));
@ -1678,7 +1684,8 @@ void test_spdylay_session_flow_control()
negative. */ negative. */
new_initial_window_size = 16*1024; new_initial_window_size = 16*1024;
stream->window_size = new_initial_window_size- stream->window_size = new_initial_window_size-
(stream->initial_window_size-stream->window_size); (session->remote_settings[SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE]
-stream->window_size);
CU_ASSERT(-48*1024 == stream->window_size); CU_ASSERT(-48*1024 == stream->window_size);
/* Back 48KiB */ /* Back 48KiB */
@ -1823,3 +1830,71 @@ void test_spdylay_session_on_ctrl_not_send()
spdylay_session_del(session); spdylay_session_del(session);
} }
void test_spdylay_session_on_settings_received()
{
spdylay_session *session;
spdylay_session_callbacks callbacks;
my_user_data user_data;
spdylay_stream *stream1, *stream2;
spdylay_frame frame;
const size_t niv = 5;
spdylay_settings_entry iv[niv];
iv[0].settings_id = SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS;
iv[0].value = 1000000009;
iv[0].flags = SPDYLAY_ID_FLAG_SETTINGS_NONE;
iv[1].settings_id = SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS;
iv[1].value = 50;
iv[1].flags = SPDYLAY_ID_FLAG_SETTINGS_NONE;
iv[2].settings_id = SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE;
iv[2].value = 64*1024;
iv[2].flags = SPDYLAY_ID_FLAG_SETTINGS_NONE;
iv[3].settings_id = SPDYLAY_SETTINGS_CURRENT_CWND;
iv[3].value = 512;
iv[3].flags = SPDYLAY_ID_FLAG_SETTINGS_NONE;
iv[4].settings_id = 999;
iv[4].value = 0;
iv[4].flags = SPDYLAY_ID_FLAG_SETTINGS_NONE;
memset(&callbacks, 0, sizeof(spdylay_session_callbacks));
spdylay_session_server_new(&session, SPDYLAY_PROTO_SPDY3, &callbacks,
&user_data);
session->remote_settings[SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE] = 16*1024;
stream1 = spdylay_session_open_stream(session, 1, SPDYLAY_CTRL_FLAG_NONE,
3, SPDYLAY_STREAM_OPENING, NULL);
stream2 = spdylay_session_open_stream(session, 2, SPDYLAY_CTRL_FLAG_NONE,
3, SPDYLAY_STREAM_OPENING, NULL);
stream1->window_size = 16*1024;
stream2->window_size = -48*1024;
spdylay_frame_settings_init(&frame.settings, SPDYLAY_PROTO_SPDY3,
SPDYLAY_FLAG_SETTINGS_NONE, dup_iv(iv, niv), niv);
CU_ASSERT(0 == spdylay_session_on_settings_received(session, &frame));
CU_ASSERT(1000000009 ==
session->remote_settings[SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS]);
CU_ASSERT(64*1024 ==
session->remote_settings[SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE]);
CU_ASSERT(512 ==
session->remote_settings[SPDYLAY_SETTINGS_CURRENT_CWND]);
CU_ASSERT(64*1024 == stream1->window_size);
CU_ASSERT(0 == stream2->window_size);
frame.settings.iv[2].value = 16*1024;
CU_ASSERT(0 == spdylay_session_on_settings_received(session, &frame));
CU_ASSERT(16*1024 == stream1->window_size);
CU_ASSERT(-48*1024 == stream2->window_size);
spdylay_frame_settings_free(&frame.settings);
spdylay_session_del(session);
}

View File

@ -61,5 +61,6 @@ void test_spdylay_session_recv_invalid_frame();
void test_spdylay_session_defer_data(); void test_spdylay_session_defer_data();
void test_spdylay_session_flow_control(); void test_spdylay_session_flow_control();
void test_spdylay_session_on_ctrl_not_send(); void test_spdylay_session_on_ctrl_not_send();
void test_spdylay_session_on_settings_received();
#endif // SPDYLAY_SESSION_TEST_H #endif // SPDYLAY_SESSION_TEST_H