diff --git a/lib/spdylay_session.c b/lib/spdylay_session.c index 33f20b66..78a099e2 100644 --- a/lib/spdylay_session.c +++ b/lib/spdylay_session.c @@ -257,6 +257,39 @@ int spdylay_session_close_stream(spdylay_session *session, int32_t stream_id) } } +/* + * Returns non-zero value if local peer can send SYN_REPLY with stream + * ID |stream_id| at the moment, or 0. + */ +static int spdylay_session_is_reply_allowed(spdylay_session *session, + int32_t stream_id) +{ + spdylay_stream *stream = spdylay_session_get_stream(session, stream_id); + if(stream == NULL) { + return 0; + } + if(spdylay_session_is_my_stream_id(session, stream_id)) { + return 0; + } else { + return stream->state == SPDYLAY_STREAM_OPENING && + (stream->flags & SPDYLAY_FLAG_UNIDIRECTIONAL) == 0; + } +} + +static int spdylay_session_is_data_allowed(spdylay_session *session, + int32_t stream_id) +{ + spdylay_stream *stream = spdylay_session_get_stream(session, stream_id); + if(stream == NULL) { + return 0; + } + if(spdylay_session_is_my_stream_id(session, stream_id)) { + return (stream->flags & SPDYLAY_FLAG_FIN) == 0; + } else { + return stream->state == SPDYLAY_STREAM_OPENED; + } +} + ssize_t spdylay_session_prep_frame(spdylay_session *session, spdylay_outbound_item *item, uint8_t **framebuf_ptr) @@ -286,6 +319,10 @@ ssize_t spdylay_session_prep_frame(spdylay_session *session, break; } case SPDYLAY_SYN_REPLY: { + if(!spdylay_session_is_reply_allowed(session, + item->frame->syn_reply.stream_id)) { + return SPDYLAY_ERR_INVALID_FRAME; + } framebuflen = spdylay_frame_pack_syn_reply(&framebuf, &item->frame->syn_reply, &session->hd_deflater); @@ -295,6 +332,9 @@ ssize_t spdylay_session_prep_frame(spdylay_session *session, break; } case SPDYLAY_DATA: { + if(!spdylay_session_is_data_allowed(session, item->frame->data.stream_id)) { + return SPDYLAY_ERR_INVALID_FRAME; + } framebuflen = spdylay_session_pack_data(session, &framebuf, &item->frame->data); if(framebuflen < 0) { @@ -564,10 +604,18 @@ int spdylay_session_on_syn_stream_received(spdylay_session *session, { int r; if(spdylay_session_validate_syn_stream(session, &frame->syn_stream) == 0) { - r = spdylay_session_open_stream(session, frame->syn_stream.stream_id, - frame->syn_stream.hd.flags, - frame->syn_stream.pri, - SPDYLAY_STREAM_OPENING); + uint8_t flags = frame->syn_stream.hd.flags; + if((flags & SPDYLAY_FLAG_FIN) && (flags & SPDYLAY_FLAG_UNIDIRECTIONAL)) { + /* If the stream is UNIDIRECTIONAL and FIN bit set, we can close + stream upon receiving SYN_STREAM. So, the stream needs not to + be opened. */ + r = 0; + } else { + r = spdylay_session_open_stream(session, frame->syn_stream.stream_id, + frame->syn_stream.hd.flags, + frame->syn_stream.pri, + SPDYLAY_STREAM_OPENING); + } if(r == 0) { session->last_recv_stream_id = frame->syn_stream.stream_id; spdylay_session_call_on_ctrl_frame_received(session, SPDYLAY_SYN_STREAM, diff --git a/tests/main.c b/tests/main.c index ebdf19d4..86f93d32 100644 --- a/tests/main.c +++ b/tests/main.c @@ -80,6 +80,8 @@ int main() !CU_add_test(pSuite, "session_send_syn_reply", test_spdylay_session_send_syn_reply) || !CU_add_test(pSuite, "reply_submit", test_spdylay_reply_submit) || + !CU_add_test(pSuite, "session_reply_fail", + test_spdylay_session_reply_fail) || !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 a74c671d..d4bb6ad6 100644 --- a/tests/spdylay_session_test.c +++ b/tests/spdylay_session_test.c @@ -70,6 +70,13 @@ static ssize_t null_send_callback(spdylay_session *session, return len; } +static ssize_t fail_send_callback(spdylay_session *session, + const uint8_t *data, size_t len, int flags, + void *user_data) +{ + return SPDYLAY_ERR_CALLBACK_FAILURE; +} + static ssize_t scripted_recv_callback(spdylay_session *session, uint8_t* data, size_t len, int flags, void *user_data) @@ -417,3 +424,57 @@ void test_spdylay_reply_submit() spdylay_session_del(session); } +void test_spdylay_session_reply_fail() +{ + spdylay_session *session; + spdylay_session_callbacks callbacks = { + fail_send_callback, + NULL, + NULL, + NULL + }; + const char *nv[] = { NULL }; + spdylay_stream *stream; + int32_t stream_id = 2; + spdylay_data_source source; + spdylay_data_provider data_prd = { + source, + fixed_length_data_source_read_callback + }; + my_user_data ud; + ud.data_source_length = 4*1024; + CU_ASSERT(0 == spdylay_session_client_new(&session, &callbacks, &ud)); + CU_ASSERT(0 == spdylay_reply_submit(session, stream_id, nv, &data_prd)); + CU_ASSERT(0 == spdylay_session_send(session)); + 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); +} diff --git a/tests/spdylay_session_test.h b/tests/spdylay_session_test.h index 19158acf..00ebaa1a 100644 --- a/tests/spdylay_session_test.h +++ b/tests/spdylay_session_test.h @@ -33,5 +33,6 @@ void test_spdylay_session_on_syn_reply_received(); void test_spdylay_session_send_syn_stream(); void test_spdylay_session_send_syn_reply(); void test_spdylay_reply_submit(); +void test_spdylay_session_reply_fail(); #endif // SPDYLAY_SESSION_TEST_H