From 0a6ce87c22c69438ecbffe52a2859c3a32f1620f Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Tue, 25 Jun 2019 22:33:35 +0900 Subject: [PATCH] Add nghttp2_option_set_max_outbound_ack --- doc/Makefile.am | 1 + lib/includes/nghttp2/nghttp2.h | 11 +++++++++++ lib/nghttp2_option.c | 5 +++++ lib/nghttp2_option.h | 5 +++++ lib/nghttp2_session.c | 9 +++++++-- lib/nghttp2_session.h | 8 ++++++-- tests/nghttp2_session_test.c | 4 ++-- 7 files changed, 37 insertions(+), 6 deletions(-) diff --git a/doc/Makefile.am b/doc/Makefile.am index 199c8952..c17d9338 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -67,6 +67,7 @@ APIDOCS= \ nghttp2_option_set_no_recv_client_magic.rst \ nghttp2_option_set_peer_max_concurrent_streams.rst \ nghttp2_option_set_user_recv_extension_type.rst \ + nghttp2_option_set_max_outbound_ack.rst \ nghttp2_pack_settings_payload.rst \ nghttp2_priority_spec_check_default.rst \ nghttp2_priority_spec_default_init.rst \ diff --git a/lib/includes/nghttp2/nghttp2.h b/lib/includes/nghttp2/nghttp2.h index 925a4cbc..313fb23d 100644 --- a/lib/includes/nghttp2/nghttp2.h +++ b/lib/includes/nghttp2/nghttp2.h @@ -2648,6 +2648,17 @@ nghttp2_option_set_max_deflate_dynamic_table_size(nghttp2_option *option, NGHTTP2_EXTERN void nghttp2_option_set_no_closed_streams(nghttp2_option *option, int val); +/** + * @function + * + * This function sets the maximum number of outgoing SETTINGS ACK and + * PING ACK frames retained in :type:`nghttp2_session` object. If + * more than those frames are retained, the peer is considered to be + * misbehaving and session will be closed. The default value is 1000. + */ +NGHTTP2_EXTERN void nghttp2_option_set_max_outbound_ack(nghttp2_option *option, + size_t val); + /** * @function * diff --git a/lib/nghttp2_option.c b/lib/nghttp2_option.c index 8946d7dd..e53f22d3 100644 --- a/lib/nghttp2_option.c +++ b/lib/nghttp2_option.c @@ -116,3 +116,8 @@ void nghttp2_option_set_no_closed_streams(nghttp2_option *option, int val) { option->opt_set_mask |= NGHTTP2_OPT_NO_CLOSED_STREAMS; option->no_closed_streams = val; } + +void nghttp2_option_set_max_outbound_ack(nghttp2_option *option, size_t val) { + option->opt_set_mask |= NGHTTP2_OPT_MAX_OUTBOUND_ACK; + option->max_outbound_ack = val; +} diff --git a/lib/nghttp2_option.h b/lib/nghttp2_option.h index 29e72aa3..1f740aaa 100644 --- a/lib/nghttp2_option.h +++ b/lib/nghttp2_option.h @@ -66,6 +66,7 @@ typedef enum { NGHTTP2_OPT_MAX_SEND_HEADER_BLOCK_LENGTH = 1 << 8, NGHTTP2_OPT_MAX_DEFLATE_DYNAMIC_TABLE_SIZE = 1 << 9, NGHTTP2_OPT_NO_CLOSED_STREAMS = 1 << 10, + NGHTTP2_OPT_MAX_OUTBOUND_ACK = 1 << 11, } nghttp2_option_flag; /** @@ -80,6 +81,10 @@ struct nghttp2_option { * NGHTTP2_OPT_MAX_DEFLATE_DYNAMIC_TABLE_SIZE */ size_t max_deflate_dynamic_table_size; + /** + * NGHTTP2_OPT_MAX_OUTBOUND_ACK + */ + size_t max_outbound_ack; /** * Bitwise OR of nghttp2_option_flag to determine that which fields * are specified. diff --git a/lib/nghttp2_session.c b/lib/nghttp2_session.c index 56f31c4b..e3b00dcc 100644 --- a/lib/nghttp2_session.c +++ b/lib/nghttp2_session.c @@ -457,6 +457,7 @@ static int session_new(nghttp2_session **session_ptr, (*session_ptr)->remote_settings.max_concurrent_streams = 100; (*session_ptr)->max_send_header_block_length = NGHTTP2_MAX_HEADERSLEN; + (*session_ptr)->max_outbound_ack = NGHTTP2_DEFAULT_MAX_OBQ_FLOOD_ITEM; if (option) { if ((option->opt_set_mask & NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE) && @@ -516,6 +517,10 @@ static int session_new(nghttp2_session **session_ptr, option->no_closed_streams) { (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_CLOSED_STREAMS; } + + if (option->opt_set_mask & NGHTTP2_OPT_MAX_OUTBOUND_ACK) { + (*session_ptr)->max_outbound_ack = option->max_outbound_ack; + } } rv = nghttp2_hd_deflate_init2(&(*session_ptr)->hd_deflater, @@ -6857,7 +6862,7 @@ int nghttp2_session_add_ping(nghttp2_session *session, uint8_t flags, mem = &session->mem; if ((flags & NGHTTP2_FLAG_ACK) && - session->obq_flood_counter_ >= NGHTTP2_MAX_OBQ_FLOOD_ITEM) { + session->obq_flood_counter_ >= session->max_outbound_ack) { return NGHTTP2_ERR_FLOODED; } @@ -7002,7 +7007,7 @@ int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags, return NGHTTP2_ERR_INVALID_ARGUMENT; } - if (session->obq_flood_counter_ >= NGHTTP2_MAX_OBQ_FLOOD_ITEM) { + if (session->obq_flood_counter_ >= session->max_outbound_ack) { return NGHTTP2_ERR_FLOODED; } } diff --git a/lib/nghttp2_session.h b/lib/nghttp2_session.h index bd7a5b35..90ead9c0 100644 --- a/lib/nghttp2_session.h +++ b/lib/nghttp2_session.h @@ -97,7 +97,7 @@ typedef struct { response frames are stacked up, which leads to memory exhaustion. The value selected here is arbitrary, but safe value and if we have these frames in this number, it is considered suspicious. */ -#define NGHTTP2_MAX_OBQ_FLOOD_ITEM 10000 +#define NGHTTP2_DEFAULT_MAX_OBQ_FLOOD_ITEM 1000 /* The default value of maximum number of concurrent streams. */ #define NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS 0xffffffffu @@ -258,8 +258,12 @@ struct nghttp2_session { size_t num_idle_streams; /* The number of bytes allocated for nvbuf */ size_t nvbuflen; - /* Counter for detecting flooding in outbound queue */ + /* Counter for detecting flooding in outbound queue. If it exceeds + max_outbound_ack, session will be closed. */ size_t obq_flood_counter_; + /* The maximum number of outgoing SETTINGS ACK and PING ACK in + outbound queue. */ + size_t max_outbound_ack; /* The maximum length of header block to send. Calculated by the same way as nghttp2_hd_deflate_bound() does. */ size_t max_send_header_block_length; diff --git a/tests/nghttp2_session_test.c b/tests/nghttp2_session_test.c index d1420473..0622b22a 100644 --- a/tests/nghttp2_session_test.c +++ b/tests/nghttp2_session_test.c @@ -10002,7 +10002,7 @@ void test_nghttp2_session_flooding(void) { buf = &bufs.head->buf; - for (i = 0; i < NGHTTP2_MAX_OBQ_FLOOD_ITEM; ++i) { + for (i = 0; i < NGHTTP2_DEFAULT_MAX_OBQ_FLOOD_ITEM; ++i) { CU_ASSERT( (ssize_t)nghttp2_buf_len(buf) == nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf))); @@ -10024,7 +10024,7 @@ void test_nghttp2_session_flooding(void) { buf = &bufs.head->buf; - for (i = 0; i < NGHTTP2_MAX_OBQ_FLOOD_ITEM; ++i) { + for (i = 0; i < NGHTTP2_DEFAULT_MAX_OBQ_FLOOD_ITEM; ++i) { CU_ASSERT( (ssize_t)nghttp2_buf_len(buf) == nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf)));