nghttp2_hd: Implement local header table size limit for encoder
This commit is contained in:
parent
8f8c841df0
commit
cbdd44c4ae
|
@ -389,6 +389,14 @@ typedef enum {
|
|||
* The SETTINGS ID.
|
||||
*/
|
||||
typedef enum {
|
||||
/**
|
||||
* SETTINGS_HEADER_TABLE_SIZE
|
||||
*/
|
||||
NGHTTP2_SETTINGS_HEADER_TABLE_SIZE = 1,
|
||||
/**
|
||||
* SETTINGS_ENABLE_PUSH
|
||||
*/
|
||||
NGHTTP2_SETTINGS_ENABLE_PUSH = 2,
|
||||
/**
|
||||
* SETTINGS_MAX_CONCURRENT_STREAMS
|
||||
*/
|
||||
|
|
187
lib/nghttp2_hd.c
187
lib/nghttp2_hd.c
|
@ -187,6 +187,31 @@ static nghttp2_hd_entry* nghttp2_hd_ringbuf_get(nghttp2_hd_ringbuf *ringbuf,
|
|||
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)
|
||||
{
|
||||
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,
|
||||
nghttp2_hd_role role,
|
||||
nghttp2_hd_side side,
|
||||
size_t hd_table_bufsize_max)
|
||||
size_t local_hd_table_bufsize_max)
|
||||
{
|
||||
int rv;
|
||||
context->role = role;
|
||||
context->side = side;
|
||||
context->bad = 0;
|
||||
context->hd_table_bufsize_max = hd_table_bufsize_max;
|
||||
rv = nghttp2_hd_ringbuf_init(&context->hd_table,
|
||||
hd_table_bufsize_max/NGHTTP2_HD_ENTRY_OVERHEAD);
|
||||
context->hd_table_bufsize_max = NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE;
|
||||
rv = nghttp2_hd_ringbuf_init
|
||||
(&context->hd_table,
|
||||
context->hd_table_bufsize_max/NGHTTP2_HD_ENTRY_OVERHEAD);
|
||||
if(rv != 0) {
|
||||
return rv;
|
||||
}
|
||||
|
@ -255,6 +281,9 @@ static int nghttp2_hd_context_init(nghttp2_hd_context *context,
|
|||
context->buf_track = NULL;
|
||||
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->buf_tracklen = 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)
|
||||
{
|
||||
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,
|
||||
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,
|
||||
hd_table_bufsize_max);
|
||||
local_hd_table_bufsize_max);
|
||||
}
|
||||
|
||||
int nghttp2_hd_inflate_init(nghttp2_hd_context *inflater, nghttp2_hd_side side)
|
||||
{
|
||||
return nghttp2_hd_context_init(inflater, NGHTTP2_HD_ROLE_INFLATE, side,
|
||||
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);
|
||||
return nghttp2_hd_context_init(inflater, NGHTTP2_HD_ROLE_INFLATE, side, 0);
|
||||
}
|
||||
|
||||
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;
|
||||
nghttp2_hd_entry *new_ent;
|
||||
size_t room = entry_room(nv->namelen, nv->valuelen);
|
||||
context->hd_table_bufsize += room;
|
||||
while(context->hd_table_bufsize > context->hd_table_bufsize_max &&
|
||||
while(context->hd_table_bufsize + room > 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 &&
|
||||
(ent->flags & NGHTTP2_HD_FLAG_IMPLICIT_EMIT)) {
|
||||
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;
|
||||
}
|
||||
if(ent->flags & NGHTTP2_HD_FLAG_IMPLICIT_EMIT) {
|
||||
/* 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. */
|
||||
|
@ -696,16 +720,83 @@ static nghttp2_hd_entry* add_hd_table_incremental(nghttp2_hd_context *context,
|
|||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
nghttp2_hd_ringbuf_pop_back(&context->hd_table);
|
||||
if(--ent->ref == 0) {
|
||||
nghttp2_hd_entry_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));
|
||||
if(new_ent == 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,
|
||||
entry_flags,
|
||||
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);
|
||||
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) {
|
||||
/* The entry taking more than NGHTTP2_HD_MAX_BUFFER_SIZE is
|
||||
immediately evicted. */
|
||||
--new_ent->ref;
|
||||
} else {
|
||||
context->hd_table_bufsize += room;
|
||||
new_ent->flags |= NGHTTP2_HD_FLAG_REFSET;
|
||||
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)
|
||||
{
|
||||
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);
|
||||
if(nghttp2_nv_equal(&ent->nv, nv)) {
|
||||
return i;
|
||||
|
@ -746,7 +845,9 @@ static ssize_t find_name_in_hd_table(nghttp2_hd_context *context,
|
|||
nghttp2_nv *nv)
|
||||
{
|
||||
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);
|
||||
if(ent->nv.namelen == nv->namelen &&
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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) {
|
||||
nghttp2_hd_entry_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;
|
||||
}
|
||||
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) {
|
||||
nghttp2_hd_entry_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;
|
||||
}
|
||||
incidx = 1;
|
||||
|
|
|
@ -38,6 +38,11 @@
|
|||
#define NGHTTP2_HD_MAX_ENTRY_SIZE 3072
|
||||
#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 {
|
||||
NGHTTP2_HD_SIDE_REQUEST = 0,
|
||||
NGHTTP2_HD_SIDE_RESPONSE = 1
|
||||
|
@ -85,6 +90,24 @@ typedef struct {
|
|||
typedef struct {
|
||||
/* dynamic header 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
|
||||
reference count. */
|
||||
nghttp2_hd_entry **emit_set;
|
||||
|
@ -105,8 +128,6 @@ typedef struct {
|
|||
/* NGHTTP2_HD_SIDE_REQUEST for processing request, otherwise
|
||||
response. */
|
||||
nghttp2_hd_side side;
|
||||
/* Maximum header table size */
|
||||
size_t hd_table_bufsize_max;
|
||||
/* Keep track of allocated buffers in inflation */
|
||||
uint8_t **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.
|
||||
*
|
||||
* 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
|
||||
* 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,
|
||||
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,
|
||||
nghttp2_hd_side side,
|
||||
size_t hd_table_bufsize_max);
|
||||
size_t local_hd_table_bufsize_max);
|
||||
|
||||
/*
|
||||
* 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,
|
||||
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|.
|
||||
*/
|
||||
|
@ -176,6 +211,20 @@ void nghttp2_hd_deflate_free(nghttp2_hd_context *deflater);
|
|||
*/
|
||||
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
|
||||
* the buffer pointed by the |*buf_ptr| with the length |*buflen_ptr|.
|
||||
|
|
|
@ -231,6 +231,8 @@ int main(int argc, char* argv[])
|
|||
test_nghttp2_hd_deflate_same_indexed_repr) ||
|
||||
!CU_add_test(pSuite, "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",
|
||||
test_nghttp2_hd_inflate_indname_inc) ||
|
||||
!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) ||
|
||||
!CU_add_test(pSuite, "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",
|
||||
test_nghttp2_hd_deflate_inflate) ||
|
||||
!CU_add_test(pSuite, "gzip_inflate", test_nghttp2_gzip_inflate) ||
|
||||
|
|
|
@ -247,6 +247,197 @@ void test_nghttp2_hd_deflate_common_header_eviction(void)
|
|||
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)
|
||||
{
|
||||
nghttp2_hd_context inflater;
|
||||
|
@ -389,6 +580,40 @@ void test_nghttp2_hd_inflate_clearall_inc(void)
|
|||
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,
|
||||
nghttp2_hd_context *inflater,
|
||||
nghttp2_nv *nva, size_t nvlen)
|
||||
|
|
|
@ -28,10 +28,12 @@
|
|||
void test_nghttp2_hd_deflate(void);
|
||||
void test_nghttp2_hd_deflate_same_indexed_repr(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_eviction(void);
|
||||
void test_nghttp2_hd_inflate_newname_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);
|
||||
|
||||
#endif /* NGHTTP2_HD_TEST_H */
|
||||
|
|
Loading…
Reference in New Issue