diff --git a/lib/includes/nghttp2/nghttp2.h b/lib/includes/nghttp2/nghttp2.h index 91030f28..e098bcce 100644 --- a/lib/includes/nghttp2/nghttp2.h +++ b/lib/includes/nghttp2/nghttp2.h @@ -2186,6 +2186,21 @@ NGHTTP2_EXTERN void nghttp2_option_set_max_reserved_remote_streams(nghttp2_option *option, uint32_t val); +/** + * @function + * + * Set extension frame type the application is willing to handle with + * user defined callbacks (see + * :type:`nghttp2_on_extension_chunk_recv_callback` and + * :type:`nghttp2_unpack_extension_callback`). The |type| is + * extension frame type, and must be strictly greater than 0x9. + * Otherwise, this function does nothing. The application does not + * have to call this function if it just sends extension frames. + */ +NGHTTP2_EXTERN void +nghttp2_option_set_user_recv_extension_type(nghttp2_option *option, + uint8_t type); + /** * @function * diff --git a/lib/nghttp2_option.c b/lib/nghttp2_option.c index 04dbbc6a..81ae6ba7 100644 --- a/lib/nghttp2_option.c +++ b/lib/nghttp2_option.c @@ -62,3 +62,13 @@ void nghttp2_option_set_max_reserved_remote_streams(nghttp2_option *option, option->opt_set_mask |= NGHTTP2_OPT_MAX_RESERVED_REMOTE_STREAMS; option->max_reserved_remote_streams = val; } + +void nghttp2_option_set_user_recv_extension_type(nghttp2_option *option, + uint8_t type) { + if (type < 10) { + return; + } + + option->opt_set_mask |= NGHTTP2_OPT_USER_RECV_EXT_TYPES; + option->user_recv_ext_types[type / 8] |= 1 << (7 - (type & 0x7)); +} diff --git a/lib/nghttp2_option.h b/lib/nghttp2_option.h index ebf416ac..a2d090fb 100644 --- a/lib/nghttp2_option.h +++ b/lib/nghttp2_option.h @@ -59,7 +59,8 @@ typedef enum { NGHTTP2_OPT_PEER_MAX_CONCURRENT_STREAMS = 1 << 1, NGHTTP2_OPT_NO_RECV_CLIENT_MAGIC = 1 << 2, NGHTTP2_OPT_NO_HTTP_MESSAGING = 1 << 3, - NGHTTP2_OPT_MAX_RESERVED_REMOTE_STREAMS = 1 << 4 + NGHTTP2_OPT_MAX_RESERVED_REMOTE_STREAMS = 1 << 4, + NGHTTP2_OPT_USER_RECV_EXT_TYPES = 1 << 5 } nghttp2_option_flag; /** @@ -91,6 +92,10 @@ struct nghttp2_option { * NGHTTP2_OPT_NO_HTTP_MESSAGING */ int no_http_messaging; + /** + * NGHTTP2_OPT_USER_RECV_EXT_TYPES + */ + uint8_t user_recv_ext_types[32]; }; #endif /* NGHTTP2_OPTION_H */ diff --git a/lib/nghttp2_session.c b/lib/nghttp2_session.c index 787bc311..1e8e81e5 100644 --- a/lib/nghttp2_session.c +++ b/lib/nghttp2_session.c @@ -402,6 +402,11 @@ static int session_new(nghttp2_session **session_ptr, (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_HTTP_MESSAGING; } + + if (option->opt_set_mask & NGHTTP2_OPT_USER_RECV_EXT_TYPES) { + memcpy((*session_ptr)->user_recv_ext_types, option->user_recv_ext_types, + sizeof((*session_ptr)->user_recv_ext_types)); + } } (*session_ptr)->callbacks = *callbacks; @@ -5291,7 +5296,9 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, const uint8_t *in, default: DEBUGF(fprintf(stderr, "recv: unknown frame\n")); - if (!session->callbacks.unpack_extension_callback) { + if (!session->callbacks.unpack_extension_callback || + (session->user_recv_ext_types[iframe->frame.hd.type / 8] & + (1 << (7 - (iframe->frame.hd.type & 0x7)))) == 0) { /* Silently ignore unknown frame type. */ busy = 1; diff --git a/lib/nghttp2_session.h b/lib/nghttp2_session.h index a996f8fd..73c10cdf 100644 --- a/lib/nghttp2_session.h +++ b/lib/nghttp2_session.h @@ -296,6 +296,12 @@ struct nghttp2_session { this session. The nonzero does not necessarily mean WINDOW_UPDATE is not queued. */ uint8_t window_update_queued; + /* Bitfield of extension frame types that application is willing to + receive. First most significant 10 bits are standard frame types + and not used. If bit is set, it indicates that incoming frame + with that type is passed to user defined callbacks, otherwise + they are ignored. */ + uint8_t user_recv_ext_types[32]; }; /* Struct used when updating initial window size of each active diff --git a/tests/nghttp2_session_test.c b/tests/nghttp2_session_test.c index d34b1569..7ed6af65 100644 --- a/tests/nghttp2_session_test.c +++ b/tests/nghttp2_session_test.c @@ -1769,6 +1769,7 @@ void test_nghttp2_session_recv_extension(void) { nghttp2_mem *mem; const char data[] = "Hello World!"; ssize_t rv; + nghttp2_option *option; mem = nghttp2_mem_default(); @@ -1778,6 +1779,9 @@ void test_nghttp2_session_recv_extension(void) { callbacks.unpack_extension_callback = unpack_extension_callback; callbacks.on_frame_recv_callback = on_frame_recv_callback; + nghttp2_option_new(&option); + nghttp2_option_set_user_recv_extension_type(option, 111); + nghttp2_buf_init2(&ud.scratchbuf, 4096, mem); nghttp2_buf_init2(&buf, 4096, mem); @@ -1786,7 +1790,7 @@ void test_nghttp2_session_recv_extension(void) { buf.last += NGHTTP2_FRAME_HDLEN; buf.last = nghttp2_cpymem(buf.last, data, sizeof(data)); - nghttp2_session_client_new(&session, &callbacks, &ud); + nghttp2_session_client_new2(&session, &callbacks, &ud, option); nghttp2_frame_hd_init(&ud.recv_frame_hd, 0, 0, 0, 0); rv = nghttp2_session_mem_recv(session, buf.pos, nghttp2_buf_len(&buf)); @@ -1805,7 +1809,7 @@ void test_nghttp2_session_recv_extension(void) { callbacks.on_extension_chunk_recv_callback = cancel_on_extension_chunk_recv_callback; - nghttp2_session_server_new(&session, &callbacks, &ud); + nghttp2_session_server_new2(&session, &callbacks, &ud, option); ud.frame_recv_cb_called = 0; rv = nghttp2_session_mem_recv(session, buf.pos, nghttp2_buf_len(&buf)); @@ -1821,7 +1825,7 @@ void test_nghttp2_session_recv_extension(void) { callbacks.on_extension_chunk_recv_callback = on_extension_chunk_recv_callback; callbacks.unpack_extension_callback = cancel_unpack_extension_callback; - nghttp2_session_server_new(&session, &callbacks, &ud); + nghttp2_session_server_new2(&session, &callbacks, &ud, option); ud.frame_recv_cb_called = 0; rv = nghttp2_session_mem_recv(session, buf.pos, nghttp2_buf_len(&buf)); @@ -1833,6 +1837,8 @@ void test_nghttp2_session_recv_extension(void) { nghttp2_buf_free(&buf, mem); nghttp2_buf_free(&ud.scratchbuf, mem); + + nghttp2_option_del(option); } void test_nghttp2_session_continue(void) {