Merge branch 'fix-hpack'

This commit is contained in:
Tatsuhiro Tsujikawa 2015-10-24 17:58:46 +09:00
commit 62a8132c02
5 changed files with 118 additions and 14 deletions

View File

@ -800,6 +800,7 @@ int nghttp2_hd_inflate_init(nghttp2_hd_inflater *inflater, nghttp2_mem *mem) {
}
inflater->settings_hd_table_bufsize_max = NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE;
inflater->min_hd_table_bufsize_max = UINT32_MAX;
inflater->ent_keep = NULL;
inflater->nv_keep = NULL;
@ -1383,9 +1384,24 @@ int nghttp2_hd_inflate_change_table_size(nghttp2_hd_inflater *inflater,
return NGHTTP2_ERR_INVALID_STATE;
}
inflater->state = NGHTTP2_HD_STATE_EXPECT_TABLE_SIZE;
/* It seems that encoder is not required to send dynamic table size
update if the table size is not changed after applying
SETTINGS_HEADER_TABLE_SIZE. RFC 7541 is ambiguous here, but this
is the intention of the editor. If new maximum table size is
strictly smaller than the current negotiated maximum size,
encoder must send dynamic table size update. In other cases, we
cannot expect it to do so. */
if (inflater->ctx.hd_table_bufsize_max > settings_hd_table_bufsize_max) {
inflater->state = NGHTTP2_HD_STATE_EXPECT_TABLE_SIZE;
/* Remember minimum value, and validate that encoder sends the
value less than or equal to this. */
inflater->min_hd_table_bufsize_max = settings_hd_table_bufsize_max;
}
inflater->settings_hd_table_bufsize_max = settings_hd_table_bufsize_max;
inflater->ctx.hd_table_bufsize_max = settings_hd_table_bufsize_max;
hd_context_shrink_table_size(&inflater->ctx, NULL);
return 0;
}
@ -2136,8 +2152,10 @@ ssize_t nghttp2_hd_inflate_hd2(nghttp2_hd_inflater *inflater,
break;
case NGHTTP2_HD_STATE_READ_TABLE_SIZE:
rfin = 0;
rv = hd_inflate_read_len(inflater, &rfin, in, last, 5,
inflater->settings_hd_table_bufsize_max);
rv = hd_inflate_read_len(
inflater, &rfin, in, last, 5,
nghttp2_min(inflater->min_hd_table_bufsize_max,
inflater->settings_hd_table_bufsize_max));
if (rv < 0) {
goto fail;
}
@ -2146,6 +2164,7 @@ ssize_t nghttp2_hd_inflate_hd2(nghttp2_hd_inflater *inflater,
goto almost_ok;
}
DEBUGF(fprintf(stderr, "inflatehd: table_size=%zu\n", inflater->left));
inflater->min_hd_table_bufsize_max = UINT32_MAX;
inflater->ctx.hd_table_bufsize_max = inflater->left;
hd_context_shrink_table_size(&inflater->ctx, NULL);
inflater->state = NGHTTP2_HD_STATE_INFLATE_START;
@ -2403,7 +2422,8 @@ ssize_t nghttp2_hd_inflate_hd2(nghttp2_hd_inflater *inflater,
if (in_final) {
DEBUGF(fprintf(stderr, "inflatehd: in_final set\n"));
if (inflater->state != NGHTTP2_HD_STATE_OPCODE) {
if (inflater->state != NGHTTP2_HD_STATE_OPCODE &&
inflater->state != NGHTTP2_HD_STATE_INFLATE_START) {
DEBUGF(fprintf(stderr, "inflatehd: unacceptable state=%d\n",
inflater->state));
rv = NGHTTP2_ERR_HEADER_COMP;
@ -2415,7 +2435,7 @@ ssize_t nghttp2_hd_inflate_hd2(nghttp2_hd_inflater *inflater,
return (ssize_t)(in - first);
almost_ok:
if (in_final && inflater->state != NGHTTP2_HD_STATE_OPCODE) {
if (in_final) {
DEBUGF(fprintf(stderr, "inflatehd: input ended prematurely\n"));
rv = NGHTTP2_ERR_HEADER_COMP;

View File

@ -240,6 +240,8 @@ struct nghttp2_hd_inflater {
/* The maximum header table size the inflater supports. This is the
same value transmitted in SETTINGS_HEADER_TABLE_SIZE */
size_t settings_hd_table_bufsize_max;
/* Minimum header table size set by nghttp2_hd_inflate_change_table_size */
size_t min_hd_table_bufsize_max;
/* The number of next shift to decode integer */
size_t shift;
nghttp2_hd_opcode opcode;

View File

@ -3892,13 +3892,17 @@ int nghttp2_session_update_local_settings(nghttp2_session *session,
size_t i;
int32_t new_initial_window_size = -1;
uint32_t header_table_size = 0;
uint32_t min_header_table_size = UINT32_MAX;
uint8_t header_table_size_seen = 0;
/* Use the value last seen. */
/* For NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, use the value last
seen. For NGHTTP2_SETTINGS_HEADER_TABLE_SIZE, use both minimum
value and last seen value. */
for (i = 0; i < niv; ++i) {
switch (iv[i].settings_id) {
case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE:
header_table_size_seen = 1;
header_table_size = iv[i].value;
min_header_table_size = nghttp2_min(min_header_table_size, iv[i].value);
break;
case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:
new_initial_window_size = (int32_t)iv[i].value;
@ -3906,6 +3910,14 @@ int nghttp2_session_update_local_settings(nghttp2_session *session,
}
}
if (header_table_size_seen) {
if (min_header_table_size < header_table_size) {
rv = nghttp2_hd_inflate_change_table_size(&session->hd_inflater,
min_header_table_size);
if (rv != 0) {
return rv;
}
}
rv = nghttp2_hd_inflate_change_table_size(&session->hd_inflater,
header_table_size);
if (rv != 0) {

View File

@ -563,13 +563,79 @@ void test_nghttp2_hd_inflate_expect_table_size_update(void) {
nghttp2_hd_inflate_init(&inflater, mem);
/* This will make inflater require table size update in the next
inflation. */
nghttp2_hd_inflate_change_table_size(&inflater, 4095);
nghttp2_hd_inflate_change_table_size(&inflater, 4096);
CU_ASSERT(NGHTTP2_ERR_HEADER_COMP ==
inflate_hd(&inflater, &out, &bufs, 0, mem));
nva_out_reset(&out, mem);
nghttp2_bufs_free(&bufs);
nghttp2_hd_inflate_free(&inflater);
/* This does not require for encoder to emit table size update since
* size is not changed. */
nghttp2_hd_inflate_init(&inflater, mem);
nghttp2_hd_inflate_change_table_size(&inflater, 4096);
CU_ASSERT((ssize_t)nghttp2_bufs_len(&bufs) ==
inflate_hd(&inflater, &out, &bufs, 0, mem));
nva_out_reset(&out, mem);
nghttp2_hd_inflate_free(&inflater);
/* This does not require for encodre to emit table size update since
new size is larger than current size. */
nghttp2_hd_inflate_init(&inflater, mem);
nghttp2_hd_inflate_change_table_size(&inflater, 4097);
CU_ASSERT((ssize_t)nghttp2_bufs_len(&bufs) ==
inflate_hd(&inflater, &out, &bufs, 0, mem));
nva_out_reset(&out, mem);
nghttp2_hd_inflate_free(&inflater);
/* Received table size is strictly larger than minimum table size */
nghttp2_hd_inflate_init(&inflater, mem);
nghttp2_hd_inflate_change_table_size(&inflater, 111);
nghttp2_hd_inflate_change_table_size(&inflater, 4096);
nghttp2_bufs_reset(&bufs);
nghttp2_hd_emit_table_size(&bufs, 112);
CU_ASSERT(NGHTTP2_ERR_HEADER_COMP ==
inflate_hd(&inflater, &out, &bufs, 0, mem));
nva_out_reset(&out, mem);
nghttp2_hd_inflate_free(&inflater);
/* Receiving 2 table size updates, min and last value */
nghttp2_hd_inflate_init(&inflater, mem);
nghttp2_hd_inflate_change_table_size(&inflater, 111);
nghttp2_hd_inflate_change_table_size(&inflater, 4096);
nghttp2_bufs_reset(&bufs);
nghttp2_hd_emit_table_size(&bufs, 111);
nghttp2_hd_emit_table_size(&bufs, 4096);
CU_ASSERT((ssize_t)nghttp2_bufs_len(&bufs) ==
inflate_hd(&inflater, &out, &bufs, 0, mem));
nva_out_reset(&out, mem);
nghttp2_hd_inflate_free(&inflater);
/* 2nd update is larger than last value */
nghttp2_hd_inflate_init(&inflater, mem);
nghttp2_hd_inflate_change_table_size(&inflater, 111);
nghttp2_hd_inflate_change_table_size(&inflater, 4095);
nghttp2_bufs_reset(&bufs);
nghttp2_hd_emit_table_size(&bufs, 111);
nghttp2_hd_emit_table_size(&bufs, 4096);
CU_ASSERT(NGHTTP2_ERR_HEADER_COMP ==
inflate_hd(&inflater, &out, &bufs, 0, mem));
nva_out_reset(&out, mem);
nghttp2_hd_inflate_free(&inflater);
nghttp2_bufs_free(&bufs);
}
void test_nghttp2_hd_inflate_unexpected_table_size_update(void) {

View File

@ -4214,13 +4214,16 @@ void test_nghttp2_submit_settings(void) {
iv[2].value = 50;
iv[3].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
iv[3].value = 0;
iv[3].value = 111;
iv[4].settings_id = UNKNOWN_ID;
iv[4].value = 999;
iv[5].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
iv[5].value = (uint32_t)NGHTTP2_MAX_WINDOW_SIZE + 1;
iv[5].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
iv[5].value = 1023;
iv[6].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
iv[6].value = (uint32_t)NGHTTP2_MAX_WINDOW_SIZE + 1;
memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
callbacks.send_callback = null_send_callback;
@ -4228,7 +4231,7 @@ void test_nghttp2_submit_settings(void) {
nghttp2_session_server_new(&session, &callbacks, &ud);
CU_ASSERT(NGHTTP2_ERR_INVALID_ARGUMENT ==
nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 6));
nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 7));
/* Make sure that local settings are not changed */
CU_ASSERT(NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS ==
@ -4237,14 +4240,14 @@ void test_nghttp2_submit_settings(void) {
session->local_settings.initial_window_size);
/* Now sends without 6th one */
CU_ASSERT(0 == nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 5));
CU_ASSERT(0 == nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 6));
item = nghttp2_session_get_next_ob_item(session);
CU_ASSERT(NGHTTP2_SETTINGS == item->frame.hd.type);
frame = &item->frame;
CU_ASSERT(5 == frame->settings.niv);
CU_ASSERT(6 == frame->settings.niv);
CU_ASSERT(5 == frame->settings.iv[0].value);
CU_ASSERT(NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS ==
frame->settings.iv[0].settings_id);
@ -4267,7 +4270,8 @@ void test_nghttp2_submit_settings(void) {
nghttp2_frame_settings_free(&ack_frame.settings, mem);
CU_ASSERT(16 * 1024 == session->local_settings.initial_window_size);
CU_ASSERT(0 == session->hd_inflater.ctx.hd_table_bufsize_max);
CU_ASSERT(1023 == session->hd_inflater.ctx.hd_table_bufsize_max);
CU_ASSERT(111 == session->hd_inflater.min_hd_table_bufsize_max);
CU_ASSERT(50 == session->local_settings.max_concurrent_streams);
/* We just keep the last seen value */
CU_ASSERT(50 == session->pending_local_max_concurrent_stream);