From 4030c5ccf551578d52dbcb31c838df045aaee889 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Thu, 2 Feb 2012 23:19:01 +0900 Subject: [PATCH] Closes all server-pushed streams when original stream is closed by RST_STREAM with CANCEL from client. Fixed spdylay_session_is_my_stream_id() --- lib/spdylay_session.c | 38 +++++++++++++--- lib/spdylay_session.h | 15 +++++++ tests/main.c | 6 +++ tests/spdylay_session_test.c | 85 ++++++++++++++++++++++++++++++++++++ tests/spdylay_session_test.h | 3 ++ 5 files changed, 140 insertions(+), 7 deletions(-) diff --git a/lib/spdylay_session.c b/lib/spdylay_session.c index a8180468..634ed431 100644 --- a/lib/spdylay_session.c +++ b/lib/spdylay_session.c @@ -32,19 +32,15 @@ #include "spdylay_helper.h" -/* - * Returns non-zero value if |stream_id| is initiated by local host. - * Otherwrise returns 0. - */ -static int spdylay_session_is_my_stream_id(spdylay_session *session, - int32_t stream_id) +int spdylay_session_is_my_stream_id(spdylay_session *session, + int32_t stream_id) { int r; if(stream_id == 0) { return 0; } r = stream_id % 2; - return (session->server && r == 0) || r == 1; + return (session->server && r == 0) || (!session->server && r == 1); } spdylay_stream* spdylay_session_get_stream(spdylay_session *session, @@ -359,6 +355,21 @@ int spdylay_session_close_stream_if_shut_rdwr(spdylay_session *session, } } +void spdylay_session_close_pushed_streams(spdylay_session *session, + int32_t stream_id, + spdylay_status_code status_code) +{ + spdylay_stream *stream; + stream = spdylay_session_get_stream(session, stream_id); + if(stream) { + int i; + for(i = 0; i < stream->pushed_streams_length; ++i) { + spdylay_session_close_stream(session, stream->pushed_streams[i], + status_code); + } + } +} + /* * Returns non-zero value if local peer can send SYN_REPLY with stream * ID |stream_id| at the moment, or 0. @@ -398,6 +409,7 @@ ssize_t spdylay_session_prep_frame(spdylay_session *session, uint8_t **framebuf_ptr) { /* TODO Get or validate stream ID here */ + /* TODO Validate assoc_stream_id here */ uint8_t *framebuf; ssize_t framebuflen; switch(item->frame_type) { @@ -582,6 +594,12 @@ static int spdylay_session_after_frame_sent(spdylay_session *session) break; } case SPDYLAY_RST_STREAM: + if(!session->server && + spdylay_session_is_my_stream_id(session, frame->rst_stream.stream_id) && + frame->rst_stream.status_code == SPDYLAY_CANCEL) { + spdylay_session_close_pushed_streams(session, frame->rst_stream.stream_id, + frame->rst_stream.status_code); + } spdylay_session_close_stream(session, frame->rst_stream.stream_id, frame->rst_stream.status_code); break; @@ -931,6 +949,12 @@ int spdylay_session_on_syn_reply_received(spdylay_session *session, int spdylay_session_on_rst_stream_received(spdylay_session *session, spdylay_frame *frame) { + if(session->server && + !spdylay_session_is_my_stream_id(session, frame->rst_stream.stream_id) && + frame->rst_stream.status_code == SPDYLAY_CANCEL) { + spdylay_session_close_pushed_streams(session, frame->rst_stream.stream_id, + frame->rst_stream.status_code); + } spdylay_session_close_stream(session, frame->rst_stream.stream_id, frame->rst_stream.status_code); return 0; diff --git a/lib/spdylay_session.h b/lib/spdylay_session.h index 3b413634..5849ad1e 100644 --- a/lib/spdylay_session.h +++ b/lib/spdylay_session.h @@ -125,6 +125,13 @@ struct spdylay_session { /* TODO stream timeout etc */ +/* + * Returns non-zero value if |stream_id| is initiated by local host. + * Otherwrise returns 0. + */ +int spdylay_session_is_my_stream_id(spdylay_session *session, + int32_t stream_id); + /* * Adds frame |frame| of type |frame_type| to tx queue in |session|. * |aux_data| is a pointer to arbitrary data. Its interpretation is @@ -169,6 +176,14 @@ spdylay_stream* spdylay_session_open_stream(spdylay_session *session, int spdylay_session_close_stream(spdylay_session *session, int32_t stream_id, spdylay_status_code status_code); +/* + * Closes all pushed streams which associate them to stream + * |stream_id| with the status code |status_code|. + */ +void spdylay_session_close_pushed_streams(spdylay_session *session, + int32_t stream_id, + spdylay_status_code status_code); + /* * If further receptions and transmissions over this stream are * disallowed, close this stream. This function returns 0 if it diff --git a/tests/main.c b/tests/main.c index 4671bb04..b3c72bc4 100644 --- a/tests/main.c +++ b/tests/main.c @@ -95,6 +95,12 @@ int main() test_spdylay_session_on_goaway_received) || !CU_add_test(pSuite, "session_on_data_received", test_spdylay_session_on_data_received) || + !CU_add_test(pSuite, "session_on_rst_stream_received", + test_spdylay_session_on_rst_received) || + !CU_add_test(pSuite, "session_is_my_stream_id", + test_spdylay_session_is_my_stream_id) || + !CU_add_test(pSuite, "session_send_rst_stream", + test_spdylay_session_send_rst_stream) || !CU_add_test(pSuite, "frame_unpack_nv", test_spdylay_frame_unpack_nv) || !CU_add_test(pSuite, "frame_count_nv_space", test_spdylay_frame_count_nv_space) || diff --git a/tests/spdylay_session_test.c b/tests/spdylay_session_test.c index e04fe76f..4399a780 100644 --- a/tests/spdylay_session_test.c +++ b/tests/spdylay_session_test.c @@ -707,3 +707,88 @@ void test_spdylay_session_on_data_received() spdylay_session_del(session); } + +void test_spdylay_session_is_my_stream_id() +{ + spdylay_session *session; + spdylay_session_callbacks callbacks; + memset(&callbacks, 0, sizeof(spdylay_session_callbacks)); + spdylay_session_server_new(&session, &callbacks, NULL); + + CU_ASSERT(0 == spdylay_session_is_my_stream_id(session, 0)); + CU_ASSERT(0 == spdylay_session_is_my_stream_id(session, 1)); + CU_ASSERT(1 == spdylay_session_is_my_stream_id(session, 2)); + + spdylay_session_del(session); + + spdylay_session_client_new(&session, &callbacks, NULL); + + CU_ASSERT(0 == spdylay_session_is_my_stream_id(session, 0)); + CU_ASSERT(1 == spdylay_session_is_my_stream_id(session, 1)); + CU_ASSERT(0 == spdylay_session_is_my_stream_id(session, 2)); + + spdylay_session_del(session); +} + +void test_spdylay_session_on_rst_received() +{ + spdylay_session *session; + spdylay_session_callbacks callbacks; + my_user_data user_data; + spdylay_stream *stream; + spdylay_frame frame; + memset(&callbacks, 0, sizeof(spdylay_session_callbacks)); + spdylay_session_server_new(&session, &callbacks, &user_data); + stream = spdylay_session_open_stream(session, 1, SPDYLAY_FLAG_NONE, + 3, SPDYLAY_STREAM_OPENING); + /* server push */ + spdylay_session_open_stream(session, 2, SPDYLAY_FLAG_NONE, + 3, SPDYLAY_STREAM_OPENING); + spdylay_stream_add_pushed_stream(stream, 2); + spdylay_session_open_stream(session, 4, SPDYLAY_FLAG_NONE, + 3, SPDYLAY_STREAM_OPENING); + spdylay_stream_add_pushed_stream(stream, 4); + + spdylay_frame_rst_stream_init(&frame.rst_stream, 1, SPDYLAY_CANCEL); + + CU_ASSERT(0 == spdylay_session_on_rst_stream_received(session, &frame)); + + CU_ASSERT(NULL == spdylay_session_get_stream(session, 1)); + CU_ASSERT(NULL == spdylay_session_get_stream(session, 2)); + CU_ASSERT(NULL == spdylay_session_get_stream(session, 4)); + + spdylay_frame_rst_stream_free(&frame.rst_stream); + spdylay_session_del(session); +} + +void test_spdylay_session_send_rst_stream() +{ + spdylay_session *session; + spdylay_session_callbacks callbacks; + my_user_data user_data; + spdylay_stream *stream; + spdylay_frame *frame; + memset(&callbacks, 0, sizeof(spdylay_session_callbacks)); + callbacks.send_callback = null_send_callback; + spdylay_session_client_new(&session, &callbacks, &user_data); + stream = spdylay_session_open_stream(session, 1, SPDYLAY_FLAG_NONE, + 3, SPDYLAY_STREAM_OPENING); + /* server push */ + spdylay_session_open_stream(session, 2, SPDYLAY_FLAG_NONE, + 3, SPDYLAY_STREAM_OPENING); + spdylay_stream_add_pushed_stream(stream, 2); + spdylay_session_open_stream(session, 4, SPDYLAY_FLAG_NONE, + 3, SPDYLAY_STREAM_OPENING); + spdylay_stream_add_pushed_stream(stream, 4); + + frame = malloc(sizeof(spdylay_frame)); + spdylay_frame_rst_stream_init(&frame->rst_stream, 1, SPDYLAY_CANCEL); + spdylay_session_add_frame(session, SPDYLAY_RST_STREAM, frame, NULL); + CU_ASSERT(0 == spdylay_session_send(session)); + + CU_ASSERT(NULL == spdylay_session_get_stream(session, 1)); + CU_ASSERT(NULL == spdylay_session_get_stream(session, 2)); + CU_ASSERT(NULL == spdylay_session_get_stream(session, 4)); + + spdylay_session_del(session); +} diff --git a/tests/spdylay_session_test.h b/tests/spdylay_session_test.h index 6ea99bcd..75134524 100644 --- a/tests/spdylay_session_test.h +++ b/tests/spdylay_session_test.h @@ -40,5 +40,8 @@ void test_spdylay_session_on_headers_received(); void test_spdylay_session_on_ping_received(); void test_spdylay_session_on_goaway_received(); void test_spdylay_session_on_data_received(); +void test_spdylay_session_on_rst_received(); +void test_spdylay_session_is_my_stream_id(); +void test_spdylay_session_send_rst_stream(); #endif // SPDYLAY_SESSION_TEST_H