Reference counted HPACK name/value pair

This commit is contained in:
Tatsuhiro Tsujikawa 2016-03-12 13:23:12 +09:00
parent 8da20975f9
commit ff0d137fb3
13 changed files with 557 additions and 430 deletions

View File

@ -162,7 +162,7 @@ def gen_enum():
def gen_index_header():
print '''\
static inline int lookup_token(const uint8_t *name, size_t namelen) {
static inline int32_t lookup_token(const uint8_t *name, size_t namelen) {
switch (namelen) {'''
b = build_header(HEADERS)
for size in sorted(b.keys()):

View File

@ -47,7 +47,8 @@ OBJECTS = nghttp2_pq.c nghttp2_map.c nghttp2_queue.c \
nghttp2_option.c \
nghttp2_callbacks.c \
nghttp2_mem.c \
nghttp2_http.c
nghttp2_http.c \
nghttp2_rcbuf.c
HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \
nghttp2_frame.h \
@ -61,7 +62,8 @@ HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \
nghttp2_option.h \
nghttp2_callbacks.h \
nghttp2_mem.h \
nghttp2_http.h
nghttp2_http.h \
nghttp2_rcbuf.h
libnghttp2_la_SOURCES = $(HFILES) $(OBJECTS)
libnghttp2_la_LDFLAGS = -no-undefined \

View File

@ -419,6 +419,53 @@ typedef enum {
NGHTTP2_ERR_FLOODED = -904
} nghttp2_error;
/**
* @struct
*
* The object representing single contagious buffer.
*/
typedef struct {
/**
* The pointer to the buffer.
*/
uint8_t *base;
/**
* The length of the buffer.
*/
size_t len;
} nghttp2_vec;
struct nghttp2_rcbuf;
/**
* @struct
*
* The object representing reference counted buffer. The details of
* this structure are intentionally hidden from the public API.
*/
typedef struct nghttp2_rcbuf nghttp2_rcbuf;
/**
* @function
*
* Increments the reference count of |rcbuf| by 1.
*/
void nghttp2_rcbuf_incref(nghttp2_rcbuf *rcbuf);
/**
* @function
*
* Decrements the reference count of |rcbuf| by 1. If the reference
* count becomes zero, the object pointed by |rcbuf| will be freed.
* In this case, application must not use |rcbuf| again.
*/
void nghttp2_rcbuf_decref(nghttp2_rcbuf *rcbuf);
/**
* Returns the underlying buffer managed by |rcbuf|.
*/
nghttp2_vec nghttp2_rcbuf_get_buf(nghttp2_rcbuf *rcbuf);
/**
* @enum
*

View File

@ -34,15 +34,17 @@
/* Make scalar initialization form of nghttp2_hd_entry */
#define MAKE_STATIC_ENT(N, V, T, H) \
{ \
{ (uint8_t *)(N), (uint8_t *)(V), sizeof((N)) - 1, sizeof((V)) - 1, 0 } \
, NULL, 0, (H), (T), 1, NGHTTP2_HD_FLAG_NONE \
{ NULL, NULL, (uint8_t *)(N), sizeof((N)) - 1, -1 } \
, {NULL, NULL, (uint8_t *)(V), sizeof((V)) - 1, -1}, \
{(uint8_t *)(N), (uint8_t *)(V), sizeof((N)) - 1, sizeof((V)) - 1, 0}, \
T, H \
}
/* Generated by mkstatictbl.py */
/* 3rd parameter is nghttp2_token value for header field name. We use
first enum value if same header names are repeated (e.g.,
:status). */
static nghttp2_hd_entry static_table[] = {
static nghttp2_hd_static_entry static_table[] = {
MAKE_STATIC_ENT(":authority", "", 0, 3153725150u),
MAKE_STATIC_ENT(":method", "GET", 1, 695666056u),
MAKE_STATIC_ENT(":method", "POST", 1, 695666056u),
@ -114,7 +116,7 @@ static int memeq(const void *s1, const void *s2, size_t n) {
* This function was generated by genlibtokenlookup.py. Inspired by
* h2o header lookup. https://github.com/h2o/h2o
*/
static int lookup_token(const uint8_t *name, size_t namelen) {
static int32_t lookup_token(const uint8_t *name, size_t namelen) {
switch (namelen) {
case 2:
switch (name[1]) {
@ -790,86 +792,33 @@ static int lookup_token(const uint8_t *name, size_t namelen) {
return -1;
}
int nghttp2_hd_entry_init(nghttp2_hd_entry *ent, uint8_t flags, uint8_t *name,
size_t namelen, uint8_t *value, size_t valuelen,
int token, nghttp2_mem *mem) {
int rv = 0;
/* Since nghttp2_hd_entry is used for indexing, ent->nv.flags always
NGHTTP2_NV_FLAG_NONE */
ent->nv.flags = NGHTTP2_NV_FLAG_NONE;
if ((flags & NGHTTP2_HD_FLAG_NAME_ALLOC) &&
(flags & NGHTTP2_HD_FLAG_NAME_GIFT) == 0) {
if (namelen == 0) {
flags = (uint8_t)(flags & ~NGHTTP2_HD_FLAG_NAME_ALLOC);
ent->nv.name = (uint8_t *)"";
} else {
/* name may not be NULL terminated on compression. */
ent->nv.name = nghttp2_mem_malloc(mem, namelen + 1);
if (ent->nv.name == NULL) {
rv = NGHTTP2_ERR_NOMEM;
goto fail;
}
memcpy(ent->nv.name, name, namelen);
ent->nv.name[namelen] = '\0';
}
} else {
ent->nv.name = name;
}
if ((flags & NGHTTP2_HD_FLAG_VALUE_ALLOC) &&
(flags & NGHTTP2_HD_FLAG_VALUE_GIFT) == 0) {
if (valuelen == 0) {
flags = (uint8_t)(flags & ~NGHTTP2_HD_FLAG_VALUE_ALLOC);
ent->nv.value = (uint8_t *)"";
} else {
/* value may not be NULL terminated on compression. */
ent->nv.value = nghttp2_mem_malloc(mem, valuelen + 1);
if (ent->nv.value == NULL) {
rv = NGHTTP2_ERR_NOMEM;
goto fail2;
}
memcpy(ent->nv.value, value, valuelen);
ent->nv.value[valuelen] = '\0';
}
} else {
ent->nv.value = value;
}
ent->nv.namelen = namelen;
ent->nv.valuelen = valuelen;
ent->token = token;
ent->ref = 1;
ent->flags = flags;
void nghttp2_hd_entry_init(nghttp2_hd_entry *ent, nghttp2_hd_nv *nv) {
ent->nv = *nv;
ent->cnv.name = nv->name->base;
ent->cnv.namelen = nv->name->len;
ent->cnv.value = nv->value->base;
ent->cnv.valuelen = nv->value->len;
ent->cnv.flags = nv->flags;
ent->next = NULL;
ent->hash = 0;
return 0;
fail2:
if ((flags & NGHTTP2_HD_FLAG_NAME_ALLOC) &&
(flags & NGHTTP2_HD_FLAG_NAME_GIFT) == 0) {
nghttp2_mem_free(mem, ent->nv.name);
}
fail:
return rv;
nghttp2_rcbuf_incref(ent->nv.name);
nghttp2_rcbuf_incref(ent->nv.value);
}
void nghttp2_hd_entry_free(nghttp2_hd_entry *ent, nghttp2_mem *mem) {
assert(ent->ref == 0);
if (ent->flags & NGHTTP2_HD_FLAG_NAME_ALLOC) {
nghttp2_mem_free(mem, ent->nv.name);
}
if (ent->flags & NGHTTP2_HD_FLAG_VALUE_ALLOC) {
nghttp2_mem_free(mem, ent->nv.value);
}
void nghttp2_hd_entry_free(nghttp2_hd_entry *ent) {
nghttp2_rcbuf_decref(ent->nv.value);
nghttp2_rcbuf_decref(ent->nv.name);
}
static int name_eq(const nghttp2_nv *a, const nghttp2_nv *b) {
return a->namelen == b->namelen && memeq(a->name, b->name, a->namelen);
static int name_eq(const nghttp2_hd_nv *a, const nghttp2_nv *b) {
return a->name->len == b->namelen &&
memeq(a->name->base, b->name, b->namelen);
}
static int value_eq(const nghttp2_nv *a, const nghttp2_nv *b) {
return a->valuelen == b->valuelen && memeq(a->value, b->value, a->valuelen);
static int value_eq(const nghttp2_hd_nv *a, const nghttp2_nv *b) {
return a->value->len == b->valuelen &&
memeq(a->value->base, b->value, b->valuelen);
}
static uint32_t name_hash(const nghttp2_nv *nv) {
@ -905,7 +854,7 @@ static void hd_map_insert(nghttp2_hd_map *map, nghttp2_hd_entry *ent) {
}
static nghttp2_hd_entry *hd_map_find(nghttp2_hd_map *map, int *exact_match,
const nghttp2_nv *nv, int token,
const nghttp2_nv *nv, int32_t token,
uint32_t hash) {
nghttp2_hd_entry *p;
nghttp2_hd_entry *res = NULL;
@ -913,7 +862,7 @@ static nghttp2_hd_entry *hd_map_find(nghttp2_hd_map *map, int *exact_match,
*exact_match = 0;
for (p = map->table[hash & (HD_MAP_SIZE - 1)]; p; p = p->next) {
if (token != p->token ||
if (token != p->nv.token ||
(token == -1 && (hash != p->hash || !name_eq(&p->nv, nv)))) {
continue;
}
@ -1008,8 +957,8 @@ static void hd_ringbuf_free(nghttp2_hd_ringbuf *ringbuf, nghttp2_mem *mem) {
}
for (i = 0; i < ringbuf->len; ++i) {
nghttp2_hd_entry *ent = hd_ringbuf_get(ringbuf, i);
--ent->ref;
nghttp2_hd_entry_free(ent, mem);
nghttp2_hd_entry_free(ent);
nghttp2_mem_free(mem, ent);
}
nghttp2_mem_free(mem, ringbuf->buffer);
@ -1098,7 +1047,6 @@ int nghttp2_hd_inflate_init(nghttp2_hd_inflater *inflater, nghttp2_mem *mem) {
inflater->settings_hd_table_bufsize_max = NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE;
inflater->min_hd_table_bufsize_max = UINT32_MAX;
inflater->ent_keep = NULL;
inflater->nv_name_keep = NULL;
inflater->nv_value_keep = NULL;
@ -1108,6 +1056,9 @@ int nghttp2_hd_inflate_init(nghttp2_hd_inflater *inflater, nghttp2_mem *mem) {
nghttp2_buf_init(&inflater->namebuf);
nghttp2_buf_init(&inflater->valuebuf);
inflater->namercbuf = NULL;
inflater->valuercbuf = NULL;
inflater->huffman_encoded = 0;
inflater->index = 0;
inflater->left = 0;
@ -1122,21 +1073,10 @@ fail:
}
static void hd_inflate_keep_free(nghttp2_hd_inflater *inflater) {
nghttp2_mem *mem;
nghttp2_rcbuf_decref(inflater->nv_value_keep);
nghttp2_rcbuf_decref(inflater->nv_name_keep);
mem = inflater->ctx.mem;
if (inflater->ent_keep) {
if (inflater->ent_keep->ref == 0) {
nghttp2_hd_entry_free(inflater->ent_keep, mem);
nghttp2_mem_free(mem, inflater->ent_keep);
}
inflater->ent_keep = NULL;
}
nghttp2_mem_free(mem, inflater->nv_value_keep);
inflater->nv_value_keep = NULL;
nghttp2_mem_free(mem, inflater->nv_name_keep);
inflater->nv_name_keep = NULL;
}
@ -1145,13 +1085,11 @@ void nghttp2_hd_deflate_free(nghttp2_hd_deflater *deflater) {
}
void nghttp2_hd_inflate_free(nghttp2_hd_inflater *inflater) {
nghttp2_mem *mem;
mem = inflater->ctx.mem;
hd_inflate_keep_free(inflater);
nghttp2_buf_free(&inflater->valuebuf, mem);
nghttp2_buf_free(&inflater->namebuf, mem);
nghttp2_rcbuf_decref(inflater->valuercbuf);
nghttp2_rcbuf_decref(inflater->namercbuf);
hd_context_free(&inflater->ctx);
}
@ -1159,23 +1097,13 @@ static size_t entry_room(size_t namelen, size_t valuelen) {
return NGHTTP2_HD_ENTRY_OVERHEAD + namelen + valuelen;
}
static int emit_indexed_header(nghttp2_nv *nv_out, int *token_out,
nghttp2_hd_entry *ent) {
DEBUGF(fprintf(stderr, "inflatehd: header emission: %s: %s\n", ent->nv.name,
ent->nv.value));
static int emit_header(nghttp2_hd_nv *nv_out, nghttp2_hd_nv *nv) {
DEBUGF(fprintf(stderr, "inflatehd: header emission: %s: %s\n", nv->name->base,
nv->value->base));
/* ent->ref may be 0. This happens if the encoder emits literal
block larger than header table capacity with indexing. */
*nv_out = ent->nv;
*token_out = ent->token;
return 0;
}
static int emit_literal_header(nghttp2_nv *nv_out, int *token_out,
nghttp2_nv *nv) {
DEBUGF(fprintf(stderr, "inflatehd: header emission: %s: %s\n", nv->name,
nv->value));
*nv_out = *nv;
*token_out = lookup_token(nv->name, nv->namelen);
return 0;
}
@ -1483,17 +1411,16 @@ static int emit_newname_block(nghttp2_bufs *bufs, const nghttp2_nv *nv,
return 0;
}
static nghttp2_hd_entry *
add_hd_table_incremental(nghttp2_hd_context *context, const nghttp2_nv *nv,
int token, uint8_t entry_flags, nghttp2_hd_map *map,
uint32_t hash) {
static int add_hd_table_incremental(nghttp2_hd_context *context,
nghttp2_hd_nv *nv, nghttp2_hd_map *map,
uint32_t hash) {
int rv;
nghttp2_hd_entry *new_ent;
size_t room;
nghttp2_mem *mem;
mem = context->mem;
room = entry_room(nv->namelen, nv->valuelen);
room = entry_room(nv->name->len, nv->value->len);
while (context->hd_table_bufsize + room > context->hd_table_bufsize_max &&
context->hd_table.len > 0) {
@ -1501,72 +1428,53 @@ add_hd_table_incremental(nghttp2_hd_context *context, const nghttp2_nv *nv,
size_t idx = context->hd_table.len - 1;
nghttp2_hd_entry *ent = hd_ringbuf_get(&context->hd_table, idx);
context->hd_table_bufsize -= entry_room(ent->nv.namelen, ent->nv.valuelen);
context->hd_table_bufsize -=
entry_room(ent->nv.name->len, ent->nv.value->len);
DEBUGF(fprintf(stderr, "hpack: remove item from header table: %s: %s\n",
ent->nv.name, ent->nv.value));
(char *)ent->nv.name->base, (char *)ent->nv.value->base));
hd_ringbuf_pop_back(&context->hd_table);
if (map) {
hd_map_remove(map, ent);
}
if (--ent->ref == 0) {
nghttp2_hd_entry_free(ent, mem);
nghttp2_mem_free(mem, ent);
}
}
new_ent = nghttp2_mem_malloc(mem, sizeof(nghttp2_hd_entry));
if (new_ent == NULL) {
return NULL;
}
rv = nghttp2_hd_entry_init(new_ent, entry_flags, nv->name, nv->namelen,
nv->value, nv->valuelen, token, mem);
if (rv != 0) {
nghttp2_mem_free(mem, new_ent);
return NULL;
nghttp2_hd_entry_free(ent);
nghttp2_mem_free(mem, ent);
}
if (room > context->hd_table_bufsize_max) {
/* The entry taking more than NGHTTP2_HD_MAX_BUFFER_SIZE is
immediately evicted. */
--new_ent->ref;
} else {
rv = hd_ringbuf_push_front(&context->hd_table, new_ent, mem);
if (rv != 0) {
--new_ent->ref;
if ((entry_flags & NGHTTP2_HD_FLAG_NAME_ALLOC) &&
(entry_flags & NGHTTP2_HD_FLAG_NAME_GIFT)) {
/* nv->name are managed by caller. */
new_ent->nv.name = NULL;
new_ent->nv.namelen = 0;
}
if ((entry_flags & NGHTTP2_HD_FLAG_VALUE_ALLOC) &&
(entry_flags & NGHTTP2_HD_FLAG_VALUE_GIFT)) {
/* nv->value are managed by caller. */
new_ent->nv.value = NULL;
new_ent->nv.valuelen = 0;
}
nghttp2_hd_entry_free(new_ent, mem);
nghttp2_mem_free(mem, new_ent);
return NULL;
}
new_ent->seq = context->next_seq++;
new_ent->hash = hash;
if (map) {
hd_map_insert(map, new_ent);
}
context->hd_table_bufsize += room;
immediately evicted. So we don't allocate memory for it. */
return 0;
}
return new_ent;
new_ent = nghttp2_mem_malloc(mem, sizeof(nghttp2_hd_entry));
if (new_ent == NULL) {
return NGHTTP2_ERR_NOMEM;
}
nghttp2_hd_entry_init(new_ent, nv);
rv = hd_ringbuf_push_front(&context->hd_table, new_ent, mem);
if (rv != 0) {
nghttp2_hd_entry_free(new_ent);
nghttp2_mem_free(mem, new_ent);
return rv;
}
new_ent->seq = context->next_seq++;
new_ent->hash = hash;
if (map) {
hd_map_insert(map, new_ent);
}
context->hd_table_bufsize += room;
return 0;
}
typedef struct {
@ -1575,10 +1483,11 @@ typedef struct {
uint8_t name_value_match;
} search_result;
static search_result search_static_table(const nghttp2_nv *nv, int token,
static search_result search_static_table(const nghttp2_nv *nv, int32_t token,
int indexing_mode) {
search_result res = {token, 0};
int i;
nghttp2_hd_static_entry *ent;
if (indexing_mode == NGHTTP2_HD_NEVER_INDEXING) {
return res;
@ -1587,7 +1496,9 @@ static search_result search_static_table(const nghttp2_nv *nv, int token,
for (i = token;
i <= NGHTTP2_TOKEN_WWW_AUTHENTICATE && static_table[i].token == token;
++i) {
if (value_eq(&static_table[i].nv, nv)) {
ent = &static_table[i];
if (ent->value.len == nv->valuelen &&
memcmp(ent->value.base, nv->value, nv->valuelen) == 0) {
res.index = i;
res.name_value_match = 1;
return res;
@ -1597,7 +1508,7 @@ static search_result search_static_table(const nghttp2_nv *nv, int token,
}
static search_result search_hd_table(nghttp2_hd_context *context,
const nghttp2_nv *nv, int token,
const nghttp2_nv *nv, int32_t token,
int indexing_mode, nghttp2_hd_map *map,
uint32_t hash) {
search_result res = {-1, 0};
@ -1641,15 +1552,15 @@ static void hd_context_shrink_table_size(nghttp2_hd_context *context,
context->hd_table.len > 0) {
size_t idx = context->hd_table.len - 1;
nghttp2_hd_entry *ent = hd_ringbuf_get(&context->hd_table, idx);
context->hd_table_bufsize -= entry_room(ent->nv.namelen, ent->nv.valuelen);
context->hd_table_bufsize -=
entry_room(ent->nv.name->len, ent->nv.value->len);
hd_ringbuf_pop_back(&context->hd_table);
if (map) {
hd_map_remove(map, ent);
}
if (--ent->ref == 0) {
nghttp2_hd_entry_free(ent, mem);
nghttp2_mem_free(mem, ent);
}
nghttp2_hd_entry_free(ent);
nghttp2_mem_free(mem, ent);
}
}
@ -1708,19 +1619,33 @@ static size_t get_max_index(nghttp2_hd_context *context) {
return context->hd_table.len + NGHTTP2_STATIC_TABLE_LENGTH - 1;
}
nghttp2_hd_entry *nghttp2_hd_table_get(nghttp2_hd_context *context,
size_t idx) {
nghttp2_hd_nv nghttp2_hd_table_get(nghttp2_hd_context *context, size_t idx) {
assert(INDEX_RANGE_VALID(context, idx));
if (idx >= NGHTTP2_STATIC_TABLE_LENGTH) {
return hd_ringbuf_get(&context->hd_table,
idx - NGHTTP2_STATIC_TABLE_LENGTH);
return hd_ringbuf_get(&context->hd_table, idx - NGHTTP2_STATIC_TABLE_LENGTH)
->nv;
} else {
return &static_table[idx];
nghttp2_hd_static_entry *ent;
ent = &static_table[idx];
return (nghttp2_hd_nv){&ent->name, &ent->value, ent->token,
NGHTTP2_NV_FLAG_NONE};
}
}
static const nghttp2_nv *nghttp2_hd_table_get2(nghttp2_hd_context *context,
size_t idx) {
assert(INDEX_RANGE_VALID(context, idx));
if (idx >= NGHTTP2_STATIC_TABLE_LENGTH) {
return &hd_ringbuf_get(&context->hd_table,
idx - NGHTTP2_STATIC_TABLE_LENGTH)->cnv;
}
return &static_table[idx].cnv;
}
static int hd_deflate_decide_indexing(nghttp2_hd_deflater *deflater,
const nghttp2_nv *nv, int token) {
const nghttp2_nv *nv, int32_t token) {
if (token == NGHTTP2_TOKEN__PATH || token == NGHTTP2_TOKEN_AGE ||
token == NGHTTP2_TOKEN_CONTENT_LENGTH || token == NGHTTP2_TOKEN_ETAG ||
token == NGHTTP2_TOKEN_IF_MODIFIED_SINCE ||
@ -1740,7 +1665,7 @@ static int deflate_nv(nghttp2_hd_deflater *deflater, nghttp2_bufs *bufs,
search_result res;
ssize_t idx;
int indexing_mode;
int token;
int32_t token;
nghttp2_mem *mem;
uint32_t hash = 0;
@ -1789,28 +1714,36 @@ static int deflate_nv(nghttp2_hd_deflater *deflater, nghttp2_bufs *bufs,
}
if (indexing_mode == NGHTTP2_HD_WITH_INDEXING) {
nghttp2_hd_entry *new_ent;
nghttp2_hd_nv hd_nv;
if (idx != -1 && idx < (ssize_t)NGHTTP2_STATIC_TABLE_LENGTH) {
nghttp2_nv nv_indname;
nv_indname = *nv;
nv_indname.name =
nghttp2_hd_table_get(&deflater->ctx, (size_t)idx)->nv.name;
new_ent = add_hd_table_incremental(&deflater->ctx, &nv_indname, token,
NGHTTP2_HD_FLAG_VALUE_ALLOC,
&deflater->map, hash);
hd_nv.name = nghttp2_hd_table_get(&deflater->ctx, (size_t)idx).name;
nghttp2_rcbuf_incref(hd_nv.name);
} else {
new_ent = add_hd_table_incremental(&deflater->ctx, nv, token,
NGHTTP2_HD_FLAG_NAME_ALLOC |
NGHTTP2_HD_FLAG_VALUE_ALLOC,
&deflater->map, hash);
rv = nghttp2_rcbuf_new2(&hd_nv.name, nv->name, nv->namelen, mem);
if (rv != 0) {
return rv;
}
}
if (!new_ent) {
rv = nghttp2_rcbuf_new2(&hd_nv.value, nv->value, nv->valuelen, mem);
if (rv != 0) {
nghttp2_rcbuf_decref(hd_nv.name);
return rv;
}
hd_nv.token = token;
hd_nv.flags = NGHTTP2_NV_FLAG_NONE;
rv = add_hd_table_incremental(&deflater->ctx, &hd_nv, &deflater->map, hash);
nghttp2_rcbuf_decref(hd_nv.value);
nghttp2_rcbuf_decref(hd_nv.name);
if (rv != 0) {
return NGHTTP2_ERR_HEADER_COMP;
}
if (new_ent->ref == 0) {
nghttp2_hd_entry_free(new_ent, mem);
nghttp2_mem_free(mem, new_ent);
}
}
if (idx == -1) {
rv = emit_newname_block(bufs, nv, indexing_mode);
@ -2093,10 +2026,10 @@ static ssize_t hd_inflate_read(nghttp2_hd_inflater *inflater, nghttp2_buf *buf,
* Out of memory
*/
static int hd_inflate_commit_indexed(nghttp2_hd_inflater *inflater,
nghttp2_nv *nv_out, int *token_out) {
nghttp2_hd_entry *ent = nghttp2_hd_table_get(&inflater->ctx, inflater->index);
nghttp2_hd_nv *nv_out) {
nghttp2_hd_nv nv = nghttp2_hd_table_get(&inflater->ctx, inflater->index);
emit_indexed_header(nv_out, token_out, ent);
emit_header(nv_out, &nv);
return 0;
}
@ -2113,19 +2046,9 @@ static int hd_inflate_commit_indexed(nghttp2_hd_inflater *inflater,
* Out of memory
*/
static int hd_inflate_commit_newname(nghttp2_hd_inflater *inflater,
nghttp2_nv *nv_out, int *token_out) {
nghttp2_nv nv;
nghttp2_mem *mem;
mem = inflater->ctx.mem;
nv.name = inflater->namebuf.pos;
nv.namelen = nghttp2_buf_len(&inflater->namebuf);
nv.value = inflater->valuebuf.pos;
nv.valuelen = nghttp2_buf_len(&inflater->valuebuf);
nghttp2_buf_init(&inflater->valuebuf);
nghttp2_buf_init(&inflater->namebuf);
nghttp2_hd_nv *nv_out) {
nghttp2_hd_nv nv;
int rv;
if (inflater->no_index) {
nv.flags = NGHTTP2_NV_FLAG_NO_INDEX;
@ -2133,35 +2056,26 @@ static int hd_inflate_commit_newname(nghttp2_hd_inflater *inflater,
nv.flags = NGHTTP2_NV_FLAG_NONE;
}
nv.name = inflater->namercbuf;
nv.value = inflater->valuercbuf;
nv.token = lookup_token(inflater->namercbuf->base, inflater->namercbuf->len);
if (inflater->index_required) {
nghttp2_hd_entry *new_ent;
uint8_t ent_flags;
rv = add_hd_table_incremental(&inflater->ctx, &nv, NULL, 0);
ent_flags = NGHTTP2_HD_FLAG_NAME_ALLOC | NGHTTP2_HD_FLAG_NAME_GIFT |
NGHTTP2_HD_FLAG_VALUE_ALLOC | NGHTTP2_HD_FLAG_VALUE_GIFT;
new_ent = add_hd_table_incremental(&inflater->ctx, &nv,
lookup_token(nv.name, nv.namelen),
ent_flags, NULL, 0);
if (new_ent) {
emit_indexed_header(nv_out, token_out, new_ent);
inflater->ent_keep = new_ent;
return 0;
if (rv != 0) {
return rv;
}
nghttp2_mem_free(mem, nv.value);
nghttp2_mem_free(mem, nv.name);
return NGHTTP2_ERR_NOMEM;
}
emit_literal_header(nv_out, token_out, &nv);
emit_header(nv_out, &nv);
inflater->nv_name_keep = nv.name;
inflater->nv_value_keep = nv.value;
inflater->namercbuf = NULL;
inflater->valuercbuf = NULL;
return 0;
}
@ -2177,12 +2091,11 @@ static int hd_inflate_commit_newname(nghttp2_hd_inflater *inflater,
* Out of memory
*/
static int hd_inflate_commit_indname(nghttp2_hd_inflater *inflater,
nghttp2_nv *nv_out, int *token_out) {
nghttp2_nv nv;
nghttp2_hd_entry *ent_name;
nghttp2_mem *mem;
nghttp2_hd_nv *nv_out) {
nghttp2_hd_nv nv;
int rv;
mem = inflater->ctx.mem;
nv = nghttp2_hd_table_get(&inflater->ctx, inflater->index);
if (inflater->no_index) {
nv.flags = NGHTTP2_NV_FLAG_NO_INDEX;
@ -2190,72 +2103,57 @@ static int hd_inflate_commit_indname(nghttp2_hd_inflater *inflater,
nv.flags = NGHTTP2_NV_FLAG_NONE;
}
ent_name = nghttp2_hd_table_get(&inflater->ctx, inflater->index);
nghttp2_rcbuf_incref(nv.name);
nv.value = inflater->valuercbuf;
if (inflater->index_required) {
nghttp2_hd_entry *new_ent;
uint8_t ent_flags;
ent_flags = NGHTTP2_HD_FLAG_VALUE_ALLOC | NGHTTP2_HD_FLAG_VALUE_GIFT;
if (inflater->index >= NGHTTP2_STATIC_TABLE_LENGTH) {
/* We don't copy name in static table */
ent_flags |= NGHTTP2_HD_FLAG_NAME_ALLOC;
rv = add_hd_table_incremental(&inflater->ctx, &nv, NULL, 0);
if (rv != 0) {
nghttp2_rcbuf_decref(nv.name);
return NGHTTP2_ERR_NOMEM;
}
nv.name = ent_name->nv.name;
nv.namelen = ent_name->nv.namelen;
nv.value = inflater->valuebuf.pos;
nv.valuelen = nghttp2_buf_len(&inflater->valuebuf);
nghttp2_buf_init(&inflater->valuebuf);
new_ent = add_hd_table_incremental(&inflater->ctx, &nv, ent_name->token,
ent_flags, NULL, 0);
/* At this point, ent_name might be deleted. */
if (new_ent) {
emit_indexed_header(nv_out, token_out, new_ent);
inflater->ent_keep = new_ent;
return 0;
}
nghttp2_mem_free(mem, nv.value);
return NGHTTP2_ERR_NOMEM;
}
nv.name = ent_name->nv.name;
nv.namelen = ent_name->nv.namelen;
nv.value = inflater->valuebuf.pos;
nv.valuelen = nghttp2_buf_len(&inflater->valuebuf);
nghttp2_buf_init(&inflater->valuebuf);
emit_literal_header(nv_out, token_out, &nv);
emit_header(nv_out, &nv);
inflater->nv_name_keep = nv.name;
inflater->nv_value_keep = nv.value;
inflater->valuercbuf = NULL;
return 0;
}
ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out,
int *inflate_flags, uint8_t *in, size_t inlen,
int in_final) {
int token;
ssize_t rv;
nghttp2_hd_nv hd_nv;
return nghttp2_hd_inflate_hd2(inflater, nv_out, inflate_flags, &token, in,
inlen, in_final);
rv = nghttp2_hd_inflate_hd2(inflater, &hd_nv, inflate_flags, in, inlen,
in_final);
if (rv < 0) {
return rv;
}
if (*inflate_flags & NGHTTP2_HD_INFLATE_EMIT) {
nv_out->name = hd_nv.name->base;
nv_out->namelen = hd_nv.name->len;
nv_out->value = hd_nv.value->base;
nv_out->valuelen = hd_nv.value->len;
nv_out->flags = hd_nv.flags;
}
return rv;
}
ssize_t nghttp2_hd_inflate_hd2(nghttp2_hd_inflater *inflater,
nghttp2_nv *nv_out, int *inflate_flags,
int *token_out, uint8_t *in, size_t inlen,
int in_final) {
nghttp2_hd_nv *nv_out, int *inflate_flags,
uint8_t *in, size_t inlen, int in_final) {
ssize_t rv = 0;
uint8_t *first = in;
uint8_t *last = in + inlen;
@ -2271,7 +2169,6 @@ ssize_t nghttp2_hd_inflate_hd2(nghttp2_hd_inflater *inflater,
DEBUGF(fprintf(stderr, "inflatehd: start state=%d\n", inflater->state));
hd_inflate_keep_free(inflater);
*token_out = -1;
*inflate_flags = NGHTTP2_HD_INFLATE_NONE;
for (; in != last || busy;) {
busy = 0;
@ -2377,7 +2274,7 @@ ssize_t nghttp2_hd_inflate_hd2(nghttp2_hd_inflater *inflater,
inflater->index = inflater->left;
--inflater->index;
rv = hd_inflate_commit_indexed(inflater, nv_out, token_out);
rv = hd_inflate_commit_indexed(inflater, nv_out);
if (rv < 0) {
goto fail;
}
@ -2422,17 +2319,21 @@ ssize_t nghttp2_hd_inflate_hd2(nghttp2_hd_inflater *inflater,
nghttp2_hd_huff_decode_context_init(&inflater->huff_decode_ctx);
inflater->state = NGHTTP2_HD_STATE_NEWNAME_READ_NAMEHUFF;
rv = nghttp2_buf_reserve(&inflater->namebuf, inflater->left * 2 + 1,
mem);
rv = nghttp2_rcbuf_new(&inflater->namercbuf, inflater->left * 2 + 1,
mem);
} else {
inflater->state = NGHTTP2_HD_STATE_NEWNAME_READ_NAME;
rv = nghttp2_buf_reserve(&inflater->namebuf, inflater->left + 1, mem);
rv = nghttp2_rcbuf_new(&inflater->namercbuf, inflater->left + 1, mem);
}
if (rv != 0) {
goto fail;
}
nghttp2_buf_wrap_init(&inflater->namebuf, inflater->namercbuf->base,
inflater->namercbuf->len);
break;
case NGHTTP2_HD_STATE_NEWNAME_READ_NAMEHUFF:
rv = hd_inflate_read_huff(inflater, &inflater->namebuf, in, last);
@ -2452,6 +2353,7 @@ ssize_t nghttp2_hd_inflate_hd2(nghttp2_hd_inflater *inflater,
}
*inflater->namebuf.last = '\0';
inflater->namercbuf->len = nghttp2_buf_len(&inflater->namebuf);
inflater->state = NGHTTP2_HD_STATE_CHECK_VALUELEN;
@ -2473,6 +2375,7 @@ ssize_t nghttp2_hd_inflate_hd2(nghttp2_hd_inflater *inflater,
}
*inflater->namebuf.last = '\0';
inflater->namercbuf->len = nghttp2_buf_len(&inflater->namebuf);
inflater->state = NGHTTP2_HD_STATE_CHECK_VALUELEN;
@ -2505,18 +2408,21 @@ ssize_t nghttp2_hd_inflate_hd2(nghttp2_hd_inflater *inflater,
inflater->state = NGHTTP2_HD_STATE_READ_VALUEHUFF;
rv = nghttp2_buf_reserve(&inflater->valuebuf, inflater->left * 2 + 1,
mem);
rv = nghttp2_rcbuf_new(&inflater->valuercbuf, inflater->left * 2 + 1,
mem);
} else {
inflater->state = NGHTTP2_HD_STATE_READ_VALUE;
rv = nghttp2_buf_reserve(&inflater->valuebuf, inflater->left + 1, mem);
rv = nghttp2_rcbuf_new(&inflater->valuercbuf, inflater->left + 1, mem);
}
if (rv != 0) {
goto fail;
}
nghttp2_buf_wrap_init(&inflater->valuebuf, inflater->valuercbuf->base,
inflater->valuercbuf->len);
busy = 1;
break;
@ -2538,11 +2444,12 @@ ssize_t nghttp2_hd_inflate_hd2(nghttp2_hd_inflater *inflater,
}
*inflater->valuebuf.last = '\0';
inflater->valuercbuf->len = nghttp2_buf_len(&inflater->valuebuf);
if (inflater->opcode == NGHTTP2_HD_OPCODE_NEWNAME) {
rv = hd_inflate_commit_newname(inflater, nv_out, token_out);
rv = hd_inflate_commit_newname(inflater, nv_out);
} else {
rv = hd_inflate_commit_indname(inflater, nv_out, token_out);
rv = hd_inflate_commit_indname(inflater, nv_out);
}
if (rv != 0) {
@ -2572,11 +2479,12 @@ ssize_t nghttp2_hd_inflate_hd2(nghttp2_hd_inflater *inflater,
}
*inflater->valuebuf.last = '\0';
inflater->valuercbuf->len = nghttp2_buf_len(&inflater->valuebuf);
if (inflater->opcode == NGHTTP2_HD_OPCODE_NEWNAME) {
rv = hd_inflate_commit_newname(inflater, nv_out, token_out);
rv = hd_inflate_commit_newname(inflater, nv_out);
} else {
rv = hd_inflate_commit_indname(inflater, nv_out, token_out);
rv = hd_inflate_commit_indname(inflater, nv_out);
}
if (rv != 0) {
@ -2710,7 +2618,7 @@ static const nghttp2_nv *hd_get_table_entry(nghttp2_hd_context *context,
return NULL;
}
return &nghttp2_hd_table_get(context, idx)->nv;
return nghttp2_hd_table_get2(context, idx);
}
size_t nghttp2_hd_deflate_get_num_table_entries(nghttp2_hd_deflater *deflater) {

View File

@ -34,6 +34,7 @@
#include "nghttp2_hd_huffman.h"
#include "nghttp2_buf.h"
#include "nghttp2_mem.h"
#include "nghttp2_rcbuf.h"
#define NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE NGHTTP2_DEFAULT_HEADER_TABLE_SIZE
#define NGHTTP2_HD_ENTRY_OVERHEAD 32
@ -168,25 +169,29 @@ typedef enum {
NGHTTP2_TOKEN_X_XSS_PROTECTION,
} nghttp2_token;
typedef enum {
NGHTTP2_HD_FLAG_NONE = 0,
/* Indicates name was dynamically allocated and must be freed */
NGHTTP2_HD_FLAG_NAME_ALLOC = 1,
/* Indicates value was dynamically allocated and must be freed */
NGHTTP2_HD_FLAG_VALUE_ALLOC = 1 << 1,
/* Indicates that the name was gifted to the entry and no copying
necessary. */
NGHTTP2_HD_FLAG_NAME_GIFT = 1 << 2,
/* Indicates that the value was gifted to the entry and no copying
necessary. */
NGHTTP2_HD_FLAG_VALUE_GIFT = 1 << 3
} nghttp2_hd_flags;
struct nghttp2_hd_entry;
typedef struct nghttp2_hd_entry nghttp2_hd_entry;
typedef struct {
/* The buffer containing header field name. NULL-termination is
guaranteed. */
nghttp2_rcbuf *name;
/* The buffer containing header field value. NULL-termination is
guaranteed. */
nghttp2_rcbuf *value;
/* nghttp2_token value for name. It could be -1 if we have no token
for that header field name. */
int32_t token;
/* Bitwise OR of one or more of nghttp2_nv_flag. */
uint8_t flags;
} nghttp2_hd_nv;
struct nghttp2_hd_entry {
nghttp2_nv nv;
/* The header field name/value pair */
nghttp2_hd_nv nv;
/* This is solely for nghttp2_hd_{deflate,inflate}_get_table_entry
APIs to keep backward compatibility. */
nghttp2_nv cnv;
/* The next entry which shares same bucket in hash table. */
nghttp2_hd_entry *next;
/* The sequence number. We will increment it by one whenever we
@ -194,14 +199,17 @@ struct nghttp2_hd_entry {
uint32_t seq;
/* The hash value for header name (nv.name). */
uint32_t hash;
/* nghttp2_token value for nv.name. It could be -1 if we have no
token for that header field name. */
int token;
/* Reference count */
uint8_t ref;
uint8_t flags;
};
/* The entry used for static header table. */
typedef struct {
nghttp2_rcbuf name;
nghttp2_rcbuf value;
nghttp2_nv cnv;
int32_t token;
uint32_t hash;
} nghttp2_hd_static_entry;
typedef struct {
nghttp2_hd_entry **buffer;
size_t mask;
@ -275,17 +283,14 @@ struct nghttp2_hd_deflater {
struct nghttp2_hd_inflater {
nghttp2_hd_context ctx;
/* header buffer */
nghttp2_buf namebuf, valuebuf;
/* Stores current state of huffman decoding */
nghttp2_hd_huff_decode_context huff_decode_ctx;
/* Pointer to the nghttp2_hd_entry which is used current header
emission. This is required because in some cases the
ent_keep->ref == 0 and we have to keep track of it. */
nghttp2_hd_entry *ent_keep;
/* Pointer to the name/value pair buffer which is used in the
current header emission. */
uint8_t *nv_name_keep, *nv_value_keep;
/* header buffer */
nghttp2_buf namebuf, valuebuf;
nghttp2_rcbuf *namercbuf, *valuercbuf;
/* Pointer to the name/value pair which are used in the current
header emission. */
nghttp2_rcbuf *nv_name_keep, *nv_value_keep;
/* The number of bytes to read */
size_t left;
/* The index in indexed repr or indexed name */
@ -309,24 +314,16 @@ struct nghttp2_hd_inflater {
};
/*
* Initializes the |ent| members. If NGHTTP2_HD_FLAG_NAME_ALLOC bit
* set in the |flags|, the content pointed by the |name| with length
* |namelen| is copied. Likewise, if NGHTTP2_HD_FLAG_VALUE_ALLOC bit
* set in the |flags|, the content pointed by the |value| with length
* |valuelen| is copied. The |token| is enum number looked up by
* |name|. It could be -1 if we don't have that enum value.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* NGHTTP2_ERR_NOMEM
* Out of memory.
* Initializes the |ent| members. The reference counts of nv->name
* and nv->value are increased by one for each.
*/
int nghttp2_hd_entry_init(nghttp2_hd_entry *ent, uint8_t flags, uint8_t *name,
size_t namelen, uint8_t *value, size_t valuelen,
int token, nghttp2_mem *mem);
void nghttp2_hd_entry_init(nghttp2_hd_entry *ent, nghttp2_hd_nv *nv);
void nghttp2_hd_entry_free(nghttp2_hd_entry *ent, nghttp2_mem *mem);
/*
* This function decreases the reference counts of nv->name and
* nv->value.
*/
void nghttp2_hd_entry_free(nghttp2_hd_entry *ent);
/*
* Initializes |deflater| for deflating name/values pairs.
@ -407,16 +404,14 @@ int nghttp2_hd_inflate_init(nghttp2_hd_inflater *inflater, nghttp2_mem *mem);
void nghttp2_hd_inflate_free(nghttp2_hd_inflater *inflater);
/*
* Similar to nghttp2_hd_inflate_hd(), but this takes additional
* output parameter |token|. On successful header emission, it
* contains nghttp2_token value for nv_out->name. It could be -1 if
* we don't have enum value for the name. Other than that return
* values and semantics are the same as nghttp2_hd_inflate_hd().
* Similar to nghttp2_hd_inflate_hd(), but this takes nghttp2_hd_nv
* instead of nghttp2_nv as output parameter |nv_out|. Other than
* that return values and semantics are the same as
* nghttp2_hd_inflate_hd().
*/
ssize_t nghttp2_hd_inflate_hd2(nghttp2_hd_inflater *inflater,
nghttp2_nv *nv_out, int *inflate_flags,
int *token, uint8_t *in, size_t inlen,
int in_final);
nghttp2_hd_nv *nv_out, int *inflate_flags,
uint8_t *in, size_t inlen, int in_final);
/* For unittesting purpose */
int nghttp2_hd_emit_indname_block(nghttp2_bufs *bufs, size_t index,
@ -430,8 +425,7 @@ int nghttp2_hd_emit_newname_block(nghttp2_bufs *bufs, nghttp2_nv *nv,
int nghttp2_hd_emit_table_size(nghttp2_bufs *bufs, size_t table_size);
/* For unittesting purpose */
nghttp2_hd_entry *nghttp2_hd_table_get(nghttp2_hd_context *context,
size_t index);
nghttp2_hd_nv nghttp2_hd_table_get(nghttp2_hd_context *context, size_t index);
/* For unittesting purpose */
ssize_t nghttp2_hd_decode_length(uint32_t *res, size_t *shift_ptr, int *final,

View File

@ -82,12 +82,12 @@ static int lws(const uint8_t *s, size_t n) {
return 1;
}
static int check_pseudo_header(nghttp2_stream *stream, const nghttp2_nv *nv,
static int check_pseudo_header(nghttp2_stream *stream, const nghttp2_hd_nv *nv,
int flag) {
if (stream->http_flags & flag) {
return 0;
}
if (lws(nv->value, nv->valuelen)) {
if (lws(nv->value->base, nv->value->len)) {
return 0;
}
stream->http_flags = (uint16_t)(stream->http_flags | flag);
@ -112,16 +112,16 @@ static int check_path(nghttp2_stream *stream) {
(stream->http_flags & NGHTTP2_HTTP_FLAG_PATH_ASTERISK)));
}
static int http_request_on_header(nghttp2_stream *stream, nghttp2_nv *nv,
int token, int trailer) {
if (nv->name[0] == ':') {
static int http_request_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
int trailer) {
if (nv->name->base[0] == ':') {
if (trailer ||
(stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
return NGHTTP2_ERR_HTTP_HEADER;
}
}
switch (token) {
switch (nv->token) {
case NGHTTP2_TOKEN__AUTHORITY:
if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__AUTHORITY)) {
return NGHTTP2_ERR_HTTP_HEADER;
@ -131,16 +131,16 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_nv *nv,
if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__METHOD)) {
return NGHTTP2_ERR_HTTP_HEADER;
}
switch (nv->valuelen) {
switch (nv->value->len) {
case 4:
if (lstreq("HEAD", nv->value, nv->valuelen)) {
if (lstreq("HEAD", nv->value->base, nv->value->len)) {
stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_HEAD;
}
break;
case 7:
switch (nv->value[6]) {
switch (nv->value->base[6]) {
case 'T':
if (lstreq("CONNECT", nv->value, nv->valuelen)) {
if (lstreq("CONNECT", nv->value->base, nv->value->len)) {
if (stream->stream_id % 2 == 0) {
/* we won't allow CONNECT for push */
return NGHTTP2_ERR_HTTP_HEADER;
@ -153,7 +153,7 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_nv *nv,
}
break;
case 'S':
if (lstreq("OPTIONS", nv->value, nv->valuelen)) {
if (lstreq("OPTIONS", nv->value->base, nv->value->len)) {
stream->http_flags |= NGHTTP2_HTTP_FLAG_METH_OPTIONS;
}
break;
@ -168,9 +168,9 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_nv *nv,
if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__PATH)) {
return NGHTTP2_ERR_HTTP_HEADER;
}
if (nv->value[0] == '/') {
if (nv->value->base[0] == '/') {
stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_REGULAR;
} else if (nv->valuelen == 1 && nv->value[0] == '*') {
} else if (nv->value->len == 1 && nv->value->base[0] == '*') {
stream->http_flags |= NGHTTP2_HTTP_FLAG_PATH_ASTERISK;
}
break;
@ -181,8 +181,8 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_nv *nv,
if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__SCHEME)) {
return NGHTTP2_ERR_HTTP_HEADER;
}
if ((nv->valuelen == 4 && memieq("http", nv->value, 4)) ||
(nv->valuelen == 5 && memieq("https", nv->value, 5))) {
if ((nv->value->len == 4 && memieq("http", nv->value->base, 4)) ||
(nv->value->len == 5 && memieq("https", nv->value->base, 5))) {
stream->http_flags |= NGHTTP2_HTTP_FLAG_SCHEME_HTTP;
}
break;
@ -195,7 +195,7 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_nv *nv,
if (stream->content_length != -1) {
return NGHTTP2_ERR_HTTP_HEADER;
}
stream->content_length = parse_uint(nv->value, nv->valuelen);
stream->content_length = parse_uint(nv->value->base, nv->value->len);
if (stream->content_length == -1) {
return NGHTTP2_ERR_HTTP_HEADER;
}
@ -209,41 +209,41 @@ static int http_request_on_header(nghttp2_stream *stream, nghttp2_nv *nv,
case NGHTTP2_TOKEN_UPGRADE:
return NGHTTP2_ERR_HTTP_HEADER;
case NGHTTP2_TOKEN_TE:
if (!lstrieq("trailers", nv->value, nv->valuelen)) {
if (!lstrieq("trailers", nv->value->base, nv->value->len)) {
return NGHTTP2_ERR_HTTP_HEADER;
}
break;
default:
if (nv->name[0] == ':') {
if (nv->name->base[0] == ':') {
return NGHTTP2_ERR_HTTP_HEADER;
}
}
if (nv->name[0] != ':') {
if (nv->name->base[0] != ':') {
stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
}
return 0;
}
static int http_response_on_header(nghttp2_stream *stream, nghttp2_nv *nv,
int token, int trailer) {
if (nv->name[0] == ':') {
static int http_response_on_header(nghttp2_stream *stream, nghttp2_hd_nv *nv,
int trailer) {
if (nv->name->base[0] == ':') {
if (trailer ||
(stream->http_flags & NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) {
return NGHTTP2_ERR_HTTP_HEADER;
}
}
switch (token) {
switch (nv->token) {
case NGHTTP2_TOKEN__STATUS: {
if (!check_pseudo_header(stream, nv, NGHTTP2_HTTP_FLAG__STATUS)) {
return NGHTTP2_ERR_HTTP_HEADER;
}
if (nv->valuelen != 3) {
if (nv->value->len != 3) {
return NGHTTP2_ERR_HTTP_HEADER;
}
stream->status_code = (int16_t)parse_uint(nv->value, nv->valuelen);
stream->status_code = (int16_t)parse_uint(nv->value->base, nv->value->len);
if (stream->status_code == -1) {
return NGHTTP2_ERR_HTTP_HEADER;
}
@ -253,7 +253,7 @@ static int http_response_on_header(nghttp2_stream *stream, nghttp2_nv *nv,
if (stream->content_length != -1) {
return NGHTTP2_ERR_HTTP_HEADER;
}
stream->content_length = parse_uint(nv->value, nv->valuelen);
stream->content_length = parse_uint(nv->value->base, nv->value->len);
if (stream->content_length == -1) {
return NGHTTP2_ERR_HTTP_HEADER;
}
@ -267,17 +267,17 @@ static int http_response_on_header(nghttp2_stream *stream, nghttp2_nv *nv,
case NGHTTP2_TOKEN_UPGRADE:
return NGHTTP2_ERR_HTTP_HEADER;
case NGHTTP2_TOKEN_TE:
if (!lstrieq("trailers", nv->value, nv->valuelen)) {
if (!lstrieq("trailers", nv->value->base, nv->value->len)) {
return NGHTTP2_ERR_HTTP_HEADER;
}
break;
default:
if (nv->name[0] == ':') {
if (nv->name->base[0] == ':') {
return NGHTTP2_ERR_HTTP_HEADER;
}
}
if (nv->name[0] != ':') {
if (nv->name->base[0] != ':') {
stream->http_flags |= NGHTTP2_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED;
}
@ -375,7 +375,7 @@ static int check_scheme(const uint8_t *value, size_t len) {
}
int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
nghttp2_frame *frame, nghttp2_nv *nv, int token,
nghttp2_frame *frame, nghttp2_hd_nv *nv,
int trailer) {
int rv;
@ -386,14 +386,14 @@ int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
this, we may disrupt many web sites and/or libraries. So we
become conservative here, and just ignore those illegal regular
headers. */
if (!nghttp2_check_header_name(nv->name, nv->namelen)) {
if (!nghttp2_check_header_name(nv->name->base, nv->name->len)) {
size_t i;
if (nv->namelen > 0 && nv->name[0] == ':') {
if (nv->name->len > 0 && nv->name->base[0] == ':') {
return NGHTTP2_ERR_HTTP_HEADER;
}
/* header field name must be lower-cased without exception */
for (i = 0; i < nv->namelen; ++i) {
uint8_t c = nv->name[i];
for (i = 0; i < nv->name->len; ++i) {
uint8_t c = nv->name->base[i];
if ('A' <= c && c <= 'Z') {
return NGHTTP2_ERR_HTTP_HEADER;
}
@ -405,17 +405,18 @@ int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
return NGHTTP2_ERR_IGN_HTTP_HEADER;
}
if (token == NGHTTP2_TOKEN__AUTHORITY || token == NGHTTP2_TOKEN_HOST) {
rv = check_authority(nv->value, nv->valuelen);
} else if (token == NGHTTP2_TOKEN__SCHEME) {
rv = check_scheme(nv->value, nv->valuelen);
if (nv->token == NGHTTP2_TOKEN__AUTHORITY ||
nv->token == NGHTTP2_TOKEN_HOST) {
rv = check_authority(nv->value->base, nv->value->len);
} else if (nv->token == NGHTTP2_TOKEN__SCHEME) {
rv = check_scheme(nv->value->base, nv->value->len);
} else {
rv = nghttp2_check_header_value(nv->value, nv->valuelen);
rv = nghttp2_check_header_value(nv->value->base, nv->value->len);
}
if (rv == 0) {
assert(nv->namelen > 0);
if (nv->name[0] == ':') {
assert(nv->name->len > 0);
if (nv->name->base[0] == ':') {
return NGHTTP2_ERR_HTTP_HEADER;
}
/* When ignoring regular headers, we set this flag so that we
@ -426,10 +427,10 @@ int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
}
if (session->server || frame->hd.type == NGHTTP2_PUSH_PROMISE) {
return http_request_on_header(stream, nv, token, trailer);
return http_request_on_header(stream, nv, trailer);
}
return http_response_on_header(stream, nv, token, trailer);
return http_response_on_header(stream, nv, trailer);
}
int nghttp2_http_on_request_headers(nghttp2_stream *stream,

View File

@ -36,8 +36,7 @@
/*
* This function is called when HTTP header field |nv| in |frame| is
* received for |stream|. This function will validate |nv| against
* the current state of stream. The |token| is nghttp2_token value
* for nv->name, or -1 if we don't have enum value for the name.
* the current state of stream.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
@ -49,7 +48,7 @@
* if it was not received because of compatibility reasons.
*/
int nghttp2_http_on_header(nghttp2_session *session, nghttp2_stream *stream,
nghttp2_frame *frame, nghttp2_nv *nv, int token,
nghttp2_frame *frame, nghttp2_hd_nv *nv,
int trailer);
/*

View File

@ -52,6 +52,10 @@ void nghttp2_mem_free(nghttp2_mem *mem, void *ptr) {
mem->free(ptr, mem->mem_user_data);
}
void nghttp2_mem_free2(nghttp2_free free, void *ptr, void *mem_user_data) {
free(ptr, mem_user_data);
}
void *nghttp2_mem_calloc(nghttp2_mem *mem, size_t nmemb, size_t size) {
return mem->calloc(nmemb, size, mem->mem_user_data);
}

View File

@ -38,6 +38,7 @@ nghttp2_mem *nghttp2_mem_default(void);
|mem|. */
void *nghttp2_mem_malloc(nghttp2_mem *mem, size_t size);
void nghttp2_mem_free(nghttp2_mem *mem, void *ptr);
void nghttp2_mem_free2(nghttp2_free free, void *ptr, void *mem_user_data);
void *nghttp2_mem_calloc(nghttp2_mem *mem, size_t nmemb, size_t size);
void *nghttp2_mem_realloc(nghttp2_mem *mem, void *ptr, size_t size);

98
lib/nghttp2_rcbuf.c Normal file
View File

@ -0,0 +1,98 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2016 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "nghttp2_rcbuf.h"
#include <string.h>
#include <assert.h>
#include "nghttp2_mem.h"
int nghttp2_rcbuf_new(nghttp2_rcbuf **rcbuf_ptr, size_t size,
nghttp2_mem *mem) {
uint8_t *p;
p = nghttp2_mem_malloc(mem, sizeof(nghttp2_rcbuf) + size);
if (p == NULL) {
return NGHTTP2_ERR_NOMEM;
}
*rcbuf_ptr = (void *)p;
(*rcbuf_ptr)->mem_user_data = mem->mem_user_data;
(*rcbuf_ptr)->free = mem->free;
(*rcbuf_ptr)->base = p + sizeof(nghttp2_rcbuf);
(*rcbuf_ptr)->len = size;
(*rcbuf_ptr)->ref = 1;
return 0;
}
int nghttp2_rcbuf_new2(nghttp2_rcbuf **rcbuf_ptr, const uint8_t *src,
size_t srclen, nghttp2_mem *mem) {
int rv;
rv = nghttp2_rcbuf_new(rcbuf_ptr, srclen + 1, mem);
if (rv != 0) {
return rv;
}
memcpy((*rcbuf_ptr)->base, src, srclen);
(*rcbuf_ptr)->len = srclen;
(*rcbuf_ptr)->base[srclen] = '\0';
return 0;
}
/*
* Frees |rcbuf| itself, regardless of its reference cout.
*/
void nghttp2_rcbuf_del(nghttp2_rcbuf *rcbuf) {
nghttp2_mem_free2(rcbuf->free, rcbuf, rcbuf->mem_user_data);
}
void nghttp2_rcbuf_incref(nghttp2_rcbuf *rcbuf) {
if (rcbuf->ref == -1) {
return;
}
++rcbuf->ref;
}
void nghttp2_rcbuf_decref(nghttp2_rcbuf *rcbuf) {
if (rcbuf == NULL || rcbuf->ref == -1) {
return;
}
assert(rcbuf->ref > 0);
if (--rcbuf->ref == 0) {
nghttp2_rcbuf_del(rcbuf);
}
}
nghttp2_vec nghttp2_rcbuf_get_buf(nghttp2_rcbuf *rcbuf) {
return (nghttp2_vec){rcbuf->base, rcbuf->len};
}

80
lib/nghttp2_rcbuf.h Normal file
View File

@ -0,0 +1,80 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2016 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef NGHTTP2_RCBUF_H
#define NGHTTP2_RCBUF_H
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /* HAVE_CONFIG_H */
#include <nghttp2/nghttp2.h>
struct nghttp2_rcbuf {
/* custom memory allocator belongs to the mem parameter when
creating this object. */
void *mem_user_data;
nghttp2_free free;
/* The pointer to the underlying buffer */
uint8_t *base;
/* Size of buffer pointed by |base|. */
size_t len;
/* Reference count */
int32_t ref;
};
/*
* Allocates nghttp2_rcbuf object with |size| as initial buffer size.
* When the function succeeds, the reference count becomes 1.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* NGHTTP2_ERR_NOMEM:
* Out of memory.
*/
int nghttp2_rcbuf_new(nghttp2_rcbuf **rcbuf_ptr, size_t size, nghttp2_mem *mem);
/*
* Like nghttp2_rcbuf_new(), but initializes the buffer with |src| of
* length |srclen|. This function allocates additional byte at the
* end and puts '\0' into it, so that the resulting buffer could be
* used as NULL-terminated string. Still (*rcbuf_ptr)->len equals to
* |srclen|.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* NGHTTP2_ERR_NOMEM:
* Out of memory.
*/
int nghttp2_rcbuf_new2(nghttp2_rcbuf **rcbuf_ptr, const uint8_t *src,
size_t srclen, nghttp2_mem *mem);
/*
* Frees |rcbuf| itself, regardless of its reference cout.
*/
void nghttp2_rcbuf_del(nghttp2_rcbuf *rcbuf);
#endif /* NGHTTP2_RCBUF_H */

View File

@ -3122,12 +3122,12 @@ static int session_call_on_begin_headers(nghttp2_session *session,
static int session_call_on_header(nghttp2_session *session,
const nghttp2_frame *frame,
const nghttp2_nv *nv) {
const nghttp2_hd_nv *nv) {
int rv;
if (session->callbacks.on_header_callback) {
rv = session->callbacks.on_header_callback(
session, frame, nv->name, nv->namelen, nv->value, nv->valuelen,
nv->flags, session->user_data);
session, frame, nv->name->base, nv->name->len, nv->value->base,
nv->value->len, nv->flags, session->user_data);
if (rv == NGHTTP2_ERR_PAUSE ||
rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) {
return rv;
@ -3317,11 +3317,10 @@ static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame,
ssize_t proclen;
int rv;
int inflate_flags;
nghttp2_nv nv;
nghttp2_hd_nv nv;
nghttp2_stream *stream;
nghttp2_stream *subject_stream;
int trailer = 0;
int token;
*readlen_ptr = 0;
stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
@ -3338,7 +3337,7 @@ static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame,
for (;;) {
inflate_flags = 0;
proclen = nghttp2_hd_inflate_hd2(&session->hd_inflater, &nv, &inflate_flags,
&token, in, inlen, final);
in, inlen, final);
if (nghttp2_is_fatal((int)proclen)) {
return (int)proclen;
}
@ -3373,13 +3372,13 @@ static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame,
if (call_header_cb && (inflate_flags & NGHTTP2_HD_INFLATE_EMIT)) {
rv = 0;
if (subject_stream && session_enforce_http_messaging(session)) {
rv = nghttp2_http_on_header(session, subject_stream, frame, &nv, token,
rv = nghttp2_http_on_header(session, subject_stream, frame, &nv,
trailer);
if (rv == NGHTTP2_ERR_HTTP_HEADER) {
DEBUGF(fprintf(
stderr, "recv: HTTP error: type=%d, id=%d, header %.*s: %.*s\n",
frame->hd.type, subject_stream->stream_id, (int)nv.namelen,
nv.name, (int)nv.valuelen, nv.value));
frame->hd.type, subject_stream->stream_id, (int)nv.name->len,
nv.name->base, (int)nv.value->len, nv.value->base));
rv =
session_handle_invalid_stream2(session, subject_stream->stream_id,
@ -3394,8 +3393,8 @@ static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame,
/* header is ignored */
DEBUGF(fprintf(
stderr, "recv: HTTP ignored: type=%d, id=%d, header %.*s: %.*s\n",
frame->hd.type, subject_stream->stream_id, (int)nv.namelen,
nv.name, (int)nv.valuelen, nv.value));
frame->hd.type, subject_stream->stream_id, (int)nv.name->len,
nv.name->base, (int)nv.value->len, nv.value->base));
}
}
if (rv == 0) {

View File

@ -295,11 +295,6 @@ void test_nghttp2_hd_inflate_indname_inc(void) {
assert_nv_equal(&nv, out.nva, 1, mem);
CU_ASSERT(1 == inflater.ctx.hd_table.len);
CU_ASSERT(62 == nghttp2_hd_inflate_get_num_table_entries(&inflater));
assert_nv_equal(&nv,
&nghttp2_hd_table_get(&inflater.ctx,
NGHTTP2_STATIC_TABLE_LENGTH +
inflater.ctx.hd_table.len - 1)->nv,
1, mem);
assert_nv_equal(&nv, nghttp2_hd_inflate_get_table_entry(
&inflater, NGHTTP2_STATIC_TABLE_LENGTH +
inflater.ctx.hd_table.len),
@ -429,10 +424,9 @@ void test_nghttp2_hd_inflate_newname_inc(void) {
CU_ASSERT(1 == out.nvlen);
assert_nv_equal(&nv, out.nva, 1, mem);
CU_ASSERT(1 == inflater.ctx.hd_table.len);
assert_nv_equal(&nv,
&nghttp2_hd_table_get(&inflater.ctx,
NGHTTP2_STATIC_TABLE_LENGTH +
inflater.ctx.hd_table.len - 1)->nv,
assert_nv_equal(&nv, nghttp2_hd_inflate_get_table_entry(
&inflater, NGHTTP2_STATIC_TABLE_LENGTH +
inflater.ctx.hd_table.len),
1, mem);
nva_out_reset(&out, mem);