diff --git a/lib/includes/spdylay/spdylay.h b/lib/includes/spdylay/spdylay.h index 056f58ae..f2ed357f 100644 --- a/lib/includes/spdylay/spdylay.h +++ b/lib/includes/spdylay/spdylay.h @@ -663,7 +663,8 @@ void* spdylay_session_get_stream_user_data(spdylay_session *session, * negative error codes: * * SPDYLAY_ERR_INVALID_ARGUMENT - * |pri| is invalid. + * The |pri| is invalid; or the Associated-To-Stream-ID is + * invalid. * SPDYLAY_ERR_NOMEM * Out of memory. */ @@ -722,14 +723,15 @@ int spdylay_submit_response(spdylay_session *session, * data which is associated to the stream this frame will open. * * This function is low-level in a sense that the application code can - * specify flags and assoc-stream-ID directly. For usual HTTP request, - * spdylay_submit_request() is useful. + * specify flags and the Associated-To-Stream-ID directly. For usual + * HTTP request, spdylay_submit_request() is useful. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * SPDYLAY_ERR_INVALID_ARGUMENT - * |pri| is invalid. + * The |pri| is invalid; or the Associated-To-Stream-ID is + * invalid. * SPDYLAY_ERR_NOMEM * Out of memory. */ diff --git a/lib/spdylay_session.c b/lib/spdylay_session.c index 17a751c2..04e31507 100644 --- a/lib/spdylay_session.c +++ b/lib/spdylay_session.c @@ -478,6 +478,45 @@ static int spdylay_predicate_stream_for_send(spdylay_stream *stream) } } +/* + * This function checks SYN_STREAM frame |frame| can be sent at this + * time. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * SPDYLAY_ERR_STREAM_CLOSED + * The Associated-To-Stream is already closed or does not exist. + * SPDYLAY_ERR_STREAM_ID_NOT_AVAILABLE + * Stream ID has reached maximum value. Therefore no stream ID is + * available. + */ +static int spdylay_session_predicate_syn_stream_send +(spdylay_session *session, + spdylay_syn_stream *frame) +{ + if(session->goaway_flags) { + /* When GOAWAY is sent or received, peer must not send new + SYN_STREAM. */ + return SPDYLAY_ERR_SYN_STREAM_NOT_ALLOWED; + } + /* All 32bit signed stream IDs are spent. */ + if(session->next_stream_id > INT32_MAX) { + return SPDYLAY_ERR_STREAM_ID_NOT_AVAILABLE; + } + if(frame->assoc_stream_id != 0) { + /* Check associated stream is active. */ + /* We assume here that if frame->assoc_stream_id != 0, + session->server is always 1 and frame->assoc_stream_id is + odd. */ + if(spdylay_session_get_stream(session, frame->assoc_stream_id) == + NULL) { + return SPDYLAY_ERR_STREAM_CLOSED; + } + } + return 0; +} + /* * This function checks SYN_REPLY with the stream ID |stream_id| can * be sent at this time. @@ -673,21 +712,16 @@ static int spdylay_session_predicate_data_send(spdylay_session *session, static ssize_t spdylay_session_prep_frame(spdylay_session *session, spdylay_outbound_item *item) { - /* TODO Get or validate stream ID here */ - /* TODO Validate assoc_stream_id here */ ssize_t framebuflen; switch(item->frame_type) { case SPDYLAY_SYN_STREAM: { int32_t stream_id; spdylay_syn_stream_aux_data *aux_data; - if(session->goaway_flags) { - /* When GOAWAY is sent or received, peer must not send new - SYN_STREAM. */ - return SPDYLAY_ERR_SYN_STREAM_NOT_ALLOWED; - } - /* All 32bit signed stream IDs are spent. */ - if(session->next_stream_id > INT32_MAX) { - return SPDYLAY_ERR_STREAM_ID_NOT_AVAILABLE; + int r; + r = spdylay_session_predicate_syn_stream_send(session, + &item->frame->syn_stream); + if(r != 0) { + return r; } stream_id = session->next_stream_id; diff --git a/lib/spdylay_submit.c b/lib/spdylay_submit.c index 7671e3a7..d99cf741 100644 --- a/lib/spdylay_submit.c +++ b/lib/spdylay_submit.c @@ -47,8 +47,12 @@ static int spdylay_submit_syn_stream_shared if(pri > spdylay_session_get_pri_lowest(session)) { return SPDYLAY_ERR_INVALID_ARGUMENT; } - if(session->server == 0) { - assoc_stream_id = 0; + if(assoc_stream_id != 0) { + if(session->server == 0) { + assoc_stream_id = 0; + } else if(spdylay_session_is_my_stream_id(session, assoc_stream_id)) { + return SPDYLAY_ERR_INVALID_ARGUMENT; + } } if(data_prd != NULL && data_prd->read_callback != NULL) { data_prd_copy = malloc(sizeof(spdylay_data_provider)); diff --git a/tests/spdylay_session_test.c b/tests/spdylay_session_test.c index 8fc135a6..98a5b881 100644 --- a/tests/spdylay_session_test.c +++ b/tests/spdylay_session_test.c @@ -730,6 +730,11 @@ void test_spdylay_submit_syn_stream() CU_ASSERT(1 == item->frame->syn_stream.assoc_stream_id); CU_ASSERT(3 == item->frame->syn_stream.pri); + /* Invalid assoc-stream-ID */ + CU_ASSERT(SPDYLAY_ERR_INVALID_ARGUMENT == + spdylay_submit_syn_stream(session, SPDYLAY_CTRL_FLAG_FIN, 2, 3, + nv, NULL)); + spdylay_session_del(session); } @@ -1738,9 +1743,21 @@ void test_spdylay_session_on_ctrl_not_send() stream = spdylay_session_open_stream(session, 1, SPDYLAY_CTRL_FLAG_NONE, 3, SPDYLAY_STREAM_OPENED, &user_data); + /* Check SYN_STREAM */ + CU_ASSERT(0 == spdylay_submit_syn_stream(session, SPDYLAY_CTRL_FLAG_FIN, 3, 3, + nv, NULL)); + + user_data.ctrl_not_send_cb_called = 0; + /* Associated stream closed */ + CU_ASSERT(0 == spdylay_session_send(session)); + CU_ASSERT(1 == user_data.ctrl_not_send_cb_called); + CU_ASSERT(SPDYLAY_SYN_STREAM == user_data.not_sent_frame_type); + CU_ASSERT(SPDYLAY_ERR_STREAM_CLOSED == user_data.not_sent_error); + /* Check SYN_REPLY */ CU_ASSERT(0 == spdylay_submit_syn_reply(session, SPDYLAY_CTRL_FLAG_FIN, 1, nv)); + user_data.ctrl_not_send_cb_called = 0; CU_ASSERT(0 == spdylay_session_send(session)); CU_ASSERT(1 == user_data.ctrl_not_send_cb_called); CU_ASSERT(SPDYLAY_SYN_REPLY == user_data.not_sent_frame_type);