Provide a possibility to confirm SETTINGS frames manually

This commit is contained in:
Sergei Gunchenko 2021-05-07 15:22:16 +03:00
parent 20079b4c2f
commit 29ae3264f3
6 changed files with 58 additions and 11 deletions

View File

@ -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.
*/

View File

@ -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;
}

View File

@ -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 */

View File

@ -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) {

View File

@ -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;
/*

View File

@ -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,