diff --git a/lib/includes/spdylay/spdylay.h b/lib/includes/spdylay/spdylay.h index f0ebbe56..24748f96 100644 --- a/lib/includes/spdylay/spdylay.h +++ b/lib/includes/spdylay/spdylay.h @@ -304,6 +304,15 @@ typedef void (*spdylay_on_stream_close_callback) (spdylay_session *session, int32_t stream_id, spdylay_status_code status_code, void *user_data); +/* + * Callback function invoked when request from remote peer is + * received. In other words, frame with FIN flag set is received. In + * HTTP, this means HTTP request, including request body, is fully + * received. + */ +typedef void (*spdylay_on_request_recv_callback) +(spdylay_session *session, int32_t stream_id, void *user_data); + typedef struct { spdylay_send_callback send_callback; spdylay_recv_callback recv_callback; @@ -315,6 +324,7 @@ typedef struct { spdylay_on_ctrl_send_callback on_ctrl_send_callback; spdylay_on_data_send_callback on_data_send_callback; spdylay_on_stream_close_callback on_stream_close_callback; + spdylay_on_request_recv_callback on_request_recv_callback; } spdylay_session_callbacks; /* diff --git a/lib/spdylay_session.c b/lib/spdylay_session.c index f0b22e2a..30219951 100644 --- a/lib/spdylay_session.c +++ b/lib/spdylay_session.c @@ -903,6 +903,15 @@ static void spdylay_debug_print_nv(char **nv) } } +static void spdylay_session_call_on_request_recv +(spdylay_session *session, int32_t stream_id) +{ + if(session->callbacks.on_request_recv_callback) { + session->callbacks.on_request_recv_callback(session, stream_id, + session->user_data); + } +} + static void spdylay_session_call_on_ctrl_frame_received (spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame) { @@ -1022,6 +1031,10 @@ int spdylay_session_on_syn_stream_received(spdylay_session *session, session->last_recv_stream_id = frame->syn_stream.stream_id; spdylay_session_call_on_ctrl_frame_received(session, SPDYLAY_SYN_STREAM, frame); + if(flags & SPDYLAY_FLAG_FIN) { + spdylay_session_call_on_request_recv(session, + frame->syn_stream.stream_id); + } } } else { r = spdylay_session_handle_invalid_stream @@ -1152,6 +1165,8 @@ int spdylay_session_on_headers_received(spdylay_session *session, spdylay_session_call_on_ctrl_frame_received(session, SPDYLAY_HEADERS, frame); if(frame->headers.hd.flags & SPDYLAY_FLAG_FIN) { + spdylay_session_call_on_request_recv(session, + frame->headers.stream_id); spdylay_stream_shutdown(stream, SPDYLAY_SHUT_RD); spdylay_session_close_stream_if_shut_rdwr(session, stream); } @@ -1272,16 +1287,20 @@ int spdylay_session_on_data_received(spdylay_session *session, uint8_t flags, int32_t length, int32_t stream_id) { - int valid = 0; int r = 0; spdylay_status_code status_code = 0; spdylay_stream *stream; stream = spdylay_session_get_stream(session, stream_id); if(stream) { if((stream->shut_flags & SPDYLAY_SHUT_RD) == 0) { + int valid = 0; if(spdylay_session_is_my_stream_id(session, stream_id)) { if(stream->state == SPDYLAY_STREAM_OPENED) { valid = 1; + if(session->callbacks.on_data_recv_callback) { + session->callbacks.on_data_recv_callback + (session, flags, stream_id, length, session->user_data); + } } else if(stream->state != SPDYLAY_STREAM_CLOSING) { status_code = SPDYLAY_PROTOCOL_ERROR; } @@ -1290,6 +1309,13 @@ int spdylay_session_on_data_received(spdylay_session *session, not receive FIN unless stream is in SPDYLAY_STREAM_CLOSING state. This is a race condition. */ valid = 1; + if(session->callbacks.on_data_recv_callback) { + session->callbacks.on_data_recv_callback + (session, flags, stream_id, length, session->user_data); + } + if(flags & SPDYLAY_FLAG_FIN) { + spdylay_session_call_on_request_recv(session, stream_id); + } } if(valid) { if(flags & SPDYLAY_FLAG_FIN) { @@ -1303,12 +1329,7 @@ int spdylay_session_on_data_received(spdylay_session *session, } else { status_code = SPDYLAY_INVALID_STREAM; } - if(valid) { - if(session->callbacks.on_data_recv_callback) { - session->callbacks.on_data_recv_callback - (session, flags, stream_id, length, session->user_data); - } - } else if(status_code != 0) { + if(status_code != 0) { r = spdylay_session_add_rst_stream(session, stream_id, status_code); } return r; diff --git a/tests/main.c b/tests/main.c index 006f24d9..a5adfafa 100644 --- a/tests/main.c +++ b/tests/main.c @@ -107,6 +107,8 @@ int main(int argc, char* argv[]) test_spdylay_session_get_next_ob_item) || !CU_add_test(pSuite, "session_pop_next_ob_item", test_spdylay_session_pop_next_ob_item) || + !CU_add_test(pSuite, "session_on_request_recv_callback", + test_spdylay_session_on_request_recv_callback) || !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 a67a9003..fb18fb71 100644 --- a/tests/spdylay_session_test.c +++ b/tests/spdylay_session_test.c @@ -51,6 +51,7 @@ typedef struct { scripted_data_feed *df; int valid, invalid; size_t data_source_length; + int32_t stream_id; } my_user_data; static void scripted_data_feed_init(scripted_data_feed *df, @@ -140,6 +141,14 @@ static ssize_t fixed_length_data_source_read_callback return wlen; } +static void on_request_recv_callback(spdylay_session *session, + int32_t stream_id, + void *user_data) +{ + my_user_data *ud = (my_user_data*)user_data; + ud->stream_id = stream_id; +} + static char** dup_nv(const char **src) { return spdylay_frame_nv_copy(src); @@ -536,15 +545,13 @@ void test_spdylay_session_reply_fail() 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 - }; + spdylay_session_callbacks callbacks; my_user_data user_data; const char *nv[] = { NULL }; spdylay_frame frame; + 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; @@ -883,3 +890,59 @@ void test_spdylay_session_pop_next_ob_item() spdylay_session_del(session); } + +void test_spdylay_session_on_request_recv_callback() +{ + spdylay_session *session; + spdylay_session_callbacks callbacks; + my_user_data user_data; + const char *nv[] = { NULL }; + spdylay_frame frame; + memset(&callbacks, 0, sizeof(spdylay_session_callbacks)); + callbacks.on_request_recv_callback = on_request_recv_callback; + user_data.stream_id = 0; + + spdylay_session_server_new(&session, &callbacks, &user_data); + spdylay_frame_syn_stream_init(&frame.syn_stream, SPDYLAY_FLAG_NONE, 1, 0, 3, + dup_nv(nv)); + CU_ASSERT(0 == spdylay_session_on_syn_stream_received(session, &frame)); + CU_ASSERT(0 == user_data.stream_id); + + frame.syn_stream.stream_id = 3; + frame.syn_stream.hd.flags |= SPDYLAY_FLAG_FIN; + + CU_ASSERT(0 == spdylay_session_on_syn_stream_received(session, &frame)); + CU_ASSERT(3 == user_data.stream_id); + + user_data.stream_id = 0; + + frame.syn_stream.stream_id = 0; + + CU_ASSERT(0 == spdylay_session_on_syn_stream_received(session, &frame)); + CU_ASSERT(0 == user_data.stream_id); + + spdylay_frame_syn_stream_free(&frame.syn_stream); + + user_data.stream_id = 0; + + spdylay_session_open_stream(session, 5, SPDYLAY_FLAG_NONE, 0, + SPDYLAY_STREAM_OPENING, NULL); + spdylay_frame_headers_init(&frame.headers, SPDYLAY_FLAG_NONE, 5, dup_nv(nv)); + + CU_ASSERT(0 == spdylay_session_on_headers_received(session, &frame)); + CU_ASSERT(0 == user_data.stream_id); + + frame.headers.hd.flags |= SPDYLAY_FLAG_FIN; + + CU_ASSERT(0 == spdylay_session_on_headers_received(session, &frame)); + CU_ASSERT(5 == user_data.stream_id); + + user_data.stream_id = 0; + + CU_ASSERT(0 == spdylay_session_on_headers_received(session, &frame)); + CU_ASSERT(0 == user_data.stream_id); + + 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 66efd7bb..f0edb697 100644 --- a/tests/spdylay_session_test.h +++ b/tests/spdylay_session_test.h @@ -45,5 +45,6 @@ void test_spdylay_session_is_my_stream_id(); void test_spdylay_session_send_rst_stream(); void test_spdylay_session_get_next_ob_item(); void test_spdylay_session_pop_next_ob_item(); +void test_spdylay_session_on_request_recv_callback(); #endif // SPDYLAY_SESSION_TEST_H