Reference counted HPACK name/value pair
This commit is contained in:
parent
8da20975f9
commit
ff0d137fb3
|
@ -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()):
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
484
lib/nghttp2_hd.c
484
lib/nghttp2_hd.c
|
@ -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,9 +1411,8 @@ 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,
|
||||
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;
|
||||
|
@ -1493,7 +1420,7 @@ add_hd_table_incremental(nghttp2_hd_context *context, const nghttp2_nv *nv,
|
|||
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,60 +1428,41 @@ 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_hd_entry_free(ent);
|
||||
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;
|
||||
}
|
||||
|
||||
if (room > context->hd_table_bufsize_max) {
|
||||
/* The entry taking more than NGHTTP2_HD_MAX_BUFFER_SIZE is
|
||||
immediately evicted. */
|
||||
--new_ent->ref;
|
||||
} else {
|
||||
immediately evicted. So we don't allocate memory for it. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
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) {
|
||||
--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_hd_entry_free(new_ent);
|
||||
nghttp2_mem_free(mem, new_ent);
|
||||
|
||||
return NULL;
|
||||
return rv;
|
||||
}
|
||||
|
||||
new_ent->seq = context->next_seq++;
|
||||
|
@ -1565,8 +1473,8 @@ add_hd_table_incremental(nghttp2_hd_context *context, const nghttp2_nv *nv,
|
|||
}
|
||||
|
||||
context->hd_table_bufsize += room;
|
||||
}
|
||||
return new_ent;
|
||||
|
||||
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,17 +1552,17 @@ 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_hd_entry_free(ent);
|
||||
nghttp2_mem_free(mem, ent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int nghttp2_hd_deflate_change_table_size(nghttp2_hd_deflater *deflater,
|
||||
size_t settings_hd_table_bufsize_max) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
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,
|
||||
|
||||
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,
|
||||
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) {
|
||||
|
|
106
lib/nghttp2_hd.h
106
lib/nghttp2_hd.h
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
||||
/*
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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};
|
||||
}
|
|
@ -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 */
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue