diff --git a/lib/includes/spdylay/spdylay.h b/lib/includes/spdylay/spdylay.h index 36074e61..117d48cd 100644 --- a/lib/includes/spdylay/spdylay.h +++ b/lib/includes/spdylay/spdylay.h @@ -316,6 +316,14 @@ int spdylay_session_client_new(spdylay_session **session_ptr, const spdylay_session_callbacks *callbacks, void *user_data); +/* + * Initializes |*session_ptr| for server use. This function returns 0 + * if it succeeds, or negative error code. + */ +int spdylay_session_server_new(spdylay_session **session_ptr, + const spdylay_session_callbacks *callbacks, + void *user_data); + /* * Frees any resources allocated for |session|. */ diff --git a/lib/spdylay_session.c b/lib/spdylay_session.c index 890e8a2a..a8180468 100644 --- a/lib/spdylay_session.c +++ b/lib/spdylay_session.c @@ -65,7 +65,7 @@ int spdylay_outbound_item_compar(const void *lhsx, const void *rhsx) } } -int spdylay_session_client_new(spdylay_session **session_ptr, +static int spdylay_session_new(spdylay_session **session_ptr, const spdylay_session_callbacks *callbacks, void *user_data) { @@ -76,10 +76,9 @@ int spdylay_session_client_new(spdylay_session **session_ptr, } memset(*session_ptr, 0, sizeof(spdylay_session)); - /* IDs for use in client */ - (*session_ptr)->next_stream_id = 1; - (*session_ptr)->last_recv_stream_id = 0; - (*session_ptr)->next_unique_id = 1; + /* next_stream_id, last_recv_stream_id and next_unique_id are + initialized in either spdylay_session_client_new or + spdylay_session_server_new */ (*session_ptr)->last_ping_unique_id = 0; @@ -124,6 +123,37 @@ int spdylay_session_client_new(spdylay_session **session_ptr, return 0; } +int spdylay_session_client_new(spdylay_session **session_ptr, + const spdylay_session_callbacks *callbacks, + void *user_data) +{ + int r; + r = spdylay_session_new(session_ptr, callbacks, user_data); + if(r == 0) { + /* IDs for use in client */ + (*session_ptr)->next_stream_id = 1; + (*session_ptr)->last_recv_stream_id = 0; + (*session_ptr)->next_unique_id = 1; + } + return r; +} + +int spdylay_session_server_new(spdylay_session **session_ptr, + const spdylay_session_callbacks *callbacks, + void *user_data) +{ + int r; + r = spdylay_session_new(session_ptr, callbacks, user_data); + if(r == 0) { + (*session_ptr)->server = 1; + /* IDs for use in client */ + (*session_ptr)->next_stream_id = 2; + (*session_ptr)->last_recv_stream_id = 0; + (*session_ptr)->next_unique_id = 2; + } + return r; +} + static void spdylay_free_streams(key_type key, void *val) { spdylay_stream_free((spdylay_stream*)val); @@ -769,13 +799,31 @@ static int spdylay_session_is_new_peer_stream_id(spdylay_session *session, static int spdylay_session_validate_syn_stream(spdylay_session *session, spdylay_syn_stream *frame) { - /* TODO Check assoc_stream_id */ if(!spdylay_session_is_new_peer_stream_id(session, frame->stream_id)) { return SPDYLAY_PROTOCOL_ERROR; } if(frame->hd.version != SPDYLAY_PROTO_VERSION) { return SPDYLAY_UNSUPPORTED_VERSION; } + if(session->server) { + if(frame->assoc_stream_id != 0) { + return SPDYLAY_PROTOCOL_ERROR; + } + } else { + if(frame->assoc_stream_id == 0) { + /* spdy/2 spec: When a client receives a SYN_STREAM from the + server with an Associated-To-Stream-ID of 0, it must reply with + a RST_STREAM with error code INVALID_STREAM. */ + return SPDYLAY_INVALID_STREAM; + } + if((frame->hd.flags & SPDYLAY_FLAG_UNIDIRECTIONAL) == 0 || + frame->assoc_stream_id % 2 == 0 || + spdylay_session_get_stream(session, frame->assoc_stream_id) == NULL) { + /* It seems spdy/2 spec does not say which status code should be + returned in these cases. */ + return SPDYLAY_PROTOCOL_ERROR; + } + } return 0; } diff --git a/lib/spdylay_stream.h b/lib/spdylay_stream.h index ee4c60aa..f85426c5 100644 --- a/lib/spdylay_stream.h +++ b/lib/spdylay_stream.h @@ -77,6 +77,9 @@ typedef struct { uint8_t pri; /* Bitwise OR of zero or more spdylay_shut_flag values */ uint8_t shut_flags; + /* TODO spdylay_stream should remember pushed stream ID, so that if + RST_STREAM with CANCEL (mandatory?) is sent, we can close all of + them. */ } spdylay_stream; void spdylay_stream_init(spdylay_stream *stream, int32_t stream_id, diff --git a/tests/main.c b/tests/main.c index 76d199f6..dff44e59 100644 --- a/tests/main.c +++ b/tests/main.c @@ -73,6 +73,8 @@ int main() test_spdylay_session_add_frame) || !CU_add_test(pSuite, "session_on_syn_stream_received", test_spdylay_session_on_syn_stream_received) || + !CU_add_test(pSuite, "session_on_syn_stream_received_with_push", + test_spdylay_session_on_syn_stream_received_with_push) || !CU_add_test(pSuite, "session_on_syn_reply_received", test_spdylay_session_on_syn_reply_received) || !CU_add_test(pSuite, "session_send_syn_stream", diff --git a/tests/spdylay_session_test.c b/tests/spdylay_session_test.c index b6dfc679..e04fe76f 100644 --- a/tests/spdylay_session_test.c +++ b/tests/spdylay_session_test.c @@ -279,33 +279,92 @@ void test_spdylay_session_recv_invalid_stream_id() void test_spdylay_session_on_syn_stream_received() { spdylay_session *session; - spdylay_session_callbacks callbacks = { - NULL, - NULL, - on_ctrl_recv_callback, - on_invalid_ctrl_recv_callback - }; + spdylay_session_callbacks callbacks; my_user_data user_data; const char *nv[] = { NULL }; spdylay_frame frame; spdylay_stream *stream; + int32_t stream_id = 1; + uint8_t pri = 3; + memset(&callbacks, 0, sizeof(spdylay_session_callbacks)); + callbacks.on_ctrl_recv_callback = on_ctrl_recv_callback; + callbacks.on_invalid_ctrl_recv_callback = on_invalid_ctrl_recv_callback; + user_data.valid = 0; + user_data.invalid = 0; + + spdylay_session_server_new(&session, &callbacks, &user_data); + spdylay_frame_syn_stream_init(&frame.syn_stream, SPDYLAY_FLAG_NONE, + stream_id, 0, pri, dup_nv(nv)); + + CU_ASSERT(0 == spdylay_session_on_syn_stream_received(session, &frame)); + CU_ASSERT(1 == user_data.valid); + stream = spdylay_session_get_stream(session, stream_id); + CU_ASSERT(SPDYLAY_STREAM_OPENING == stream->state); + CU_ASSERT(pri == stream->pri); + + /* Same stream ID twice leads stream closing */ + CU_ASSERT(0 == spdylay_session_on_syn_stream_received(session, &frame)); + CU_ASSERT(1 == user_data.invalid); + CU_ASSERT(SPDYLAY_STREAM_CLOSING == + spdylay_session_get_stream(session, stream_id)->state); + + /* assoc_stream_id != 0 from client is invalid. */ + frame.syn_stream.assoc_stream_id = 1; + CU_ASSERT(0 == spdylay_session_on_syn_stream_received(session, &frame)); + CU_ASSERT(2 == user_data.invalid); + + spdylay_frame_syn_stream_free(&frame.syn_stream); + spdylay_session_del(session); +} + +void test_spdylay_session_on_syn_stream_received_with_push() +{ + spdylay_session *session; + spdylay_session_callbacks callbacks; + my_user_data user_data; + const char *nv[] = { NULL }; + spdylay_frame frame; + spdylay_stream *stream; + int32_t stream_id = 2; + int32_t assoc_stream_id = 1; + uint8_t pri = 3; + memset(&callbacks, 0, sizeof(spdylay_session_callbacks)); + callbacks.on_ctrl_recv_callback = on_ctrl_recv_callback; + callbacks.on_invalid_ctrl_recv_callback = on_invalid_ctrl_recv_callback; user_data.valid = 0; user_data.invalid = 0; spdylay_session_client_new(&session, &callbacks, &user_data); - spdylay_frame_syn_stream_init(&frame.syn_stream, SPDYLAY_FLAG_NONE, - 2, 0, 3, dup_nv(nv)); + spdylay_session_open_stream(session, assoc_stream_id, SPDYLAY_FLAG_NONE, + pri, SPDYLAY_STREAM_OPENED); + spdylay_frame_syn_stream_init(&frame.syn_stream, + SPDYLAY_FLAG_UNIDIRECTIONAL, + stream_id, assoc_stream_id, pri, dup_nv(nv)); CU_ASSERT(0 == spdylay_session_on_syn_stream_received(session, &frame)); CU_ASSERT(1 == user_data.valid); - stream = (spdylay_stream*)spdylay_map_find(&session->streams, 2); + stream = spdylay_session_get_stream(session, stream_id); CU_ASSERT(SPDYLAY_STREAM_OPENING == stream->state); - CU_ASSERT(3 == stream->pri); + /* assoc_stream_id == 0 is invalid */ + frame.syn_stream.stream_id = 4; + frame.syn_stream.assoc_stream_id = 0; CU_ASSERT(0 == spdylay_session_on_syn_stream_received(session, &frame)); CU_ASSERT(1 == user_data.invalid); - CU_ASSERT(SPDYLAY_STREAM_CLOSING == - ((spdylay_stream*)spdylay_map_find(&session->streams, 2))->state); + + /* Push without SPDYLAY_FLAG_UNIDIRECTIONAL is invalid */ + frame.syn_stream.stream_id = 6; + frame.syn_stream.assoc_stream_id = 1; + frame.syn_stream.hd.flags = SPDYLAY_FLAG_FIN; + CU_ASSERT(0 == spdylay_session_on_syn_stream_received(session, &frame)); + CU_ASSERT(2 == user_data.invalid); + + /* Push to non-existent stream is invalid */ + frame.syn_stream.stream_id = 8; + frame.syn_stream.assoc_stream_id = 3; + frame.syn_stream.hd.flags = SPDYLAY_FLAG_UNIDIRECTIONAL; + CU_ASSERT(0 == spdylay_session_on_syn_stream_received(session, &frame)); + CU_ASSERT(3 == user_data.invalid); spdylay_frame_syn_stream_free(&frame.syn_stream); spdylay_session_del(session); @@ -476,36 +535,6 @@ void test_spdylay_session_reply_fail() spdylay_session_del(session); } -void test_spdylay_session_on_syn_stream_received_with_unidir_fin() -{ - spdylay_session *session; - spdylay_session_callbacks callbacks = { - NULL, - NULL, - on_ctrl_recv_callback, - NULL - }; - my_user_data user_data; - const char *nv[] = { NULL }; - spdylay_frame frame; - spdylay_stream *stream; - user_data.valid = 0; - user_data.invalid = 0; - - spdylay_session_client_new(&session, &callbacks, &user_data); - spdylay_frame_syn_stream_init(&frame.syn_stream, - SPDYLAY_FLAG_FIN | SPDYLAY_FLAG_UNIDIRECTIONAL, - 2, 0, 3, dup_nv(nv)); - - CU_ASSERT(0 == spdylay_session_on_syn_stream_received(session, &frame)); - CU_ASSERT(1 == user_data.valid); - stream = (spdylay_stream*)spdylay_map_find(&session->streams, 2); - CU_ASSERT(NULL == stream); - - spdylay_frame_syn_stream_free(&frame.syn_stream); - spdylay_session_del(session); -} - void test_spdylay_session_on_headers_received() { spdylay_session *session; diff --git a/tests/spdylay_session_test.h b/tests/spdylay_session_test.h index 29a7af4d..6ea99bcd 100644 --- a/tests/spdylay_session_test.h +++ b/tests/spdylay_session_test.h @@ -29,6 +29,7 @@ void test_spdylay_session_recv(); void test_spdylay_session_recv_invalid_stream_id(); void test_spdylay_session_add_frame(); void test_spdylay_session_on_syn_stream_received(); +void test_spdylay_session_on_syn_stream_received_with_push(); void test_spdylay_session_on_syn_reply_received(); void test_spdylay_session_send_syn_stream(); void test_spdylay_session_send_syn_reply();