diff --git a/lib/spdylay_session.c b/lib/spdylay_session.c index 4179d4a7..fbc605b3 100644 --- a/lib/spdylay_session.c +++ b/lib/spdylay_session.c @@ -131,6 +131,10 @@ static void spdylay_outbound_item_free(spdylay_outbound_item *item) case SPDYLAY_RST_STREAM: spdylay_frame_rst_stream_free(&item->frame->rst_stream); break; + case SPDYLAY_HEADERS: + /* Currently we don't have any API to send HEADERS frame, so this + is unreachable. */ + abort(); case SPDYLAY_DATA: spdylay_frame_data_free(&item->frame->data); break; @@ -191,6 +195,10 @@ int spdylay_session_add_frame(spdylay_session *session, } break; } + case SPDYLAY_HEADERS: + /* Currently we don't have any API to send HEADERS frame, so this + is unreachable. */ + abort(); case SPDYLAY_DATA: { spdylay_stream *stream = spdylay_session_get_stream (session, frame->data.stream_id); @@ -331,6 +339,10 @@ ssize_t spdylay_session_prep_frame(spdylay_session *session, } break; } + case SPDYLAY_HEADERS: + /* Currently we don't have any API to send HEADERS frame, so this + is unreachable. */ + abort(); case SPDYLAY_DATA: { if(!spdylay_session_is_data_allowed(session, item->frame->data.stream_id)) { return SPDYLAY_ERR_INVALID_FRAME; @@ -389,6 +401,10 @@ static int spdylay_session_after_frame_sent(spdylay_session *session) case SPDYLAY_RST_STREAM: spdylay_session_close_stream(session, frame->rst_stream.stream_id); break; + case SPDYLAY_HEADERS: + /* Currently we don't have any API to send HEADERS frame, so this + is unreachable. */ + abort(); case SPDYLAY_DATA: if(frame->data.flags & SPDYLAY_FLAG_FIN) { if(spdylay_session_is_my_stream_id(session, frame->data.stream_id)) { @@ -672,6 +688,52 @@ int spdylay_session_on_syn_reply_received(spdylay_session *session, return r; } +int spdylay_session_on_headers_received(spdylay_session *session, + spdylay_frame *frame) +{ + int r = 0; + int valid = 0; + if(spdylay_session_is_my_stream_id(session, frame->headers.stream_id)) { + spdylay_stream *stream = spdylay_session_get_stream + (session, frame->headers.stream_id); + if(stream) { + if(stream->state == SPDYLAY_STREAM_OPENED) { + valid = 1; + spdylay_session_call_on_ctrl_frame_received(session, SPDYLAY_HEADERS, + frame); + if(frame->headers.hd.flags & SPDYLAY_FLAG_FIN) { + /* Close the stream because this is the last frame of the + stream. */ + spdylay_session_close_stream(session, frame->headers.stream_id); + } + } else if(stream->state == SPDYLAY_STREAM_CLOSING) { + /* This is race condition. SPDYLAY_STREAM_CLOSING indicates + that we queued RST_STREAM but it has not been sent. It will + eventually sent, so we just ignore this frame. */ + valid = 1; + } + } + } else { + spdylay_stream *stream = spdylay_session_get_stream + (session, frame->headers.stream_id); + if(stream) { + if((stream->flags & SPDYLAY_FLAG_FIN) == 0) { + valid = 1; + spdylay_session_call_on_ctrl_frame_received(session, SPDYLAY_HEADERS, + frame); + if(frame->headers.hd.flags & SPDYLAY_FLAG_FIN) { + stream->flags |= SPDYLAY_FLAG_FIN; + } + } + } + } + if(!valid) { + r = spdylay_session_handle_invalid_ctrl_frame + (session, frame->headers.stream_id, SPDYLAY_HEADERS, frame); + } + return r; +} + int spdylay_session_process_ctrl_frame(spdylay_session *session) { int r = 0; @@ -708,6 +770,18 @@ int spdylay_session_process_ctrl_frame(spdylay_session *session) spdylay_frame_syn_reply_free(&frame.syn_reply); } break; + case SPDYLAY_HEADERS: + r = spdylay_frame_unpack_headers(&frame.headers, + session->iframe.headbuf, + sizeof(session->iframe.headbuf), + session->iframe.buf, + session->iframe.len, + &session->hd_inflater); + if(r == 0) { + r = spdylay_session_on_headers_received(session, &frame); + spdylay_frame_headers_free(&frame.headers); + } + break; } if(r < SPDYLAY_ERR_FATAL) { return r; diff --git a/lib/spdylay_session.h b/lib/spdylay_session.h index 0188a3ae..90f72de3 100644 --- a/lib/spdylay_session.h +++ b/lib/spdylay_session.h @@ -124,22 +124,27 @@ int spdylay_session_close_stream(spdylay_session *session, int32_t stream_id); /* * Called when SYN_STREAM is received. Received frame is |frame|. - * This function does first - * validate received frame and then open stream and call callback - * functions. + * This function does first validate received frame and then open + * stream and call callback functions. This function returns 0 if it + * succeeds, or negative error code. This function does not return + * error if frame is not valid. */ int spdylay_session_on_syn_stream_received(spdylay_session *session, spdylay_frame *frame); /* - * Called when SYN_STREAM is received. Received frame is |frame|. - * This function does first validate received frame and then open - * stream and call callback functions. + * Called when SYN_REPLY is received. Received frame is |frame|. */ int spdylay_session_on_syn_reply_received(spdylay_session *session, spdylay_frame *frame); +/* + * Called when HEADERS is recieved. Received frame is |frame|. + */ +int spdylay_session_on_headers_received(spdylay_session *session, + spdylay_frame *frame); + /* * Returns spdylay_stream* object whose stream ID is |stream_id|. It * could be NULL if such stream does not exist. diff --git a/tests/main.c b/tests/main.c index 5afa6687..411ceebb 100644 --- a/tests/main.c +++ b/tests/main.c @@ -82,6 +82,8 @@ int main() !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, "session_on_headers_received", + test_spdylay_session_on_headers_received) || !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 d4bb6ad6..cf63edcf 100644 --- a/tests/spdylay_session_test.c +++ b/tests/spdylay_session_test.c @@ -478,3 +478,58 @@ void test_spdylay_session_on_syn_stream_received_with_unidir_fin() spdylay_frame_syn_stream_free(&frame.syn_stream); spdylay_session_del(session); } + +void test_spdylay_session_on_headers_received() +{ + spdylay_session *session; + spdylay_session_callbacks callbacks = { + NULL, + NULL, + on_ctrl_recv_callback, + on_invalid_ctrl_recv_callback + }; + my_user_data user_data; + const char *nv[] = { NULL }; + spdylay_frame frame; + user_data.valid = 0; + user_data.invalid = 0; + + spdylay_session_client_new(&session, &callbacks, &user_data); + spdylay_session_open_stream(session, 1, SPDYLAY_FLAG_NONE, 0, + SPDYLAY_STREAM_OPENED); + spdylay_frame_headers_init(&frame.headers, SPDYLAY_FLAG_NONE, 1, + dup_nv(nv)); + + CU_ASSERT(0 == spdylay_session_on_headers_received(session, &frame)); + CU_ASSERT(1 == user_data.valid); + CU_ASSERT(SPDYLAY_STREAM_OPENED == + spdylay_session_get_stream(session, 1)->state); + + frame.headers.hd.flags |= SPDYLAY_FLAG_FIN; + + CU_ASSERT(0 == spdylay_session_on_headers_received(session, &frame)); + CU_ASSERT(2 == user_data.valid); + CU_ASSERT(NULL == spdylay_session_get_stream(session, 1)); + + CU_ASSERT(0 == spdylay_session_on_headers_received(session, &frame)); + CU_ASSERT(1 == user_data.invalid); + + /* Server initiated stream */ + spdylay_session_open_stream(session, 2, SPDYLAY_FLAG_NONE, 0, + SPDYLAY_STREAM_OPENING); + + frame.headers.hd.flags = SPDYLAY_FLAG_FIN; + frame.headers.stream_id = 2; + + CU_ASSERT(0 == spdylay_session_on_headers_received(session, &frame)); + CU_ASSERT(3 == user_data.valid); + CU_ASSERT(SPDYLAY_STREAM_OPENING == + spdylay_session_get_stream(session, 2)->state); + CU_ASSERT(spdylay_session_get_stream(session, 2)->flags & SPDYLAY_FLAG_FIN); + + CU_ASSERT(0 == spdylay_session_on_headers_received(session, &frame)); + CU_ASSERT(2 == user_data.invalid); + + spdylay_frame_headers_free(&frame.headers); + spdylay_session_del(session); +} diff --git a/tests/spdylay_session_test.h b/tests/spdylay_session_test.h index 00ebaa1a..cf4da3b5 100644 --- a/tests/spdylay_session_test.h +++ b/tests/spdylay_session_test.h @@ -34,5 +34,6 @@ 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(); +void test_spdylay_session_on_headers_received(); #endif // SPDYLAY_SESSION_TEST_H