Rework header compression
This commit is contained in:
parent
c4f6e069ac
commit
005e60a5ed
341
lib/nghttp2_hd.c
341
lib/nghttp2_hd.c
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue