Experiment HPACK with upcoming changes

* remove substitution
* reversed insertion and removal from header table
* unified initial static table
This commit is contained in:
Tatsuhiro Tsujikawa 2013-10-12 21:49:01 +09:00
parent e85418f045
commit 8cf3731802
5 changed files with 252 additions and 473 deletions

View File

@ -26,11 +26,12 @@
#include <string.h>
#include <assert.h>
#include <stdio.h>
#include "nghttp2_frame.h"
#include "nghttp2_helper.h"
static const char *reqhd_table[] = {
static const char *static_table[] = {
":scheme", "http",
":scheme", "https",
":host", "",
@ -61,10 +62,7 @@ static const char *reqhd_table[] = {
"proxy-authorization", "",
"range", "",
"via", "",
NULL
};
static const char *reshd_table[] = {
":status", "200",
"age", "",
"cache-control", "",
@ -104,7 +102,7 @@ typedef struct {
size_t nvlen;
} nghttp2_nva_out;
int nghttp2_hd_entry_init(nghttp2_hd_entry *ent, uint8_t index, uint8_t flags,
int nghttp2_hd_entry_init(nghttp2_hd_entry *ent, uint8_t flags,
uint8_t *name, uint16_t namelen,
uint8_t *value, uint16_t valuelen)
{
@ -139,7 +137,6 @@ int nghttp2_hd_entry_init(nghttp2_hd_entry *ent, uint8_t index, uint8_t flags,
ent->nv.namelen = namelen;
ent->nv.valuelen = valuelen;
ent->ref = 1;
ent->index = index;
ent->flags = flags;
return 0;
@ -162,29 +159,81 @@ void nghttp2_hd_entry_free(nghttp2_hd_entry *ent)
}
}
static int nghttp2_hd_ringbuf_init(nghttp2_hd_ringbuf *ringbuf,
size_t bufsize)
{
size_t size;
for(size = 1; size < bufsize; size <<= 1);
ringbuf->buffer = malloc(sizeof(nghttp2_hd_entry*)*size);
if(ringbuf->buffer == NULL) {
return NGHTTP2_ERR_NOMEM;
}
ringbuf->mask = size - 1;
ringbuf->first = 0;
ringbuf->len = 0;
return 0;
}
static nghttp2_hd_entry* nghttp2_hd_ringbuf_get(nghttp2_hd_ringbuf *ringbuf,
size_t index)
{
assert(index < ringbuf->len);
return ringbuf->buffer[(ringbuf->first + index) & ringbuf->mask];
}
static void nghttp2_hd_ringbuf_free(nghttp2_hd_ringbuf *ringbuf)
{
size_t i;
if(ringbuf == NULL) {
return;
}
for(i = 0; i < ringbuf->len; ++i) {
nghttp2_hd_entry *ent = nghttp2_hd_ringbuf_get(ringbuf, i);
--ent->ref;
nghttp2_hd_entry_free(ent);
free(ent);
}
free(ringbuf->buffer);
}
static size_t nghttp2_hd_ringbuf_push_front(nghttp2_hd_ringbuf *ringbuf,
nghttp2_hd_entry *ent)
{
assert(ringbuf->len + 1 <= ringbuf->mask);
ringbuf->buffer[--ringbuf->first & ringbuf->mask] = ent;
++ringbuf->len;
return 0;
}
static void nghttp2_hd_ringbuf_pop_back(nghttp2_hd_ringbuf *ringbuf)
{
assert(ringbuf->len > 0);
--ringbuf->len;
}
static int nghttp2_hd_context_init(nghttp2_hd_context *context,
nghttp2_hd_role role,
nghttp2_hd_side side)
{
int i;
const char **ini_table;
int rv;
context->role = role;
context->bad = 0;
context->hd_table = malloc(sizeof(nghttp2_hd_entry*)*
rv = nghttp2_hd_ringbuf_init(&context->hd_table,
NGHTTP2_INITIAL_HD_TABLE_SIZE);
if(context->hd_table == NULL) {
return NGHTTP2_ERR_NOMEM;
if(rv != 0) {
return rv;
}
memset(context->hd_table, 0, sizeof(nghttp2_hd_entry*)*
NGHTTP2_INITIAL_HD_TABLE_SIZE);
context->hd_table_capacity = NGHTTP2_INITIAL_HD_TABLE_SIZE;
context->hd_tablelen = 0;
for(i = 0; static_table[i]; i += 2);
/* TODO handle nomem */
context->static_hd_table = malloc(sizeof(nghttp2_hd_entry*)*(i / 2 + 1));
context->static_hd_table[i / 2] = NULL;
if(role == NGHTTP2_HD_ROLE_INFLATE) {
context->emit_set = malloc(sizeof(nghttp2_hd_entry*)*
NGHTTP2_INITIAL_EMIT_SET_SIZE);
if(context->emit_set == NULL) {
free(context->hd_table);
nghttp2_hd_ringbuf_free(&context->hd_table);
return NGHTTP2_ERR_NOMEM;
}
memset(context->emit_set, 0, sizeof(nghttp2_hd_entry*)*
@ -196,30 +245,19 @@ static int nghttp2_hd_context_init(nghttp2_hd_context *context,
}
context->emit_setlen = 0;
if(side == NGHTTP2_HD_SIDE_CLIENT) {
ini_table = reqhd_table;
} else {
ini_table = reshd_table;
}
context->hd_table_bufsize = 0;
for(i = 0; ini_table[i]; i += 2) {
for(i = 0; static_table[i]; i += 2) {
nghttp2_hd_entry *p = malloc(sizeof(nghttp2_hd_entry));
if(p == NULL) {
for(i = 0; i < context->hd_tablelen; ++i) {
nghttp2_hd_entry_free(context->hd_table[i]);
free(context->hd_table[i]);
}
nghttp2_hd_ringbuf_free(&context->hd_table);
free(context->emit_set);
free(context->hd_table);
return NGHTTP2_ERR_NOMEM;
}
nghttp2_hd_entry_init(p, i / 2, NGHTTP2_HD_FLAG_NONE,
(uint8_t*)ini_table[i], strlen(ini_table[i]),
(uint8_t*)ini_table[i + 1],
strlen(ini_table[i+1]));
context->hd_table[context->hd_tablelen++] = p;
context->hd_table_bufsize += NGHTTP2_HD_ENTRY_OVERHEAD +
p->nv.namelen + p->nv.valuelen;
nghttp2_hd_entry_init(p, NGHTTP2_HD_FLAG_NONE,
(uint8_t*)static_table[i], strlen(static_table[i]),
(uint8_t*)static_table[i + 1],
strlen(static_table[i+1]));
context->static_hd_table[i / 2] = p;
}
return 0;
}
@ -244,14 +282,9 @@ static void nghttp2_hd_context_free(nghttp2_hd_context *context)
free(ent);
}
}
for(i = 0; i < context->hd_tablelen; ++i) {
nghttp2_hd_entry *ent = context->hd_table[i];
--ent->ref;
nghttp2_hd_entry_free(ent);
free(ent);
}
nghttp2_hd_ringbuf_free(&context->hd_table);
free(context->static_hd_table);
free(context->emit_set);
free(context->hd_table);
}
void nghttp2_hd_deflate_free(nghttp2_hd_context *deflater)
@ -478,17 +511,19 @@ static int emit_indname_block(uint8_t **buf_ptr, size_t *buflen_ptr,
{
int rv;
uint8_t *bufp;
size_t blocklen = count_encoded_length(index + 1, 5) +
size_t blocklen = count_encoded_length(index + 1, 6) +
count_encoded_length(valuelen, 0) + valuelen;
rv = ensure_write_buffer(buf_ptr, buflen_ptr, *offset_ptr, blocklen);
if(rv != 0) {
return rv;
}
bufp = *buf_ptr + *offset_ptr;
bufp += encode_length(bufp, index + 1, 5);
bufp += encode_length(bufp, index + 1, 6);
bufp += encode_length(bufp, valuelen, 0);
memcpy(bufp, value, valuelen);
(*buf_ptr)[*offset_ptr] |= inc_indexing ? 0x40u : 0x60u;
if(!inc_indexing) {
(*buf_ptr)[*offset_ptr] |= 0x40u;
}
assert(bufp+valuelen - (*buf_ptr + *offset_ptr) == (ssize_t)blocklen);
*offset_ptr += blocklen;
return 0;
@ -507,7 +542,7 @@ static int emit_newname_block(uint8_t **buf_ptr, size_t *buflen_ptr,
return rv;
}
bufp = *buf_ptr + *offset_ptr;
*bufp++ = inc_indexing ? 0x40u : 0x60u;
*bufp++ = inc_indexing ? 0 : 0x40u;
bufp += encode_length(bufp, nv->namelen, 0);
memcpy(bufp, nv->name, nv->namelen);
bufp += nv->namelen;
@ -517,54 +552,6 @@ static int emit_newname_block(uint8_t **buf_ptr, size_t *buflen_ptr,
return 0;
}
static int emit_subst_indname_block(uint8_t **buf_ptr, size_t *buflen_ptr,
size_t *offset_ptr, size_t index,
const uint8_t *value, size_t valuelen,
size_t subindex)
{
int rv;
uint8_t *bufp;
size_t blocklen = count_encoded_length(index + 1, 6) +
count_encoded_length(subindex, 0) +
count_encoded_length(valuelen, 0) + valuelen;
rv = ensure_write_buffer(buf_ptr, buflen_ptr, *offset_ptr, blocklen);
if(rv != 0) {
return rv;
}
bufp = *buf_ptr + *offset_ptr;
bufp += encode_length(bufp, index + 1, 6);
bufp += encode_length(bufp, subindex, 0);
bufp += encode_length(bufp, valuelen, 0);
memcpy(bufp, value, valuelen);
*offset_ptr += blocklen;
return 0;
}
static int emit_subst_newname_block(uint8_t **buf_ptr, size_t *buflen_ptr,
size_t *offset_ptr, nghttp2_nv *nv,
size_t subindex)
{
int rv;
uint8_t *bufp;
size_t blocklen = 1 + count_encoded_length(nv->namelen, 0) + nv->namelen +
count_encoded_length(subindex, 0) +
count_encoded_length(nv->valuelen, 0) + nv->valuelen;
rv = ensure_write_buffer(buf_ptr, buflen_ptr, *offset_ptr, blocklen);
if(rv != 0) {
return rv;
}
bufp = *buf_ptr + *offset_ptr;
*bufp++ = 0;
bufp += encode_length(bufp, nv->namelen, 0);
memcpy(bufp, nv->name, nv->namelen);
bufp += nv->namelen;
bufp += encode_length(bufp, subindex, 0);
bufp += encode_length(bufp, nv->valuelen, 0);
memcpy(bufp, nv->value, nv->valuelen);
*offset_ptr += blocklen;
return 0;
}
/*
* Emit common header with |index| by toggle off and on (thus 2
* indexed representation emissions).
@ -592,43 +579,35 @@ static nghttp2_hd_entry* add_hd_table_incremental(nghttp2_hd_context *context,
nghttp2_nv *nv)
{
int rv;
size_t i;
nghttp2_hd_entry *new_ent;
size_t room = entry_room(nv->namelen, nv->valuelen);
context->hd_table_bufsize += room;
for(i = 0; i < context->hd_tablelen &&
context->hd_table_bufsize > NGHTTP2_HD_MAX_BUFFER_SIZE; ++i) {
nghttp2_hd_entry *ent = context->hd_table[i];
while(context->hd_table_bufsize > NGHTTP2_HD_MAX_BUFFER_SIZE &&
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)) {
/* 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, ent->index);
rv = emit_implicit(buf_ptr, buflen_ptr, offset_ptr, index);
if(rv != 0) {
return NULL;
}
}
ent->index = NGHTTP2_HD_INVALID_INDEX;
nghttp2_hd_ringbuf_pop_back(&context->hd_table);
if(--ent->ref == 0) {
nghttp2_hd_entry_free(ent);
free(ent);
}
}
if(i > 0) {
size_t j;
for(j = 0; i < context->hd_tablelen; ++i, ++j) {
context->hd_table[j] = context->hd_table[i];
context->hd_table[j]->index = j;
}
context->hd_tablelen = j;
}
new_ent = malloc(sizeof(nghttp2_hd_entry));
if(new_ent == NULL) {
return NULL;
}
rv = nghttp2_hd_entry_init(new_ent, context->hd_tablelen,
rv = nghttp2_hd_entry_init(new_ent,
NGHTTP2_HD_FLAG_NAME_ALLOC |
NGHTTP2_HD_FLAG_VALUE_ALLOC,
nv->name, nv->namelen, nv->value, nv->valuelen);
@ -639,127 +618,61 @@ static nghttp2_hd_entry* add_hd_table_incremental(nghttp2_hd_context *context,
if(room > NGHTTP2_HD_MAX_BUFFER_SIZE) {
/* The entry taking more than NGHTTP2_HD_MAX_BUFFER_SIZE is
immediately evicted. */
new_ent->index = NGHTTP2_HD_INVALID_INDEX;
--new_ent->ref;
} else {
/* Because of current NGHTTP2_HD_MAX_BUFFER_SIZE,
NGHTTP2_HD_ENTRY_OVERHEAD and NGHTTP2_INITIAL_HD_TABLE_SIZE,
context->hd_tablelen is strictly less than
context->hd_table_capacity. */
assert(context->hd_tablelen < context->hd_table_capacity);
context->hd_table[context->hd_tablelen++] = new_ent;
new_ent->flags |= NGHTTP2_HD_FLAG_REFSET;
nghttp2_hd_ringbuf_push_front(&context->hd_table, new_ent);
}
return new_ent;
}
/*
* This function does not take parameter for header block emission
* because our encoder never use substitution.
*/
static nghttp2_hd_entry* add_hd_table_subst(nghttp2_hd_context *context,
nghttp2_nv *nv, size_t subindex)
{
int rv;
size_t i;
int k;
nghttp2_hd_entry *new_ent;
size_t room = entry_room(nv->namelen, nv->valuelen);
if(context->hd_tablelen <= subindex) {
return NULL;
}
context->hd_table_bufsize -=
entry_room(context->hd_table[subindex]->nv.namelen,
context->hd_table[subindex]->nv.valuelen);
context->hd_table_bufsize += room;
k = subindex;
for(i = 0; i < context->hd_tablelen &&
context->hd_table_bufsize > NGHTTP2_HD_MAX_BUFFER_SIZE; ++i, --k) {
nghttp2_hd_entry *ent = context->hd_table[i];
if(i != subindex) {
context->hd_table_bufsize -= entry_room(ent->nv.namelen,
ent->nv.valuelen);
}
ent->index = NGHTTP2_HD_INVALID_INDEX;
if(--ent->ref == 0) {
nghttp2_hd_entry_free(ent);
free(ent);
}
}
if(i > 0) {
size_t j;
/* k < 0 means that the index to substitute originally was
evicted. Therefore, index 0 is the position to substitute
now. */
if(k < 0) {
j = 1;
} else {
j = 0;
}
for(; i < context->hd_tablelen; ++i, ++j) {
context->hd_table[j] = context->hd_table[i];
context->hd_table[j]->index = j;
}
context->hd_tablelen = j;
}
new_ent = malloc(sizeof(nghttp2_hd_entry));
if(new_ent == NULL) {
return NULL;
}
if(k >= 0) {
nghttp2_hd_entry *ent = context->hd_table[k];
ent->index = NGHTTP2_HD_INVALID_INDEX;
if(--ent->ref == 0) {
nghttp2_hd_entry_free(ent);
free(ent);
}
} else {
k = 0;
}
rv = nghttp2_hd_entry_init(new_ent, k,
NGHTTP2_HD_FLAG_NAME_ALLOC |
NGHTTP2_HD_FLAG_VALUE_ALLOC,
nv->name, nv->namelen, nv->value, nv->valuelen);
if(rv != 0) {
free(new_ent);
return NULL;
}
if(room > NGHTTP2_HD_MAX_BUFFER_SIZE) {
new_ent->index = NGHTTP2_HD_INVALID_INDEX;
--new_ent->ref;
context->hd_tablelen = 0;
} else {
context->hd_table[new_ent->index] = new_ent;
new_ent->flags |= NGHTTP2_HD_FLAG_REFSET;
}
return new_ent;
}
static nghttp2_hd_entry* 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;
for(i = 0; i < context->hd_tablelen; ++i) {
nghttp2_hd_entry *ent = context->hd_table[i];
for(i = 0; i < context->hd_table.len; ++i) {
nghttp2_hd_entry *ent = nghttp2_hd_ringbuf_get(&context->hd_table, i);
if(nghttp2_nv_equal(&ent->nv, nv)) {
return ent;
return i;
}
}
return NULL;
for(i = 0; context->static_hd_table[i]; ++i) {
nghttp2_hd_entry *ent = context->static_hd_table[i];
if(nghttp2_nv_equal(&ent->nv, nv)) {
return context->hd_table.len + i;
}
}
return -1;
}
static nghttp2_hd_entry* find_name_in_hd_table(nghttp2_hd_context *context,
static ssize_t find_name_in_hd_table(nghttp2_hd_context *context,
nghttp2_nv *nv)
{
size_t i;
for(i = 0; i < context->hd_tablelen; ++i) {
nghttp2_hd_entry *ent = context->hd_table[i];
for(i = 0; i < context->hd_table.len; ++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) {
return ent;
return i;
}
}
return NULL;
for(i = 0; context->static_hd_table[i]; ++i) {
nghttp2_hd_entry *ent = context->static_hd_table[i];
if(ent->nv.namelen == nv->namelen &&
memcmp(ent->nv.name, nv->name, nv->namelen) == 0) {
return context->hd_table.len + i;
}
}
return -1;
}
nghttp2_hd_entry* nghttp2_hd_table_get(nghttp2_hd_context *context,
size_t index)
{
if(index < context->hd_table.len) {
return nghttp2_hd_ringbuf_get(&context->hd_table, index);
} else {
return context->static_hd_table[index - context->hd_table.len];
}
}
static int deflate_nv(nghttp2_hd_context *deflater,
@ -769,11 +682,13 @@ static int deflate_nv(nghttp2_hd_context *deflater,
{
int rv;
nghttp2_hd_entry *ent;
ent = find_in_hd_table(deflater, nv);
if(ent) {
rv = find_in_hd_table(deflater, nv);
if(rv != -1) {
size_t index = rv;
ent = nghttp2_hd_table_get(deflater, index);
if((ent->flags & NGHTTP2_HD_FLAG_REFSET) == 0) {
ent->flags |= NGHTTP2_HD_FLAG_REFSET | NGHTTP2_HD_FLAG_EMIT;
rv = emit_indexed_block(buf_ptr, buflen_ptr, offset_ptr, ent->index);
rv = emit_indexed_block(buf_ptr, buflen_ptr, offset_ptr, index);
if(rv != 0) {
return rv;
}
@ -803,18 +718,18 @@ static int deflate_nv(nghttp2_hd_context *deflater,
ent->flags |= NGHTTP2_HD_FLAG_IMPLICIT_EMIT;
}
for(; num_emits > 0; --num_emits) {
rv = emit_indexed_block(buf_ptr, buflen_ptr, offset_ptr, ent->index);
rv = emit_indexed_block(buf_ptr, buflen_ptr, offset_ptr, index);
if(rv != 0) {
break;
}
}
}
} else {
uint8_t index = NGHTTP2_HD_INVALID_INDEX;
ssize_t index = -1;
int incidx = 0;
ent = find_name_in_hd_table(deflater, nv);
if(ent) {
index = ent->index;
rv = find_name_in_hd_table(deflater, nv);
if(rv != -1) {
index = rv;
}
if(entry_room(nv->namelen, nv->valuelen) <= NGHTTP2_HD_MAX_ENTRY_SIZE) {
nghttp2_hd_entry *new_ent;
@ -826,7 +741,7 @@ static int deflate_nv(nghttp2_hd_context *deflater,
new_ent->flags |= NGHTTP2_HD_FLAG_EMIT;
incidx = 1;
}
if(index == NGHTTP2_HD_INVALID_INDEX) {
if(index == -1) {
rv = emit_newname_block(buf_ptr, buflen_ptr, offset_ptr, nv, incidx);
} else {
rv = emit_indname_block(buf_ptr, buflen_ptr, offset_ptr, index,
@ -839,6 +754,28 @@ static int deflate_nv(nghttp2_hd_context *deflater,
return 0;
}
static int deflate_post_process_hd_entry(nghttp2_hd_entry *ent,
size_t index,
uint8_t **buf_ptr,
size_t *buflen_ptr,
size_t *offset_ptr)
{
int rv;
if((ent->flags & NGHTTP2_HD_FLAG_REFSET) &&
(ent->flags & NGHTTP2_HD_FLAG_IMPLICIT_EMIT) == 0 &&
(ent->flags & NGHTTP2_HD_FLAG_EMIT) == 0) {
/* This entry is not present in the current header set and must
be removed. */
ent->flags ^= NGHTTP2_HD_FLAG_REFSET;
rv = emit_indexed_block(buf_ptr, buflen_ptr, offset_ptr, index);
if(rv != 0) {
return rv;
}
}
ent->flags &= ~(NGHTTP2_HD_FLAG_EMIT | NGHTTP2_HD_FLAG_IMPLICIT_EMIT);
return 0;
}
ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_context *deflater,
uint8_t **buf_ptr, size_t *buflen_ptr,
size_t nv_offset,
@ -856,17 +793,20 @@ ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_context *deflater,
goto fail;
}
}
for(i = 0; i < deflater->hd_tablelen; ++i) {
nghttp2_hd_entry *ent = deflater->hd_table[i];
if((ent->flags & NGHTTP2_HD_FLAG_REFSET) &&
(ent->flags & NGHTTP2_HD_FLAG_IMPLICIT_EMIT) == 0 &&
(ent->flags & NGHTTP2_HD_FLAG_EMIT) == 0) {
/* This entry is not present in the current header set and must
be removed. */
ent->flags ^= NGHTTP2_HD_FLAG_REFSET;
rv = emit_indexed_block(buf_ptr, buflen_ptr, &offset, ent->index);
for(i = 0; i < deflater->hd_table.len; ++i) {
nghttp2_hd_entry *ent = nghttp2_hd_ringbuf_get(&deflater->hd_table, i);
rv = deflate_post_process_hd_entry(ent, i, buf_ptr, buflen_ptr, &offset);
if(rv != 0) {
goto fail;
}
}
for(i = 0; deflater->static_hd_table[i]; ++i) {
nghttp2_hd_entry *ent = deflater->static_hd_table[i];
rv = deflate_post_process_hd_entry(ent, i + deflater->hd_table.len,
buf_ptr, buflen_ptr, &offset);
if(rv != 0) {
goto fail;
}
ent->flags &= ~(NGHTTP2_HD_FLAG_EMIT | NGHTTP2_HD_FLAG_IMPLICIT_EMIT);
}
return offset - nv_offset;
fail:
@ -874,6 +814,28 @@ ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_context *deflater,
return rv;
}
static int inflater_post_process_hd_entry(nghttp2_hd_context *inflater,
nghttp2_hd_entry *ent,
nghttp2_nva_out *nva_out_ptr)
{
int rv;
if((ent->flags & NGHTTP2_HD_FLAG_REFSET) &&
(ent->flags & NGHTTP2_HD_FLAG_EMIT) == 0) {
rv = emit_indexed_header(inflater, nva_out_ptr, ent);
if(rv != 0) {
return rv;
}
}
ent->flags &= ~NGHTTP2_HD_FLAG_EMIT;
return 0;
}
static int check_index_range(nghttp2_hd_context *context, size_t index)
{
return index < context->hd_table.len +
sizeof(static_table)/sizeof(static_table[0])/2;
}
ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_context *inflater,
nghttp2_nv **nva_ptr,
uint8_t *in, size_t inlen)
@ -898,11 +860,11 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_context *inflater,
rv = NGHTTP2_ERR_HEADER_COMP;
goto fail;
}
if(inflater->hd_tablelen <= index) {
if(!check_index_range(inflater, index)) {
rv = NGHTTP2_ERR_HEADER_COMP;
goto fail;
}
ent = inflater->hd_table[index];
ent = nghttp2_hd_table_get(inflater, index);
ent->flags ^= NGHTTP2_HD_FLAG_REFSET;
if(ent->flags & NGHTTP2_HD_FLAG_REFSET) {
rv = emit_indexed_header(inflater, &nva_out, ent);
@ -910,10 +872,10 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_context *inflater,
goto fail;
}
}
} else if(c == 0x40u || c == 0x60u || c == 0) {
} else if(c == 0x40u || c == 0) {
/* Literal Header Repr - New Name */
nghttp2_nv nv;
ssize_t namelen, valuelen, subindex;
ssize_t namelen, valuelen;
if(++in == last) {
rv = NGHTTP2_ERR_HEADER_COMP;
goto fail;
@ -929,13 +891,6 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_context *inflater,
}
nv.name = in;
in += namelen;
if(c == 0) {
in = decode_length(&subindex, in, last, 0);
if(subindex < 0) {
rv = NGHTTP2_ERR_HEADER_COMP;
goto fail;
}
}
in = decode_length(&valuelen, in, last, 0);
if(valuelen < 0 || in + valuelen > last) {
rv = NGHTTP2_ERR_HEADER_COMP;
@ -946,15 +901,11 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_context *inflater,
nv.valuelen = valuelen;
in += valuelen;
nghttp2_downcase(nv.name, nv.namelen);
if(c == 0x60u) {
if(c == 0x40u) {
rv = emit_newname_header(inflater, &nva_out, &nv);
} else {
nghttp2_hd_entry *new_ent;
if(c == 0) {
new_ent = add_hd_table_subst(inflater, &nv, subindex);
} else {
new_ent = add_hd_table_incremental(inflater, NULL, NULL, NULL, &nv);
}
if(new_ent) {
rv = emit_indexed_header(inflater, &nva_out, new_ent);
} else {
@ -968,25 +919,18 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_context *inflater,
/* Literal Header Repr - Indexed Name */
nghttp2_hd_entry *ent;
uint8_t *value;
ssize_t valuelen, index, subindex;
in = decode_length(&index, in, last, (c & 0x40u) ? 5 : 6);
ssize_t valuelen, index;
in = decode_length(&index, in, last, 6);
if(index < 0) {
rv = NGHTTP2_ERR_HEADER_COMP;
goto fail;
}
--index;
if(inflater->hd_tablelen <= index) {
if(!check_index_range(inflater, index)) {
rv = NGHTTP2_ERR_HEADER_COMP;
goto fail;
}
ent = inflater->hd_table[index];
if((c & 0x40u) == 0) {
in = decode_length(&subindex, in, last, 0);
if(subindex < 0) {
rv = NGHTTP2_ERR_HEADER_COMP;
goto fail;
}
}
ent = nghttp2_hd_table_get(inflater, index);
in = decode_length(&valuelen, in , last, 0);
if(valuelen < 0 || in + valuelen > last) {
rv = NGHTTP2_ERR_HEADER_COMP;
@ -994,7 +938,7 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_context *inflater,
}
value = in;
in += valuelen;
if((c & 0x60u) == 0x60u) {
if((c & 0x40u) == 0x40u) {
rv = emit_indname_header(inflater, &nva_out, ent, value, valuelen);
} else {
nghttp2_nv nv;
@ -1004,11 +948,7 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_context *inflater,
nv.namelen = ent->nv.namelen;
nv.value = value;
nv.valuelen = valuelen;
if((c & 0x40u) == 0) {
new_ent = add_hd_table_subst(inflater, &nv, subindex);
} else {
new_ent = add_hd_table_incremental(inflater, NULL, NULL, NULL, &nv);
}
if(--ent->ref == 0) {
nghttp2_hd_entry_free(ent);
free(ent);
@ -1024,16 +964,19 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_context *inflater,
}
}
}
for(i = 0; i < inflater->hd_tablelen; ++i) {
nghttp2_hd_entry *ent = inflater->hd_table[i];
if((ent->flags & NGHTTP2_HD_FLAG_REFSET) &&
(ent->flags & NGHTTP2_HD_FLAG_EMIT) == 0) {
rv = emit_indexed_header(inflater, &nva_out, ent);
for(i = 0; i < inflater->hd_table.len; ++i) {
nghttp2_hd_entry *ent = nghttp2_hd_ringbuf_get(&inflater->hd_table, i);
rv = inflater_post_process_hd_entry(inflater, ent, &nva_out);
if(rv != 0) {
goto fail;
}
}
ent->flags &= ~NGHTTP2_HD_FLAG_EMIT;
for(i = 0; inflater->static_hd_table[i]; ++i) {
nghttp2_hd_entry *ent = inflater->static_hd_table[i];
rv = inflater_post_process_hd_entry(inflater, ent, &nva_out);
if(rv != 0) {
goto fail;
}
}
nghttp2_nv_array_sort(nva_out.nva, nva_out.nvlen);
*nva_ptr = nva_out.nva;
@ -1073,20 +1016,3 @@ int nghttp2_hd_emit_newname_block(uint8_t **buf_ptr, size_t *buflen_ptr,
{
return emit_newname_block(buf_ptr, buflen_ptr, offset_ptr, nv, inc_indexing);
}
int nghttp2_hd_emit_subst_indname_block(uint8_t **buf_ptr, size_t *buflen_ptr,
size_t *offset_ptr, size_t index,
const uint8_t *value, size_t valuelen,
size_t subindex)
{
return emit_subst_indname_block(buf_ptr, buflen_ptr, offset_ptr, index,
value, valuelen, subindex);
}
int nghttp2_hd_emit_subst_newname_block(uint8_t **buf_ptr, size_t *buflen_ptr,
size_t *offset_ptr, nghttp2_nv *nv,
size_t subindex)
{
return emit_subst_newname_block(buf_ptr, buflen_ptr, offset_ptr, nv,
subindex);
}

View File

@ -70,21 +70,23 @@ typedef struct {
nghttp2_nv nv;
/* Reference count */
uint8_t ref;
/* Index in the header table */
uint8_t index;
uint8_t flags;
} nghttp2_hd_entry;
typedef struct {
nghttp2_hd_entry **buffer;
size_t mask;
size_t first;
size_t len;
} nghttp2_hd_ringbuf;
typedef struct {
/* Header table */
nghttp2_hd_entry **hd_table;
nghttp2_hd_ringbuf hd_table;
nghttp2_hd_entry **static_hd_table;
/* Holding emitted entry in deflating header block to retain
reference count. */
nghttp2_hd_entry **emit_set;
/* The capacity of the |hd_table| */
uint16_t hd_table_capacity;
/* The number of entry the |hd_table| contains */
uint16_t hd_tablelen;
/* The capacity of the |emit_set| */
uint16_t emit_set_capacity;
/* The number of entry the |emit_set| contains */
@ -114,7 +116,7 @@ typedef struct {
* NGHTTP2_ERR_NOMEM
* Out of memory.
*/
int nghttp2_hd_entry_init(nghttp2_hd_entry *ent, uint8_t index, uint8_t flags,
int nghttp2_hd_entry_init(nghttp2_hd_entry *ent, uint8_t flags,
uint8_t *name, uint16_t namelen,
uint8_t *value, uint16_t valuelen);
@ -238,4 +240,8 @@ int nghttp2_hd_emit_subst_newname_block(uint8_t **buf_ptr, size_t *buflen_ptr,
size_t *offset_ptr, nghttp2_nv *nv,
size_t subindex);
/* For unittesting purpose */
nghttp2_hd_entry* nghttp2_hd_table_get(nghttp2_hd_context *context,
size_t index);
#endif /* NGHTTP2_HD_COMP_H */

View File

@ -237,16 +237,6 @@ int main(int argc, char* argv[])
test_nghttp2_hd_inflate_indname_inc_eviction) ||
!CU_add_test(pSuite, "hd_inflate_newname_inc",
test_nghttp2_hd_inflate_newname_inc) ||
!CU_add_test(pSuite, "hd_inflate_indname_subst",
test_nghttp2_hd_inflate_indname_subst) ||
!CU_add_test(pSuite, "hd_inflate_indname_subst_eviction",
test_nghttp2_hd_inflate_indname_subst_eviction) ||
!CU_add_test(pSuite, "hd_inflate_indname_subst_eviction_neg",
test_nghttp2_hd_inflate_indname_subst_eviction_neg) ||
!CU_add_test(pSuite, "hd_inflate_newname_subst",
test_nghttp2_hd_inflate_newname_subst) ||
!CU_add_test(pSuite, "hd_inflate_clearall_subst",
test_nghttp2_hd_inflate_clearall_subst) ||
!CU_add_test(pSuite, "hd_inflate_clearall_inc",
test_nghttp2_hd_inflate_clearall_inc) ||
!CU_add_test(pSuite, "hd_deflate_inflate",

View File

@ -33,6 +33,8 @@
#include "nghttp2_frame.h"
#include "nghttp2_test_helper.h"
#define GET_TABLE_ENT(context, index) nghttp2_hd_table_get(context, index)
static void assert_nv_equal(nghttp2_nv *a, nghttp2_nv *b, size_t len)
{
size_t i;
@ -65,15 +67,12 @@ void test_nghttp2_hd_deflate(void)
CU_ASSERT(0 == nghttp2_hd_deflate_init(&deflater, NGHTTP2_HD_SIDE_CLIENT));
CU_ASSERT(0 == nghttp2_hd_inflate_init(&inflater, NGHTTP2_HD_SIDE_SERVER));
blocklen = nghttp2_hd_deflate_hd(&deflater, &buf, &buflen, nv_offset, nva1,
sizeof(nva1)/sizeof(nghttp2_nv));
CU_ASSERT(blocklen > 0);
nghttp2_hd_end_headers(&deflater);
CU_ASSERT(3 == nghttp2_hd_inflate_hd(&inflater, &resnva, buf + nv_offset,
blocklen));
assert_nv_equal(nva1, resnva, 3);
nghttp2_nv_array_del(resnva);
@ -99,10 +98,8 @@ void test_nghttp2_hd_deflate(void)
sizeof(nva3)/sizeof(nghttp2_nv));
CU_ASSERT(blocklen > 0);
nghttp2_hd_end_headers(&deflater);
CU_ASSERT(3 == nghttp2_hd_inflate_hd(&inflater, &resnva, buf + nv_offset,
blocklen));
assert_nv_equal(nva3, resnva, 3);
nghttp2_nv_array_del(resnva);
@ -216,8 +213,8 @@ void test_nghttp2_hd_deflate_common_header_eviction(void)
nghttp2_hd_inflate_init(&inflater, NGHTTP2_HD_SIDE_SERVER);
/* Put :scheme: http (index = 0) in reference set */
deflater.hd_table[0]->flags |= NGHTTP2_HD_FLAG_REFSET;
inflater.hd_table[0]->flags |= NGHTTP2_HD_FLAG_REFSET;
GET_TABLE_ENT(&deflater, 0)->flags |= NGHTTP2_HD_FLAG_REFSET;
GET_TABLE_ENT(&inflater, 0)->flags |= NGHTTP2_HD_FLAG_REFSET;
blocklen = nghttp2_hd_deflate_hd(&deflater, &buf, &buflen, 0, nva,
sizeof(nva)/sizeof(nghttp2_nv));
CU_ASSERT(blocklen > 0);
@ -252,8 +249,9 @@ void test_nghttp2_hd_inflate_indname_inc(void)
nv.value, nv.valuelen, 1));
CU_ASSERT(1 == nghttp2_hd_inflate_hd(&inflater, &resnva, buf, offset));
assert_nv_equal(&nv, resnva, 1);
CU_ASSERT(31 == inflater.hd_tablelen);
assert_nv_equal(&nv, &inflater.hd_table[inflater.hd_tablelen-1]->nv, 1);
CU_ASSERT(1 == inflater.hd_table.len);
assert_nv_equal(&nv,
&GET_TABLE_ENT(&inflater, inflater.hd_table.len-1)->nv, 1);
nghttp2_nv_array_del(resnva);
free(buf);
@ -266,17 +264,21 @@ void test_nghttp2_hd_inflate_indname_inc_eviction(void)
uint8_t *buf = NULL;
size_t buflen = 0;
size_t offset = 0;
/* Default header table capacity is 1262. Adding 2835 bytes,
including overhead, to the table evicts first entry.
use name ":host" which index 2 and value length 2798. */
uint8_t value[2798];
uint8_t value[1024];
nghttp2_nv *resnva;
nghttp2_hd_inflate_init(&inflater, NGHTTP2_HD_SIDE_SERVER);
memset(value, '0', sizeof(value));
CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&buf, &buflen, &offset, 2,
value, sizeof(value), 1));
CU_ASSERT(1 == nghttp2_hd_inflate_hd(&inflater, &resnva, buf, offset));
CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&buf, &buflen, &offset, 3,
value, sizeof(value), 1));
CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&buf, &buflen, &offset, 4,
value, sizeof(value), 1));
CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&buf, &buflen, &offset, 5,
value, sizeof(value), 1));
CU_ASSERT(4 == nghttp2_hd_inflate_hd(&inflater, &resnva, buf, offset));
CU_ASSERT(5 == resnva[0].namelen);
CU_ASSERT(0 == memcmp(":host", resnva[0].name, resnva[0].namelen));
CU_ASSERT(sizeof(value) == resnva[0].valuelen);
@ -284,8 +286,8 @@ void test_nghttp2_hd_inflate_indname_inc_eviction(void)
nghttp2_nv_array_del(resnva);
nghttp2_hd_end_headers(&inflater);
CU_ASSERT(30 == inflater.hd_tablelen);
CU_ASSERT(inflater.hd_table[29]->flags & NGHTTP2_HD_FLAG_REFSET);
CU_ASSERT(3 == inflater.hd_table.len);
CU_ASSERT(GET_TABLE_ENT(&inflater, 0)->flags & NGHTTP2_HD_FLAG_REFSET);
free(buf);
nghttp2_hd_inflate_free(&inflater);
@ -305,118 +307,9 @@ void test_nghttp2_hd_inflate_newname_inc(void)
&nv, 1));
CU_ASSERT(1 == nghttp2_hd_inflate_hd(&inflater, &resnva, buf, offset));
assert_nv_equal(&nv, resnva, 1);
CU_ASSERT(31 == inflater.hd_tablelen);
assert_nv_equal(&nv, &inflater.hd_table[inflater.hd_tablelen-1]->nv, 1);
nghttp2_nv_array_del(resnva);
free(buf);
nghttp2_hd_inflate_free(&inflater);
}
void test_nghttp2_hd_inflate_indname_subst(void)
{
nghttp2_hd_context inflater;
uint8_t *buf = NULL;
size_t buflen = 0;
size_t offset = 0;
nghttp2_nv nv = MAKE_NV("user-agent", "nghttp2");
nghttp2_nv *resnva;
nghttp2_hd_inflate_init(&inflater, NGHTTP2_HD_SIDE_SERVER);
CU_ASSERT(0 == nghttp2_hd_emit_subst_indname_block(&buf, &buflen, &offset,
11,
nv.value, nv.valuelen,
11));
CU_ASSERT(1 == nghttp2_hd_inflate_hd(&inflater, &resnva, buf, offset));
assert_nv_equal(&nv, resnva, 1);
CU_ASSERT(30 == inflater.hd_tablelen);
assert_nv_equal(&nv, &inflater.hd_table[11]->nv, 1);
nghttp2_nv_array_del(resnva);
free(buf);
nghttp2_hd_inflate_free(&inflater);
}
void test_nghttp2_hd_inflate_indname_subst_eviction(void)
{
nghttp2_hd_context inflater;
uint8_t *buf = NULL;
size_t buflen = 0;
size_t offset = 0;
/* Default header table capacity is 1262. Adding 2877 bytes,
including overhead, to the table evicts first entry.
use name ":host" which index 2 and value length 2840. */
uint8_t value[2840];
nghttp2_nv *resnva;
nghttp2_hd_inflate_init(&inflater, NGHTTP2_HD_SIDE_SERVER);
memset(value, '0', sizeof(value));
CU_ASSERT(0 == nghttp2_hd_emit_subst_indname_block(&buf, &buflen, &offset,
2,
value, sizeof(value), 2));
CU_ASSERT(1 == nghttp2_hd_inflate_hd(&inflater, &resnva, buf, offset));
CU_ASSERT(5 == resnva[0].namelen);
CU_ASSERT(0 == memcmp(":host", resnva[0].name, resnva[0].namelen));
CU_ASSERT(sizeof(value) == resnva[0].valuelen);
nghttp2_nv_array_del(resnva);
nghttp2_hd_end_headers(&inflater);
CU_ASSERT(29 == inflater.hd_tablelen);
CU_ASSERT(inflater.hd_table[1]->flags & NGHTTP2_HD_FLAG_REFSET);
free(buf);
nghttp2_hd_inflate_free(&inflater);
}
void test_nghttp2_hd_inflate_indname_subst_eviction_neg(void)
{
nghttp2_hd_context inflater;
uint8_t *buf = NULL;
size_t buflen = 0;
size_t offset = 0;
/* Default header table capacity is 1262. Adding 2878 bytes,
including overhead, to the table evicts first 2 entries.
use name ":host" which index 2 and value length 2841. */
uint8_t value[2841];
nghttp2_nv *resnva;
nghttp2_hd_inflate_init(&inflater, NGHTTP2_HD_SIDE_SERVER);
memset(value, '0', sizeof(value));
/* Try to substitute index 0, but it will be evicted */
CU_ASSERT(0 == nghttp2_hd_emit_subst_indname_block(&buf, &buflen, &offset,
2,
value, sizeof(value), 0));
CU_ASSERT(1 == nghttp2_hd_inflate_hd(&inflater, &resnva, buf, offset));
CU_ASSERT(5 == resnva[0].namelen);
CU_ASSERT(0 == memcmp(":host", resnva[0].name, resnva[0].namelen));
CU_ASSERT(sizeof(value) == resnva[0].valuelen);
nghttp2_nv_array_del(resnva);
nghttp2_hd_end_headers(&inflater);
CU_ASSERT(29 == inflater.hd_tablelen);
CU_ASSERT(inflater.hd_table[0]->flags & NGHTTP2_HD_FLAG_REFSET);
free(buf);
nghttp2_hd_inflate_free(&inflater);
}
void test_nghttp2_hd_inflate_newname_subst(void)
{
nghttp2_hd_context inflater;
uint8_t *buf = NULL;
size_t buflen = 0;
size_t offset = 0;
nghttp2_nv nv = MAKE_NV("x-rel", "nghttp2");
nghttp2_nv *resnva;
nghttp2_hd_inflate_init(&inflater, NGHTTP2_HD_SIDE_SERVER);
CU_ASSERT(0 == nghttp2_hd_emit_subst_newname_block(&buf, &buflen, &offset,
&nv, 1));
CU_ASSERT(1 == nghttp2_hd_inflate_hd(&inflater, &resnva, buf, offset));
assert_nv_equal(&nv, resnva, 1);
CU_ASSERT(30 == inflater.hd_tablelen);
assert_nv_equal(&nv, &inflater.hd_table[1]->nv, 1);
CU_ASSERT(1 == inflater.hd_table.len);
assert_nv_equal(&nv,
&GET_TABLE_ENT(&inflater, inflater.hd_table.len-1)->nv, 1);
nghttp2_nv_array_del(resnva);
free(buf);
@ -446,7 +339,7 @@ void test_nghttp2_hd_inflate_clearall_inc(void)
&nv, 1));
CU_ASSERT(1 == nghttp2_hd_inflate_hd(&inflater, &resnva, buf, offset));
assert_nv_equal(&nv, resnva, 1);
CU_ASSERT(0 == inflater.hd_tablelen);
CU_ASSERT(0 == inflater.hd_table.len);
nghttp2_nv_array_del(resnva);
nghttp2_hd_end_headers(&inflater);
@ -454,7 +347,7 @@ void test_nghttp2_hd_inflate_clearall_inc(void)
/* Do it again */
CU_ASSERT(1 == nghttp2_hd_inflate_hd(&inflater, &resnva, buf, offset));
assert_nv_equal(&nv, resnva, 1);
CU_ASSERT(0 == inflater.hd_tablelen);
CU_ASSERT(0 == inflater.hd_table.len);
nghttp2_nv_array_del(resnva);
nghttp2_hd_end_headers(&inflater);
@ -468,38 +361,7 @@ void test_nghttp2_hd_inflate_clearall_inc(void)
&nv, 1));
CU_ASSERT(1 == nghttp2_hd_inflate_hd(&inflater, &resnva, buf, offset));
assert_nv_equal(&nv, resnva, 1);
CU_ASSERT(1 == inflater.hd_tablelen);
nghttp2_nv_array_del(resnva);
free(buf);
nghttp2_hd_inflate_free(&inflater);
}
void test_nghttp2_hd_inflate_clearall_subst(void)
{
nghttp2_hd_context inflater;
uint8_t *buf = NULL;
size_t buflen = 0;
size_t offset = 0;
nghttp2_nv nv;
nghttp2_nv *resnva;
uint8_t value[4060];
/* Total 4097 bytes space required to hold this entry */
nv.name = (uint8_t*)"alpha";
nv.namelen = strlen((char*)nv.name);
memset(value, '0', sizeof(value));
nv.value = value;
nv.valuelen = sizeof(value);
nghttp2_hd_inflate_init(&inflater, NGHTTP2_HD_SIDE_SERVER);
CU_ASSERT(0 == nghttp2_hd_emit_subst_newname_block(&buf, &buflen, &offset,
&nv, 1));
CU_ASSERT(1 == nghttp2_hd_inflate_hd(&inflater, &resnva, buf, offset));
assert_nv_equal(&nv, resnva, 1);
CU_ASSERT(0 == inflater.hd_tablelen);
CU_ASSERT(1 == inflater.hd_table.len);
nghttp2_nv_array_del(resnva);

View File

@ -31,12 +31,7 @@ void test_nghttp2_hd_deflate_common_header_eviction(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_indname_subst(void);
void test_nghttp2_hd_inflate_indname_subst_eviction(void);
void test_nghttp2_hd_inflate_indname_subst_eviction_neg(void);
void test_nghttp2_hd_inflate_newname_subst(void);
void test_nghttp2_hd_inflate_clearall_inc(void);
void test_nghttp2_hd_inflate_clearall_subst(void);
void test_nghttp2_hd_deflate_inflate(void);
#endif /* NGHTTP2_HD_TEST_H */