Add nghttp2_option_set_max_send_header_block_length API function

This function sets the maximum length of header block (a set of header
fields per HEADERS frame) to send.  The length of given set of header
fields is calculated using nghttp2_hd_deflate_bound().  Previously,
this is hard-coded, and is 64KiB.
This commit is contained in:
Tatsuhiro Tsujikawa 2016-06-15 00:00:30 +09:00
parent 47fa56fd0a
commit c7b0e04498
10 changed files with 111 additions and 19 deletions

View File

@ -57,6 +57,7 @@ APIDOCS= \
nghttp2_option_new.rst \
nghttp2_option_set_builtin_recv_extension_type.rst \
nghttp2_option_set_max_reserved_remote_streams.rst \
nghttp2_option_set_max_send_header_block_length.rst \
nghttp2_option_set_no_auto_ping_ack.rst \
nghttp2_option_set_no_auto_window_update.rst \
nghttp2_option_set_no_http_messaging.rst \

View File

@ -2412,6 +2412,21 @@ nghttp2_option_set_builtin_recv_extension_type(nghttp2_option *option,
NGHTTP2_EXTERN void nghttp2_option_set_no_auto_ping_ack(nghttp2_option *option,
int val);
/**
* @function
*
* This option sets the maximum length of header block (a set of
* header fields per one HEADERS frame) to send. The length of a
* given set of header fields is calculated using
* `nghttp2_hd_deflate_bound()`. The default value is 64KiB. If
* application attempts to send header fields larger than this limit,
* the transmission of the frame fails with error code
* :enum:`NGHTTP2_ERR_FRAME_SIZE_ERROR`.
*/
NGHTTP2_EXTERN void
nghttp2_option_set_max_send_header_block_length(nghttp2_option *option,
size_t val);
/**
* @function
*

View File

@ -52,14 +52,12 @@
#define NGHTTP2_FRAMEBUF_CHUNKLEN \
(NGHTTP2_FRAME_HDLEN + 1 + NGHTTP2_MAX_PAYLOADLEN)
/* Number of inbound buffer */
#define NGHTTP2_FRAMEBUF_MAX_NUM 5
/* The default length of DATA frame payload. */
#define NGHTTP2_DATA_PAYLOADLEN NGHTTP2_MAX_FRAME_SIZE_MIN
/* Maximum headers payload length, calculated in compressed form.
This applies to transmission only. */
/* Maximum headers block size to send, calculated using
nghttp2_hd_deflate_bound(). This is the default value, and can be
overridden by nghttp2_option_set_max_send_header_block_size(). */
#define NGHTTP2_MAX_HEADERSLEN 65536
/* The number of bytes for each SETTINGS entry */

View File

@ -95,3 +95,9 @@ void nghttp2_option_set_no_auto_ping_ack(nghttp2_option *option, int val) {
option->opt_set_mask |= NGHTTP2_OPT_NO_AUTO_PING_ACK;
option->no_auto_ping_ack = val;
}
void nghttp2_option_set_max_send_header_block_length(nghttp2_option *option,
size_t val) {
option->opt_set_mask |= NGHTTP2_OPT_MAX_SEND_HEADER_BLOCK_LENGTH;
option->max_send_header_block_length = val;
}

View File

@ -62,13 +62,18 @@ typedef enum {
NGHTTP2_OPT_MAX_RESERVED_REMOTE_STREAMS = 1 << 4,
NGHTTP2_OPT_USER_RECV_EXT_TYPES = 1 << 5,
NGHTTP2_OPT_NO_AUTO_PING_ACK = 1 << 6,
NGHTTP2_OPT_BUILTIN_RECV_EXT_TYPES = 1 << 7
NGHTTP2_OPT_BUILTIN_RECV_EXT_TYPES = 1 << 7,
NGHTTP2_OPT_MAX_SEND_HEADER_BLOCK_LENGTH = 1 << 8,
} nghttp2_option_flag;
/**
* Struct to store option values for nghttp2_session.
*/
struct nghttp2_option {
/**
* NGHTTP2_OPT_MAX_SEND_HEADER_BLOCK_LENGTH
*/
size_t max_send_header_block_length;
/**
* Bitwise OR of nghttp2_option_flag to determine that which fields
* are specified.

View File

@ -389,6 +389,7 @@ static int session_new(nghttp2_session **session_ptr,
void *user_data, int server,
const nghttp2_option *option, nghttp2_mem *mem) {
int rv;
size_t nbuffer;
if (mem == NULL) {
mem = nghttp2_mem_default();
@ -441,16 +442,6 @@ static int session_new(nghttp2_session **session_ptr,
(*session_ptr)->server = 1;
}
/* 1 for Pad Field. */
rv = nghttp2_bufs_init3(&(*session_ptr)->aob.framebufs,
NGHTTP2_FRAMEBUF_CHUNKLEN, NGHTTP2_FRAMEBUF_MAX_NUM,
1, NGHTTP2_FRAME_HDLEN + 1, mem);
if (rv != 0) {
goto fail_aob_framebuf;
}
active_outbound_item_reset(&(*session_ptr)->aob, mem);
init_settings(&(*session_ptr)->remote_settings);
init_settings(&(*session_ptr)->local_settings);
@ -460,6 +451,8 @@ static int session_new(nghttp2_session **session_ptr,
/* Limit max outgoing concurrent streams to sensible value */
(*session_ptr)->remote_settings.max_concurrent_streams = 100;
(*session_ptr)->max_send_header_block_length = NGHTTP2_MAX_HEADERSLEN;
if (option) {
if ((option->opt_set_mask & NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE) &&
option->no_auto_window_update) {
@ -504,8 +497,31 @@ static int session_new(nghttp2_session **session_ptr,
option->no_auto_ping_ack) {
(*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_AUTO_PING_ACK;
}
if (option->opt_set_mask & NGHTTP2_OPT_MAX_SEND_HEADER_BLOCK_LENGTH) {
(*session_ptr)->max_send_header_block_length =
option->max_send_header_block_length;
}
}
nbuffer = ((*session_ptr)->max_send_header_block_length +
NGHTTP2_FRAMEBUF_CHUNKLEN - 1) /
NGHTTP2_FRAMEBUF_CHUNKLEN;
if (nbuffer == 0) {
nbuffer = 1;
}
/* 1 for Pad Field. */
rv = nghttp2_bufs_init3(&(*session_ptr)->aob.framebufs,
NGHTTP2_FRAMEBUF_CHUNKLEN, nbuffer, 1,
NGHTTP2_FRAME_HDLEN + 1, mem);
if (rv != 0) {
goto fail_aob_framebuf;
}
active_outbound_item_reset(&(*session_ptr)->aob, mem);
(*session_ptr)->callbacks = *callbacks;
(*session_ptr)->user_data = user_data;
@ -1951,7 +1967,7 @@ static int session_prep_frame(nghttp2_session *session,
session, frame->headers.nva, frame->headers.nvlen,
NGHTTP2_PRIORITY_SPECLEN);
if (estimated_payloadlen > NGHTTP2_MAX_HEADERSLEN) {
if (estimated_payloadlen > session->max_send_header_block_length) {
return NGHTTP2_ERR_FRAME_SIZE_ERROR;
}
@ -1970,7 +1986,7 @@ static int session_prep_frame(nghttp2_session *session,
session, frame->headers.nva, frame->headers.nvlen,
NGHTTP2_PRIORITY_SPECLEN);
if (estimated_payloadlen > NGHTTP2_MAX_HEADERSLEN) {
if (estimated_payloadlen > session->max_send_header_block_length) {
return NGHTTP2_ERR_FRAME_SIZE_ERROR;
}
@ -2089,7 +2105,7 @@ static int session_prep_frame(nghttp2_session *session,
estimated_payloadlen = session_estimate_headers_payload(
session, frame->push_promise.nva, frame->push_promise.nvlen, 0);
if (estimated_payloadlen > NGHTTP2_MAX_HEADERSLEN) {
if (estimated_payloadlen > session->max_send_header_block_length) {
return NGHTTP2_ERR_FRAME_SIZE_ERROR;
}

View File

@ -256,6 +256,9 @@ struct nghttp2_session {
size_t nvbuflen;
/* Counter for detecting flooding in outbound queue */
size_t obq_flood_counter_;
/* 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;
/* Next Stream ID. Made unsigned int to detect >= (1 << 31). */
uint32_t next_stream_id;
/* The last stream ID this session initiated. For client session,

View File

@ -183,6 +183,8 @@ int main(int argc _U_, char *argv[] _U_) {
!CU_add_test(pSuite, "submit_headers", test_nghttp2_submit_headers) ||
!CU_add_test(pSuite, "submit_headers_continuation",
test_nghttp2_submit_headers_continuation) ||
!CU_add_test(pSuite, "submit_headers_continuation_extra_large",
test_nghttp2_submit_headers_continuation_extra_large) ||
!CU_add_test(pSuite, "submit_priority", test_nghttp2_submit_priority) ||
!CU_add_test(pSuite, "session_submit_settings",
test_nghttp2_submit_settings) ||

View File

@ -4842,6 +4842,51 @@ void test_nghttp2_submit_headers_continuation(void) {
nghttp2_session_del(session);
}
void test_nghttp2_submit_headers_continuation_extra_large(void) {
nghttp2_session *session;
nghttp2_session_callbacks callbacks;
nghttp2_nv nv[] = {
MAKE_NV("h1", ""), MAKE_NV("h1", ""), MAKE_NV("h1", ""),
MAKE_NV("h1", ""), MAKE_NV("h1", ""), MAKE_NV("h1", ""),
};
nghttp2_outbound_item *item;
uint8_t data[16384];
size_t i;
my_user_data ud;
nghttp2_option *opt;
memset(data, '0', sizeof(data));
for (i = 0; i < ARRLEN(nv); ++i) {
nv[i].valuelen = sizeof(data);
nv[i].value = data;
}
memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
callbacks.send_callback = null_send_callback;
callbacks.on_frame_send_callback = on_frame_send_callback;
/* The default size of max send header block length is too small to
send these header fields. Expand it. */
nghttp2_option_new(&opt);
nghttp2_option_set_max_send_header_block_length(opt, 102400);
CU_ASSERT(0 == nghttp2_session_client_new2(&session, &callbacks, &ud, opt));
CU_ASSERT(1 == nghttp2_submit_headers(session, NGHTTP2_FLAG_END_STREAM, -1,
NULL, nv, ARRLEN(nv), NULL));
item = nghttp2_session_get_next_ob_item(session);
CU_ASSERT(NGHTTP2_HEADERS == item->frame.hd.type);
CU_ASSERT((NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS) ==
item->frame.hd.flags);
CU_ASSERT(0 == (item->frame.hd.flags & NGHTTP2_FLAG_PRIORITY));
ud.frame_send_cb_called = 0;
CU_ASSERT(0 == nghttp2_session_send(session));
CU_ASSERT(1 == ud.frame_send_cb_called);
nghttp2_session_del(session);
nghttp2_option_del(opt);
}
void test_nghttp2_submit_priority(void) {
nghttp2_session *session;
nghttp2_session_callbacks callbacks;

View File

@ -87,6 +87,7 @@ void test_nghttp2_submit_headers_reply(void);
void test_nghttp2_submit_headers_push_reply(void);
void test_nghttp2_submit_headers(void);
void test_nghttp2_submit_headers_continuation(void);
void test_nghttp2_submit_headers_continuation_extra_large(void);
void test_nghttp2_submit_priority(void);
void test_nghttp2_submit_settings(void);
void test_nghttp2_submit_settings_update_local_window_size(void);