From 82e20192d894c4026b61bb7c5979c73aa25b358d Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Sat, 10 Mar 2012 00:10:11 +0900 Subject: [PATCH] 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. --- lib/spdylay_helper.h | 3 ++ lib/spdylay_map.c | 15 ++++--- lib/spdylay_map.h | 9 ++-- lib/spdylay_session.c | 76 ++++++++++++++++++++++++++----- lib/spdylay_session.h | 8 ++-- lib/spdylay_stream.c | 10 ++++- lib/spdylay_stream.h | 19 ++++---- tests/main.c | 2 + tests/spdylay_session_test.c | 87 +++++++++++++++++++++++++++++++++--- tests/spdylay_session_test.h | 1 + 10 files changed, 193 insertions(+), 37 deletions(-) diff --git a/lib/spdylay_helper.h b/lib/spdylay_helper.h index 6108b0e..7355cd7 100644 --- a/lib/spdylay_helper.h +++ b/lib/spdylay_helper.h @@ -31,6 +31,9 @@ #include +#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 * network byte order. diff --git a/lib/spdylay_map.c b/lib/spdylay_map.c index 842eef1..8fb8dc4 100644 --- a/lib/spdylay_map.c +++ b/lib/spdylay_map.c @@ -197,16 +197,19 @@ size_t spdylay_map_size(spdylay_map *map) } 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) { - for_each(entry->left, func); - func(entry->key, entry->val); - for_each(entry->right, func); + for_each(entry->left, func, ptr); + func(entry->key, entry->val, ptr); + 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); } diff --git a/lib/spdylay_map.h b/lib/spdylay_map.h index 780a620..6237c61 100644 --- a/lib/spdylay_map.h +++ b/lib/spdylay_map.h @@ -99,9 +99,12 @@ void spdylay_map_erase(spdylay_map *map, key_type key); size_t spdylay_map_size(spdylay_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. + * Applies the function |func| to each key/item pair in the map |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 */ diff --git a/lib/spdylay_session.c b/lib/spdylay_session.c index f468272..6818d48 100644 --- a/lib/spdylay_session.c +++ b/lib/spdylay_session.c @@ -39,7 +39,11 @@ static int spdylay_session_get_max_concurrent_streams_reached (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); } @@ -157,11 +161,18 @@ static int spdylay_session_new(spdylay_session **session_ptr, spdylay_buffer_init(&(*session_ptr)->inflatebuf, 4096); - memset((*session_ptr)->settings, 0, sizeof((*session_ptr)->settings)); - (*session_ptr)->settings[SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS] = + memset((*session_ptr)->remote_settings, 0, + sizeof((*session_ptr)->remote_settings)); + (*session_ptr)->remote_settings[SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS] = 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; (*session_ptr)->callbacks = *callbacks; @@ -232,7 +243,7 @@ int spdylay_session_server_new(spdylay_session **session_ptr, 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); free(val); @@ -263,7 +274,7 @@ void spdylay_session_del(spdylay_session *session) if(session == NULL) { return; } - spdylay_map_each(&session->streams, spdylay_free_streams); + spdylay_map_each(&session->streams, spdylay_free_streams, NULL); spdylay_map_free(&session->streams); spdylay_session_ob_pq_free(&session->ob_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; } 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); r = spdylay_map_insert(&session->streams, stream_id, stream); if(r != 0) { @@ -1390,7 +1402,6 @@ static int spdylay_session_handle_invalid_stream int spdylay_session_on_syn_stream_received(spdylay_session *session, spdylay_frame *frame) { - /* TODO Check SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS */ int r = 0; int status_code; if(session->goaway_flags) { @@ -1507,13 +1518,58 @@ int spdylay_session_on_rst_stream_received(spdylay_session *session, 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, spdylay_frame *frame) { + int i, check[SPDYLAY_SETTINGS_MAX+1]; if(!spdylay_session_check_version(session, frame->settings.hd.version)) { 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); return 0; } @@ -1901,7 +1957,7 @@ static int spdylay_session_update_recv_window_size(spdylay_session *session, stream->recv_window_size += delta_size; /* This is just a heuristics. */ if(stream->recv_window_size*2 >= - session->settings[SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE]) { + session->remote_settings[SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE]) { int r; r = spdylay_session_add_window_update(session, stream_id, stream->recv_window_size); diff --git a/lib/spdylay_session.h b/lib/spdylay_session.h index 7dfe43f..994e9ad 100644 --- a/lib/spdylay_session.h +++ b/lib/spdylay_session.h @@ -156,9 +156,11 @@ struct spdylay_session { control. Nonzero for flow control enabled. */ uint8_t flow_control; - /* Settings value store. We just use ID as index. The index = 0 is - unused. */ - uint32_t settings[SPDYLAY_SETTINGS_MAX+1]; + /* Settings value received from the remote endpoint. We just use ID + as index. The index = 0 is unused. */ + 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; void *user_data; diff --git a/lib/spdylay_stream.c b/lib/spdylay_stream.c index 49e53a6..a652f23 100644 --- a/lib/spdylay_stream.c +++ b/lib/spdylay_stream.c @@ -43,7 +43,7 @@ void spdylay_stream_init(spdylay_stream *stream, int32_t stream_id, stream->stream_user_data = stream_user_data; stream->deferred_data = NULL; 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; } @@ -90,3 +90,11 @@ void spdylay_stream_detach_deferred_data(spdylay_stream *stream) stream->deferred_data = NULL; 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); +} diff --git a/lib/spdylay_stream.h b/lib/spdylay_stream.h index d418f25..0279fb6 100644 --- a/lib/spdylay_stream.h +++ b/lib/spdylay_stream.h @@ -99,14 +99,8 @@ typedef struct { /* The flags for defered DATA. Bitwise OR of zero or more spdylay_deferred_flag values */ uint8_t deferred_flags; - /* Initial window size where window_size is compuated - against. Initially, window_size = initial_window_size. When N - 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 */ + /* Current sender window size. This value is computed against the + current initial window size of remote endpoint. */ int32_t window_size; /* Keep track of the number of bytes received without WINDOW_UPDATE. */ @@ -155,4 +149,13 @@ void spdylay_stream_defer_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 */ diff --git a/tests/main.c b/tests/main.c index b8d5128..439e833 100644 --- a/tests/main.c +++ b/tests/main.c @@ -138,6 +138,8 @@ int main(int argc, char* argv[]) test_spdylay_session_flow_control) || !CU_add_test(pSuite, "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", test_spdylay_frame_unpack_nv_spdy2) || !CU_add_test(pSuite, "frame_unpack_nv_spdy3", diff --git a/tests/spdylay_session_test.c b/tests/spdylay_session_test.c index 0aab1b8..308cc4a 100644 --- a/tests/spdylay_session_test.c +++ b/tests/spdylay_session_test.c @@ -194,6 +194,12 @@ static char** dup_nv(const char **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() { spdylay_session *session; @@ -1171,7 +1177,7 @@ void test_spdylay_session_get_next_ob_item() callbacks.send_callback = null_send_callback; 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)); spdylay_submit_ping(session); @@ -1195,7 +1201,7 @@ void test_spdylay_session_get_next_ob_item() CU_ASSERT(SPDYLAY_SYN_REPLY == 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 == 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; 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)); 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)); 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); CU_ASSERT(SPDYLAY_SYN_STREAM == item->frame_type); @@ -1360,7 +1366,7 @@ void test_spdylay_session_max_concurrent_streams() SPDYLAY_STREAM_OPENED, NULL); spdylay_frame_syn_stream_init(&frame.syn_stream, SPDYLAY_PROTO_SPDY2, 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)); @@ -1678,7 +1684,8 @@ void test_spdylay_session_flow_control() negative. */ new_initial_window_size = 16*1024; 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); /* Back 48KiB */ @@ -1823,3 +1830,71 @@ void test_spdylay_session_on_ctrl_not_send() 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); +} diff --git a/tests/spdylay_session_test.h b/tests/spdylay_session_test.h index 3dc4910..c248406 100644 --- a/tests/spdylay_session_test.h +++ b/tests/spdylay_session_test.h @@ -61,5 +61,6 @@ void test_spdylay_session_recv_invalid_frame(); void test_spdylay_session_defer_data(); void test_spdylay_session_flow_control(); void test_spdylay_session_on_ctrl_not_send(); +void test_spdylay_session_on_settings_received(); #endif // SPDYLAY_SESSION_TEST_H