From 29ae3264f32a8a36fc9b56374d76652119e5306a Mon Sep 17 00:00:00 2001 From: Sergei Gunchenko <26045362+sxgunchenko@users.noreply.github.com> Date: Fri, 7 May 2021 15:22:16 +0300 Subject: [PATCH] Provide a possibility to confirm SETTINGS frames manually --- lib/includes/nghttp2/nghttp2.h | 43 ++++++++++++++++++++++++++++------ lib/nghttp2_option.c | 5 ++++ lib/nghttp2_option.h | 5 ++++ lib/nghttp2_session.c | 7 +++++- lib/nghttp2_session.h | 3 ++- lib/nghttp2_submit.c | 6 +++-- 6 files changed, 58 insertions(+), 11 deletions(-) diff --git a/lib/includes/nghttp2/nghttp2.h b/lib/includes/nghttp2/nghttp2.h index edc0defc..7501d285 100644 --- a/lib/includes/nghttp2/nghttp2.h +++ b/lib/includes/nghttp2/nghttp2.h @@ -2719,6 +2719,19 @@ NGHTTP2_EXTERN void nghttp2_option_set_max_outbound_ack(nghttp2_option *option, NGHTTP2_EXTERN void nghttp2_option_set_max_settings(nghttp2_option *option, size_t val); +/** + * @function + * + * This option prevents the library from sending empty SETTINGS frame with ACK + * flag set automatically when SETTINGS frame is received. + * If this option is set to nonzero, the library won't send empty + * SETTINGS frame with ACK flag set in the response for incoming SETTINGS + * frame. The application can send SETTINGS frame with ACK flag set using + * `nghttp2_submit_settings()` with :enum:`nghttp2_flag.NGHTTP2_FLAG_ACK` + * as flags parameter. + */ +NGHTTP2_EXTERN void nghttp2_option_set_no_auto_settings_ack(nghttp2_option *option, int val); + /** * @function * @@ -4228,8 +4241,21 @@ NGHTTP2_EXTERN int nghttp2_submit_rst_stream(nghttp2_session *session, * pointer to the array of :type:`nghttp2_settings_entry`. The |niv| * indicates the number of :type:`nghttp2_settings_entry`. * - * The |flags| is currently ignored and should be - * :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`. + * The |flags| must be one of the following values: + * + * * :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE` + * * :enum:`nghttp2_flag.NGHTTP2_FLAG_ACK` + * + * Unless `nghttp2_option_set_no_auto_settings_ack()` is used, the |flags| + * should be :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE` and + * SETTINGS with :enum:`nghttp2_flag.NGHTTP2_FLAG_ACK` is + * automatically submitted by the library and application could not + * send it at its will. + * + * Otherwise the application must confirm the received settings by + * calling `nghttp2_submit_settings` with the flags set to + * :enum:`nghttp2_flag.NGHTTP2_FLAG_ACK` and empty settings + * (e.g. the |niv| must be 0). * * This function does not take ownership of the |iv|. This function * copies all the elements in the |iv|. @@ -4238,16 +4264,19 @@ NGHTTP2_EXTERN int nghttp2_submit_rst_stream(nghttp2_session *session, * size becomes strictly larger than NGHTTP2_MAX_WINDOW_SIZE, * RST_STREAM is issued against such a stream. * - * SETTINGS with :enum:`nghttp2_flag.NGHTTP2_FLAG_ACK` is - * automatically submitted by the library and application could not - * send it at its will. - * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` * The |iv| contains invalid value (e.g., initial window size - * strictly greater than (1 << 31) - 1. + * strictly greater than (1 << 31) - 1; + * or the |flags| is set to :enum:`nghttp2_flag.NGHTTP2_FLAG_ACK` + * but the manual SETTINGS confirmation was not configured with + * `nghttp2_option_set_no_auto_settings_ack()`; + * or the |flags| is set to :enum:`nghttp2_flag.NGHTTP2_FLAG_ACK` + * and the |niv| is non-zero; + * or the |flags| is neither :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE` + * nor :enum:`nghttp2_flag.NGHTTP2_FLAG_ACK`. * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. */ diff --git a/lib/nghttp2_option.c b/lib/nghttp2_option.c index 34348e66..19a48eee 100644 --- a/lib/nghttp2_option.c +++ b/lib/nghttp2_option.c @@ -126,3 +126,8 @@ void nghttp2_option_set_max_settings(nghttp2_option *option, size_t val) { option->opt_set_mask |= NGHTTP2_OPT_MAX_SETTINGS; option->max_settings = val; } + +void nghttp2_option_set_no_auto_settings_ack(nghttp2_option *option, int val) { + option->opt_set_mask |= NGHTTP2_OPT_NO_AUTO_SETTINGS_ACK; + option->no_auto_settings_ack = val; +} diff --git a/lib/nghttp2_option.h b/lib/nghttp2_option.h index 939729fd..b2668e80 100644 --- a/lib/nghttp2_option.h +++ b/lib/nghttp2_option.h @@ -68,6 +68,7 @@ typedef enum { NGHTTP2_OPT_NO_CLOSED_STREAMS = 1 << 10, NGHTTP2_OPT_MAX_OUTBOUND_ACK = 1 << 11, NGHTTP2_OPT_MAX_SETTINGS = 1 << 12, + NGHTTP2_OPT_NO_AUTO_SETTINGS_ACK = 1 << 13, } nghttp2_option_flag; /** @@ -131,6 +132,10 @@ struct nghttp2_option { * NGHTTP2_OPT_USER_RECV_EXT_TYPES */ uint8_t user_recv_ext_types[32]; + /** + * NGHTTP2_OPT_NO_AUTO_SETTINGS_ACK + */ + int no_auto_settings_ack; }; #endif /* NGHTTP2_OPTION_H */ diff --git a/lib/nghttp2_session.c b/lib/nghttp2_session.c index 2e7e907f..736e3efb 100644 --- a/lib/nghttp2_session.c +++ b/lib/nghttp2_session.c @@ -527,6 +527,11 @@ static int session_new(nghttp2_session **session_ptr, option->max_settings) { (*session_ptr)->max_settings = option->max_settings; } + + if ((option->opt_set_mask & NGHTTP2_OPT_NO_AUTO_SETTINGS_ACK) && + option->no_auto_settings_ack) { + (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_AUTO_SETTINGS_ACK; + } } rv = nghttp2_hd_deflate_init2(&(*session_ptr)->hd_deflater, @@ -4546,7 +4551,7 @@ int nghttp2_session_on_settings_received(nghttp2_session *session, } } - if (!noack && !session_is_closing(session)) { + if (!(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_SETTINGS_ACK) && !noack && !session_is_closing(session)) { rv = nghttp2_session_add_settings(session, NGHTTP2_FLAG_ACK, NULL, 0); if (rv != 0) { diff --git a/lib/nghttp2_session.h b/lib/nghttp2_session.h index 07bfbb6c..6f6e626a 100644 --- a/lib/nghttp2_session.h +++ b/lib/nghttp2_session.h @@ -52,7 +52,8 @@ typedef enum { NGHTTP2_OPTMASK_NO_RECV_CLIENT_MAGIC = 1 << 1, NGHTTP2_OPTMASK_NO_HTTP_MESSAGING = 1 << 2, NGHTTP2_OPTMASK_NO_AUTO_PING_ACK = 1 << 3, - NGHTTP2_OPTMASK_NO_CLOSED_STREAMS = 1 << 4 + NGHTTP2_OPTMASK_NO_CLOSED_STREAMS = 1 << 4, + NGHTTP2_OPTMASK_NO_AUTO_SETTINGS_ACK = 1 << 5, } nghttp2_optmask; /* diff --git a/lib/nghttp2_submit.c b/lib/nghttp2_submit.c index 744a49cf..ea14d7da 100644 --- a/lib/nghttp2_submit.c +++ b/lib/nghttp2_submit.c @@ -303,8 +303,10 @@ int nghttp2_submit_shutdown_notice(nghttp2_session *session) { int nghttp2_submit_settings(nghttp2_session *session, uint8_t flags, const nghttp2_settings_entry *iv, size_t niv) { - (void)flags; - return nghttp2_session_add_settings(session, NGHTTP2_FLAG_NONE, iv, niv); + if (flags == NGHTTP2_FLAG_ACK && !(session->opt_flags & NGHTTP2_OPTMASK_NO_AUTO_SETTINGS_ACK)) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + return nghttp2_session_add_settings(session, flags, iv, niv); } int32_t nghttp2_submit_push_promise(nghttp2_session *session, uint8_t flags,