From 8cf3731802ef86c95362dfb0dbe9b2169b37b7a8 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Sat, 12 Oct 2013 21:49:01 +0900 Subject: [PATCH] Experiment HPACK with upcoming changes * remove substitution * reversed insertion and removal from header table * unified initial static table --- lib/nghttp2_hd.c | 502 +++++++++++++++++----------------------- lib/nghttp2_hd.h | 22 +- tests/main.c | 10 - tests/nghttp2_hd_test.c | 186 ++------------- tests/nghttp2_hd_test.h | 5 - 5 files changed, 252 insertions(+), 473 deletions(-) diff --git a/lib/nghttp2_hd.c b/lib/nghttp2_hd.c index 1b919f9d..b2216c76 100644 --- a/lib/nghttp2_hd.c +++ b/lib/nghttp2_hd.c @@ -26,11 +26,12 @@ #include #include +#include #include "nghttp2_frame.h" #include "nghttp2_helper.h" -static const char *reqhd_table[] = { +static const char *static_table[] = { ":scheme", "http", ":scheme", "https", ":host", "", @@ -61,10 +62,7 @@ static const char *reqhd_table[] = { "proxy-authorization", "", "range", "", "via", "", - NULL -}; -static const char *reshd_table[] = { ":status", "200", "age", "", "cache-control", "", @@ -104,7 +102,7 @@ typedef struct { size_t nvlen; } nghttp2_nva_out; -int nghttp2_hd_entry_init(nghttp2_hd_entry *ent, uint8_t index, uint8_t flags, +int nghttp2_hd_entry_init(nghttp2_hd_entry *ent, uint8_t flags, uint8_t *name, uint16_t namelen, uint8_t *value, uint16_t valuelen) { @@ -139,7 +137,6 @@ int nghttp2_hd_entry_init(nghttp2_hd_entry *ent, uint8_t index, uint8_t flags, ent->nv.namelen = namelen; ent->nv.valuelen = valuelen; ent->ref = 1; - ent->index = index; ent->flags = flags; return 0; @@ -162,29 +159,81 @@ void nghttp2_hd_entry_free(nghttp2_hd_entry *ent) } } +static int nghttp2_hd_ringbuf_init(nghttp2_hd_ringbuf *ringbuf, + size_t bufsize) +{ + size_t size; + for(size = 1; size < bufsize; size <<= 1); + ringbuf->buffer = malloc(sizeof(nghttp2_hd_entry*)*size); + if(ringbuf->buffer == NULL) { + return NGHTTP2_ERR_NOMEM; + } + ringbuf->mask = size - 1; + ringbuf->first = 0; + ringbuf->len = 0; + return 0; +} + +static nghttp2_hd_entry* nghttp2_hd_ringbuf_get(nghttp2_hd_ringbuf *ringbuf, + size_t index) +{ + assert(index < ringbuf->len); + return ringbuf->buffer[(ringbuf->first + index) & ringbuf->mask]; +} + +static void nghttp2_hd_ringbuf_free(nghttp2_hd_ringbuf *ringbuf) +{ + size_t i; + if(ringbuf == NULL) { + return; + } + for(i = 0; i < ringbuf->len; ++i) { + nghttp2_hd_entry *ent = nghttp2_hd_ringbuf_get(ringbuf, i); + --ent->ref; + nghttp2_hd_entry_free(ent); + free(ent); + } + free(ringbuf->buffer); +} + +static size_t nghttp2_hd_ringbuf_push_front(nghttp2_hd_ringbuf *ringbuf, + nghttp2_hd_entry *ent) +{ + assert(ringbuf->len + 1 <= ringbuf->mask); + ringbuf->buffer[--ringbuf->first & ringbuf->mask] = ent; + ++ringbuf->len; + return 0; +} + +static void nghttp2_hd_ringbuf_pop_back(nghttp2_hd_ringbuf *ringbuf) +{ + assert(ringbuf->len > 0); + --ringbuf->len; +} + static int nghttp2_hd_context_init(nghttp2_hd_context *context, nghttp2_hd_role role, nghttp2_hd_side side) { int i; - const char **ini_table; + int rv; context->role = role; context->bad = 0; - context->hd_table = malloc(sizeof(nghttp2_hd_entry*)* - NGHTTP2_INITIAL_HD_TABLE_SIZE); - if(context->hd_table == NULL) { - return NGHTTP2_ERR_NOMEM; + rv = nghttp2_hd_ringbuf_init(&context->hd_table, + NGHTTP2_INITIAL_HD_TABLE_SIZE); + if(rv != 0) { + return rv; } - memset(context->hd_table, 0, sizeof(nghttp2_hd_entry*)* - NGHTTP2_INITIAL_HD_TABLE_SIZE); - context->hd_table_capacity = NGHTTP2_INITIAL_HD_TABLE_SIZE; - context->hd_tablelen = 0; + for(i = 0; static_table[i]; i += 2); + /* TODO handle nomem */ + context->static_hd_table = malloc(sizeof(nghttp2_hd_entry*)*(i / 2 + 1)); + context->static_hd_table[i / 2] = NULL; if(role == NGHTTP2_HD_ROLE_INFLATE) { context->emit_set = malloc(sizeof(nghttp2_hd_entry*)* NGHTTP2_INITIAL_EMIT_SET_SIZE); if(context->emit_set == NULL) { - free(context->hd_table); + nghttp2_hd_ringbuf_free(&context->hd_table); return NGHTTP2_ERR_NOMEM; } memset(context->emit_set, 0, sizeof(nghttp2_hd_entry*)* @@ -196,30 +245,19 @@ static int nghttp2_hd_context_init(nghttp2_hd_context *context, } context->emit_setlen = 0; - if(side == NGHTTP2_HD_SIDE_CLIENT) { - ini_table = reqhd_table; - } else { - ini_table = reshd_table; - } context->hd_table_bufsize = 0; - for(i = 0; ini_table[i]; i += 2) { + for(i = 0; static_table[i]; i += 2) { nghttp2_hd_entry *p = malloc(sizeof(nghttp2_hd_entry)); if(p == NULL) { - for(i = 0; i < context->hd_tablelen; ++i) { - nghttp2_hd_entry_free(context->hd_table[i]); - free(context->hd_table[i]); - } + nghttp2_hd_ringbuf_free(&context->hd_table); free(context->emit_set); - free(context->hd_table); return NGHTTP2_ERR_NOMEM; } - nghttp2_hd_entry_init(p, i / 2, NGHTTP2_HD_FLAG_NONE, - (uint8_t*)ini_table[i], strlen(ini_table[i]), - (uint8_t*)ini_table[i + 1], - strlen(ini_table[i+1])); - context->hd_table[context->hd_tablelen++] = p; - context->hd_table_bufsize += NGHTTP2_HD_ENTRY_OVERHEAD + - p->nv.namelen + p->nv.valuelen; + nghttp2_hd_entry_init(p, NGHTTP2_HD_FLAG_NONE, + (uint8_t*)static_table[i], strlen(static_table[i]), + (uint8_t*)static_table[i + 1], + strlen(static_table[i+1])); + context->static_hd_table[i / 2] = p; } return 0; } @@ -244,14 +282,9 @@ static void nghttp2_hd_context_free(nghttp2_hd_context *context) free(ent); } } - for(i = 0; i < context->hd_tablelen; ++i) { - nghttp2_hd_entry *ent = context->hd_table[i]; - --ent->ref; - nghttp2_hd_entry_free(ent); - free(ent); - } + nghttp2_hd_ringbuf_free(&context->hd_table); + free(context->static_hd_table); free(context->emit_set); - free(context->hd_table); } void nghttp2_hd_deflate_free(nghttp2_hd_context *deflater) @@ -478,17 +511,19 @@ static int emit_indname_block(uint8_t **buf_ptr, size_t *buflen_ptr, { int rv; uint8_t *bufp; - size_t blocklen = count_encoded_length(index + 1, 5) + + size_t blocklen = count_encoded_length(index + 1, 6) + count_encoded_length(valuelen, 0) + valuelen; rv = ensure_write_buffer(buf_ptr, buflen_ptr, *offset_ptr, blocklen); if(rv != 0) { return rv; } bufp = *buf_ptr + *offset_ptr; - bufp += encode_length(bufp, index + 1, 5); + bufp += encode_length(bufp, index + 1, 6); bufp += encode_length(bufp, valuelen, 0); memcpy(bufp, value, valuelen); - (*buf_ptr)[*offset_ptr] |= inc_indexing ? 0x40u : 0x60u; + if(!inc_indexing) { + (*buf_ptr)[*offset_ptr] |= 0x40u; + } assert(bufp+valuelen - (*buf_ptr + *offset_ptr) == (ssize_t)blocklen); *offset_ptr += blocklen; return 0; @@ -507,7 +542,7 @@ static int emit_newname_block(uint8_t **buf_ptr, size_t *buflen_ptr, return rv; } bufp = *buf_ptr + *offset_ptr; - *bufp++ = inc_indexing ? 0x40u : 0x60u; + *bufp++ = inc_indexing ? 0 : 0x40u; bufp += encode_length(bufp, nv->namelen, 0); memcpy(bufp, nv->name, nv->namelen); bufp += nv->namelen; @@ -517,54 +552,6 @@ static int emit_newname_block(uint8_t **buf_ptr, size_t *buflen_ptr, return 0; } -static int emit_subst_indname_block(uint8_t **buf_ptr, size_t *buflen_ptr, - size_t *offset_ptr, size_t index, - const uint8_t *value, size_t valuelen, - size_t subindex) -{ - int rv; - uint8_t *bufp; - size_t blocklen = count_encoded_length(index + 1, 6) + - count_encoded_length(subindex, 0) + - count_encoded_length(valuelen, 0) + valuelen; - rv = ensure_write_buffer(buf_ptr, buflen_ptr, *offset_ptr, blocklen); - if(rv != 0) { - return rv; - } - bufp = *buf_ptr + *offset_ptr; - bufp += encode_length(bufp, index + 1, 6); - bufp += encode_length(bufp, subindex, 0); - bufp += encode_length(bufp, valuelen, 0); - memcpy(bufp, value, valuelen); - *offset_ptr += blocklen; - return 0; -} - -static int emit_subst_newname_block(uint8_t **buf_ptr, size_t *buflen_ptr, - size_t *offset_ptr, nghttp2_nv *nv, - size_t subindex) -{ - int rv; - uint8_t *bufp; - size_t blocklen = 1 + count_encoded_length(nv->namelen, 0) + nv->namelen + - count_encoded_length(subindex, 0) + - count_encoded_length(nv->valuelen, 0) + nv->valuelen; - rv = ensure_write_buffer(buf_ptr, buflen_ptr, *offset_ptr, blocklen); - if(rv != 0) { - return rv; - } - bufp = *buf_ptr + *offset_ptr; - *bufp++ = 0; - bufp += encode_length(bufp, nv->namelen, 0); - memcpy(bufp, nv->name, nv->namelen); - bufp += nv->namelen; - bufp += encode_length(bufp, subindex, 0); - bufp += encode_length(bufp, nv->valuelen, 0); - memcpy(bufp, nv->value, nv->valuelen); - *offset_ptr += blocklen; - return 0; -} - /* * Emit common header with |index| by toggle off and on (thus 2 * indexed representation emissions). @@ -592,43 +579,35 @@ static nghttp2_hd_entry* add_hd_table_incremental(nghttp2_hd_context *context, nghttp2_nv *nv) { int rv; - size_t i; nghttp2_hd_entry *new_ent; size_t room = entry_room(nv->namelen, nv->valuelen); context->hd_table_bufsize += room; - for(i = 0; i < context->hd_tablelen && - context->hd_table_bufsize > NGHTTP2_HD_MAX_BUFFER_SIZE; ++i) { - nghttp2_hd_entry *ent = context->hd_table[i]; + while(context->hd_table_bufsize > NGHTTP2_HD_MAX_BUFFER_SIZE && + context->hd_table.len > 0) { + size_t index = context->hd_table.len - 1; + nghttp2_hd_entry* ent = nghttp2_hd_ringbuf_get(&context->hd_table, index); context->hd_table_bufsize -= entry_room(ent->nv.namelen, ent->nv.valuelen); if(context->role == NGHTTP2_HD_ROLE_DEFLATE && (ent->flags & NGHTTP2_HD_FLAG_IMPLICIT_EMIT)) { /* Emit common header just before it slips away from the table. If we don't do this, we have to emit it in literal representation which hurts compression. */ - rv = emit_implicit(buf_ptr, buflen_ptr, offset_ptr, ent->index); + rv = emit_implicit(buf_ptr, buflen_ptr, offset_ptr, index); if(rv != 0) { return NULL; } } - ent->index = NGHTTP2_HD_INVALID_INDEX; + nghttp2_hd_ringbuf_pop_back(&context->hd_table); if(--ent->ref == 0) { nghttp2_hd_entry_free(ent); free(ent); } } - if(i > 0) { - size_t j; - for(j = 0; i < context->hd_tablelen; ++i, ++j) { - context->hd_table[j] = context->hd_table[i]; - context->hd_table[j]->index = j; - } - context->hd_tablelen = j; - } new_ent = malloc(sizeof(nghttp2_hd_entry)); if(new_ent == NULL) { return NULL; } - rv = nghttp2_hd_entry_init(new_ent, context->hd_tablelen, + rv = nghttp2_hd_entry_init(new_ent, NGHTTP2_HD_FLAG_NAME_ALLOC | NGHTTP2_HD_FLAG_VALUE_ALLOC, nv->name, nv->namelen, nv->value, nv->valuelen); @@ -639,127 +618,61 @@ static nghttp2_hd_entry* add_hd_table_incremental(nghttp2_hd_context *context, if(room > NGHTTP2_HD_MAX_BUFFER_SIZE) { /* The entry taking more than NGHTTP2_HD_MAX_BUFFER_SIZE is immediately evicted. */ - new_ent->index = NGHTTP2_HD_INVALID_INDEX; --new_ent->ref; } else { - /* Because of current NGHTTP2_HD_MAX_BUFFER_SIZE, - NGHTTP2_HD_ENTRY_OVERHEAD and NGHTTP2_INITIAL_HD_TABLE_SIZE, - context->hd_tablelen is strictly less than - context->hd_table_capacity. */ - assert(context->hd_tablelen < context->hd_table_capacity); - context->hd_table[context->hd_tablelen++] = new_ent; new_ent->flags |= NGHTTP2_HD_FLAG_REFSET; + nghttp2_hd_ringbuf_push_front(&context->hd_table, new_ent); } return new_ent; } -/* - * This function does not take parameter for header block emission - * because our encoder never use substitution. - */ -static nghttp2_hd_entry* add_hd_table_subst(nghttp2_hd_context *context, - nghttp2_nv *nv, size_t subindex) -{ - int rv; - size_t i; - int k; - nghttp2_hd_entry *new_ent; - size_t room = entry_room(nv->namelen, nv->valuelen); - if(context->hd_tablelen <= subindex) { - return NULL; - } - context->hd_table_bufsize -= - entry_room(context->hd_table[subindex]->nv.namelen, - context->hd_table[subindex]->nv.valuelen); - context->hd_table_bufsize += room; - k = subindex; - for(i = 0; i < context->hd_tablelen && - context->hd_table_bufsize > NGHTTP2_HD_MAX_BUFFER_SIZE; ++i, --k) { - nghttp2_hd_entry *ent = context->hd_table[i]; - if(i != subindex) { - context->hd_table_bufsize -= entry_room(ent->nv.namelen, - ent->nv.valuelen); - } - ent->index = NGHTTP2_HD_INVALID_INDEX; - if(--ent->ref == 0) { - nghttp2_hd_entry_free(ent); - free(ent); - } - } - if(i > 0) { - size_t j; - /* k < 0 means that the index to substitute originally was - evicted. Therefore, index 0 is the position to substitute - now. */ - if(k < 0) { - j = 1; - } else { - j = 0; - } - for(; i < context->hd_tablelen; ++i, ++j) { - context->hd_table[j] = context->hd_table[i]; - context->hd_table[j]->index = j; - } - context->hd_tablelen = j; - } - new_ent = malloc(sizeof(nghttp2_hd_entry)); - if(new_ent == NULL) { - return NULL; - } - if(k >= 0) { - nghttp2_hd_entry *ent = context->hd_table[k]; - ent->index = NGHTTP2_HD_INVALID_INDEX; - if(--ent->ref == 0) { - nghttp2_hd_entry_free(ent); - free(ent); - } - } else { - k = 0; - } - rv = nghttp2_hd_entry_init(new_ent, k, - NGHTTP2_HD_FLAG_NAME_ALLOC | - NGHTTP2_HD_FLAG_VALUE_ALLOC, - nv->name, nv->namelen, nv->value, nv->valuelen); - if(rv != 0) { - free(new_ent); - return NULL; - } - if(room > NGHTTP2_HD_MAX_BUFFER_SIZE) { - new_ent->index = NGHTTP2_HD_INVALID_INDEX; - --new_ent->ref; - context->hd_tablelen = 0; - } else { - context->hd_table[new_ent->index] = new_ent; - new_ent->flags |= NGHTTP2_HD_FLAG_REFSET; - } - return new_ent; -} - -static nghttp2_hd_entry* find_in_hd_table(nghttp2_hd_context *context, - nghttp2_nv *nv) +static ssize_t find_in_hd_table(nghttp2_hd_context *context, nghttp2_nv *nv) { size_t i; - for(i = 0; i < context->hd_tablelen; ++i) { - nghttp2_hd_entry *ent = context->hd_table[i]; + for(i = 0; i < context->hd_table.len; ++i) { + nghttp2_hd_entry *ent = nghttp2_hd_ringbuf_get(&context->hd_table, i); if(nghttp2_nv_equal(&ent->nv, nv)) { - return ent; + return i; } } - return NULL; + for(i = 0; context->static_hd_table[i]; ++i) { + nghttp2_hd_entry *ent = context->static_hd_table[i]; + if(nghttp2_nv_equal(&ent->nv, nv)) { + return context->hd_table.len + i; + } + } + return -1; } -static nghttp2_hd_entry* find_name_in_hd_table(nghttp2_hd_context *context, - nghttp2_nv *nv) +static ssize_t find_name_in_hd_table(nghttp2_hd_context *context, + nghttp2_nv *nv) { size_t i; - for(i = 0; i < context->hd_tablelen; ++i) { - nghttp2_hd_entry *ent = context->hd_table[i]; + for(i = 0; i < context->hd_table.len; ++i) { + nghttp2_hd_entry *ent = nghttp2_hd_ringbuf_get(&context->hd_table, i); if(ent->nv.namelen == nv->namelen && memcmp(ent->nv.name, nv->name, nv->namelen) == 0) { - return ent; + return i; } } - return NULL; + for(i = 0; context->static_hd_table[i]; ++i) { + nghttp2_hd_entry *ent = context->static_hd_table[i]; + if(ent->nv.namelen == nv->namelen && + memcmp(ent->nv.name, nv->name, nv->namelen) == 0) { + return context->hd_table.len + i; + } + } + return -1; +} + +nghttp2_hd_entry* nghttp2_hd_table_get(nghttp2_hd_context *context, + size_t index) +{ + if(index < context->hd_table.len) { + return nghttp2_hd_ringbuf_get(&context->hd_table, index); + } else { + return context->static_hd_table[index - context->hd_table.len]; + } } static int deflate_nv(nghttp2_hd_context *deflater, @@ -769,11 +682,13 @@ static int deflate_nv(nghttp2_hd_context *deflater, { int rv; nghttp2_hd_entry *ent; - ent = find_in_hd_table(deflater, nv); - if(ent) { + rv = find_in_hd_table(deflater, nv); + if(rv != -1) { + size_t index = rv; + ent = nghttp2_hd_table_get(deflater, index); if((ent->flags & NGHTTP2_HD_FLAG_REFSET) == 0) { ent->flags |= NGHTTP2_HD_FLAG_REFSET | NGHTTP2_HD_FLAG_EMIT; - rv = emit_indexed_block(buf_ptr, buflen_ptr, offset_ptr, ent->index); + rv = emit_indexed_block(buf_ptr, buflen_ptr, offset_ptr, index); if(rv != 0) { return rv; } @@ -803,18 +718,18 @@ static int deflate_nv(nghttp2_hd_context *deflater, ent->flags |= NGHTTP2_HD_FLAG_IMPLICIT_EMIT; } for(; num_emits > 0; --num_emits) { - rv = emit_indexed_block(buf_ptr, buflen_ptr, offset_ptr, ent->index); + rv = emit_indexed_block(buf_ptr, buflen_ptr, offset_ptr, index); if(rv != 0) { break; } } } } else { - uint8_t index = NGHTTP2_HD_INVALID_INDEX; + ssize_t index = -1; int incidx = 0; - ent = find_name_in_hd_table(deflater, nv); - if(ent) { - index = ent->index; + rv = find_name_in_hd_table(deflater, nv); + if(rv != -1) { + index = rv; } if(entry_room(nv->namelen, nv->valuelen) <= NGHTTP2_HD_MAX_ENTRY_SIZE) { nghttp2_hd_entry *new_ent; @@ -826,7 +741,7 @@ static int deflate_nv(nghttp2_hd_context *deflater, new_ent->flags |= NGHTTP2_HD_FLAG_EMIT; incidx = 1; } - if(index == NGHTTP2_HD_INVALID_INDEX) { + if(index == -1) { rv = emit_newname_block(buf_ptr, buflen_ptr, offset_ptr, nv, incidx); } else { rv = emit_indname_block(buf_ptr, buflen_ptr, offset_ptr, index, @@ -839,6 +754,28 @@ static int deflate_nv(nghttp2_hd_context *deflater, return 0; } +static int deflate_post_process_hd_entry(nghttp2_hd_entry *ent, + size_t index, + uint8_t **buf_ptr, + size_t *buflen_ptr, + size_t *offset_ptr) +{ + int rv; + if((ent->flags & NGHTTP2_HD_FLAG_REFSET) && + (ent->flags & NGHTTP2_HD_FLAG_IMPLICIT_EMIT) == 0 && + (ent->flags & NGHTTP2_HD_FLAG_EMIT) == 0) { + /* This entry is not present in the current header set and must + be removed. */ + ent->flags ^= NGHTTP2_HD_FLAG_REFSET; + rv = emit_indexed_block(buf_ptr, buflen_ptr, offset_ptr, index); + if(rv != 0) { + return rv; + } + } + ent->flags &= ~(NGHTTP2_HD_FLAG_EMIT | NGHTTP2_HD_FLAG_IMPLICIT_EMIT); + return 0; +} + ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_context *deflater, uint8_t **buf_ptr, size_t *buflen_ptr, size_t nv_offset, @@ -856,17 +793,20 @@ ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_context *deflater, goto fail; } } - for(i = 0; i < deflater->hd_tablelen; ++i) { - nghttp2_hd_entry *ent = deflater->hd_table[i]; - if((ent->flags & NGHTTP2_HD_FLAG_REFSET) && - (ent->flags & NGHTTP2_HD_FLAG_IMPLICIT_EMIT) == 0 && - (ent->flags & NGHTTP2_HD_FLAG_EMIT) == 0) { - /* This entry is not present in the current header set and must - be removed. */ - ent->flags ^= NGHTTP2_HD_FLAG_REFSET; - rv = emit_indexed_block(buf_ptr, buflen_ptr, &offset, ent->index); + for(i = 0; i < deflater->hd_table.len; ++i) { + nghttp2_hd_entry *ent = nghttp2_hd_ringbuf_get(&deflater->hd_table, i); + rv = deflate_post_process_hd_entry(ent, i, buf_ptr, buflen_ptr, &offset); + if(rv != 0) { + goto fail; + } + } + for(i = 0; deflater->static_hd_table[i]; ++i) { + nghttp2_hd_entry *ent = deflater->static_hd_table[i]; + rv = deflate_post_process_hd_entry(ent, i + deflater->hd_table.len, + buf_ptr, buflen_ptr, &offset); + if(rv != 0) { + goto fail; } - ent->flags &= ~(NGHTTP2_HD_FLAG_EMIT | NGHTTP2_HD_FLAG_IMPLICIT_EMIT); } return offset - nv_offset; fail: @@ -874,6 +814,28 @@ ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_context *deflater, return rv; } +static int inflater_post_process_hd_entry(nghttp2_hd_context *inflater, + nghttp2_hd_entry *ent, + nghttp2_nva_out *nva_out_ptr) +{ + int rv; + if((ent->flags & NGHTTP2_HD_FLAG_REFSET) && + (ent->flags & NGHTTP2_HD_FLAG_EMIT) == 0) { + rv = emit_indexed_header(inflater, nva_out_ptr, ent); + if(rv != 0) { + return rv; + } + } + ent->flags &= ~NGHTTP2_HD_FLAG_EMIT; + return 0; +} + +static int check_index_range(nghttp2_hd_context *context, size_t index) +{ + return index < context->hd_table.len + + sizeof(static_table)/sizeof(static_table[0])/2; +} + ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_context *inflater, nghttp2_nv **nva_ptr, uint8_t *in, size_t inlen) @@ -898,11 +860,11 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_context *inflater, rv = NGHTTP2_ERR_HEADER_COMP; goto fail; } - if(inflater->hd_tablelen <= index) { + if(!check_index_range(inflater, index)) { rv = NGHTTP2_ERR_HEADER_COMP; goto fail; } - ent = inflater->hd_table[index]; + ent = nghttp2_hd_table_get(inflater, index); ent->flags ^= NGHTTP2_HD_FLAG_REFSET; if(ent->flags & NGHTTP2_HD_FLAG_REFSET) { rv = emit_indexed_header(inflater, &nva_out, ent); @@ -910,10 +872,10 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_context *inflater, goto fail; } } - } else if(c == 0x40u || c == 0x60u || c == 0) { + } else if(c == 0x40u || c == 0) { /* Literal Header Repr - New Name */ nghttp2_nv nv; - ssize_t namelen, valuelen, subindex; + ssize_t namelen, valuelen; if(++in == last) { rv = NGHTTP2_ERR_HEADER_COMP; goto fail; @@ -929,13 +891,6 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_context *inflater, } nv.name = in; in += namelen; - if(c == 0) { - in = decode_length(&subindex, in, last, 0); - if(subindex < 0) { - rv = NGHTTP2_ERR_HEADER_COMP; - goto fail; - } - } in = decode_length(&valuelen, in, last, 0); if(valuelen < 0 || in + valuelen > last) { rv = NGHTTP2_ERR_HEADER_COMP; @@ -946,15 +901,11 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_context *inflater, nv.valuelen = valuelen; in += valuelen; nghttp2_downcase(nv.name, nv.namelen); - if(c == 0x60u) { + if(c == 0x40u) { rv = emit_newname_header(inflater, &nva_out, &nv); } else { nghttp2_hd_entry *new_ent; - if(c == 0) { - new_ent = add_hd_table_subst(inflater, &nv, subindex); - } else { - new_ent = add_hd_table_incremental(inflater, NULL, NULL, NULL, &nv); - } + new_ent = add_hd_table_incremental(inflater, NULL, NULL, NULL, &nv); if(new_ent) { rv = emit_indexed_header(inflater, &nva_out, new_ent); } else { @@ -968,25 +919,18 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_context *inflater, /* Literal Header Repr - Indexed Name */ nghttp2_hd_entry *ent; uint8_t *value; - ssize_t valuelen, index, subindex; - in = decode_length(&index, in, last, (c & 0x40u) ? 5 : 6); + ssize_t valuelen, index; + in = decode_length(&index, in, last, 6); if(index < 0) { rv = NGHTTP2_ERR_HEADER_COMP; goto fail; } --index; - if(inflater->hd_tablelen <= index) { + if(!check_index_range(inflater, index)) { rv = NGHTTP2_ERR_HEADER_COMP; goto fail; } - ent = inflater->hd_table[index]; - if((c & 0x40u) == 0) { - in = decode_length(&subindex, in, last, 0); - if(subindex < 0) { - rv = NGHTTP2_ERR_HEADER_COMP; - goto fail; - } - } + ent = nghttp2_hd_table_get(inflater, index); in = decode_length(&valuelen, in , last, 0); if(valuelen < 0 || in + valuelen > last) { rv = NGHTTP2_ERR_HEADER_COMP; @@ -994,7 +938,7 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_context *inflater, } value = in; in += valuelen; - if((c & 0x60u) == 0x60u) { + if((c & 0x40u) == 0x40u) { rv = emit_indname_header(inflater, &nva_out, ent, value, valuelen); } else { nghttp2_nv nv; @@ -1004,11 +948,7 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_context *inflater, nv.namelen = ent->nv.namelen; nv.value = value; nv.valuelen = valuelen; - if((c & 0x40u) == 0) { - new_ent = add_hd_table_subst(inflater, &nv, subindex); - } else { - new_ent = add_hd_table_incremental(inflater, NULL, NULL, NULL, &nv); - } + new_ent = add_hd_table_incremental(inflater, NULL, NULL, NULL, &nv); if(--ent->ref == 0) { nghttp2_hd_entry_free(ent); free(ent); @@ -1024,16 +964,19 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_context *inflater, } } } - for(i = 0; i < inflater->hd_tablelen; ++i) { - nghttp2_hd_entry *ent = inflater->hd_table[i]; - if((ent->flags & NGHTTP2_HD_FLAG_REFSET) && - (ent->flags & NGHTTP2_HD_FLAG_EMIT) == 0) { - rv = emit_indexed_header(inflater, &nva_out, ent); - if(rv != 0) { - goto fail; - } + for(i = 0; i < inflater->hd_table.len; ++i) { + nghttp2_hd_entry *ent = nghttp2_hd_ringbuf_get(&inflater->hd_table, i); + rv = inflater_post_process_hd_entry(inflater, ent, &nva_out); + if(rv != 0) { + goto fail; + } + } + for(i = 0; inflater->static_hd_table[i]; ++i) { + nghttp2_hd_entry *ent = inflater->static_hd_table[i]; + rv = inflater_post_process_hd_entry(inflater, ent, &nva_out); + if(rv != 0) { + goto fail; } - ent->flags &= ~NGHTTP2_HD_FLAG_EMIT; } nghttp2_nv_array_sort(nva_out.nva, nva_out.nvlen); *nva_ptr = nva_out.nva; @@ -1073,20 +1016,3 @@ int nghttp2_hd_emit_newname_block(uint8_t **buf_ptr, size_t *buflen_ptr, { return emit_newname_block(buf_ptr, buflen_ptr, offset_ptr, nv, inc_indexing); } - -int nghttp2_hd_emit_subst_indname_block(uint8_t **buf_ptr, size_t *buflen_ptr, - size_t *offset_ptr, size_t index, - const uint8_t *value, size_t valuelen, - size_t subindex) -{ - return emit_subst_indname_block(buf_ptr, buflen_ptr, offset_ptr, index, - value, valuelen, subindex); -} - -int nghttp2_hd_emit_subst_newname_block(uint8_t **buf_ptr, size_t *buflen_ptr, - size_t *offset_ptr, nghttp2_nv *nv, - size_t subindex) -{ - return emit_subst_newname_block(buf_ptr, buflen_ptr, offset_ptr, nv, - subindex); -} diff --git a/lib/nghttp2_hd.h b/lib/nghttp2_hd.h index 84fe45c9..cd011a6c 100644 --- a/lib/nghttp2_hd.h +++ b/lib/nghttp2_hd.h @@ -70,21 +70,23 @@ typedef struct { nghttp2_nv nv; /* Reference count */ uint8_t ref; - /* Index in the header table */ - uint8_t index; uint8_t flags; } nghttp2_hd_entry; +typedef struct { + nghttp2_hd_entry **buffer; + size_t mask; + size_t first; + size_t len; +} nghttp2_hd_ringbuf; + typedef struct { /* Header table */ - nghttp2_hd_entry **hd_table; + nghttp2_hd_ringbuf hd_table; + nghttp2_hd_entry **static_hd_table; /* Holding emitted entry in deflating header block to retain reference count. */ nghttp2_hd_entry **emit_set; - /* The capacity of the |hd_table| */ - uint16_t hd_table_capacity; - /* The number of entry the |hd_table| contains */ - uint16_t hd_tablelen; /* The capacity of the |emit_set| */ uint16_t emit_set_capacity; /* The number of entry the |emit_set| contains */ @@ -114,7 +116,7 @@ typedef struct { * NGHTTP2_ERR_NOMEM * Out of memory. */ -int nghttp2_hd_entry_init(nghttp2_hd_entry *ent, uint8_t index, uint8_t flags, +int nghttp2_hd_entry_init(nghttp2_hd_entry *ent, uint8_t flags, uint8_t *name, uint16_t namelen, uint8_t *value, uint16_t valuelen); @@ -238,4 +240,8 @@ int nghttp2_hd_emit_subst_newname_block(uint8_t **buf_ptr, size_t *buflen_ptr, size_t *offset_ptr, nghttp2_nv *nv, size_t subindex); +/* For unittesting purpose */ +nghttp2_hd_entry* nghttp2_hd_table_get(nghttp2_hd_context *context, + size_t index); + #endif /* NGHTTP2_HD_COMP_H */ diff --git a/tests/main.c b/tests/main.c index 529df8b2..73c7b69b 100644 --- a/tests/main.c +++ b/tests/main.c @@ -237,16 +237,6 @@ int main(int argc, char* argv[]) test_nghttp2_hd_inflate_indname_inc_eviction) || !CU_add_test(pSuite, "hd_inflate_newname_inc", test_nghttp2_hd_inflate_newname_inc) || - !CU_add_test(pSuite, "hd_inflate_indname_subst", - test_nghttp2_hd_inflate_indname_subst) || - !CU_add_test(pSuite, "hd_inflate_indname_subst_eviction", - test_nghttp2_hd_inflate_indname_subst_eviction) || - !CU_add_test(pSuite, "hd_inflate_indname_subst_eviction_neg", - test_nghttp2_hd_inflate_indname_subst_eviction_neg) || - !CU_add_test(pSuite, "hd_inflate_newname_subst", - test_nghttp2_hd_inflate_newname_subst) || - !CU_add_test(pSuite, "hd_inflate_clearall_subst", - test_nghttp2_hd_inflate_clearall_subst) || !CU_add_test(pSuite, "hd_inflate_clearall_inc", test_nghttp2_hd_inflate_clearall_inc) || !CU_add_test(pSuite, "hd_deflate_inflate", diff --git a/tests/nghttp2_hd_test.c b/tests/nghttp2_hd_test.c index 7deb2234..d1972691 100644 --- a/tests/nghttp2_hd_test.c +++ b/tests/nghttp2_hd_test.c @@ -33,6 +33,8 @@ #include "nghttp2_frame.h" #include "nghttp2_test_helper.h" +#define GET_TABLE_ENT(context, index) nghttp2_hd_table_get(context, index) + static void assert_nv_equal(nghttp2_nv *a, nghttp2_nv *b, size_t len) { size_t i; @@ -65,15 +67,12 @@ void test_nghttp2_hd_deflate(void) CU_ASSERT(0 == nghttp2_hd_deflate_init(&deflater, NGHTTP2_HD_SIDE_CLIENT)); CU_ASSERT(0 == nghttp2_hd_inflate_init(&inflater, NGHTTP2_HD_SIDE_SERVER)); - blocklen = nghttp2_hd_deflate_hd(&deflater, &buf, &buflen, nv_offset, nva1, sizeof(nva1)/sizeof(nghttp2_nv)); CU_ASSERT(blocklen > 0); nghttp2_hd_end_headers(&deflater); - CU_ASSERT(3 == nghttp2_hd_inflate_hd(&inflater, &resnva, buf + nv_offset, blocklen)); - assert_nv_equal(nva1, resnva, 3); nghttp2_nv_array_del(resnva); @@ -99,10 +98,8 @@ void test_nghttp2_hd_deflate(void) sizeof(nva3)/sizeof(nghttp2_nv)); CU_ASSERT(blocklen > 0); nghttp2_hd_end_headers(&deflater); - CU_ASSERT(3 == nghttp2_hd_inflate_hd(&inflater, &resnva, buf + nv_offset, blocklen)); - assert_nv_equal(nva3, resnva, 3); nghttp2_nv_array_del(resnva); @@ -216,8 +213,8 @@ void test_nghttp2_hd_deflate_common_header_eviction(void) nghttp2_hd_inflate_init(&inflater, NGHTTP2_HD_SIDE_SERVER); /* Put :scheme: http (index = 0) in reference set */ - deflater.hd_table[0]->flags |= NGHTTP2_HD_FLAG_REFSET; - inflater.hd_table[0]->flags |= NGHTTP2_HD_FLAG_REFSET; + GET_TABLE_ENT(&deflater, 0)->flags |= NGHTTP2_HD_FLAG_REFSET; + GET_TABLE_ENT(&inflater, 0)->flags |= NGHTTP2_HD_FLAG_REFSET; blocklen = nghttp2_hd_deflate_hd(&deflater, &buf, &buflen, 0, nva, sizeof(nva)/sizeof(nghttp2_nv)); CU_ASSERT(blocklen > 0); @@ -252,8 +249,9 @@ void test_nghttp2_hd_inflate_indname_inc(void) nv.value, nv.valuelen, 1)); CU_ASSERT(1 == nghttp2_hd_inflate_hd(&inflater, &resnva, buf, offset)); assert_nv_equal(&nv, resnva, 1); - CU_ASSERT(31 == inflater.hd_tablelen); - assert_nv_equal(&nv, &inflater.hd_table[inflater.hd_tablelen-1]->nv, 1); + CU_ASSERT(1 == inflater.hd_table.len); + assert_nv_equal(&nv, + &GET_TABLE_ENT(&inflater, inflater.hd_table.len-1)->nv, 1); nghttp2_nv_array_del(resnva); free(buf); @@ -266,17 +264,21 @@ void test_nghttp2_hd_inflate_indname_inc_eviction(void) uint8_t *buf = NULL; size_t buflen = 0; size_t offset = 0; - /* Default header table capacity is 1262. Adding 2835 bytes, - including overhead, to the table evicts first entry. - use name ":host" which index 2 and value length 2798. */ - uint8_t value[2798]; + uint8_t value[1024]; nghttp2_nv *resnva; nghttp2_hd_inflate_init(&inflater, NGHTTP2_HD_SIDE_SERVER); memset(value, '0', sizeof(value)); CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&buf, &buflen, &offset, 2, value, sizeof(value), 1)); - CU_ASSERT(1 == nghttp2_hd_inflate_hd(&inflater, &resnva, buf, offset)); + CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&buf, &buflen, &offset, 3, + value, sizeof(value), 1)); + CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&buf, &buflen, &offset, 4, + value, sizeof(value), 1)); + CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&buf, &buflen, &offset, 5, + value, sizeof(value), 1)); + + CU_ASSERT(4 == nghttp2_hd_inflate_hd(&inflater, &resnva, buf, offset)); CU_ASSERT(5 == resnva[0].namelen); CU_ASSERT(0 == memcmp(":host", resnva[0].name, resnva[0].namelen)); CU_ASSERT(sizeof(value) == resnva[0].valuelen); @@ -284,8 +286,8 @@ void test_nghttp2_hd_inflate_indname_inc_eviction(void) nghttp2_nv_array_del(resnva); nghttp2_hd_end_headers(&inflater); - CU_ASSERT(30 == inflater.hd_tablelen); - CU_ASSERT(inflater.hd_table[29]->flags & NGHTTP2_HD_FLAG_REFSET); + CU_ASSERT(3 == inflater.hd_table.len); + CU_ASSERT(GET_TABLE_ENT(&inflater, 0)->flags & NGHTTP2_HD_FLAG_REFSET); free(buf); nghttp2_hd_inflate_free(&inflater); @@ -305,118 +307,9 @@ void test_nghttp2_hd_inflate_newname_inc(void) &nv, 1)); CU_ASSERT(1 == nghttp2_hd_inflate_hd(&inflater, &resnva, buf, offset)); assert_nv_equal(&nv, resnva, 1); - CU_ASSERT(31 == inflater.hd_tablelen); - assert_nv_equal(&nv, &inflater.hd_table[inflater.hd_tablelen-1]->nv, 1); - - nghttp2_nv_array_del(resnva); - free(buf); - nghttp2_hd_inflate_free(&inflater); -} - -void test_nghttp2_hd_inflate_indname_subst(void) -{ - nghttp2_hd_context inflater; - uint8_t *buf = NULL; - size_t buflen = 0; - size_t offset = 0; - nghttp2_nv nv = MAKE_NV("user-agent", "nghttp2"); - nghttp2_nv *resnva; - nghttp2_hd_inflate_init(&inflater, NGHTTP2_HD_SIDE_SERVER); - - CU_ASSERT(0 == nghttp2_hd_emit_subst_indname_block(&buf, &buflen, &offset, - 11, - nv.value, nv.valuelen, - 11)); - CU_ASSERT(1 == nghttp2_hd_inflate_hd(&inflater, &resnva, buf, offset)); - assert_nv_equal(&nv, resnva, 1); - CU_ASSERT(30 == inflater.hd_tablelen); - assert_nv_equal(&nv, &inflater.hd_table[11]->nv, 1); - - nghttp2_nv_array_del(resnva); - free(buf); - nghttp2_hd_inflate_free(&inflater); -} - -void test_nghttp2_hd_inflate_indname_subst_eviction(void) -{ - nghttp2_hd_context inflater; - uint8_t *buf = NULL; - size_t buflen = 0; - size_t offset = 0; - /* Default header table capacity is 1262. Adding 2877 bytes, - including overhead, to the table evicts first entry. - use name ":host" which index 2 and value length 2840. */ - uint8_t value[2840]; - nghttp2_nv *resnva; - nghttp2_hd_inflate_init(&inflater, NGHTTP2_HD_SIDE_SERVER); - - memset(value, '0', sizeof(value)); - CU_ASSERT(0 == nghttp2_hd_emit_subst_indname_block(&buf, &buflen, &offset, - 2, - value, sizeof(value), 2)); - CU_ASSERT(1 == nghttp2_hd_inflate_hd(&inflater, &resnva, buf, offset)); - CU_ASSERT(5 == resnva[0].namelen); - CU_ASSERT(0 == memcmp(":host", resnva[0].name, resnva[0].namelen)); - CU_ASSERT(sizeof(value) == resnva[0].valuelen); - - nghttp2_nv_array_del(resnva); - nghttp2_hd_end_headers(&inflater); - - CU_ASSERT(29 == inflater.hd_tablelen); - CU_ASSERT(inflater.hd_table[1]->flags & NGHTTP2_HD_FLAG_REFSET); - - free(buf); - nghttp2_hd_inflate_free(&inflater); -} - -void test_nghttp2_hd_inflate_indname_subst_eviction_neg(void) -{ - nghttp2_hd_context inflater; - uint8_t *buf = NULL; - size_t buflen = 0; - size_t offset = 0; - /* Default header table capacity is 1262. Adding 2878 bytes, - including overhead, to the table evicts first 2 entries. - use name ":host" which index 2 and value length 2841. */ - uint8_t value[2841]; - nghttp2_nv *resnva; - nghttp2_hd_inflate_init(&inflater, NGHTTP2_HD_SIDE_SERVER); - memset(value, '0', sizeof(value)); - /* Try to substitute index 0, but it will be evicted */ - CU_ASSERT(0 == nghttp2_hd_emit_subst_indname_block(&buf, &buflen, &offset, - 2, - value, sizeof(value), 0)); - CU_ASSERT(1 == nghttp2_hd_inflate_hd(&inflater, &resnva, buf, offset)); - CU_ASSERT(5 == resnva[0].namelen); - CU_ASSERT(0 == memcmp(":host", resnva[0].name, resnva[0].namelen)); - CU_ASSERT(sizeof(value) == resnva[0].valuelen); - - nghttp2_nv_array_del(resnva); - nghttp2_hd_end_headers(&inflater); - - CU_ASSERT(29 == inflater.hd_tablelen); - CU_ASSERT(inflater.hd_table[0]->flags & NGHTTP2_HD_FLAG_REFSET); - - free(buf); - nghttp2_hd_inflate_free(&inflater); -} - -void test_nghttp2_hd_inflate_newname_subst(void) -{ - nghttp2_hd_context inflater; - uint8_t *buf = NULL; - size_t buflen = 0; - size_t offset = 0; - nghttp2_nv nv = MAKE_NV("x-rel", "nghttp2"); - nghttp2_nv *resnva; - nghttp2_hd_inflate_init(&inflater, NGHTTP2_HD_SIDE_SERVER); - - CU_ASSERT(0 == nghttp2_hd_emit_subst_newname_block(&buf, &buflen, &offset, - &nv, 1)); - CU_ASSERT(1 == nghttp2_hd_inflate_hd(&inflater, &resnva, buf, offset)); - assert_nv_equal(&nv, resnva, 1); - CU_ASSERT(30 == inflater.hd_tablelen); - assert_nv_equal(&nv, &inflater.hd_table[1]->nv, 1); + CU_ASSERT(1 == inflater.hd_table.len); + assert_nv_equal(&nv, + &GET_TABLE_ENT(&inflater, inflater.hd_table.len-1)->nv, 1); nghttp2_nv_array_del(resnva); free(buf); @@ -446,7 +339,7 @@ void test_nghttp2_hd_inflate_clearall_inc(void) &nv, 1)); CU_ASSERT(1 == nghttp2_hd_inflate_hd(&inflater, &resnva, buf, offset)); assert_nv_equal(&nv, resnva, 1); - CU_ASSERT(0 == inflater.hd_tablelen); + CU_ASSERT(0 == inflater.hd_table.len); nghttp2_nv_array_del(resnva); nghttp2_hd_end_headers(&inflater); @@ -454,7 +347,7 @@ void test_nghttp2_hd_inflate_clearall_inc(void) /* Do it again */ CU_ASSERT(1 == nghttp2_hd_inflate_hd(&inflater, &resnva, buf, offset)); assert_nv_equal(&nv, resnva, 1); - CU_ASSERT(0 == inflater.hd_tablelen); + CU_ASSERT(0 == inflater.hd_table.len); nghttp2_nv_array_del(resnva); nghttp2_hd_end_headers(&inflater); @@ -468,38 +361,7 @@ void test_nghttp2_hd_inflate_clearall_inc(void) &nv, 1)); CU_ASSERT(1 == nghttp2_hd_inflate_hd(&inflater, &resnva, buf, offset)); assert_nv_equal(&nv, resnva, 1); - CU_ASSERT(1 == inflater.hd_tablelen); - - nghttp2_nv_array_del(resnva); - - free(buf); - nghttp2_hd_inflate_free(&inflater); -} - -void test_nghttp2_hd_inflate_clearall_subst(void) -{ - nghttp2_hd_context inflater; - uint8_t *buf = NULL; - size_t buflen = 0; - size_t offset = 0; - nghttp2_nv nv; - nghttp2_nv *resnva; - uint8_t value[4060]; - - /* Total 4097 bytes space required to hold this entry */ - nv.name = (uint8_t*)"alpha"; - nv.namelen = strlen((char*)nv.name); - memset(value, '0', sizeof(value)); - nv.value = value; - nv.valuelen = sizeof(value); - - nghttp2_hd_inflate_init(&inflater, NGHTTP2_HD_SIDE_SERVER); - - CU_ASSERT(0 == nghttp2_hd_emit_subst_newname_block(&buf, &buflen, &offset, - &nv, 1)); - CU_ASSERT(1 == nghttp2_hd_inflate_hd(&inflater, &resnva, buf, offset)); - assert_nv_equal(&nv, resnva, 1); - CU_ASSERT(0 == inflater.hd_tablelen); + CU_ASSERT(1 == inflater.hd_table.len); nghttp2_nv_array_del(resnva); diff --git a/tests/nghttp2_hd_test.h b/tests/nghttp2_hd_test.h index f5893f80..eab10001 100644 --- a/tests/nghttp2_hd_test.h +++ b/tests/nghttp2_hd_test.h @@ -31,12 +31,7 @@ void test_nghttp2_hd_deflate_common_header_eviction(void); void test_nghttp2_hd_inflate_indname_inc(void); void test_nghttp2_hd_inflate_indname_inc_eviction(void); void test_nghttp2_hd_inflate_newname_inc(void); -void test_nghttp2_hd_inflate_indname_subst(void); -void test_nghttp2_hd_inflate_indname_subst_eviction(void); -void test_nghttp2_hd_inflate_indname_subst_eviction_neg(void); -void test_nghttp2_hd_inflate_newname_subst(void); void test_nghttp2_hd_inflate_clearall_inc(void); -void test_nghttp2_hd_inflate_clearall_subst(void); void test_nghttp2_hd_deflate_inflate(void); #endif /* NGHTTP2_HD_TEST_H */