From d4144a74758782553f7065ab9363a34496628cdd Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Sat, 9 Apr 2016 19:14:15 +0900 Subject: [PATCH] altsvc: Add tests, ignore altsvc if stream does not exist --- lib/nghttp2_session.c | 16 ++- tests/main.c | 4 + tests/nghttp2_session_test.c | 225 +++++++++++++++++++++++++++++++++++ tests/nghttp2_session_test.h | 2 + 4 files changed, 245 insertions(+), 2 deletions(-) diff --git a/lib/nghttp2_session.c b/lib/nghttp2_session.c index d204596a..d859e2fb 100644 --- a/lib/nghttp2_session.c +++ b/lib/nghttp2_session.c @@ -4663,6 +4663,7 @@ static int session_process_window_update_frame(nghttp2_session *session) { int nghttp2_session_on_altsvc_received(nghttp2_session *session, nghttp2_frame *frame) { nghttp2_ext_altsvc *altsvc; + nghttp2_stream *stream; altsvc = frame->ext.payload; @@ -4672,8 +4673,19 @@ int nghttp2_session_on_altsvc_received(nghttp2_session *session, if (altsvc->origin_len == 0) { return 0; } - } else if (altsvc->origin_len > 0) { - return 0; + } else { + if (altsvc->origin_len > 0) { + return 0; + } + + stream = nghttp2_session_get_stream(session, frame->hd.stream_id); + if (!stream) { + return 0; + } + + if (stream->state == NGHTTP2_STREAM_CLOSING) { + return 0; + } } return session_call_on_frame_received(session, frame); diff --git a/tests/main.c b/tests/main.c index fe8ec8e6..3aae0376 100644 --- a/tests/main.c +++ b/tests/main.c @@ -103,6 +103,8 @@ int main(int argc _U_, char *argv[] _U_) { test_nghttp2_session_recv_too_large_frame_length) || !CU_add_test(pSuite, "session_recv_extension", test_nghttp2_session_recv_extension) || + !CU_add_test(pSuite, "session_recv_altsvc", + test_nghttp2_session_recv_altsvc) || !CU_add_test(pSuite, "session_continue", test_nghttp2_session_continue) || !CU_add_test(pSuite, "session_add_frame", test_nghttp2_session_add_frame) || @@ -132,6 +134,8 @@ int main(int argc _U_, char *argv[] _U_) { test_nghttp2_session_on_data_received) || !CU_add_test(pSuite, "session_on_data_received_fail_fast", test_nghttp2_session_on_data_received_fail_fast) || + !CU_add_test(pSuite, "session_on_altsvc_received", + test_nghttp2_session_on_altsvc_received) || !CU_add_test(pSuite, "session_send_headers_start_stream", test_nghttp2_session_send_headers_start_stream) || !CU_add_test(pSuite, "session_send_headers_reply", diff --git a/tests/nghttp2_session_test.c b/tests/nghttp2_session_test.c index 509a15de..b80b41b1 100644 --- a/tests/nghttp2_session_test.c +++ b/tests/nghttp2_session_test.c @@ -1955,6 +1955,124 @@ void test_nghttp2_session_recv_extension(void) { nghttp2_option_del(option); } +void test_nghttp2_session_recv_altsvc(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data ud; + nghttp2_buf buf; + nghttp2_frame_hd hd; + nghttp2_mem *mem; + ssize_t rv; + nghttp2_option *option; + static const uint8_t origin[] = "nghttp2.org"; + static const uint8_t field_value[] = "h2=\":443\""; + + mem = nghttp2_mem_default(); + + nghttp2_buf_init2(&buf, NGHTTP2_FRAME_HDLEN + NGHTTP2_MAX_FRAME_SIZE_MIN, + mem); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + + callbacks.on_frame_recv_callback = on_frame_recv_callback; + + nghttp2_option_new(&option); + nghttp2_option_set_builtin_recv_extension_type(option, NGHTTP2_ALTSVC); + + nghttp2_session_client_new2(&session, &callbacks, &ud, option); + + nghttp2_frame_hd_init(&hd, 2 + sizeof(origin) - 1 + sizeof(field_value) - 1, + NGHTTP2_ALTSVC, NGHTTP2_FLAG_NONE, 0); + nghttp2_frame_pack_frame_hd(buf.last, &hd); + buf.last += NGHTTP2_FRAME_HDLEN; + nghttp2_put_uint16be(buf.last, sizeof(origin) - 1); + buf.last += 2; + buf.last = nghttp2_cpymem(buf.last, origin, sizeof(origin) - 1); + buf.last = nghttp2_cpymem(buf.last, field_value, sizeof(field_value) - 1); + + ud.frame_recv_cb_called = 0; + rv = nghttp2_session_mem_recv(session, buf.pos, nghttp2_buf_len(&buf)); + + CU_ASSERT((ssize_t)nghttp2_buf_len(&buf) == rv); + CU_ASSERT(1 == ud.frame_recv_cb_called); + CU_ASSERT(NGHTTP2_ALTSVC == ud.recv_frame_hd.type); + CU_ASSERT(NGHTTP2_FLAG_NONE == ud.recv_frame_hd.flags); + CU_ASSERT(0 == ud.recv_frame_hd.stream_id); + + nghttp2_session_del(session); + + /* size of origin is larger than frame length */ + nghttp2_buf_reset(&buf); + + nghttp2_session_client_new2(&session, &callbacks, &ud, option); + + nghttp2_frame_hd_init(&hd, 2 + sizeof(origin) - 1 - 1, NGHTTP2_ALTSVC, + NGHTTP2_FLAG_NONE, 0); + nghttp2_frame_pack_frame_hd(buf.last, &hd); + buf.last += NGHTTP2_FRAME_HDLEN; + nghttp2_put_uint16be(buf.last, sizeof(origin) - 1); + buf.last += 2; + buf.last = nghttp2_cpymem(buf.last, origin, sizeof(origin) - 1 - 1); + + ud.frame_recv_cb_called = 0; + rv = nghttp2_session_mem_recv(session, buf.pos, nghttp2_buf_len(&buf)); + + CU_ASSERT((ssize_t)nghttp2_buf_len(&buf) == rv); + CU_ASSERT(0 == ud.frame_recv_cb_called); + + nghttp2_session_del(session); + + /* send large frame (16KiB) */ + nghttp2_buf_reset(&buf); + + nghttp2_session_client_new2(&session, &callbacks, &ud, option); + + nghttp2_frame_hd_init(&hd, NGHTTP2_MAX_FRAME_SIZE_MIN, NGHTTP2_ALTSVC, + NGHTTP2_FLAG_NONE, 0); + nghttp2_frame_pack_frame_hd(buf.last, &hd); + buf.last += NGHTTP2_FRAME_HDLEN; + nghttp2_put_uint16be(buf.last, sizeof(origin) - 1); + buf.last += 2; + buf.last = nghttp2_cpymem(buf.last, origin, sizeof(origin) - 1); + memset(buf.last, 0, nghttp2_buf_avail(&buf)); + buf.last += nghttp2_buf_avail(&buf); + + ud.frame_recv_cb_called = 0; + rv = nghttp2_session_mem_recv(session, buf.pos, nghttp2_buf_len(&buf)); + + CU_ASSERT((ssize_t)nghttp2_buf_len(&buf) == rv); + CU_ASSERT(1 == ud.frame_recv_cb_called); + CU_ASSERT(NGHTTP2_ALTSVC == ud.recv_frame_hd.type); + CU_ASSERT(NGHTTP2_MAX_FRAME_SIZE_MIN == ud.recv_frame_hd.length); + + nghttp2_session_del(session); + + /* received by server */ + nghttp2_buf_reset(&buf); + + nghttp2_session_server_new2(&session, &callbacks, &ud, option); + + nghttp2_frame_hd_init(&hd, 2 + sizeof(origin) - 1 + sizeof(field_value) - 1, + NGHTTP2_ALTSVC, NGHTTP2_FLAG_NONE, 0); + nghttp2_frame_pack_frame_hd(buf.last, &hd); + buf.last += NGHTTP2_FRAME_HDLEN; + nghttp2_put_uint16be(buf.last, sizeof(origin) - 1); + buf.last += 2; + buf.last = nghttp2_cpymem(buf.last, origin, sizeof(origin) - 1); + buf.last = nghttp2_cpymem(buf.last, field_value, sizeof(field_value) - 1); + + ud.frame_recv_cb_called = 0; + rv = nghttp2_session_mem_recv(session, buf.pos, nghttp2_buf_len(&buf)); + + CU_ASSERT((ssize_t)nghttp2_buf_len(&buf) == rv); + CU_ASSERT(0 == ud.frame_recv_cb_called); + + nghttp2_session_del(session); + + nghttp2_buf_free(&buf, mem); + nghttp2_option_del(option); +} + void test_nghttp2_session_continue(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; @@ -3364,6 +3482,113 @@ void test_nghttp2_session_on_data_received_fail_fast(void) { nghttp2_session_del(session); } +void test_nghttp2_session_on_altsvc_received(void) { + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + my_user_data ud; + nghttp2_frame frame; + nghttp2_mem *mem; + nghttp2_option *option; + uint8_t origin[] = "nghttp2.org"; + uint8_t field_value[] = "h2=\":443\""; + int rv; + + mem = nghttp2_mem_default(); + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.on_frame_recv_callback = on_frame_recv_callback; + + nghttp2_option_new(&option); + nghttp2_option_set_builtin_recv_extension_type(option, NGHTTP2_ALTSVC); + + nghttp2_session_client_new2(&session, &callbacks, &ud, option); + + frame.ext.payload = &session->iframe.ext_frame_payload; + + /* We just pass the strings without making a copy. This is OK, + since we never call nghttp2_frame_altsvc_free(). */ + nghttp2_frame_altsvc_init(&frame.ext, 0, origin, sizeof(origin) - 1, + field_value, sizeof(field_value) - 1); + + ud.frame_recv_cb_called = 0; + rv = nghttp2_session_on_altsvc_received(session, &frame); + + CU_ASSERT(0 == rv); + CU_ASSERT(1 == ud.frame_recv_cb_called); + + nghttp2_session_del(session); + + /* Receiving empty origin with stream ID == 0 */ + nghttp2_session_client_new2(&session, &callbacks, &ud, option); + + frame.ext.payload = &session->iframe.ext_frame_payload; + + nghttp2_frame_altsvc_init(&frame.ext, 0, origin, 0, field_value, + sizeof(field_value) - 1); + + ud.frame_recv_cb_called = 0; + rv = nghttp2_session_on_altsvc_received(session, &frame); + + CU_ASSERT(0 == rv); + CU_ASSERT(0 == ud.frame_recv_cb_called); + + nghttp2_session_del(session); + + /* Receiving non-empty origin with stream ID != 0 */ + nghttp2_session_client_new2(&session, &callbacks, &ud, option); + + frame.ext.payload = &session->iframe.ext_frame_payload; + + open_sent_stream(session, 1); + + nghttp2_frame_altsvc_init(&frame.ext, 1, origin, sizeof(origin) - 1, + field_value, sizeof(field_value) - 1); + + ud.frame_recv_cb_called = 0; + rv = nghttp2_session_on_altsvc_received(session, &frame); + + CU_ASSERT(0 == rv); + CU_ASSERT(0 == ud.frame_recv_cb_called); + + nghttp2_session_del(session); + + /* Receiving empty origin with stream ID != 0; this is OK */ + nghttp2_session_client_new2(&session, &callbacks, &ud, option); + + frame.ext.payload = &session->iframe.ext_frame_payload; + + open_sent_stream(session, 1); + + nghttp2_frame_altsvc_init(&frame.ext, 1, origin, 0, field_value, + sizeof(field_value) - 1); + + ud.frame_recv_cb_called = 0; + rv = nghttp2_session_on_altsvc_received(session, &frame); + + CU_ASSERT(0 == rv); + CU_ASSERT(1 == ud.frame_recv_cb_called); + + nghttp2_session_del(session); + + /* Stream does not exist; ALTSVC will be ignored. */ + nghttp2_session_client_new2(&session, &callbacks, &ud, option); + + frame.ext.payload = &session->iframe.ext_frame_payload; + + nghttp2_frame_altsvc_init(&frame.ext, 1, origin, 0, field_value, + sizeof(field_value) - 1); + + ud.frame_recv_cb_called = 0; + rv = nghttp2_session_on_altsvc_received(session, &frame); + + CU_ASSERT(0 == rv); + CU_ASSERT(0 == ud.frame_recv_cb_called); + + nghttp2_session_del(session); + + nghttp2_option_del(option); +} + void test_nghttp2_session_send_headers_start_stream(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; diff --git a/tests/nghttp2_session_test.h b/tests/nghttp2_session_test.h index 8199ecd6..3787b6e4 100644 --- a/tests/nghttp2_session_test.h +++ b/tests/nghttp2_session_test.h @@ -45,6 +45,7 @@ void test_nghttp2_session_recv_unexpected_continuation(void); void test_nghttp2_session_recv_settings_header_table_size(void); void test_nghttp2_session_recv_too_large_frame_length(void); void test_nghttp2_session_recv_extension(void); +void test_nghttp2_session_recv_altsvc(void); void test_nghttp2_session_continue(void); void test_nghttp2_session_add_frame(void); void test_nghttp2_session_on_request_headers_received(void); @@ -60,6 +61,7 @@ void test_nghttp2_session_on_goaway_received(void); void test_nghttp2_session_on_window_update_received(void); void test_nghttp2_session_on_data_received(void); void test_nghttp2_session_on_data_received_fail_fast(void); +void test_nghttp2_session_on_altsvc_received(void); void test_nghttp2_session_send_headers_start_stream(void); void test_nghttp2_session_send_headers_reply(void); void test_nghttp2_session_send_headers_frame_size_error(void);