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,20 +700,25 @@ 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) {
/* Emit common header just before it slips away from the context->local_hd_table_bufsize -= entry_room(ent->nv.namelen,
table. If we don't do this, we have to emit it in literal ent->nv.valuelen);
representation which hurts compression. */ --context->local_hd_tablelen;
rv = emit_implicit(buf_ptr, buflen_ptr, offset_ptr, index); }
if(rv != 0) { if(ent->flags & NGHTTP2_HD_FLAG_IMPLICIT_EMIT) {
return NULL; /* Emit common header just before it slips away from the
table. If we don't do this, we have to emit it in literal
representation which hurts compression. */
rv = emit_implicit(buf_ptr, buflen_ptr, offset_ptr, index);
if(rv != 0) {
return NULL;
}
} }
} }
nghttp2_hd_ringbuf_pop_back(&context->hd_table); nghttp2_hd_ringbuf_pop_back(&context->hd_table);
@ -702,22 +727,94 @@ static nghttp2_hd_entry* add_hd_table_incremental(nghttp2_hd_context *context,
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;
} }
rv = nghttp2_hd_entry_init(new_ent,
entry_flags, if(context->role == NGHTTP2_HD_ROLE_DEFLATE &&
nv->name, nv->namelen, nv->value, nv->valuelen); room > context->local_hd_table_bufsize_max) {
if(rv != 0) { uint8_t flags = entry_flags &
free(new_ent); ~(NGHTTP2_HD_FLAG_NAME_ALLOC | NGHTTP2_HD_FLAG_VALUE_ALLOC |
return NULL; 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,
entry_flags,
nv->name, nv->namelen, nv->value, nv->valuelen);
if(rv != 0) {
free(new_ent);
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 */