Rework header compression

This commit is contained in:
Tatsuhiro Tsujikawa 2013-08-23 23:38:28 +09:00
parent c4f6e069ac
commit 005e60a5ed
5 changed files with 262 additions and 158 deletions

View File

@ -99,6 +99,12 @@ static const char *reshd_table[] = {
NULL NULL
}; };
typedef struct {
nghttp2_nv *nva;
size_t nvacap;
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 index, uint8_t flags,
uint8_t *name, uint16_t namelen, uint8_t *name, uint16_t namelen,
uint8_t *value, uint16_t valuelen) uint8_t *value, uint16_t valuelen)
@ -158,7 +164,8 @@ void nghttp2_hd_entry_free(nghttp2_hd_entry *ent)
} }
static int nghttp2_hd_context_init(nghttp2_hd_context *context, static int nghttp2_hd_context_init(nghttp2_hd_context *context,
nghttp2_hd_side side) nghttp2_hd_side side,
uint8_t inflater)
{ {
int i; int i;
const char **ini_table; const char **ini_table;
@ -173,6 +180,7 @@ static int nghttp2_hd_context_init(nghttp2_hd_context *context,
context->hd_table_capacity = NGHTTP2_INITIAL_HD_TABLE_SIZE; context->hd_table_capacity = NGHTTP2_INITIAL_HD_TABLE_SIZE;
context->hd_tablelen = 0; context->hd_tablelen = 0;
if(inflater) {
context->emit_set = malloc(sizeof(nghttp2_hd_entry*)* context->emit_set = malloc(sizeof(nghttp2_hd_entry*)*
NGHTTP2_INITIAL_EMIT_SET_SIZE); NGHTTP2_INITIAL_EMIT_SET_SIZE);
if(context->emit_set == NULL) { if(context->emit_set == NULL) {
@ -183,6 +191,11 @@ static int nghttp2_hd_context_init(nghttp2_hd_context *context,
NGHTTP2_INITIAL_EMIT_SET_SIZE); NGHTTP2_INITIAL_EMIT_SET_SIZE);
context->emit_set_capacity = NGHTTP2_INITIAL_EMIT_SET_SIZE; context->emit_set_capacity = NGHTTP2_INITIAL_EMIT_SET_SIZE;
context->emit_setlen = 0; context->emit_setlen = 0;
} else {
context->emit_set = NULL;
context->emit_set_capacity = 0;
context->emit_setlen = 0;
}
if(side == NGHTTP2_HD_SIDE_CLIENT) { if(side == NGHTTP2_HD_SIDE_CLIENT) {
ini_table = reqhd_table; ini_table = reqhd_table;
@ -214,12 +227,12 @@ 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, side); return nghttp2_hd_context_init(deflater, side, 0);
} }
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, side^1); return nghttp2_hd_context_init(inflater, side^1, 1);
} }
static void nghttp2_hd_context_free(nghttp2_hd_context *context) static void nghttp2_hd_context_free(nghttp2_hd_context *context)
@ -257,7 +270,76 @@ static size_t entry_room(size_t namelen, size_t valuelen)
return NGHTTP2_HD_ENTRY_OVERHEAD + namelen + valuelen; return NGHTTP2_HD_ENTRY_OVERHEAD + namelen + valuelen;
} }
static int add_nva(nghttp2_nva_out *nva_out_ptr,
uint8_t *name, uint16_t namelen,
uint8_t *value, uint16_t valuelen)
{
nghttp2_nv *nv;
if(nva_out_ptr->nvacap == nva_out_ptr->nvlen) {
size_t newcap = nva_out_ptr->nvacap == 0 ? 16 : nva_out_ptr->nvacap * 2;
nghttp2_nv *new_nva = realloc(nva_out_ptr->nva, sizeof(nghttp2_nv)*newcap);
if(new_nva == NULL) {
return NGHTTP2_ERR_NOMEM;
}
nva_out_ptr->nva = new_nva;
nva_out_ptr->nvacap = newcap;
}
nv = &nva_out_ptr->nva[nva_out_ptr->nvlen++];
nv->name = name;
nv->namelen = namelen;
nv->value = value;
nv->valuelen = valuelen;
return 0;
}
static int add_emit_set(nghttp2_hd_context *context, nghttp2_hd_entry *ent)
{
if(context->emit_setlen == context->emit_set_capacity) {
return NGHTTP2_ERR_HEADER_COMP;
}
context->emit_set[context->emit_setlen++] = ent;
++ent->ref;
return 0;
}
static int emit_indexed_header(nghttp2_hd_context *context,
nghttp2_nva_out *nva_out_ptr,
nghttp2_hd_entry *ent)
{
int rv;
rv = add_emit_set(context, ent);
if(rv != 0) {
return rv;
}
ent->flags |= NGHTTP2_HD_FLAG_EMIT;
return add_nva(nva_out_ptr,
ent->nv.name, ent->nv.namelen,
ent->nv.value, ent->nv.valuelen);
}
static int emit_newname_header(nghttp2_hd_context *context,
nghttp2_nva_out *nva_out_ptr,
nghttp2_nv *nv)
{
return add_nva(nva_out_ptr,
nv->name, nv->namelen, nv->value, nv->valuelen);
}
static int emit_indname_header(nghttp2_hd_context *context,
nghttp2_nva_out *nva_out_ptr,
nghttp2_hd_entry *ent,
uint8_t *value, size_t valuelen)
{
int rv;
rv = add_emit_set(context, ent);
if(rv != 0) {
return rv;
}
return add_nva(nva_out_ptr, ent->nv.name, ent->nv.namelen, value, valuelen);
}
static nghttp2_hd_entry* add_hd_table_incremental(nghttp2_hd_context *context, static nghttp2_hd_entry* add_hd_table_incremental(nghttp2_hd_context *context,
nghttp2_nva_out *nva_out_ptr,
nghttp2_nv *nv) nghttp2_nv *nv)
{ {
int rv; int rv;
@ -274,6 +356,14 @@ static nghttp2_hd_entry* add_hd_table_incremental(nghttp2_hd_context *context,
nghttp2_hd_entry *ent = context->hd_table[i]; nghttp2_hd_entry *ent = context->hd_table[i];
context->hd_table_bufsize -= entry_room(ent->nv.namelen, ent->nv.valuelen); context->hd_table_bufsize -= entry_room(ent->nv.namelen, ent->nv.valuelen);
ent->index = NGHTTP2_HD_INVALID_INDEX; ent->index = NGHTTP2_HD_INVALID_INDEX;
if(nva_out_ptr &&
(ent->flags & NGHTTP2_HD_FLAG_REFSET) &&
(ent->flags & NGHTTP2_HD_FLAG_EMIT) == 0) {
rv = emit_indexed_header(context, nva_out_ptr, ent);
if(rv != 0) {
return NULL;
}
}
if(--ent->ref == 0) { if(--ent->ref == 0) {
nghttp2_hd_entry_free(ent); nghttp2_hd_entry_free(ent);
free(ent); free(ent);
@ -304,6 +394,7 @@ static nghttp2_hd_entry* add_hd_table_incremental(nghttp2_hd_context *context,
} }
static nghttp2_hd_entry* add_hd_table_subst(nghttp2_hd_context *context, static nghttp2_hd_entry* add_hd_table_subst(nghttp2_hd_context *context,
nghttp2_nva_out *nva_out_ptr,
nghttp2_nv *nv, size_t subindex) nghttp2_nv *nv, size_t subindex)
{ {
int rv; int rv;
@ -328,6 +419,14 @@ static nghttp2_hd_entry* add_hd_table_subst(nghttp2_hd_context *context,
ent->nv.valuelen); ent->nv.valuelen);
} }
ent->index = NGHTTP2_HD_INVALID_INDEX; ent->index = NGHTTP2_HD_INVALID_INDEX;
if(nva_out_ptr &&
(ent->flags & NGHTTP2_HD_FLAG_REFSET) &&
(ent->flags & NGHTTP2_HD_FLAG_EMIT) == 0) {
rv = emit_indexed_header(context, nva_out_ptr, ent);
if(rv != 0) {
return NULL;
}
}
if(--ent->ref == 0) { if(--ent->ref == 0) {
nghttp2_hd_entry_free(ent); nghttp2_hd_entry_free(ent);
free(ent); free(ent);
@ -353,6 +452,14 @@ static nghttp2_hd_entry* add_hd_table_subst(nghttp2_hd_context *context,
if(k >= 0) { if(k >= 0) {
nghttp2_hd_entry *ent = context->hd_table[k]; nghttp2_hd_entry *ent = context->hd_table[k];
ent->index = NGHTTP2_HD_INVALID_INDEX; ent->index = NGHTTP2_HD_INVALID_INDEX;
if(nva_out_ptr &&
(ent->flags & NGHTTP2_HD_FLAG_REFSET) &&
(ent->flags & NGHTTP2_HD_FLAG_EMIT) == 0) {
rv = emit_indexed_header(context, nva_out_ptr, ent);
if(rv != 0) {
return NULL;
}
}
if(--ent->ref == 0) { if(--ent->ref == 0) {
nghttp2_hd_entry_free(ent); nghttp2_hd_entry_free(ent);
free(ent); free(ent);
@ -372,82 +479,6 @@ static nghttp2_hd_entry* add_hd_table_subst(nghttp2_hd_context *context,
return new_ent; return new_ent;
} }
static int add_nva(nghttp2_nv **nva_ptr, size_t *nvacap_ptr,
ssize_t *nvlen_ptr,
uint8_t *name, uint16_t namelen,
uint8_t *value, uint16_t valuelen)
{
nghttp2_nv *nv;
if((ssize_t)*nvacap_ptr == *nvlen_ptr) {
size_t newcap = *nvacap_ptr == 0 ? 16 : *nvacap_ptr * 2;
nghttp2_nv *new_nva = realloc(*nva_ptr, sizeof(nghttp2_nv)*newcap);
if(new_nva == NULL) {
return NGHTTP2_ERR_NOMEM;
}
*nva_ptr = new_nva;
*nvacap_ptr = newcap;
}
nv = &(*nva_ptr)[(*nvlen_ptr)++];
nv->name = name;
nv->namelen = namelen;
nv->value = value;
nv->valuelen = valuelen;
return 0;
}
static int add_emit_set(nghttp2_hd_context *context, nghttp2_hd_entry *ent)
{
if(context->emit_setlen == context->emit_set_capacity) {
return NGHTTP2_ERR_HEADER_COMP;
}
context->emit_set[context->emit_setlen++] = ent;
++ent->ref;
return 0;
}
static int emit_indexed_header(nghttp2_hd_context *context,
nghttp2_nv **nva_ptr,
size_t *nvacap_ptr,
ssize_t *nvlen_ptr,
nghttp2_hd_entry *ent)
{
int rv;
rv = add_emit_set(context, ent);
if(rv != 0) {
return rv;
}
ent->flags |= NGHTTP2_HD_FLAG_EMIT;
return add_nva(nva_ptr, nvacap_ptr, nvlen_ptr,
ent->nv.name, ent->nv.namelen,
ent->nv.value, ent->nv.valuelen);
}
static int emit_newname_header(nghttp2_hd_context *context,
nghttp2_nv **nva_ptr,
size_t *nvacap_ptr,
ssize_t *nvlen_ptr,
nghttp2_nv *nv)
{
return add_nva(nva_ptr, nvacap_ptr, nvlen_ptr,
nv->name, nv->namelen, nv->value, nv->valuelen);
}
static int emit_indname_header(nghttp2_hd_context *context,
nghttp2_nv **nva_ptr,
size_t *nvacap_ptr,
ssize_t *nvlen_ptr,
nghttp2_hd_entry *ent,
uint8_t *value, size_t valuelen)
{
int rv;
rv = add_emit_set(context, ent);
if(rv != 0) {
return rv;
}
return add_nva(nva_ptr, nvacap_ptr, nvlen_ptr,
ent->nv.name, ent->nv.namelen, value, valuelen);
}
static nghttp2_hd_entry* find_in_hd_table(nghttp2_hd_context *context, static nghttp2_hd_entry* find_in_hd_table(nghttp2_hd_context *context,
nghttp2_nv *nv) nghttp2_nv *nv)
{ {
@ -691,30 +722,88 @@ static int emit_subst_newname_block(uint8_t **buf_ptr, size_t *buflen_ptr,
return 0; return 0;
} }
static int intcompar(const void *lhs, const void *rhs)
{
return *(int*)lhs - *(int*)rhs;
}
ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_context *deflater, ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_context *deflater,
uint8_t **buf_ptr, size_t *buflen_ptr, uint8_t **buf_ptr, size_t *buflen_ptr,
size_t nv_offset, size_t nv_offset,
nghttp2_nv *nv, size_t nvlen) nghttp2_nv *nv, size_t nvlen)
{ {
size_t i, offset; size_t i, j, k, offset;
int rv = 0; int rv = 0;
int common_hd[NGHTTP2_INITIAL_HD_TABLE_SIZE];
size_t common_hdlen = 0;
if(deflater->bad) { if(deflater->bad) {
return NGHTTP2_ERR_HEADER_COMP; return NGHTTP2_ERR_HEADER_COMP;
} }
offset = nv_offset; offset = nv_offset;
for(i = 0; i < nvlen; ++i) { /* First emit indexed repr to remove deleted header fields */
for(i = 0; i < deflater->hd_tablelen; ++i) {
nghttp2_hd_entry *ent = deflater->hd_table[i];
if((ent->flags & NGHTTP2_HD_FLAG_REFSET) == 0) {
continue;
}
for(j = 0; j < nvlen; ++j) {
if(nghttp2_nv_equal(&ent->nv, &nv[j])) {
break;
}
}
if(j < nvlen) {
/* Assuming the encoder does not put duplicated entry in the
header table, this entry is common header field between
previous header set. So it is "implicitly emitted". */
ent->flags |= NGHTTP2_HD_FLAG_IMPLICIT_EMIT;
common_hd[common_hdlen++] = j;
continue;
}
ent->flags ^= NGHTTP2_HD_FLAG_REFSET;
rv = emit_indexed_block(buf_ptr, buflen_ptr, &offset, ent->index);
if(rv < 0) {
goto fail;
}
}
qsort(common_hd, common_hdlen, sizeof(int), intcompar);
for(i = 0, k = 0; i < nvlen; ++i) {
nghttp2_hd_entry *ent; nghttp2_hd_entry *ent;
if(k < common_hdlen) {
/* Skip implicitly emitted name/value pair */
if((uint8_t)common_hd[k] == i) {
++k;
continue;
}
}
ent = find_in_hd_table(deflater, &nv[i]); ent = find_in_hd_table(deflater, &nv[i]);
if(ent) { if(ent) {
if((ent->flags & NGHTTP2_HD_FLAG_REFSET) == 0) { if((ent->flags & NGHTTP2_HD_FLAG_REFSET) == 0) {
ent->flags |= NGHTTP2_HD_FLAG_REFSET | NGHTTP2_HD_FLAG_EMIT; ent->flags |= NGHTTP2_HD_FLAG_REFSET | NGHTTP2_HD_FLAG_EMIT;
rv = emit_indexed_block(buf_ptr, buflen_ptr, &offset, ent->index); rv = emit_indexed_block(buf_ptr, buflen_ptr, &offset, ent->index);
} else { } else {
/* The common header in reference set could be removed on size_t num_emits = 0;
eviction. In that case, we have to add it again. The bad if(ent->flags & NGHTTP2_HD_FLAG_EMIT) {
thing is that we could not know it happens here. So defer /* occurrences of the same indexed representation. Emit index
its processing after all headers are processed. */ twice. */
rv = add_emit_set(deflater, ent); num_emits = 2;
} else if(ent->flags & NGHTTP2_HD_FLAG_IMPLICIT_EMIT) {
/* ent was implicitly emitted because it is the common
header field. To support occurrences of the same indexed
representation, we have to emit 4 times. This is because
"implicitly emitted" means actually not emitted at
all. So first 2 emits performs 1st header appears in the
reference set. And another 2 emits are done for 2nd
(current) header. */
ent->flags ^= NGHTTP2_HD_FLAG_IMPLICIT_EMIT;
ent->flags |= NGHTTP2_HD_FLAG_EMIT;
num_emits = 4;
}
for(j = 0; j < num_emits; ++j) {
rv = emit_indexed_block(buf_ptr, buflen_ptr, &offset, ent->index);
if(rv != 0) {
break;
}
}
} }
if(rv < 0) { if(rv < 0) {
goto fail; goto fail;
@ -729,7 +818,7 @@ ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_context *deflater,
if(entry_room(nv[i].namelen, nv[i].valuelen) if(entry_room(nv[i].namelen, nv[i].valuelen)
< NGHTTP2_HD_MAX_ENTRY_SIZE) { < NGHTTP2_HD_MAX_ENTRY_SIZE) {
nghttp2_hd_entry *new_ent; nghttp2_hd_entry *new_ent;
new_ent = add_hd_table_incremental(deflater, &nv[i]); new_ent = add_hd_table_incremental(deflater, NULL, &nv[i]);
if(!new_ent) { if(!new_ent) {
rv = NGHTTP2_ERR_HEADER_COMP; rv = NGHTTP2_ERR_HEADER_COMP;
goto fail; goto fail;
@ -749,47 +838,9 @@ ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_context *deflater,
} }
} }
} }
for(i = 0; i < deflater->emit_setlen; ++i) {
nghttp2_hd_entry *ent = deflater->emit_set[i];
if((ent->flags & NGHTTP2_HD_FLAG_EMIT) == 0 &&
ent->index == NGHTTP2_HD_INVALID_INDEX) {
/* If common header is removed from the header table, use
incremental indexing. */
uint8_t index = NGHTTP2_HD_INVALID_INDEX;
nghttp2_hd_entry *new_ent;
nghttp2_hd_entry *name_ent = find_name_in_hd_table(deflater, &ent->nv);
if(name_ent) {
index = name_ent->index;
}
new_ent = add_hd_table_incremental(deflater, &ent->nv);
if(!new_ent) {
rv = NGHTTP2_ERR_HEADER_COMP;
goto fail;
}
new_ent->flags |= NGHTTP2_HD_FLAG_EMIT;
if(index == NGHTTP2_HD_INVALID_INDEX) {
rv = emit_newname_block(buf_ptr, buflen_ptr, &offset, &ent->nv, 1);
} else {
rv = emit_indname_block(buf_ptr, buflen_ptr, &offset, index,
ent->nv.value, ent->nv.valuelen, 1);
}
}
ent->flags |= NGHTTP2_HD_FLAG_EMIT;
}
for(i = 0; i < deflater->hd_tablelen; ++i) { for(i = 0; i < deflater->hd_tablelen; ++i) {
nghttp2_hd_entry *ent = deflater->hd_table[i]; nghttp2_hd_entry *ent = deflater->hd_table[i];
if(ent->flags & NGHTTP2_HD_FLAG_REFSET) { ent->flags &= ~(NGHTTP2_HD_FLAG_EMIT | NGHTTP2_HD_FLAG_IMPLICIT_EMIT);
if((ent->flags & NGHTTP2_HD_FLAG_EMIT) == 0) {
ent->flags ^= NGHTTP2_HD_FLAG_REFSET;
rv = emit_indexed_block(buf_ptr, buflen_ptr, &offset, ent->index);
if(rv < 0) {
goto fail;
}
}
}
ent->flags &= ~NGHTTP2_HD_FLAG_EMIT;
} }
return offset - nv_offset; return offset - nv_offset;
fail: fail:
@ -803,9 +854,9 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_context *inflater,
{ {
size_t i; size_t i;
int rv = 0; int rv = 0;
ssize_t nvlen = 0;
size_t nvacap = 0;
uint8_t *last = in + inlen; uint8_t *last = in + inlen;
nghttp2_nva_out nva_out;
memset(&nva_out, 0, sizeof(nva_out));
if(inflater->bad) { if(inflater->bad) {
return NGHTTP2_ERR_HEADER_COMP; return NGHTTP2_ERR_HEADER_COMP;
} }
@ -828,7 +879,7 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_context *inflater,
ent = inflater->hd_table[index]; ent = inflater->hd_table[index];
ent->flags ^= NGHTTP2_HD_FLAG_REFSET; ent->flags ^= NGHTTP2_HD_FLAG_REFSET;
if(ent->flags & NGHTTP2_HD_FLAG_REFSET) { if(ent->flags & NGHTTP2_HD_FLAG_REFSET) {
rv = emit_indexed_header(inflater, nva_ptr, &nvacap, &nvlen, ent); rv = emit_indexed_header(inflater, &nva_out, ent);
if(rv < 0) { if(rv < 0) {
goto fail; goto fail;
} }
@ -860,12 +911,12 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_context *inflater,
in += valuelen; in += valuelen;
nghttp2_downcase(nv.name, nv.namelen); nghttp2_downcase(nv.name, nv.namelen);
if(c == 0x60u) { if(c == 0x60u) {
rv = emit_newname_header(inflater, nva_ptr, &nvacap, &nvlen, &nv); rv = emit_newname_header(inflater, &nva_out, &nv);
} else { } else {
nghttp2_hd_entry *new_ent = add_hd_table_incremental(inflater, &nv); nghttp2_hd_entry *new_ent;
new_ent = add_hd_table_incremental(inflater, &nva_out, &nv);
if(new_ent) { if(new_ent) {
rv = emit_indexed_header(inflater, nva_ptr, &nvacap, &nvlen, rv = emit_indexed_header(inflater, &nva_out, new_ent);
new_ent);
} else { } else {
rv = NGHTTP2_ERR_HEADER_COMP; rv = NGHTTP2_ERR_HEADER_COMP;
goto fail; goto fail;
@ -900,8 +951,7 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_context *inflater,
value = in; value = in;
in += valuelen; in += valuelen;
if((c & 0x60u) == 0x60u) { if((c & 0x60u) == 0x60u) {
rv = emit_indname_header(inflater, nva_ptr, &nvacap, &nvlen, ent, rv = emit_indname_header(inflater, &nva_out, ent, value, valuelen);
value, valuelen);
} else { } else {
nghttp2_nv nv; nghttp2_nv nv;
nghttp2_hd_entry *new_ent; nghttp2_hd_entry *new_ent;
@ -910,14 +960,13 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_context *inflater,
nv.namelen = ent->nv.namelen; nv.namelen = ent->nv.namelen;
nv.value = value; nv.value = value;
nv.valuelen = valuelen; nv.valuelen = valuelen;
new_ent = add_hd_table_incremental(inflater, &nv); new_ent = add_hd_table_incremental(inflater, &nva_out, &nv);
if(--ent->ref == 0) { if(--ent->ref == 0) {
nghttp2_hd_entry_free(ent); nghttp2_hd_entry_free(ent);
free(ent); free(ent);
} }
if(new_ent) { if(new_ent) {
rv = emit_indexed_header(inflater, nva_ptr, &nvacap, &nvlen, rv = emit_indexed_header(inflater, &nva_out, new_ent);
new_ent);
} else { } else {
rv = NGHTTP2_ERR_HEADER_COMP; rv = NGHTTP2_ERR_HEADER_COMP;
goto fail; goto fail;
@ -957,9 +1006,9 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_context *inflater,
nv.valuelen = valuelen; nv.valuelen = valuelen;
in += valuelen; in += valuelen;
nghttp2_downcase(nv.name, nv.namelen); nghttp2_downcase(nv.name, nv.namelen);
new_ent = add_hd_table_subst(inflater, &nv, subindex); new_ent = add_hd_table_subst(inflater, &nva_out, &nv, subindex);
if(new_ent) { if(new_ent) {
rv = emit_indexed_header(inflater, nva_ptr, &nvacap, &nvlen, new_ent); rv = emit_indexed_header(inflater, &nva_out, new_ent);
if(rv < 0) { if(rv < 0) {
goto fail; goto fail;
} }
@ -1000,13 +1049,13 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_context *inflater,
nv.value = in; nv.value = in;
nv.valuelen = valuelen; nv.valuelen = valuelen;
in += valuelen; in += valuelen;
new_ent = add_hd_table_subst(inflater, &nv, subindex); new_ent = add_hd_table_subst(inflater, &nva_out, &nv, subindex);
if(--ent->ref == 0) { if(--ent->ref == 0) {
nghttp2_hd_entry_free(ent); nghttp2_hd_entry_free(ent);
free(ent); free(ent);
} }
if(new_ent) { if(new_ent) {
rv = emit_indexed_header(inflater, nva_ptr, &nvacap, &nvlen, new_ent); rv = emit_indexed_header(inflater, &nva_out, new_ent);
if(rv < 0) { if(rv < 0) {
goto fail; goto fail;
} }
@ -1020,17 +1069,19 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_context *inflater,
nghttp2_hd_entry *ent = inflater->hd_table[i]; nghttp2_hd_entry *ent = inflater->hd_table[i];
if((ent->flags & NGHTTP2_HD_FLAG_REFSET) && if((ent->flags & NGHTTP2_HD_FLAG_REFSET) &&
(ent->flags & NGHTTP2_HD_FLAG_EMIT) == 0) { (ent->flags & NGHTTP2_HD_FLAG_EMIT) == 0) {
rv = emit_indexed_header(inflater, nva_ptr, &nvacap, &nvlen, ent); rv = emit_indexed_header(inflater, &nva_out, ent);
if(rv < 0) { if(rv < 0) {
goto fail; goto fail;
} }
} }
ent->flags &= ~NGHTTP2_HD_FLAG_EMIT; ent->flags &= ~NGHTTP2_HD_FLAG_EMIT;
} }
nghttp2_nv_array_sort(*nva_ptr, nvlen); nghttp2_nv_array_sort(nva_out.nva, nva_out.nvlen);
return nvlen; *nva_ptr = nva_out.nva;
return nva_out.nvlen;
fail: fail:
inflater->bad = 1; inflater->bad = 1;
free(nva_out.nva);
return rv; return rv;
} }

View File

@ -58,6 +58,7 @@ typedef enum {
/* Indicates that the entry is emitted in the current header /* Indicates that the entry is emitted in the current header
processing. */ processing. */
NGHTTP2_HD_FLAG_EMIT = 1 << 3, NGHTTP2_HD_FLAG_EMIT = 1 << 3,
NGHTTP2_HD_FLAG_IMPLICIT_EMIT = 1 << 4
} nghttp2_hd_flags; } nghttp2_hd_flags;
typedef struct { typedef struct {

View File

@ -220,6 +220,8 @@ int main(int argc, char* argv[])
test_nghttp2_nv_array_from_cstr) || test_nghttp2_nv_array_from_cstr) ||
!CU_add_test(pSuite, "iv_check", test_nghttp2_iv_check) || !CU_add_test(pSuite, "iv_check", test_nghttp2_iv_check) ||
!CU_add_test(pSuite, "hd_deflate", test_nghttp2_hd_deflate) || !CU_add_test(pSuite, "hd_deflate", test_nghttp2_hd_deflate) ||
!CU_add_test(pSuite, "hd_deflate_same_indexed_repr",
test_nghttp2_hd_deflate_same_indexed_repr) ||
!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",

View File

@ -111,17 +111,16 @@ void test_nghttp2_hd_deflate(void)
nghttp2_nv_array_del(resnva); nghttp2_nv_array_del(resnva);
nghttp2_hd_end_headers(&inflater); nghttp2_hd_end_headers(&inflater);
/* Fourth headers, including duplicate header fields. We don't /* Fourth headers, including duplicate header fields. */
encode duplicates. Only first one is encoded. */
blocklen = nghttp2_hd_deflate_hd(&deflater, &buf, &buflen, nv_offset, nva4, blocklen = nghttp2_hd_deflate_hd(&deflater, &buf, &buflen, nv_offset, nva4,
sizeof(nva4)/sizeof(nghttp2_nv)); sizeof(nva4)/sizeof(nghttp2_nv));
CU_ASSERT(blocklen > 0); CU_ASSERT(blocklen > 0);
nghttp2_hd_end_headers(&deflater); nghttp2_hd_end_headers(&deflater);
CU_ASSERT(2 == nghttp2_hd_inflate_hd(&inflater, &resnva, buf + nv_offset, CU_ASSERT(3 == nghttp2_hd_inflate_hd(&inflater, &resnva, buf + nv_offset,
blocklen)); blocklen));
assert_nv_equal(nva4, resnva, 2); assert_nv_equal(nva4, resnva, 3);
nghttp2_nv_array_del(resnva); nghttp2_nv_array_del(resnva);
nghttp2_hd_end_headers(&inflater); nghttp2_hd_end_headers(&inflater);
@ -146,6 +145,56 @@ void test_nghttp2_hd_deflate(void)
nghttp2_hd_deflate_free(&deflater); nghttp2_hd_deflate_free(&deflater);
} }
void test_nghttp2_hd_deflate_same_indexed_repr(void)
{
nghttp2_hd_context deflater, inflater;
nghttp2_nv nva1[] = {MAKE_NV("cookie", "alpha"),
MAKE_NV("cookie", "alpha")};
nghttp2_nv nva2[] = {MAKE_NV("cookie", "alpha"),
MAKE_NV("cookie", "alpha"),
MAKE_NV("cookie", "alpha")};
uint8_t *buf = NULL;
size_t buflen = 0;
nghttp2_nv *resnva;
ssize_t blocklen;
CU_ASSERT(0 == nghttp2_hd_deflate_init(&deflater, NGHTTP2_HD_SIDE_CLIENT));
CU_ASSERT(0 == nghttp2_hd_inflate_init(&inflater, NGHTTP2_HD_SIDE_SERVER));
/* Encode 2 same headers. cookie:alpha is not in the reference set,
so first emit literal repr and then 2 emits of indexed repr. */
blocklen = nghttp2_hd_deflate_hd(&deflater, &buf, &buflen, 0, nva1,
sizeof(nva1)/sizeof(nghttp2_nv));
CU_ASSERT(blocklen > 0);
nghttp2_hd_end_headers(&deflater);
CU_ASSERT(2 == nghttp2_hd_inflate_hd(&inflater, &resnva, buf, blocklen));
assert_nv_equal(nva1, resnva, 2);
nghttp2_nv_array_del(resnva);
nghttp2_hd_end_headers(&inflater);
/* Encode 3 same headers. This time, cookie:alpha is in the
reference set, so the encoder emits indexed repr 6 times */
blocklen = nghttp2_hd_deflate_hd(&deflater, &buf, &buflen, 0, nva1,
sizeof(nva2)/sizeof(nghttp2_nv));
CU_ASSERT(blocklen == 6);
nghttp2_hd_end_headers(&deflater);
CU_ASSERT(3 == nghttp2_hd_inflate_hd(&inflater, &resnva, buf, blocklen));
assert_nv_equal(nva2, resnva, 3);
nghttp2_nv_array_del(resnva);
nghttp2_hd_end_headers(&inflater);
/* Cleanup */
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;

View File

@ -26,6 +26,7 @@
#define NGHTTP2_HD_TEST_H #define NGHTTP2_HD_TEST_H
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_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);