diff --git a/lib/spdylay_helper.h b/lib/spdylay_helper.h index 6108b0ec..7355cd71 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 842eef11..8fb8dc47 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 780a6200..6237c612 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 f468272c..6818d486 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 7dfe43f5..994e9ad3 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 49e53a6b..a652f231 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 d418f252..0279fb6d 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 b8d5128e..439e8338 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 0aab1b85..308cc4a0 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 3dc4910f..c2484065 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