nghttp2_hd: Implement local header table size limit for encoder

This commit is contained in:
Tatsuhiro Tsujikawa 2013-10-26 01:01:28 +09:00
parent 8f8c841df0
commit cbdd44c4ae
6 changed files with 469 additions and 44 deletions

View File

@ -389,6 +389,14 @@ typedef enum {
* The SETTINGS ID. * The SETTINGS ID.
*/ */
typedef enum { typedef enum {
/**
* SETTINGS_HEADER_TABLE_SIZE
*/
NGHTTP2_SETTINGS_HEADER_TABLE_SIZE = 1,
/**
* SETTINGS_ENABLE_PUSH
*/
NGHTTP2_SETTINGS_ENABLE_PUSH = 2,
/** /**
* SETTINGS_MAX_CONCURRENT_STREAMS * SETTINGS_MAX_CONCURRENT_STREAMS
*/ */

View File

@ -187,6 +187,31 @@ static nghttp2_hd_entry* nghttp2_hd_ringbuf_get(nghttp2_hd_ringbuf *ringbuf,
return ringbuf->buffer[(ringbuf->first + index) & ringbuf->mask]; return ringbuf->buffer[(ringbuf->first + index) & ringbuf->mask];
} }
static int nghttp2_hd_ringbuf_reserve(nghttp2_hd_ringbuf *ringbuf,
size_t bufsize)
{
size_t i;
size_t size;
nghttp2_hd_entry **buffer;
if(ringbuf->mask + 1 >= bufsize) {
return 0;
}
for(size = 1; size < bufsize; size <<= 1);
buffer = malloc(sizeof(nghttp2_hd_entry*) * size);
if(buffer == NULL) {
return NGHTTP2_ERR_NOMEM;
}
for(i = 0; i < ringbuf->len; ++i) {
buffer[i] = nghttp2_hd_ringbuf_get(ringbuf, i);
}
free(ringbuf->buffer);
ringbuf->buffer = buffer;
ringbuf->mask = size - 1;
ringbuf->first = 0;
return 0;
}
static void nghttp2_hd_ringbuf_free(nghttp2_hd_ringbuf *ringbuf) static void nghttp2_hd_ringbuf_free(nghttp2_hd_ringbuf *ringbuf)
{ {
size_t i; size_t i;
@ -220,15 +245,16 @@ static void nghttp2_hd_ringbuf_pop_back(nghttp2_hd_ringbuf *ringbuf)
static int nghttp2_hd_context_init(nghttp2_hd_context *context, static int nghttp2_hd_context_init(nghttp2_hd_context *context,
nghttp2_hd_role role, nghttp2_hd_role role,
nghttp2_hd_side side, nghttp2_hd_side side,
size_t hd_table_bufsize_max) size_t local_hd_table_bufsize_max)
{ {
int rv; int rv;
context->role = role; context->role = role;
context->side = side; context->side = side;
context->bad = 0; context->bad = 0;
context->hd_table_bufsize_max = hd_table_bufsize_max; context->hd_table_bufsize_max = NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE;
rv = nghttp2_hd_ringbuf_init(&context->hd_table, rv = nghttp2_hd_ringbuf_init
hd_table_bufsize_max/NGHTTP2_HD_ENTRY_OVERHEAD); (&context->hd_table,
context->hd_table_bufsize_max/NGHTTP2_HD_ENTRY_OVERHEAD);
if(rv != 0) { if(rv != 0) {
return rv; return rv;
} }
@ -255,6 +281,9 @@ static int nghttp2_hd_context_init(nghttp2_hd_context *context,
context->buf_track = NULL; context->buf_track = NULL;
context->buf_track_capacity = 0; context->buf_track_capacity = 0;
} }
context->local_hd_table_bufsize_max = local_hd_table_bufsize_max;
context->local_hd_table_bufsize = 0;
context->local_hd_tablelen = 0;
context->emit_setlen = 0; context->emit_setlen = 0;
context->buf_tracklen = 0; context->buf_tracklen = 0;
context->hd_table_bufsize = 0; context->hd_table_bufsize = 0;
@ -269,29 +298,20 @@ static int nghttp2_hd_context_init(nghttp2_hd_context *context,
int nghttp2_hd_deflate_init(nghttp2_hd_context *deflater, nghttp2_hd_side side) int nghttp2_hd_deflate_init(nghttp2_hd_context *deflater, nghttp2_hd_side side)
{ {
return nghttp2_hd_context_init(deflater, NGHTTP2_HD_ROLE_DEFLATE, side, return nghttp2_hd_context_init(deflater, NGHTTP2_HD_ROLE_DEFLATE, side,
NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE); NGHTTP2_HD_DEFAULT_LOCAL_MAX_BUFFER_SIZE);
} }
int nghttp2_hd_deflate_init2(nghttp2_hd_context *deflater, int nghttp2_hd_deflate_init2(nghttp2_hd_context *deflater,
nghttp2_hd_side side, nghttp2_hd_side side,
size_t hd_table_bufsize_max) size_t local_hd_table_bufsize_max)
{ {
return nghttp2_hd_context_init(deflater, NGHTTP2_HD_ROLE_DEFLATE, side, return nghttp2_hd_context_init(deflater, NGHTTP2_HD_ROLE_DEFLATE, side,
hd_table_bufsize_max); local_hd_table_bufsize_max);
} }
int nghttp2_hd_inflate_init(nghttp2_hd_context *inflater, nghttp2_hd_side side) int nghttp2_hd_inflate_init(nghttp2_hd_context *inflater, nghttp2_hd_side side)
{ {
return nghttp2_hd_context_init(inflater, NGHTTP2_HD_ROLE_INFLATE, side, return nghttp2_hd_context_init(inflater, NGHTTP2_HD_ROLE_INFLATE, side, 0);
NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE);
}
int nghttp2_hd_inflate_init2(nghttp2_hd_context *inflater,
nghttp2_hd_side side,
size_t hd_table_bufsize_max)
{
return nghttp2_hd_context_init(inflater, NGHTTP2_HD_ROLE_INFLATE, side,
hd_table_bufsize_max);
} }
static void nghttp2_hd_context_free(nghttp2_hd_context *context) static void nghttp2_hd_context_free(nghttp2_hd_context *context)
@ -680,14 +700,18 @@ static nghttp2_hd_entry* add_hd_table_incremental(nghttp2_hd_context *context,
int rv; int rv;
nghttp2_hd_entry *new_ent; nghttp2_hd_entry *new_ent;
size_t room = entry_room(nv->namelen, nv->valuelen); size_t room = entry_room(nv->namelen, nv->valuelen);
context->hd_table_bufsize += room; while(context->hd_table_bufsize + room > context->hd_table_bufsize_max &&
while(context->hd_table_bufsize > context->hd_table_bufsize_max &&
context->hd_table.len > 0) { context->hd_table.len > 0) {
size_t index = context->hd_table.len - 1; size_t index = context->hd_table.len - 1;
nghttp2_hd_entry* ent = nghttp2_hd_ringbuf_get(&context->hd_table, index); nghttp2_hd_entry* ent = nghttp2_hd_ringbuf_get(&context->hd_table, index);
context->hd_table_bufsize -= entry_room(ent->nv.namelen, ent->nv.valuelen); context->hd_table_bufsize -= entry_room(ent->nv.namelen, ent->nv.valuelen);
if(context->role == NGHTTP2_HD_ROLE_DEFLATE && if(context->role == NGHTTP2_HD_ROLE_DEFLATE) {
(ent->flags & NGHTTP2_HD_FLAG_IMPLICIT_EMIT)) { if(context->hd_table_bufsize < context->local_hd_table_bufsize) {
context->local_hd_table_bufsize -= entry_room(ent->nv.namelen,
ent->nv.valuelen);
--context->local_hd_tablelen;
}
if(ent->flags & NGHTTP2_HD_FLAG_IMPLICIT_EMIT) {
/* Emit common header just before it slips away from the /* Emit common header just before it slips away from the
table. If we don't do this, we have to emit it in literal table. If we don't do this, we have to emit it in literal
representation which hurts compression. */ representation which hurts compression. */
@ -696,16 +720,83 @@ static nghttp2_hd_entry* add_hd_table_incremental(nghttp2_hd_context *context,
return NULL; return NULL;
} }
} }
}
nghttp2_hd_ringbuf_pop_back(&context->hd_table); nghttp2_hd_ringbuf_pop_back(&context->hd_table);
if(--ent->ref == 0) { if(--ent->ref == 0) {
nghttp2_hd_entry_free(ent); nghttp2_hd_entry_free(ent);
free(ent); free(ent);
} }
} }
if(context->role == NGHTTP2_HD_ROLE_DEFLATE) {
while(context->local_hd_table_bufsize + room >
context->local_hd_table_bufsize_max
&& context->local_hd_tablelen > 0) {
size_t index = context->local_hd_tablelen - 1;
nghttp2_hd_entry *ent =
nghttp2_hd_ringbuf_get(&context->hd_table, index);
context->local_hd_table_bufsize -= entry_room(ent->nv.namelen,
ent->nv.valuelen);
--context->local_hd_tablelen;
if(ent->flags & NGHTTP2_HD_FLAG_IMPLICIT_EMIT) {
/* Just like a normal eviction, implicit header must be
emitted twice. */
rv = emit_implicit(buf_ptr, buflen_ptr, offset_ptr, index);
if(rv != 0) {
return NULL;
}
ent->flags ^= NGHTTP2_HD_FLAG_IMPLICIT_EMIT;
}
if(ent->flags & NGHTTP2_HD_FLAG_REFSET) {
/* We need to drop entry from reference set. */
rv = emit_indexed_block(buf_ptr, buflen_ptr, offset_ptr, index);
if(rv != 0) {
return NULL;
}
ent->flags ^= NGHTTP2_HD_FLAG_REFSET;
}
/* Release memory. We don't remove entry from the header table
at this moment. */
if(ent->flags & NGHTTP2_HD_FLAG_NAME_ALLOC) {
free(ent->nv.name);
ent->nv.name = NULL;
ent->flags ^= NGHTTP2_HD_FLAG_NAME_ALLOC;
}
if(ent->flags & NGHTTP2_HD_FLAG_VALUE_ALLOC) {
free(ent->nv.value);
ent->nv.value = NULL;
ent->flags ^= NGHTTP2_HD_FLAG_VALUE_ALLOC;
}
}
}
new_ent = malloc(sizeof(nghttp2_hd_entry)); new_ent = malloc(sizeof(nghttp2_hd_entry));
if(new_ent == NULL) { if(new_ent == NULL) {
return NULL; return NULL;
} }
if(context->role == NGHTTP2_HD_ROLE_DEFLATE &&
room > context->local_hd_table_bufsize_max) {
uint8_t flags = entry_flags &
~(NGHTTP2_HD_FLAG_NAME_ALLOC | NGHTTP2_HD_FLAG_VALUE_ALLOC |
NGHTTP2_HD_FLAG_NAME_GIFT | NGHTTP2_HD_FLAG_VALUE_GIFT);
rv = nghttp2_hd_entry_init(new_ent, flags,
NULL, nv->namelen, NULL, nv->valuelen);
if(rv != 0) {
free(new_ent);
return NULL;
}
if(flags & NGHTTP2_HD_FLAG_NAME_GIFT) {
free(nv->name);
nv->name = NULL;
}
if(flags & NGHTTP2_HD_FLAG_VALUE_GIFT) {
free(nv->value);
nv->value = NULL;
}
/* caller must emit indexed repr to toggle off new_ent from
reference set. We cannot do it here because it may break the
indexing. */
} else {
rv = nghttp2_hd_entry_init(new_ent, rv = nghttp2_hd_entry_init(new_ent,
entry_flags, entry_flags,
nv->name, nv->namelen, nv->value, nv->valuelen); nv->name, nv->namelen, nv->value, nv->valuelen);
@ -713,11 +804,17 @@ static nghttp2_hd_entry* add_hd_table_incremental(nghttp2_hd_context *context,
free(new_ent); free(new_ent);
return NULL; return NULL;
} }
if(context->role == NGHTTP2_HD_ROLE_DEFLATE) {
context->local_hd_table_bufsize += room;
++context->local_hd_tablelen;
}
}
if(room > context->hd_table_bufsize_max) { if(room > context->hd_table_bufsize_max) {
/* The entry taking more than NGHTTP2_HD_MAX_BUFFER_SIZE is /* The entry taking more than NGHTTP2_HD_MAX_BUFFER_SIZE is
immediately evicted. */ immediately evicted. */
--new_ent->ref; --new_ent->ref;
} else { } else {
context->hd_table_bufsize += room;
new_ent->flags |= NGHTTP2_HD_FLAG_REFSET; new_ent->flags |= NGHTTP2_HD_FLAG_REFSET;
nghttp2_hd_ringbuf_push_front(&context->hd_table, new_ent); nghttp2_hd_ringbuf_push_front(&context->hd_table, new_ent);
} }
@ -727,7 +824,9 @@ static nghttp2_hd_entry* add_hd_table_incremental(nghttp2_hd_context *context,
static ssize_t find_in_hd_table(nghttp2_hd_context *context, nghttp2_nv *nv) static ssize_t find_in_hd_table(nghttp2_hd_context *context, nghttp2_nv *nv)
{ {
size_t i; size_t i;
for(i = 0; i < context->hd_table.len; ++i) { size_t max = context->role == NGHTTP2_HD_ROLE_DEFLATE ?
context->local_hd_tablelen : context->hd_table.len;
for(i = 0; i < max; ++i) {
nghttp2_hd_entry *ent = nghttp2_hd_ringbuf_get(&context->hd_table, i); nghttp2_hd_entry *ent = nghttp2_hd_ringbuf_get(&context->hd_table, i);
if(nghttp2_nv_equal(&ent->nv, nv)) { if(nghttp2_nv_equal(&ent->nv, nv)) {
return i; return i;
@ -746,7 +845,9 @@ static ssize_t find_name_in_hd_table(nghttp2_hd_context *context,
nghttp2_nv *nv) nghttp2_nv *nv)
{ {
size_t i; size_t i;
for(i = 0; i < context->hd_table.len; ++i) { size_t max = context->role == NGHTTP2_HD_ROLE_DEFLATE ?
context->local_hd_tablelen : context->hd_table.len;
for(i = 0; i < max; ++i) {
nghttp2_hd_entry *ent = nghttp2_hd_ringbuf_get(&context->hd_table, i); nghttp2_hd_entry *ent = nghttp2_hd_ringbuf_get(&context->hd_table, i);
if(ent->nv.namelen == nv->namelen && if(ent->nv.namelen == nv->namelen &&
memcmp(ent->nv.name, nv->name, nv->namelen) == 0) { memcmp(ent->nv.name, nv->name, nv->namelen) == 0) {
@ -763,6 +864,37 @@ static ssize_t find_name_in_hd_table(nghttp2_hd_context *context,
return -1; return -1;
} }
int nghttp2_hd_change_table_size(nghttp2_hd_context *context,
size_t hd_table_bufsize_max)
{
int rv;
rv = nghttp2_hd_ringbuf_reserve
(&context->hd_table, hd_table_bufsize_max / NGHTTP2_HD_ENTRY_OVERHEAD);
if(rv != 0) {
return rv;
}
context->hd_table_bufsize_max = hd_table_bufsize_max;
while(context->hd_table_bufsize > context->hd_table_bufsize_max &&
context->hd_table.len > 0) {
size_t index = context->hd_table.len - 1;
nghttp2_hd_entry* ent = nghttp2_hd_ringbuf_get(&context->hd_table, index);
context->hd_table_bufsize -= entry_room(ent->nv.namelen, ent->nv.valuelen);
if(context->role == NGHTTP2_HD_ROLE_DEFLATE) {
if(context->hd_table_bufsize < context->local_hd_table_bufsize) {
context->local_hd_table_bufsize -= entry_room(ent->nv.namelen,
ent->nv.valuelen);
--context->local_hd_tablelen;
}
}
nghttp2_hd_ringbuf_pop_back(&context->hd_table);
if(--ent->ref == 0) {
nghttp2_hd_entry_free(ent);
free(ent);
}
}
return 0;
}
static int check_index_range(nghttp2_hd_context *context, size_t index) static int check_index_range(nghttp2_hd_context *context, size_t index)
{ {
return index < context->hd_table.len + STATIC_TABLE_LENGTH; return index < context->hd_table.len + STATIC_TABLE_LENGTH;
@ -804,7 +936,10 @@ static int deflate_nv(nghttp2_hd_context *deflater,
if(new_ent->ref == 0) { if(new_ent->ref == 0) {
nghttp2_hd_entry_free(new_ent); nghttp2_hd_entry_free(new_ent);
free(new_ent); free(new_ent);
} else { new_ent = NULL;
} else if(new_ent->nv.name != NULL) {
/* new_ent->ref > 0 and nv.name is not NULL means that new_ent is
in the reference set and in local_hd_table_bufsize */
new_ent->flags |= NGHTTP2_HD_FLAG_EMIT; new_ent->flags |= NGHTTP2_HD_FLAG_EMIT;
} }
rv = emit_indexed_block(buf_ptr, buflen_ptr, offset_ptr, index); rv = emit_indexed_block(buf_ptr, buflen_ptr, offset_ptr, index);
@ -877,7 +1012,9 @@ static int deflate_nv(nghttp2_hd_context *deflater,
if(new_ent->ref == 0) { if(new_ent->ref == 0) {
nghttp2_hd_entry_free(new_ent); nghttp2_hd_entry_free(new_ent);
free(new_ent); free(new_ent);
} else { } else if(new_ent->nv.name != NULL) {
/* new_ent->ref > 0 and nv.name is not NULL means that new_ent is
in the reference set and in local_hd_table_bufsize */
new_ent->flags |= NGHTTP2_HD_FLAG_EMIT; new_ent->flags |= NGHTTP2_HD_FLAG_EMIT;
} }
incidx = 1; incidx = 1;

View File

@ -38,6 +38,11 @@
#define NGHTTP2_HD_MAX_ENTRY_SIZE 3072 #define NGHTTP2_HD_MAX_ENTRY_SIZE 3072
#define NGHTTP2_HD_ENTRY_OVERHEAD 32 #define NGHTTP2_HD_ENTRY_OVERHEAD 32
/* Default size of maximum table buffer size for encoder. Even if
remote decoder notifies larger buffer size for its decoding,
encoder only uses the memory up to this value. */
#define NGHTTP2_HD_DEFAULT_LOCAL_MAX_BUFFER_SIZE (1 << 12)
typedef enum { typedef enum {
NGHTTP2_HD_SIDE_REQUEST = 0, NGHTTP2_HD_SIDE_REQUEST = 0,
NGHTTP2_HD_SIDE_RESPONSE = 1 NGHTTP2_HD_SIDE_RESPONSE = 1
@ -85,6 +90,24 @@ typedef struct {
typedef struct { typedef struct {
/* dynamic header table */ /* dynamic header table */
nghttp2_hd_ringbuf hd_table; nghttp2_hd_ringbuf hd_table;
/* The header table size for decoding. If the context is initialized
as encoder, this value is advertised by remote endpoint
decoder. */
size_t hd_table_bufsize_max;
/* The current effective header table size for encoding. This value
is meaningful iff this context is initialized as
encoder. |local_hd_table_bufsize| <= |hd_table_bufsize| must be
hold. */
size_t local_hd_table_bufsize;
/* The maximum effective header table for encoding. Although header
table size is bounded by |hd_table_bufsize_max|, the encoder can
use smaller buffer by not retaining the header name/values beyond
the |local_hd_table_bufsize_max| and not referencing those
entries. This value is meaningful iff this context is initialized
as encoder. */
size_t local_hd_table_bufsize_max;
/* The number of effective entry in |hd_table|. */
size_t local_hd_tablelen;
/* Holding emitted entry in deflating header block to retain /* Holding emitted entry in deflating header block to retain
reference count. */ reference count. */
nghttp2_hd_entry **emit_set; nghttp2_hd_entry **emit_set;
@ -105,8 +128,6 @@ typedef struct {
/* NGHTTP2_HD_SIDE_REQUEST for processing request, otherwise /* NGHTTP2_HD_SIDE_REQUEST for processing request, otherwise
response. */ response. */
nghttp2_hd_side side; nghttp2_hd_side side;
/* Maximum header table size */
size_t hd_table_bufsize_max;
/* Keep track of allocated buffers in inflation */ /* Keep track of allocated buffers in inflation */
uint8_t **buf_track; uint8_t **buf_track;
/* The capacity of |buf_track| */ /* The capacity of |buf_track| */
@ -137,6 +158,11 @@ void nghttp2_hd_entry_free(nghttp2_hd_entry *ent);
/* /*
* Initializes |deflater| for deflating name/values pairs. * Initializes |deflater| for deflating name/values pairs.
* *
* The encoder only uses up to
* NGHTTP2_HD_DEFAULT_LOCAL_MAX_BUFFER_SIZE bytes for header table
* even if the larger value is specified later in
* nghttp2_hd_change_table_size().
*
* This function returns 0 if it succeeds, or one of the following * This function returns 0 if it succeeds, or one of the following
* negative error codes: * negative error codes:
* *
@ -146,9 +172,22 @@ void nghttp2_hd_entry_free(nghttp2_hd_entry *ent);
int nghttp2_hd_deflate_init(nghttp2_hd_context *deflater, int nghttp2_hd_deflate_init(nghttp2_hd_context *deflater,
nghttp2_hd_side side); nghttp2_hd_side side);
/*
* Initializes |deflater| for deflating name/values pairs.
*
* The encoder only uses up to |local_hd_table_bufsize_max| bytes for
* header table even if the larger value is specified later in
* nghttp2_hd_change_table_size().
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* NGHTTP2_ERR_NOMEM
* Out of memory.
*/
int nghttp2_hd_deflate_init2(nghttp2_hd_context *deflater, int nghttp2_hd_deflate_init2(nghttp2_hd_context *deflater,
nghttp2_hd_side side, nghttp2_hd_side side,
size_t hd_table_bufsize_max); size_t local_hd_table_bufsize_max);
/* /*
* Initializes |inflater| for inflating name/values pairs. * Initializes |inflater| for inflating name/values pairs.
@ -162,10 +201,6 @@ int nghttp2_hd_deflate_init2(nghttp2_hd_context *deflater,
int nghttp2_hd_inflate_init(nghttp2_hd_context *inflater, int nghttp2_hd_inflate_init(nghttp2_hd_context *inflater,
nghttp2_hd_side side); nghttp2_hd_side side);
int nghttp2_hd_inflate_init2(nghttp2_hd_context *inflater,
nghttp2_hd_side side,
size_t hd_table_bufsize_max);
/* /*
* Deallocates any resources allocated for |deflater|. * Deallocates any resources allocated for |deflater|.
*/ */
@ -176,6 +211,20 @@ void nghttp2_hd_deflate_free(nghttp2_hd_context *deflater);
*/ */
void nghttp2_hd_inflate_free(nghttp2_hd_context *inflater); void nghttp2_hd_inflate_free(nghttp2_hd_context *inflater);
/*
* Changes header table size in |context|. This may trigger eviction
* in the dynamic table.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* NGHTTP2_ERR_NOMEM
* Out of memory.
*/
int nghttp2_hd_change_table_size(nghttp2_hd_context *context,
size_t hd_table_bufsize_max);
/* /*
* Deflates the |nva|, which has the |nvlen| name/value pairs, into * Deflates the |nva|, which has the |nvlen| name/value pairs, into
* the buffer pointed by the |*buf_ptr| with the length |*buflen_ptr|. * the buffer pointed by the |*buf_ptr| with the length |*buflen_ptr|.

View File

@ -231,6 +231,8 @@ int main(int argc, char* argv[])
test_nghttp2_hd_deflate_same_indexed_repr) || test_nghttp2_hd_deflate_same_indexed_repr) ||
!CU_add_test(pSuite, "hd_deflate_common_header_eviction", !CU_add_test(pSuite, "hd_deflate_common_header_eviction",
test_nghttp2_hd_deflate_common_header_eviction) || test_nghttp2_hd_deflate_common_header_eviction) ||
!CU_add_test(pSuite, "hd_deflate_local_buffer",
test_nghttp2_hd_deflate_local_buffer) ||
!CU_add_test(pSuite, "hd_inflate_indname_inc", !CU_add_test(pSuite, "hd_inflate_indname_inc",
test_nghttp2_hd_inflate_indname_inc) || test_nghttp2_hd_inflate_indname_inc) ||
!CU_add_test(pSuite, "hd_inflate_indname_inc_eviction", !CU_add_test(pSuite, "hd_inflate_indname_inc_eviction",
@ -239,6 +241,8 @@ int main(int argc, char* argv[])
test_nghttp2_hd_inflate_newname_inc) || test_nghttp2_hd_inflate_newname_inc) ||
!CU_add_test(pSuite, "hd_inflate_clearall_inc", !CU_add_test(pSuite, "hd_inflate_clearall_inc",
test_nghttp2_hd_inflate_clearall_inc) || test_nghttp2_hd_inflate_clearall_inc) ||
!CU_add_test(pSuite, "hd_change_table_size",
test_nghttp2_hd_change_table_size) ||
!CU_add_test(pSuite, "hd_deflate_inflate", !CU_add_test(pSuite, "hd_deflate_inflate",
test_nghttp2_hd_deflate_inflate) || test_nghttp2_hd_deflate_inflate) ||
!CU_add_test(pSuite, "gzip_inflate", test_nghttp2_gzip_inflate) || !CU_add_test(pSuite, "gzip_inflate", test_nghttp2_gzip_inflate) ||

View File

@ -247,6 +247,197 @@ void test_nghttp2_hd_deflate_common_header_eviction(void)
nghttp2_hd_deflate_free(&deflater); nghttp2_hd_deflate_free(&deflater);
} }
void test_nghttp2_hd_deflate_local_buffer(void)
{
nghttp2_hd_context deflater, inflater;
size_t i;
ssize_t rv, blocklen;
uint8_t *buf = NULL;
size_t buflen = 0;
nghttp2_nv nva1[] = { MAKE_NV("k1", "v1"), /* 36 */
MAKE_NV("k10", "v10"), /* 38 */
MAKE_NV("k100", "v100"), /* 40 */
MAKE_NV("k1000", "v1000") /* 42 */
}; /* Total: 156 */
nghttp2_nv nva2[] = { MAKE_NV("k10", "v10"), /* 38 */
MAKE_NV("k1", "v1") /* 36 */
};
nghttp2_nv nv3;
uint8_t val[256];
nghttp2_nv nva4[] = { MAKE_NV(":method", "GET"),
MAKE_NV(":scheme", "http")
};
nghttp2_nv *resnva;
nghttp2_hd_entry *ent;
memset(val, 'a', sizeof(val));
nv3.name = nv3.value = val;
nv3.namelen = nv3.valuelen = sizeof(val);
/* Check the case where entry from static table is inserted to
dynamic header table. And it is out of local header table
size. */
nghttp2_hd_deflate_init2(&deflater, NGHTTP2_HD_SIDE_REQUEST, 32);
nghttp2_hd_inflate_init(&inflater, NGHTTP2_HD_SIDE_REQUEST);
blocklen = nghttp2_hd_deflate_hd(&deflater, &buf, &buflen, 0,
nva4, ARRLEN(nva4));
CU_ASSERT(blocklen > 0);
/* Now header table should look like this:
*
* 0: :scheme, http (-)
* 1: :method, GET (-)
*
* name/value of all entries must be NULL.
*/
CU_ASSERT(2 == deflater.hd_table.len);
CU_ASSERT(0 == deflater.local_hd_tablelen);
CU_ASSERT(0 == deflater.local_hd_table_bufsize);
for(i = 0; i < 2; ++i) {
ent = nghttp2_hd_table_get(&deflater, i);
CU_ASSERT(ent->nv.name == NULL);
CU_ASSERT(ent->nv.value == NULL);
CU_ASSERT(0 == (ent->flags & NGHTTP2_HD_FLAG_REFSET));
}
rv = nghttp2_hd_inflate_hd(&inflater, &resnva, buf, blocklen);
CU_ASSERT(2 == rv);
assert_nv_equal(nva4, resnva, 2);
nghttp2_hd_end_headers(&inflater);
nghttp2_nv_array_del(resnva);
nghttp2_hd_deflate_free(&deflater);
nghttp2_hd_inflate_free(&inflater);
/* 156 buffer size can hold all headers in local region */
nghttp2_hd_deflate_init2(&deflater, NGHTTP2_HD_SIDE_REQUEST,
156);
blocklen = nghttp2_hd_deflate_hd(&deflater, &buf, &buflen, 0,
nva1, ARRLEN(nva1));
CU_ASSERT(blocklen > 0);
/* Now header table should look like this:
*
* 0: k1000, v100
* 1: k100, v100
* 2: k10, v10
* 3: k1, v1
*/
CU_ASSERT(4 == deflater.hd_table.len);
CU_ASSERT(4 == deflater.local_hd_tablelen);
CU_ASSERT(156 == deflater.local_hd_table_bufsize);
for(i = 0; i < 4; ++i) {
CU_ASSERT(nghttp2_hd_table_get(&deflater, i)->nv.name != NULL);
CU_ASSERT(nghttp2_hd_table_get(&deflater, i)->nv.value != NULL);
}
CU_ASSERT(0 == nghttp2_hd_change_table_size(&deflater, 156));
CU_ASSERT(4 == deflater.hd_table.len);
CU_ASSERT(4 == deflater.local_hd_tablelen);
CU_ASSERT(156 == deflater.local_hd_table_bufsize);
blocklen = nghttp2_hd_deflate_hd(&deflater, &buf, &buflen, 0, &nv3, 1);
CU_ASSERT(blocklen > 0);
/* Now header table should be empty */
CU_ASSERT(0 == deflater.hd_table.len);
CU_ASSERT(0 == deflater.local_hd_tablelen);
CU_ASSERT(0 == deflater.local_hd_table_bufsize);
nghttp2_hd_deflate_free(&deflater);
/* Check more complex use case */
nghttp2_hd_deflate_init2(&deflater, NGHTTP2_HD_SIDE_REQUEST,
155);
nghttp2_hd_inflate_init(&inflater, NGHTTP2_HD_SIDE_REQUEST);
blocklen = nghttp2_hd_deflate_hd(&deflater, &buf, &buflen, 0,
nva1, ARRLEN(nva1));
CU_ASSERT(blocklen > 0);
/* Now header table should look like this:
*
* 0: k1000, v100 (R)
* 1: k100, v100 (R)
* 2: k10, v10 (R)
* 3: k1, v1 (-) <- name, value must be NULL and not in reference set
*
* But due to the local table size limit, name/value of index=3 must
* be NULL.
*/
CU_ASSERT(4 == deflater.hd_table.len);
CU_ASSERT(3 == deflater.local_hd_tablelen);
CU_ASSERT(120 == deflater.local_hd_table_bufsize);
for(i = 0; i < 3; ++i) {
CU_ASSERT(nghttp2_hd_table_get(&deflater, i)->nv.name != NULL);
CU_ASSERT(nghttp2_hd_table_get(&deflater, i)->nv.value != NULL);
}
ent = nghttp2_hd_table_get(&deflater, 3);
CU_ASSERT(ent->nv.name == NULL);
CU_ASSERT(ent->nv.value == NULL);
CU_ASSERT(0 == (ent->flags & NGHTTP2_HD_FLAG_REFSET));
rv = nghttp2_hd_inflate_hd(&inflater, &resnva, buf, blocklen);
CU_ASSERT(4 == rv);
assert_nv_equal(nva1, resnva, 4);
nghttp2_hd_end_headers(&inflater);
nghttp2_nv_array_del(resnva);
blocklen = nghttp2_hd_deflate_hd(&deflater, &buf, &buflen, 0,
nva2, ARRLEN(nva2));
CU_ASSERT(blocklen > 0);
/* Now header table should look like this:
*
* 0: k1, v1 (R)
* 1: k1000, v100 (R)
* 2: k100, v100 (R)
* 3: k10, v10 (-) <- name, value must be NULL
* 4: k1, v1 (-) <- name, value must be NULL
*/
CU_ASSERT(5 == deflater.hd_table.len);
CU_ASSERT(3 == deflater.local_hd_tablelen);
CU_ASSERT(118 == deflater.local_hd_table_bufsize);
ent = nghttp2_hd_table_get(&deflater, 3);
CU_ASSERT(0 == (ent->flags & NGHTTP2_HD_FLAG_REFSET));
ent = nghttp2_hd_table_get(&deflater, 3);
CU_ASSERT(0 == (ent->flags & NGHTTP2_HD_FLAG_REFSET));
rv = nghttp2_hd_inflate_hd(&inflater, &resnva, buf, blocklen);
CU_ASSERT(2 == rv);
/* Sort before comparison */
nghttp2_nv_array_sort(nva2, 2);
assert_nv_equal(nva2, resnva, 2);
nghttp2_hd_end_headers(&inflater);
nghttp2_nv_array_del(resnva);
blocklen = nghttp2_hd_deflate_hd(&deflater, &buf, &buflen, 0, &nv3, 1);
CU_ASSERT(blocklen > 0);
/* Now header table should look like this:
*
* 0: a..a, a..a (-)
* 1: k1, v1 (-)
* 2: k1000, v100 (-)
* 3: k100, v100 (-)
* 4: k10, v10 (-)
* 5: k1, v1 (-)
*
* name/value of all entries must be NULL.
*/
CU_ASSERT(6 == deflater.hd_table.len);
CU_ASSERT(0 == deflater.local_hd_tablelen);
CU_ASSERT(0 == deflater.local_hd_table_bufsize);
for(i = 0; i < 6; ++i) {
ent = nghttp2_hd_table_get(&deflater, i);
CU_ASSERT(0 == (ent->flags & NGHTTP2_HD_FLAG_REFSET));
}
rv = nghttp2_hd_inflate_hd(&inflater, &resnva, buf, blocklen);
CU_ASSERT(1 == rv);
assert_nv_equal(&nv3, resnva, 1);
nghttp2_hd_end_headers(&inflater);
nghttp2_nv_array_del(resnva);
free(buf);
nghttp2_hd_inflate_free(&inflater);
nghttp2_hd_deflate_free(&deflater);
}
void test_nghttp2_hd_inflate_indname_inc(void) void test_nghttp2_hd_inflate_indname_inc(void)
{ {
nghttp2_hd_context inflater; nghttp2_hd_context inflater;
@ -389,6 +580,40 @@ void test_nghttp2_hd_inflate_clearall_inc(void)
nghttp2_hd_inflate_free(&inflater); nghttp2_hd_inflate_free(&inflater);
} }
void test_nghttp2_hd_change_table_size(void)
{
nghttp2_hd_context deflater;
nghttp2_nv nva[] = { MAKE_NV(":method", "GET"),
MAKE_NV(":path", "/") };
uint8_t *buf = NULL;
size_t buflen = 0;
ssize_t rv;
nghttp2_hd_deflate_init(&deflater, NGHTTP2_HD_SIDE_REQUEST);
CU_ASSERT(0 == nghttp2_hd_change_table_size(&deflater, 8000));
CU_ASSERT(255 == deflater.hd_table.mask);
CU_ASSERT(8000 == deflater.hd_table_bufsize_max);
rv = nghttp2_hd_deflate_hd(&deflater, &buf, &buflen, 0, nva, 2);
CU_ASSERT(rv > 0);
CU_ASSERT(2 == deflater.hd_table.len);
CU_ASSERT(0 == nghttp2_hd_change_table_size(&deflater, 16384));
CU_ASSERT(511 == deflater.hd_table.mask);
CU_ASSERT(2 == deflater.hd_table.len);
CU_ASSERT(2 == deflater.local_hd_tablelen);
CU_ASSERT(5 ==
deflater.hd_table.buffer[deflater.hd_table.first]->nv.namelen);
CU_ASSERT(0 == nghttp2_hd_change_table_size(&deflater, 0));
CU_ASSERT(511 == deflater.hd_table.mask);
CU_ASSERT(0 == deflater.hd_table.len);
CU_ASSERT(0 == deflater.local_hd_tablelen);
free(buf);
nghttp2_hd_deflate_free(&deflater);
}
static void check_deflate_inflate(nghttp2_hd_context *deflater, static void check_deflate_inflate(nghttp2_hd_context *deflater,
nghttp2_hd_context *inflater, nghttp2_hd_context *inflater,
nghttp2_nv *nva, size_t nvlen) nghttp2_nv *nva, size_t nvlen)

View File

@ -28,10 +28,12 @@
void test_nghttp2_hd_deflate(void); void test_nghttp2_hd_deflate(void);
void test_nghttp2_hd_deflate_same_indexed_repr(void); void test_nghttp2_hd_deflate_same_indexed_repr(void);
void test_nghttp2_hd_deflate_common_header_eviction(void); void test_nghttp2_hd_deflate_common_header_eviction(void);
void test_nghttp2_hd_deflate_local_buffer(void);
void test_nghttp2_hd_inflate_indname_inc(void); void test_nghttp2_hd_inflate_indname_inc(void);
void test_nghttp2_hd_inflate_indname_inc_eviction(void); void test_nghttp2_hd_inflate_indname_inc_eviction(void);
void test_nghttp2_hd_inflate_newname_inc(void); void test_nghttp2_hd_inflate_newname_inc(void);
void test_nghttp2_hd_inflate_clearall_inc(void); void test_nghttp2_hd_inflate_clearall_inc(void);
void test_nghttp2_hd_change_table_size(void);
void test_nghttp2_hd_deflate_inflate(void); void test_nghttp2_hd_deflate_inflate(void);
#endif /* NGHTTP2_HD_TEST_H */ #endif /* NGHTTP2_HD_TEST_H */