diff --git a/lib/spdylay_frame.h b/lib/spdylay_frame.h index e06f7da3..2d02f2ca 100644 --- a/lib/spdylay_frame.h +++ b/lib/spdylay_frame.h @@ -683,7 +683,8 @@ void spdylay_frame_window_update_free(spdylay_window_update *frame); /* * Initializes SETTINGS frame |frame| with given values. |frame| takes - * ownership of |iv|, so caller must not free it. + * ownership of |iv|, so caller must not free it. The |flags| are + * bitwise-OR of one or more of spdylay_settings_flag. */ void spdylay_frame_settings_init(spdylay_settings *frame, uint16_t version, uint8_t flags, diff --git a/lib/spdylay_map.c b/lib/spdylay_map.c index eef9165e..cfddd197 100644 --- a/lib/spdylay_map.c +++ b/lib/spdylay_map.c @@ -197,20 +197,24 @@ size_t spdylay_map_size(spdylay_map *map) return map->size; } -static void for_each(spdylay_map_entry *entry, - void (*func)(key_type key, void *val, void *ptr), - void *ptr) +static int for_each(spdylay_map_entry *entry, + int (*func)(key_type key, void *val, void *ptr), + void *ptr) { - if(entry != NULL) { - for_each(entry->left, func, ptr); - func(entry->key, entry->val, ptr); - for_each(entry->right, func, ptr); + if(entry) { + int rv; + if((rv = for_each(entry->left, func, ptr)) != 0 || + (rv = func(entry->key, entry->val, ptr)) != 0 || + (rv = for_each(entry->right, func, ptr)) != 0) { + return rv; + } } + return 0; } -void spdylay_map_each(spdylay_map *map, - void (*func)(key_type key, void *val, void *ptr), - void *ptr) +int spdylay_map_each(spdylay_map *map, + int (*func)(key_type key, void *val, void *ptr), + void *ptr) { - for_each(map->root, func, ptr); + return for_each(map->root, func, ptr); } diff --git a/lib/spdylay_map.h b/lib/spdylay_map.h index 6237c612..87a1c9e0 100644 --- a/lib/spdylay_map.h +++ b/lib/spdylay_map.h @@ -102,9 +102,16 @@ size_t spdylay_map_size(spdylay_map *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. + * + * If the |func| returns 0, this function calls the |func| with the + * next key and value pair. If the |func| returns nonzero, it will not + * call the |func| for further key and value pair and return the + * return value of the |func| immediately. Thus, this function + * returns 0 if all the invocations of the |func| return 0, or nonzero + * value which the last invocation of |func| returns. */ -void spdylay_map_each(spdylay_map *map, - void (*func)(key_type key, void *val, void *ptr), - void *ptr); +int spdylay_map_each(spdylay_map *map, + int (*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 edd4d3ee..e1b593b2 100644 --- a/lib/spdylay_session.c +++ b/lib/spdylay_session.c @@ -274,10 +274,11 @@ int spdylay_session_server_new(spdylay_session **session_ptr, return r; } -static void spdylay_free_streams(key_type key, void *val, void *ptr) +static int spdylay_free_streams(key_type key, void *val, void *ptr) { spdylay_stream_free((spdylay_stream*)val); free(val); + return 0; } static void spdylay_session_ob_pq_free(spdylay_pq *pq) @@ -1783,28 +1784,56 @@ 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) +static int 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]); + spdylay_update_window_size_arg *arg; + spdylay_stream *stream; + arg = (spdylay_update_window_size_arg*)ptr; + stream = (spdylay_stream*)value; + spdylay_stream_update_initial_window_size(stream, + arg->new_window_size, + arg->old_window_size); + /* If window size gets positive, push deferred DATA frame to + outbound queue. */ + if(stream->window_size > 0 && + stream->deferred_data && + (stream->deferred_flags & SPDYLAY_DEFERRED_FLOW_CONTROL)) { + int rv; + rv = spdylay_pq_push(&arg->session->ob_pq, stream->deferred_data); + if(rv == 0) { + spdylay_stream_detach_deferred_data(stream); + } else { + /* FATAL */ + assert(rv < SPDYLAY_ERR_FATAL); + return rv; + } + } + return 0; } /* * Updates the 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: + * + * SPDYLAY_ERR_NOMEM + * Out of memory. */ -static void spdylay_session_update_initial_window_size +static int 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); + spdylay_update_window_size_arg arg; + arg.session = session; + arg.new_window_size = new_initial_window_size; + arg.old_window_size = + session->remote_settings[SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE]; + return spdylay_map_each(&session->streams, + spdylay_update_initial_window_size_func, + &arg); } void spdylay_session_update_local_settings(spdylay_session *session, @@ -1821,6 +1850,7 @@ void spdylay_session_update_local_settings(spdylay_session *session, int spdylay_session_on_settings_received(spdylay_session *session, spdylay_frame *frame) { + int rv; size_t i; int check[SPDYLAY_SETTINGS_MAX+1]; if(!spdylay_session_check_version(session, frame->settings.hd.version)) { @@ -1842,12 +1872,14 @@ int spdylay_session_on_settings_received(spdylay_session *session, /* 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); + rv = spdylay_session_update_initial_window_size(session, entry->value); + if(rv != 0) { + return rv; + } } } else if(entry->settings_id == SPDYLAY_SETTINGS_CLIENT_CERTIFICATE_VECTOR_SIZE) { if(!session->server) { - int rv; /* Limit certificate vector length in the reasonable size. */ entry->value = spdylay_min(entry->value, SPDYLAY_MAX_CLIENT_CERT_VECTOR_LENGTH); diff --git a/lib/spdylay_session.h b/lib/spdylay_session.h index c5f8f263..8551023f 100644 --- a/lib/spdylay_session.h +++ b/lib/spdylay_session.h @@ -197,6 +197,13 @@ struct spdylay_session { void *user_data; }; +/* Struct used when updating initial window size of each active + stream. */ +typedef struct { + spdylay_session *session; + int32_t new_window_size, old_window_size; +} spdylay_update_window_size_arg; + /* TODO stream timeout etc */ /* diff --git a/tests/spdylay_session_test.c b/tests/spdylay_session_test.c index 24d2fc84..bc4422a2 100644 --- a/tests/spdylay_session_test.c +++ b/tests/spdylay_session_test.c @@ -1736,6 +1736,8 @@ void test_spdylay_session_flow_control(void) spdylay_frame frame; spdylay_stream *stream; int32_t new_initial_window_size; + spdylay_settings_entry iv[1]; + spdylay_frame settings_frame; memset(&callbacks, 0, sizeof(spdylay_session_callbacks)); callbacks.send_callback = null_send_callback; @@ -1769,6 +1771,8 @@ void test_spdylay_session_flow_control(void) stream->window_size = new_initial_window_size- (session->remote_settings[SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE] -stream->window_size); + session->remote_settings[SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE] = + new_initial_window_size; CU_ASSERT(-48*1024 == stream->window_size); /* Back 48KiB */ @@ -1787,9 +1791,15 @@ void test_spdylay_session_flow_control(void) CU_ASSERT(0 == spdylay_session_send(session)); CU_ASSERT(16*1024 == ud.data_source_length); - /* Back 16KiB */ - frame.window_update.delta_window_size = 16*1024; - spdylay_session_on_window_update_received(session, &frame); + /* Increase initial window size to 32KiB */ + iv[0].settings_id = SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE; + iv[0].flags = SPDYLAY_ID_FLAG_SETTINGS_NONE; + iv[0].value = 32*1024; + + spdylay_frame_settings_init(&settings_frame.settings, SPDYLAY_PROTO_SPDY3, + SPDYLAY_FLAG_SETTINGS_NONE, dup_iv(iv, 1), 1); + spdylay_session_on_settings_received(session, &settings_frame); + spdylay_frame_settings_free(&settings_frame.settings); /* Sends another 16KiB data */ CU_ASSERT(0 == spdylay_session_send(session));