Revise common header processing
Instead of emitting common headers on decoder side, encoder now keeps track of removed common headers and encode it as necessary.
This commit is contained in:
parent
2d41c99289
commit
091f38a99d
214
lib/nghttp2_hd.c
214
lib/nghttp2_hd.c
|
@ -164,11 +164,12 @@ 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_role role,
|
||||||
uint8_t inflater)
|
nghttp2_hd_side side)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
const char **ini_table;
|
const char **ini_table;
|
||||||
|
context->role = role;
|
||||||
context->bad = 0;
|
context->bad = 0;
|
||||||
context->hd_table = malloc(sizeof(nghttp2_hd_entry*)*
|
context->hd_table = malloc(sizeof(nghttp2_hd_entry*)*
|
||||||
NGHTTP2_INITIAL_HD_TABLE_SIZE);
|
NGHTTP2_INITIAL_HD_TABLE_SIZE);
|
||||||
|
@ -180,22 +181,16 @@ 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) {
|
free(context->hd_table);
|
||||||
free(context->hd_table);
|
return NGHTTP2_ERR_NOMEM;
|
||||||
return NGHTTP2_ERR_NOMEM;
|
|
||||||
}
|
|
||||||
memset(context->emit_set, 0, sizeof(nghttp2_hd_entry*)*
|
|
||||||
NGHTTP2_INITIAL_EMIT_SET_SIZE);
|
|
||||||
context->emit_set_capacity = NGHTTP2_INITIAL_EMIT_SET_SIZE;
|
|
||||||
context->emit_setlen = 0;
|
|
||||||
} else {
|
|
||||||
context->emit_set = NULL;
|
|
||||||
context->emit_set_capacity = 0;
|
|
||||||
context->emit_setlen = 0;
|
|
||||||
}
|
}
|
||||||
|
memset(context->emit_set, 0, sizeof(nghttp2_hd_entry*)*
|
||||||
|
NGHTTP2_INITIAL_EMIT_SET_SIZE);
|
||||||
|
context->emit_set_capacity = NGHTTP2_INITIAL_EMIT_SET_SIZE;
|
||||||
|
context->emit_setlen = 0;
|
||||||
|
|
||||||
if(side == NGHTTP2_HD_SIDE_CLIENT) {
|
if(side == NGHTTP2_HD_SIDE_CLIENT) {
|
||||||
ini_table = reqhd_table;
|
ini_table = reqhd_table;
|
||||||
|
@ -227,12 +222,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, 0);
|
return nghttp2_hd_context_init(deflater, NGHTTP2_HD_ROLE_DEFLATE, side);
|
||||||
}
|
}
|
||||||
|
|
||||||
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, 1);
|
return nghttp2_hd_context_init(inflater, NGHTTP2_HD_ROLE_INFLATE, side^1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nghttp2_hd_context_free(nghttp2_hd_context *context)
|
static void nghttp2_hd_context_free(nghttp2_hd_context *context)
|
||||||
|
@ -339,7 +334,6 @@ static int emit_indname_header(nghttp2_hd_context *context,
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
|
@ -356,10 +350,11 @@ 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 &&
|
if(context->role == NGHTTP2_HD_ROLE_DEFLATE &&
|
||||||
(ent->flags & NGHTTP2_HD_FLAG_REFSET) &&
|
(ent->flags & NGHTTP2_HD_FLAG_IMPLICIT_EMIT)) {
|
||||||
(ent->flags & NGHTTP2_HD_FLAG_EMIT) == 0) {
|
/* We will emit this entry later not to lose the header
|
||||||
rv = emit_indexed_header(context, nva_out_ptr, ent);
|
field. */
|
||||||
|
rv = add_emit_set(context, ent);
|
||||||
if(rv != 0) {
|
if(rv != 0) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -394,7 +389,6 @@ 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;
|
||||||
|
@ -419,10 +413,11 @@ 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 &&
|
if(context->role == NGHTTP2_HD_ROLE_DEFLATE &&
|
||||||
(ent->flags & NGHTTP2_HD_FLAG_REFSET) &&
|
(ent->flags & NGHTTP2_HD_FLAG_IMPLICIT_EMIT)) {
|
||||||
(ent->flags & NGHTTP2_HD_FLAG_EMIT) == 0) {
|
/* We will emit this entry later not to lose the header
|
||||||
rv = emit_indexed_header(context, nva_out_ptr, ent);
|
field. */
|
||||||
|
rv = add_emit_set(context, ent);
|
||||||
if(rv != 0) {
|
if(rv != 0) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -452,10 +447,11 @@ 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 &&
|
if(context->role == NGHTTP2_HD_ROLE_DEFLATE &&
|
||||||
(ent->flags & NGHTTP2_HD_FLAG_REFSET) &&
|
(ent->flags & NGHTTP2_HD_FLAG_IMPLICIT_EMIT)) {
|
||||||
(ent->flags & NGHTTP2_HD_FLAG_EMIT) == 0) {
|
/* We will emit this entry later not to lose the header
|
||||||
rv = emit_indexed_header(context, nva_out_ptr, ent);
|
field. */
|
||||||
|
rv = add_emit_set(context, ent);
|
||||||
if(rv != 0) {
|
if(rv != 0) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -727,6 +723,76 @@ static int intcompar(const void *lhs, const void *rhs)
|
||||||
return *(int*)lhs - *(int*)rhs;
|
return *(int*)lhs - *(int*)rhs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int deflate_nv(nghttp2_hd_context *deflater,
|
||||||
|
uint8_t **buf_ptr, size_t *buflen_ptr,
|
||||||
|
size_t *offset_ptr,
|
||||||
|
nghttp2_nv *nv)
|
||||||
|
{
|
||||||
|
int rv;
|
||||||
|
nghttp2_hd_entry *ent;
|
||||||
|
ent = find_in_hd_table(deflater, nv);
|
||||||
|
if(ent) {
|
||||||
|
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);
|
||||||
|
if(rv != 0) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
int num_emits = 0;
|
||||||
|
if(ent->flags & NGHTTP2_HD_FLAG_EMIT) {
|
||||||
|
/* occurrences of the same indexed representation. Emit index
|
||||||
|
twice. */
|
||||||
|
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(; num_emits > 0; --num_emits) {
|
||||||
|
rv = emit_indexed_block(buf_ptr, buflen_ptr, offset_ptr, ent->index);
|
||||||
|
if(rv != 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
uint8_t index = NGHTTP2_HD_INVALID_INDEX;
|
||||||
|
int incidx = 0;
|
||||||
|
ent = find_name_in_hd_table(deflater, nv);
|
||||||
|
if(ent) {
|
||||||
|
index = ent->index;
|
||||||
|
}
|
||||||
|
if(entry_room(nv->namelen, nv->valuelen)
|
||||||
|
< NGHTTP2_HD_MAX_ENTRY_SIZE) {
|
||||||
|
nghttp2_hd_entry *new_ent;
|
||||||
|
new_ent = add_hd_table_incremental(deflater, nv);
|
||||||
|
if(!new_ent) {
|
||||||
|
return NGHTTP2_ERR_HEADER_COMP;
|
||||||
|
}
|
||||||
|
new_ent->flags |= NGHTTP2_HD_FLAG_EMIT;
|
||||||
|
incidx = 1;
|
||||||
|
}
|
||||||
|
if(index == NGHTTP2_HD_INVALID_INDEX) {
|
||||||
|
rv = emit_newname_block(buf_ptr, buflen_ptr, offset_ptr, nv, incidx);
|
||||||
|
} else {
|
||||||
|
rv = emit_indname_block(buf_ptr, buflen_ptr, offset_ptr, index,
|
||||||
|
nv->value, nv->valuelen, incidx);
|
||||||
|
}
|
||||||
|
if(rv != 0) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
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,
|
||||||
|
@ -767,7 +833,6 @@ ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_context *deflater,
|
||||||
}
|
}
|
||||||
qsort(common_hd, common_hdlen, sizeof(int), intcompar);
|
qsort(common_hd, common_hdlen, sizeof(int), intcompar);
|
||||||
for(i = 0, k = 0; i < nvlen; ++i) {
|
for(i = 0, k = 0; i < nvlen; ++i) {
|
||||||
nghttp2_hd_entry *ent;
|
|
||||||
if(k < common_hdlen) {
|
if(k < common_hdlen) {
|
||||||
/* Skip implicitly emitted name/value pair */
|
/* Skip implicitly emitted name/value pair */
|
||||||
if((uint8_t)common_hd[k] == i) {
|
if((uint8_t)common_hd[k] == i) {
|
||||||
|
@ -775,67 +840,18 @@ ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_context *deflater,
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ent = find_in_hd_table(deflater, &nv[i]);
|
rv = deflate_nv(deflater, buf_ptr, buflen_ptr, &offset, &nv[i]);
|
||||||
if(ent) {
|
if(rv != 0) {
|
||||||
if((ent->flags & NGHTTP2_HD_FLAG_REFSET) == 0) {
|
goto fail;
|
||||||
ent->flags |= NGHTTP2_HD_FLAG_REFSET | NGHTTP2_HD_FLAG_EMIT;
|
}
|
||||||
rv = emit_indexed_block(buf_ptr, buflen_ptr, &offset, ent->index);
|
}
|
||||||
} else {
|
/* Encode the lost common headers. Keep in mind that
|
||||||
size_t num_emits = 0;
|
deflater->emit_setlen may be increased by deflate_nv(). */
|
||||||
if(ent->flags & NGHTTP2_HD_FLAG_EMIT) {
|
for(i = 0; i < deflater->emit_setlen; ++i) {
|
||||||
/* occurrences of the same indexed representation. Emit index
|
nghttp2_hd_entry *ent = deflater->emit_set[i];
|
||||||
twice. */
|
rv = deflate_nv(deflater, buf_ptr, buflen_ptr, &offset, &ent->nv);
|
||||||
num_emits = 2;
|
if(rv != 0) {
|
||||||
} else if(ent->flags & NGHTTP2_HD_FLAG_IMPLICIT_EMIT) {
|
goto fail;
|
||||||
/* 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) {
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
uint8_t index = NGHTTP2_HD_INVALID_INDEX;
|
|
||||||
int incidx = 0;
|
|
||||||
ent = find_name_in_hd_table(deflater, &nv[i]);
|
|
||||||
if(ent) {
|
|
||||||
index = ent->index;
|
|
||||||
}
|
|
||||||
if(entry_room(nv[i].namelen, nv[i].valuelen)
|
|
||||||
< NGHTTP2_HD_MAX_ENTRY_SIZE) {
|
|
||||||
nghttp2_hd_entry *new_ent;
|
|
||||||
new_ent = add_hd_table_incremental(deflater, NULL, &nv[i]);
|
|
||||||
if(!new_ent) {
|
|
||||||
rv = NGHTTP2_ERR_HEADER_COMP;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
new_ent->flags |= NGHTTP2_HD_FLAG_EMIT;
|
|
||||||
incidx = 1;
|
|
||||||
}
|
|
||||||
if(index == NGHTTP2_HD_INVALID_INDEX) {
|
|
||||||
rv = emit_newname_block(buf_ptr, buflen_ptr, &offset, &nv[i],
|
|
||||||
incidx);
|
|
||||||
} else {
|
|
||||||
rv = emit_indname_block(buf_ptr, buflen_ptr, &offset, index,
|
|
||||||
nv[i].value, nv[i].valuelen, incidx);
|
|
||||||
}
|
|
||||||
if(rv < 0) {
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for(i = 0; i < deflater->hd_tablelen; ++i) {
|
for(i = 0; i < deflater->hd_tablelen; ++i) {
|
||||||
|
@ -914,7 +930,7 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_context *inflater,
|
||||||
rv = emit_newname_header(inflater, &nva_out, &nv);
|
rv = emit_newname_header(inflater, &nva_out, &nv);
|
||||||
} else {
|
} else {
|
||||||
nghttp2_hd_entry *new_ent;
|
nghttp2_hd_entry *new_ent;
|
||||||
new_ent = add_hd_table_incremental(inflater, &nva_out, &nv);
|
new_ent = add_hd_table_incremental(inflater, &nv);
|
||||||
if(new_ent) {
|
if(new_ent) {
|
||||||
rv = emit_indexed_header(inflater, &nva_out, new_ent);
|
rv = emit_indexed_header(inflater, &nva_out, new_ent);
|
||||||
} else {
|
} else {
|
||||||
|
@ -960,7 +976,7 @@ 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, &nva_out, &nv);
|
new_ent = add_hd_table_incremental(inflater, &nv);
|
||||||
if(--ent->ref == 0) {
|
if(--ent->ref == 0) {
|
||||||
nghttp2_hd_entry_free(ent);
|
nghttp2_hd_entry_free(ent);
|
||||||
free(ent);
|
free(ent);
|
||||||
|
@ -1006,7 +1022,7 @@ 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, &nva_out, &nv, subindex);
|
new_ent = add_hd_table_subst(inflater, &nv, subindex);
|
||||||
if(new_ent) {
|
if(new_ent) {
|
||||||
rv = emit_indexed_header(inflater, &nva_out, new_ent);
|
rv = emit_indexed_header(inflater, &nva_out, new_ent);
|
||||||
if(rv < 0) {
|
if(rv < 0) {
|
||||||
|
@ -1049,7 +1065,7 @@ 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, &nva_out, &nv, subindex);
|
new_ent = add_hd_table_subst(inflater, &nv, subindex);
|
||||||
if(--ent->ref == 0) {
|
if(--ent->ref == 0) {
|
||||||
nghttp2_hd_entry_free(ent);
|
nghttp2_hd_entry_free(ent);
|
||||||
free(ent);
|
free(ent);
|
||||||
|
|
|
@ -47,6 +47,11 @@ typedef enum {
|
||||||
NGHTTP2_HD_SIDE_SERVER = 1
|
NGHTTP2_HD_SIDE_SERVER = 1
|
||||||
} nghttp2_hd_side;
|
} nghttp2_hd_side;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
NGHTTP2_HD_ROLE_DEFLATE,
|
||||||
|
NGHTTP2_HD_ROLE_INFLATE
|
||||||
|
} nghttp2_hd_role;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
NGHTTP2_HD_FLAG_NONE = 0,
|
NGHTTP2_HD_FLAG_NONE = 0,
|
||||||
/* Indicates name was dynamically allocated and must be freed */
|
/* Indicates name was dynamically allocated and must be freed */
|
||||||
|
@ -92,6 +97,8 @@ typedef struct {
|
||||||
further invocation of inflate/deflate will fail with
|
further invocation of inflate/deflate will fail with
|
||||||
NGHTTP2_ERR_HEADER_COMP. */
|
NGHTTP2_ERR_HEADER_COMP. */
|
||||||
uint8_t bad;
|
uint8_t bad;
|
||||||
|
/* Role of this context; deflate or infalte */
|
||||||
|
nghttp2_hd_role role;
|
||||||
} nghttp2_hd_context;
|
} nghttp2_hd_context;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
Loading…
Reference in New Issue