aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core/transport
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/transport')
-rw-r--r--src/core/transport/chttp2/hpack_encoder.c123
-rw-r--r--src/core/transport/chttp2/hpack_encoder.h32
-rw-r--r--src/core/transport/chttp2/hpack_parser.c100
-rw-r--r--src/core/transport/chttp2/hpack_parser.h3
-rw-r--r--src/core/transport/chttp2/hpack_table.c122
-rw-r--r--src/core/transport/chttp2/hpack_table.h35
-rw-r--r--src/core/transport/chttp2/internal.h13
-rw-r--r--src/core/transport/chttp2/parsing.c24
-rw-r--r--src/core/transport/chttp2/writing.c11
-rw-r--r--src/core/transport/chttp2_transport.c98
-rw-r--r--src/core/transport/chttp2_transport.h2
-rw-r--r--src/core/transport/metadata.c580
-rw-r--r--src/core/transport/metadata.h42
-rw-r--r--src/core/transport/static_metadata.c157
-rw-r--r--src/core/transport/static_metadata.h402
15 files changed, 1266 insertions, 478 deletions
diff --git a/src/core/transport/chttp2/hpack_encoder.c b/src/core/transport/chttp2/hpack_encoder.c
index 6c7c00b7c3..28d5433d15 100644
--- a/src/core/transport/chttp2/hpack_encoder.c
+++ b/src/core/transport/chttp2/hpack_encoder.c
@@ -36,12 +36,15 @@
#include <assert.h>
#include <string.h>
+#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/useful.h>
+
#include "src/core/transport/chttp2/bin_encoder.h"
#include "src/core/transport/chttp2/hpack_table.h"
#include "src/core/transport/chttp2/timeout_encoding.h"
#include "src/core/transport/chttp2/varint.h"
+#include "src/core/transport/static_metadata.h"
#define HASH_FRAGMENT_1(x) ((x)&255)
#define HASH_FRAGMENT_2(x) ((x >> 8) & 255)
@@ -154,6 +157,18 @@ static gpr_uint8 *add_tiny_header_data(framer_state *st, size_t len) {
return gpr_slice_buffer_tiny_add(st->output, len);
}
+static void evict_entry(grpc_chttp2_hpack_compressor *c) {
+ c->tail_remote_index++;
+ GPR_ASSERT(c->tail_remote_index > 0);
+ GPR_ASSERT(c->table_size >=
+ c->table_elem_size[c->tail_remote_index % c->cap_table_elems]);
+ GPR_ASSERT(c->table_elems > 0);
+ c->table_size = (gpr_uint16)(
+ c->table_size -
+ c->table_elem_size[c->tail_remote_index % c->cap_table_elems]);
+ c->table_elems--;
+}
+
/* add an element to the decoder table */
static void add_elem(grpc_chttp2_hpack_compressor *c, grpc_mdelem *elem) {
gpr_uint32 key_hash = elem->key->hash;
@@ -164,26 +179,21 @@ static void add_elem(grpc_chttp2_hpack_compressor *c, grpc_mdelem *elem) {
GPR_ASSERT(elem_size < 65536);
+ if (elem_size > c->max_table_size) {
+ while (c->table_size > 0) {
+ evict_entry(c);
+ }
+ return;
+ }
+
/* Reserve space for this element in the remote table: if this overflows
the current table, drop elements until it fits, matching the decompressor
algorithm */
- /* TODO(ctiller): constant */
- while (c->table_size + elem_size > 4096) {
- c->tail_remote_index++;
- GPR_ASSERT(c->tail_remote_index > 0);
- GPR_ASSERT(c->table_size >=
- c->table_elem_size[c->tail_remote_index %
- GRPC_CHTTP2_HPACKC_MAX_TABLE_ELEMS]);
- GPR_ASSERT(c->table_elems > 0);
- c->table_size =
- (gpr_uint16)(c->table_size -
- c->table_elem_size[c->tail_remote_index %
- GRPC_CHTTP2_HPACKC_MAX_TABLE_ELEMS]);
- c->table_elems--;
+ while (c->table_size + elem_size > c->max_table_size) {
+ evict_entry(c);
}
- GPR_ASSERT(c->table_elems < GRPC_CHTTP2_HPACKC_MAX_TABLE_ELEMS);
- c->table_elem_size[new_index % GRPC_CHTTP2_HPACKC_MAX_TABLE_ELEMS] =
- (gpr_uint16)elem_size;
+ GPR_ASSERT(c->table_elems < c->max_table_size);
+ c->table_elem_size[new_index % c->cap_table_elems] = (gpr_uint16)elem_size;
c->table_size = (gpr_uint16)(c->table_size + elem_size);
c->table_elems++;
@@ -329,6 +339,14 @@ static void emit_lithdr_noidx_v(grpc_chttp2_hpack_compressor *c,
add_header_data(st, gpr_slice_ref(value_slice));
}
+static void emit_advertise_table_size_change(grpc_chttp2_hpack_compressor *c,
+ framer_state *st) {
+ gpr_uint32 len = GRPC_CHTTP2_VARINT_LENGTH(c->max_table_size, 3);
+ GRPC_CHTTP2_WRITE_VARINT(c->max_table_size, 3, 0x20,
+ add_tiny_header_data(st, len), len);
+ c->advertise_table_size_change = 0;
+}
+
static gpr_uint32 dynidx(grpc_chttp2_hpack_compressor *c,
gpr_uint32 elem_index) {
return 1 + GRPC_CHTTP2_LAST_STATIC_ENTRY + c->tail_remote_index +
@@ -435,8 +453,7 @@ static void deadline_enc(grpc_chttp2_hpack_compressor *c, gpr_timespec deadline,
grpc_chttp2_encode_timeout(
gpr_time_sub(deadline, gpr_now(deadline.clock_type)), timeout_str);
mdelem = grpc_mdelem_from_metadata_strings(
- c->mdctx, GRPC_MDSTR_REF(c->timeout_key_str),
- grpc_mdstr_from_string(c->mdctx, timeout_str));
+ GRPC_MDSTR_GRPC_TIMEOUT, grpc_mdstr_from_string(timeout_str));
hpack_enc(c, mdelem, st);
GRPC_MDELEM_UNREF(mdelem);
}
@@ -447,11 +464,20 @@ gpr_slice grpc_chttp2_data_frame_create_empty_close(gpr_uint32 id) {
return slice;
}
-void grpc_chttp2_hpack_compressor_init(grpc_chttp2_hpack_compressor *c,
- grpc_mdctx *ctx) {
+static gpr_uint32 elems_for_bytes(gpr_uint32 bytes) {
+ return (bytes + 31) / 32;
+}
+
+void grpc_chttp2_hpack_compressor_init(grpc_chttp2_hpack_compressor *c) {
memset(c, 0, sizeof(*c));
- c->mdctx = ctx;
- c->timeout_key_str = grpc_mdstr_from_string(ctx, "grpc-timeout");
+ c->max_table_size = GRPC_CHTTP2_HPACKC_INITIAL_TABLE_SIZE;
+ c->cap_table_elems = elems_for_bytes(c->max_table_size);
+ c->max_table_elems = c->cap_table_elems;
+ c->max_usable_size = GRPC_CHTTP2_HPACKC_INITIAL_TABLE_SIZE;
+ c->table_elem_size =
+ gpr_malloc(sizeof(*c->table_elem_size) * c->cap_table_elems);
+ memset(c->table_elem_size, 0,
+ sizeof(*c->table_elem_size) * c->cap_table_elems);
}
void grpc_chttp2_hpack_compressor_destroy(grpc_chttp2_hpack_compressor *c) {
@@ -460,7 +486,55 @@ void grpc_chttp2_hpack_compressor_destroy(grpc_chttp2_hpack_compressor *c) {
if (c->entries_keys[i]) GRPC_MDSTR_UNREF(c->entries_keys[i]);
if (c->entries_elems[i]) GRPC_MDELEM_UNREF(c->entries_elems[i]);
}
- GRPC_MDSTR_UNREF(c->timeout_key_str);
+ gpr_free(c->table_elem_size);
+}
+
+void grpc_chttp2_hpack_compressor_set_max_usable_size(
+ grpc_chttp2_hpack_compressor *c, gpr_uint32 max_table_size) {
+ c->max_usable_size = max_table_size;
+ grpc_chttp2_hpack_compressor_set_max_table_size(
+ c, GPR_MIN(c->max_table_size, max_table_size));
+}
+
+static void rebuild_elems(grpc_chttp2_hpack_compressor *c, gpr_uint32 new_cap) {
+ gpr_uint16 *table_elem_size = gpr_malloc(sizeof(*table_elem_size) * new_cap);
+ gpr_uint32 i;
+
+ memset(table_elem_size, 0, sizeof(*table_elem_size) * new_cap);
+ GPR_ASSERT(c->table_elems <= new_cap);
+
+ for (i = 0; i < c->table_elems; i++) {
+ gpr_uint32 ofs = c->tail_remote_index + i + 1;
+ table_elem_size[ofs % new_cap] =
+ c->table_elem_size[ofs % c->cap_table_elems];
+ }
+
+ c->cap_table_elems = new_cap;
+ gpr_free(c->table_elem_size);
+ c->table_elem_size = table_elem_size;
+}
+
+void grpc_chttp2_hpack_compressor_set_max_table_size(
+ grpc_chttp2_hpack_compressor *c, gpr_uint32 max_table_size) {
+ max_table_size = GPR_MIN(max_table_size, c->max_usable_size);
+ if (max_table_size == c->max_table_size) {
+ return;
+ }
+ while (c->table_size > 0 && c->table_size > max_table_size) {
+ evict_entry(c);
+ }
+ c->max_table_size = max_table_size;
+ c->max_table_elems = elems_for_bytes(max_table_size);
+ if (c->max_table_elems > c->cap_table_elems) {
+ rebuild_elems(c, GPR_MAX(c->max_table_elems, 2 * c->cap_table_elems));
+ } else if (c->max_table_elems < c->cap_table_elems / 3) {
+ gpr_uint32 new_cap = GPR_MAX(c->max_table_elems, 16);
+ if (new_cap != c->cap_table_elems) {
+ rebuild_elems(c, new_cap);
+ }
+ }
+ c->advertise_table_size_change = 1;
+ gpr_log(GPR_DEBUG, "set max table size from encoder to %d", max_table_size);
}
void grpc_chttp2_encode_header(grpc_chttp2_hpack_compressor *c,
@@ -483,6 +557,9 @@ void grpc_chttp2_encode_header(grpc_chttp2_hpack_compressor *c,
slot. THIS MAY NOT BE THE SAME ELEMENT (if a decoder table slot got
updated). After this loop, we'll do a batch unref of elements. */
begin_frame(&st);
+ if (c->advertise_table_size_change != 0) {
+ emit_advertise_table_size_change(c, &st);
+ }
grpc_metadata_batch_assert_ok(metadata);
for (l = metadata->list.head; l; l = l->next) {
hpack_enc(c, l->md, &st);
diff --git a/src/core/transport/chttp2/hpack_encoder.h b/src/core/transport/chttp2/hpack_encoder.h
index 59b122dfda..a3600436e9 100644
--- a/src/core/transport/chttp2/hpack_encoder.h
+++ b/src/core/transport/chttp2/hpack_encoder.h
@@ -43,14 +43,26 @@
#define GRPC_CHTTP2_HPACKC_NUM_FILTERS 256
#define GRPC_CHTTP2_HPACKC_NUM_VALUES 256
-#define GRPC_CHTTP2_HPACKC_MAX_TABLE_ELEMS (4096 / 32)
+/* initial table size, per spec */
+#define GRPC_CHTTP2_HPACKC_INITIAL_TABLE_SIZE 4096
+/* maximum table size we'll actually use */
+#define GRPC_CHTTP2_HPACKC_MAX_TABLE_SIZE (1024 * 1024)
typedef struct {
gpr_uint32 filter_elems_sum;
+ gpr_uint32 max_table_size;
+ gpr_uint32 max_table_elems;
+ gpr_uint32 cap_table_elems;
+ /** if non-zero, advertise to the decoder that we'll start using a table
+ of this size */
+ gpr_uint8 advertise_table_size_change;
+ /** maximum number of bytes we'll use for the decode table (to guard against
+ peers ooming us by setting decode table size high) */
+ gpr_uint32 max_usable_size;
/* one before the lowest usable table index */
gpr_uint32 tail_remote_index;
- gpr_uint16 table_size;
- gpr_uint16 table_elems;
+ gpr_uint32 table_size;
+ gpr_uint32 table_elems;
/* filter tables for elems: this tables provides an approximate
popularity count for particular hashes, and are used to determine whether
@@ -59,11 +71,6 @@ typedef struct {
been seen. When that count reaches max (255), all values are halved. */
gpr_uint8 filter_elems[GRPC_CHTTP2_HPACKC_NUM_FILTERS];
- /* metadata context */
- grpc_mdctx *mdctx;
- /* the string 'grpc-timeout' */
- grpc_mdstr *timeout_key_str;
-
/* entry tables for keys & elems: these tables track values that have been
seen and *may* be in the decompressor table */
grpc_mdstr *entries_keys[GRPC_CHTTP2_HPACKC_NUM_VALUES];
@@ -71,12 +78,15 @@ typedef struct {
gpr_uint32 indices_keys[GRPC_CHTTP2_HPACKC_NUM_VALUES];
gpr_uint32 indices_elems[GRPC_CHTTP2_HPACKC_NUM_VALUES];
- gpr_uint16 table_elem_size[GRPC_CHTTP2_HPACKC_MAX_TABLE_ELEMS];
+ gpr_uint16 *table_elem_size;
} grpc_chttp2_hpack_compressor;
-void grpc_chttp2_hpack_compressor_init(grpc_chttp2_hpack_compressor *c,
- grpc_mdctx *mdctx);
+void grpc_chttp2_hpack_compressor_init(grpc_chttp2_hpack_compressor *c);
void grpc_chttp2_hpack_compressor_destroy(grpc_chttp2_hpack_compressor *c);
+void grpc_chttp2_hpack_compressor_set_max_table_size(
+ grpc_chttp2_hpack_compressor *c, gpr_uint32 max_table_size);
+void grpc_chttp2_hpack_compressor_set_max_usable_size(
+ grpc_chttp2_hpack_compressor *c, gpr_uint32 max_table_size);
void grpc_chttp2_encode_header(grpc_chttp2_hpack_compressor *c, gpr_uint32 id,
grpc_metadata_batch *metadata, int is_eof,
diff --git a/src/core/transport/chttp2/hpack_parser.c b/src/core/transport/chttp2/hpack_parser.c
index 6eebfc3ce4..d38ff68754 100644
--- a/src/core/transport/chttp2/hpack_parser.c
+++ b/src/core/transport/chttp2/hpack_parser.c
@@ -74,6 +74,8 @@ static int parse_begin(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur,
const gpr_uint8 *end);
static int parse_error(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur,
const gpr_uint8 *end);
+static int parse_illegal_op(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur,
+ const gpr_uint8 *end);
static int parse_string_prefix(grpc_chttp2_hpack_parser *p,
const gpr_uint8 *cur, const gpr_uint8 *end);
@@ -156,7 +158,7 @@ static const grpc_chttp2_hpack_parser_state first_byte_action[] = {
parse_lithdr_incidx_x, parse_lithdr_incidx_v, parse_lithdr_notidx,
parse_lithdr_notidx_x, parse_lithdr_notidx_v, parse_lithdr_nvridx,
parse_lithdr_nvridx_x, parse_lithdr_nvridx_v, parse_max_tbl_size,
- parse_max_tbl_size_x, parse_error};
+ parse_max_tbl_size_x, parse_illegal_op};
/* indexes the first byte to a parse state function - generated by
gen_hpack_tables.c */
@@ -169,7 +171,7 @@ static const gpr_uint8 first_byte_lut[256] = {
LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX,
LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX,
LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX_X,
- ILLEGAL, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE,
+ MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE,
MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE,
MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE,
MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE,
@@ -622,19 +624,20 @@ static const gpr_uint8 inverse_base64[256] = {
};
/* emission helpers */
-static void on_hdr(grpc_chttp2_hpack_parser *p, grpc_mdelem *md,
- int add_to_table) {
+static int on_hdr(grpc_chttp2_hpack_parser *p, grpc_mdelem *md,
+ int add_to_table) {
if (add_to_table) {
- GRPC_MDELEM_REF(md);
- grpc_chttp2_hptbl_add(&p->table, md);
+ if (!grpc_chttp2_hptbl_add(&p->table, md)) {
+ return 0;
+ }
}
p->on_header(p->on_header_user_data, md);
+ return 1;
}
static grpc_mdstr *take_string(grpc_chttp2_hpack_parser *p,
grpc_chttp2_hpack_parser_string *str) {
- grpc_mdstr *s = grpc_mdstr_from_buffer(p->table.mdctx, (gpr_uint8 *)str->str,
- str->length);
+ grpc_mdstr *s = grpc_mdstr_from_buffer((gpr_uint8 *)str->str, str->length);
str->length = 0;
return s;
}
@@ -714,9 +717,12 @@ static int parse_stream_dep0(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur,
static int finish_indexed_field(grpc_chttp2_hpack_parser *p,
const gpr_uint8 *cur, const gpr_uint8 *end) {
grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index);
+ if (md == NULL) {
+ gpr_log(GPR_ERROR, "Invalid HPACK index received: %d", p->index);
+ return 0;
+ }
GRPC_MDELEM_REF(md);
- on_hdr(p, md, 0);
- return parse_begin(p, cur, end);
+ return on_hdr(p, md, 0) && parse_begin(p, cur, end);
}
/* parse an indexed field with index < 127 */
@@ -742,21 +748,19 @@ static int parse_indexed_field_x(grpc_chttp2_hpack_parser *p,
static int finish_lithdr_incidx(grpc_chttp2_hpack_parser *p,
const gpr_uint8 *cur, const gpr_uint8 *end) {
grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index);
- on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx,
- GRPC_MDSTR_REF(md->key),
- take_string(p, &p->value)),
- 1);
- return parse_begin(p, cur, end);
+ return on_hdr(p, grpc_mdelem_from_metadata_strings(GRPC_MDSTR_REF(md->key),
+ take_string(p, &p->value)),
+ 1) &&
+ parse_begin(p, cur, end);
}
/* finish a literal header with incremental indexing with no index */
static int finish_lithdr_incidx_v(grpc_chttp2_hpack_parser *p,
const gpr_uint8 *cur, const gpr_uint8 *end) {
- on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx,
- take_string(p, &p->key),
- take_string(p, &p->value)),
- 1);
- return parse_begin(p, cur, end);
+ return on_hdr(p, grpc_mdelem_from_metadata_strings(take_string(p, &p->key),
+ take_string(p, &p->value)),
+ 1) &&
+ parse_begin(p, cur, end);
}
/* parse a literal header with incremental indexing; index < 63 */
@@ -795,21 +799,19 @@ static int parse_lithdr_incidx_v(grpc_chttp2_hpack_parser *p,
static int finish_lithdr_notidx(grpc_chttp2_hpack_parser *p,
const gpr_uint8 *cur, const gpr_uint8 *end) {
grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index);
- on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx,
- GRPC_MDSTR_REF(md->key),
- take_string(p, &p->value)),
- 0);
- return parse_begin(p, cur, end);
+ return on_hdr(p, grpc_mdelem_from_metadata_strings(GRPC_MDSTR_REF(md->key),
+ take_string(p, &p->value)),
+ 0) &&
+ parse_begin(p, cur, end);
}
/* finish a literal header without incremental indexing with index = 0 */
static int finish_lithdr_notidx_v(grpc_chttp2_hpack_parser *p,
const gpr_uint8 *cur, const gpr_uint8 *end) {
- on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx,
- take_string(p, &p->key),
- take_string(p, &p->value)),
- 0);
- return parse_begin(p, cur, end);
+ return on_hdr(p, grpc_mdelem_from_metadata_strings(take_string(p, &p->key),
+ take_string(p, &p->value)),
+ 0) &&
+ parse_begin(p, cur, end);
}
/* parse a literal header without incremental indexing; index < 15 */
@@ -848,21 +850,19 @@ static int parse_lithdr_notidx_v(grpc_chttp2_hpack_parser *p,
static int finish_lithdr_nvridx(grpc_chttp2_hpack_parser *p,
const gpr_uint8 *cur, const gpr_uint8 *end) {
grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index);
- on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx,
- GRPC_MDSTR_REF(md->key),
- take_string(p, &p->value)),
- 0);
- return parse_begin(p, cur, end);
+ return on_hdr(p, grpc_mdelem_from_metadata_strings(GRPC_MDSTR_REF(md->key),
+ take_string(p, &p->value)),
+ 0) &&
+ parse_begin(p, cur, end);
}
/* finish a literal header that is never indexed with an extra value */
static int finish_lithdr_nvridx_v(grpc_chttp2_hpack_parser *p,
const gpr_uint8 *cur, const gpr_uint8 *end) {
- on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx,
- take_string(p, &p->key),
- take_string(p, &p->value)),
- 0);
- return parse_begin(p, cur, end);
+ return on_hdr(p, grpc_mdelem_from_metadata_strings(take_string(p, &p->key),
+ take_string(p, &p->value)),
+ 0) &&
+ parse_begin(p, cur, end);
}
/* parse a literal header that is never indexed; index < 15 */
@@ -901,14 +901,14 @@ static int parse_lithdr_nvridx_v(grpc_chttp2_hpack_parser *p,
static int finish_max_tbl_size(grpc_chttp2_hpack_parser *p,
const gpr_uint8 *cur, const gpr_uint8 *end) {
gpr_log(GPR_INFO, "MAX TABLE SIZE: %d", p->index);
- abort(); /* not implemented */
- return parse_begin(p, cur, end);
+ return grpc_chttp2_hptbl_set_current_table_size(&p->table, p->index) &&
+ parse_begin(p, cur, end);
}
/* parse a max table size change, max size < 15 */
static int parse_max_tbl_size(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur,
const gpr_uint8 *end) {
- p->index = (*cur) & 0xf;
+ p->index = (*cur) & 0x1f;
return finish_max_tbl_size(p, cur + 1, end);
}
@@ -918,7 +918,7 @@ static int parse_max_tbl_size_x(grpc_chttp2_hpack_parser *p,
static const grpc_chttp2_hpack_parser_state and_then[] = {
finish_max_tbl_size};
p->next_state = and_then;
- p->index = 0xf;
+ p->index = 0x1f;
p->parsing.value = &p->index;
return parse_value0(p, cur + 1, end);
}
@@ -930,6 +930,13 @@ static int parse_error(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur,
return 0;
}
+static int parse_illegal_op(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur,
+ const gpr_uint8 *end) {
+ GPR_ASSERT(cur != end);
+ gpr_log(GPR_DEBUG, "Illegal hpack op code %d", *cur);
+ return parse_error(p, cur, end);
+}
+
/* parse the 1st byte of a varint into p->parsing.value
no overflow is possible */
static int parse_value0(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur,
@@ -1342,8 +1349,7 @@ static void on_header_not_set(void *user_data, grpc_mdelem *md) {
abort();
}
-void grpc_chttp2_hpack_parser_init(grpc_chttp2_hpack_parser *p,
- grpc_mdctx *mdctx) {
+void grpc_chttp2_hpack_parser_init(grpc_chttp2_hpack_parser *p) {
p->on_header = on_header_not_set;
p->on_header_user_data = NULL;
p->state = parse_begin;
@@ -1353,7 +1359,7 @@ void grpc_chttp2_hpack_parser_init(grpc_chttp2_hpack_parser *p,
p->value.str = NULL;
p->value.capacity = 0;
p->value.length = 0;
- grpc_chttp2_hptbl_init(&p->table, mdctx);
+ grpc_chttp2_hptbl_init(&p->table);
}
void grpc_chttp2_hpack_parser_set_has_priority(grpc_chttp2_hpack_parser *p) {
diff --git a/src/core/transport/chttp2/hpack_parser.h b/src/core/transport/chttp2/hpack_parser.h
index f56867016c..fb894b5735 100644
--- a/src/core/transport/chttp2/hpack_parser.h
+++ b/src/core/transport/chttp2/hpack_parser.h
@@ -95,8 +95,7 @@ struct grpc_chttp2_hpack_parser {
grpc_chttp2_hptbl table;
};
-void grpc_chttp2_hpack_parser_init(grpc_chttp2_hpack_parser *p,
- grpc_mdctx *mdctx);
+void grpc_chttp2_hpack_parser_init(grpc_chttp2_hpack_parser *p);
void grpc_chttp2_hpack_parser_destroy(grpc_chttp2_hpack_parser *p);
void grpc_chttp2_hpack_parser_set_has_priority(grpc_chttp2_hpack_parser *p);
diff --git a/src/core/transport/chttp2/hpack_table.c b/src/core/transport/chttp2/hpack_table.c
index c442c2c341..59060daad3 100644
--- a/src/core/transport/chttp2/hpack_table.c
+++ b/src/core/transport/chttp2/hpack_table.c
@@ -36,7 +36,9 @@
#include <assert.h>
#include <string.h>
+#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
+
#include "src/core/support/murmur_hash.h"
static struct {
@@ -169,15 +171,24 @@ static struct {
{"www-authenticate", ""},
};
-void grpc_chttp2_hptbl_init(grpc_chttp2_hptbl *tbl, grpc_mdctx *mdctx) {
+static gpr_uint32 entries_for_bytes(gpr_uint32 bytes) {
+ return (bytes + GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD - 1) /
+ GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD;
+}
+
+void grpc_chttp2_hptbl_init(grpc_chttp2_hptbl *tbl) {
size_t i;
memset(tbl, 0, sizeof(*tbl));
- tbl->mdctx = mdctx;
- tbl->max_bytes = GRPC_CHTTP2_INITIAL_HPACK_TABLE_SIZE;
+ tbl->current_table_bytes = tbl->max_bytes =
+ GRPC_CHTTP2_INITIAL_HPACK_TABLE_SIZE;
+ tbl->max_entries = tbl->cap_entries =
+ entries_for_bytes(tbl->current_table_bytes);
+ tbl->ents = gpr_malloc(sizeof(*tbl->ents) * tbl->cap_entries);
+ memset(tbl->ents, 0, sizeof(*tbl->ents) * tbl->cap_entries);
for (i = 1; i <= GRPC_CHTTP2_LAST_STATIC_ENTRY; i++) {
- tbl->static_ents[i - 1] = grpc_mdelem_from_strings(
- mdctx, static_table[i].key, static_table[i].value);
+ tbl->static_ents[i - 1] =
+ grpc_mdelem_from_strings(static_table[i].key, static_table[i].value);
}
}
@@ -187,9 +198,9 @@ void grpc_chttp2_hptbl_destroy(grpc_chttp2_hptbl *tbl) {
GRPC_MDELEM_UNREF(tbl->static_ents[i]);
}
for (i = 0; i < tbl->num_ents; i++) {
- GRPC_MDELEM_UNREF(
- tbl->ents[(tbl->first_ent + i) % GRPC_CHTTP2_MAX_TABLE_COUNT]);
+ GRPC_MDELEM_UNREF(tbl->ents[(tbl->first_ent + i) % tbl->cap_entries]);
}
+ gpr_free(tbl->ents);
}
grpc_mdelem *grpc_chttp2_hptbl_lookup(const grpc_chttp2_hptbl *tbl,
@@ -201,8 +212,8 @@ grpc_mdelem *grpc_chttp2_hptbl_lookup(const grpc_chttp2_hptbl *tbl,
/* Otherwise, find the value in the list of valid entries */
tbl_index -= (GRPC_CHTTP2_LAST_STATIC_ENTRY + 1);
if (tbl_index < tbl->num_ents) {
- gpr_uint32 offset = (tbl->num_ents - 1u - tbl_index + tbl->first_ent) %
- GRPC_CHTTP2_MAX_TABLE_COUNT;
+ gpr_uint32 offset =
+ (tbl->num_ents - 1u - tbl_index + tbl->first_ent) % tbl->cap_entries;
return tbl->ents[offset];
}
/* Invalid entry: return error */
@@ -216,21 +227,81 @@ static void evict1(grpc_chttp2_hptbl *tbl) {
GPR_SLICE_LENGTH(first_ent->value->slice) +
GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD;
GPR_ASSERT(elem_bytes <= tbl->mem_used);
- tbl->mem_used = (gpr_uint16)(tbl->mem_used - elem_bytes);
- tbl->first_ent =
- (gpr_uint16)((tbl->first_ent + 1) % GRPC_CHTTP2_MAX_TABLE_COUNT);
+ tbl->mem_used -= (gpr_uint32)elem_bytes;
+ tbl->first_ent = ((tbl->first_ent + 1) % tbl->cap_entries);
tbl->num_ents--;
GRPC_MDELEM_UNREF(first_ent);
}
-void grpc_chttp2_hptbl_add(grpc_chttp2_hptbl *tbl, grpc_mdelem *md) {
+static void rebuild_ents(grpc_chttp2_hptbl *tbl, gpr_uint32 new_cap) {
+ grpc_mdelem **ents = gpr_malloc(sizeof(*ents) * new_cap);
+ gpr_uint32 i;
+
+ for (i = 0; i < tbl->num_ents; i++) {
+ ents[i] = tbl->ents[(tbl->first_ent + i) % tbl->cap_entries];
+ }
+ gpr_free(tbl->ents);
+ tbl->ents = ents;
+ tbl->cap_entries = new_cap;
+ tbl->first_ent = 0;
+}
+
+void grpc_chttp2_hptbl_set_max_bytes(grpc_chttp2_hptbl *tbl,
+ gpr_uint32 max_bytes) {
+ if (tbl->max_bytes == max_bytes) {
+ return;
+ }
+ gpr_log(GPR_DEBUG, "Update hpack parser max size to %d", max_bytes);
+ while (tbl->mem_used > max_bytes) {
+ evict1(tbl);
+ }
+ tbl->max_bytes = max_bytes;
+}
+
+int grpc_chttp2_hptbl_set_current_table_size(grpc_chttp2_hptbl *tbl,
+ gpr_uint32 bytes) {
+ if (tbl->current_table_bytes == bytes) {
+ return 1;
+ }
+ if (bytes > tbl->max_bytes) {
+ gpr_log(GPR_ERROR,
+ "Attempt to make hpack table %d bytes when max is %d bytes", bytes,
+ tbl->max_bytes);
+ return 0;
+ }
+ gpr_log(GPR_DEBUG, "Update hpack parser table size to %d", bytes);
+ while (tbl->mem_used > bytes) {
+ evict1(tbl);
+ }
+ tbl->current_table_bytes = bytes;
+ tbl->max_entries = entries_for_bytes(bytes);
+ if (tbl->max_entries > tbl->cap_entries) {
+ rebuild_ents(tbl, GPR_MAX(tbl->max_entries, 2 * tbl->cap_entries));
+ } else if (tbl->max_entries < tbl->cap_entries / 3) {
+ gpr_uint32 new_cap = GPR_MAX(tbl->max_entries, 16u);
+ if (new_cap != tbl->cap_entries) {
+ rebuild_ents(tbl, new_cap);
+ }
+ }
+ return 1;
+}
+
+int grpc_chttp2_hptbl_add(grpc_chttp2_hptbl *tbl, grpc_mdelem *md) {
/* determine how many bytes of buffer this entry represents */
size_t elem_bytes = GPR_SLICE_LENGTH(md->key->slice) +
GPR_SLICE_LENGTH(md->value->slice) +
GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD;
+ if (tbl->current_table_bytes > tbl->max_bytes) {
+ gpr_log(GPR_ERROR,
+ "HPACK max table size reduced to %d but not reflected by hpack "
+ "stream (still at %d)",
+ tbl->max_bytes, tbl->current_table_bytes);
+ return 0;
+ }
+
/* we can't add elements bigger than the max table size */
- if (elem_bytes > tbl->max_bytes) {
+ if (elem_bytes > tbl->current_table_bytes) {
/* HPACK draft 10 section 4.4 states:
* If the size of the new entry is less than or equal to the maximum
* size, that entry is added to the table. It is not an error to
@@ -243,44 +314,43 @@ void grpc_chttp2_hptbl_add(grpc_chttp2_hptbl *tbl, grpc_mdelem *md) {
while (tbl->num_ents) {
evict1(tbl);
}
- return;
+ return 1;
}
/* evict entries to ensure no overflow */
- while (elem_bytes > (size_t)tbl->max_bytes - tbl->mem_used) {
+ while (elem_bytes > (size_t)tbl->current_table_bytes - tbl->mem_used) {
evict1(tbl);
}
/* copy the finalized entry in */
- tbl->ents[tbl->last_ent] = md;
+ tbl->ents[(tbl->first_ent + tbl->num_ents) % tbl->cap_entries] =
+ GRPC_MDELEM_REF(md);
/* update accounting values */
- tbl->last_ent =
- (gpr_uint16)((tbl->last_ent + 1) % GRPC_CHTTP2_MAX_TABLE_COUNT);
tbl->num_ents++;
- tbl->mem_used = (gpr_uint16)(tbl->mem_used + elem_bytes);
+ tbl->mem_used += (gpr_uint32)elem_bytes;
+ return 1;
}
grpc_chttp2_hptbl_find_result grpc_chttp2_hptbl_find(
const grpc_chttp2_hptbl *tbl, grpc_mdelem *md) {
grpc_chttp2_hptbl_find_result r = {0, 0};
- gpr_uint16 i;
+ gpr_uint32 i;
/* See if the string is in the static table */
for (i = 0; i < GRPC_CHTTP2_LAST_STATIC_ENTRY; i++) {
grpc_mdelem *ent = tbl->static_ents[i];
if (md->key != ent->key) continue;
- r.index = (gpr_uint16)(i + 1);
+ r.index = i + 1u;
r.has_value = md->value == ent->value;
if (r.has_value) return r;
}
/* Scan the dynamic table */
for (i = 0; i < tbl->num_ents; i++) {
- gpr_uint16 idx =
- (gpr_uint16)(tbl->num_ents - i + GRPC_CHTTP2_LAST_STATIC_ENTRY);
- grpc_mdelem *ent =
- tbl->ents[(tbl->first_ent + i) % GRPC_CHTTP2_MAX_TABLE_COUNT];
+ gpr_uint32 idx =
+ (gpr_uint32)(tbl->num_ents - i + GRPC_CHTTP2_LAST_STATIC_ENTRY);
+ grpc_mdelem *ent = tbl->ents[(tbl->first_ent + i) % tbl->cap_entries];
if (md->key != ent->key) continue;
r.index = idx;
r.has_value = md->value == ent->value;
diff --git a/src/core/transport/chttp2/hpack_table.h b/src/core/transport/chttp2/hpack_table.h
index 4f882e2e03..a173eec30c 100644
--- a/src/core/transport/chttp2/hpack_table.h
+++ b/src/core/transport/chttp2/hpack_table.h
@@ -49,47 +49,58 @@
#define GRPC_CHTTP2_MAX_HPACK_TABLE_SIZE GRPC_CHTTP2_INITIAL_HPACK_TABLE_SIZE
/* Per entry overhead bytes as per the spec */
#define GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD 32
+#if 0
/* Maximum number of entries we could possibly fit in the table, given defined
overheads */
#define GRPC_CHTTP2_MAX_TABLE_COUNT \
((GRPC_CHTTP2_MAX_HPACK_TABLE_SIZE + GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD - 1) / \
GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD)
+#endif
/* hpack decoder table */
typedef struct {
- grpc_mdctx *mdctx;
/* the first used entry in ents */
- gpr_uint16 first_ent;
- /* the last used entry in ents */
- gpr_uint16 last_ent;
+ gpr_uint32 first_ent;
/* how many entries are in the table */
- gpr_uint16 num_ents;
+ gpr_uint32 num_ents;
/* the amount of memory used by the table, according to the hpack algorithm */
- gpr_uint16 mem_used;
+ gpr_uint32 mem_used;
/* the max memory allowed to be used by the table, according to the hpack
algorithm */
- gpr_uint16 max_bytes;
+ gpr_uint32 max_bytes;
+ /* the currently agreed size of the table, according to the hpack algorithm */
+ gpr_uint32 current_table_bytes;
+ /* Maximum number of entries we could possibly fit in the table, given defined
+ overheads */
+ gpr_uint32 max_entries;
+ /* Number of entries allocated in ents */
+ gpr_uint32 cap_entries;
/* a circular buffer of headers - this is stored in the opposite order to
what hpack specifies, in order to simplify table management a little...
meaning lookups need to SUBTRACT from the end position */
- grpc_mdelem *ents[GRPC_CHTTP2_MAX_TABLE_COUNT];
+ grpc_mdelem **ents;
grpc_mdelem *static_ents[GRPC_CHTTP2_LAST_STATIC_ENTRY];
} grpc_chttp2_hptbl;
/* initialize a hpack table */
-void grpc_chttp2_hptbl_init(grpc_chttp2_hptbl *tbl, grpc_mdctx *mdctx);
+void grpc_chttp2_hptbl_init(grpc_chttp2_hptbl *tbl);
void grpc_chttp2_hptbl_destroy(grpc_chttp2_hptbl *tbl);
+void grpc_chttp2_hptbl_set_max_bytes(grpc_chttp2_hptbl *tbl,
+ gpr_uint32 max_bytes);
+int grpc_chttp2_hptbl_set_current_table_size(grpc_chttp2_hptbl *tbl,
+ gpr_uint32 bytes);
/* lookup a table entry based on its hpack index */
grpc_mdelem *grpc_chttp2_hptbl_lookup(const grpc_chttp2_hptbl *tbl,
gpr_uint32 index);
/* add a table entry to the index */
-void grpc_chttp2_hptbl_add(grpc_chttp2_hptbl *tbl, grpc_mdelem *md);
+int grpc_chttp2_hptbl_add(grpc_chttp2_hptbl *tbl,
+ grpc_mdelem *md) GRPC_MUST_USE_RESULT;
/* Find a key/value pair in the table... returns the index in the table of the
most similar entry, or 0 if the value was not found */
typedef struct {
- gpr_uint16 index;
- gpr_uint8 has_value;
+ gpr_uint32 index;
+ int has_value;
} grpc_chttp2_hptbl_find_result;
grpc_chttp2_hptbl_find_result grpc_chttp2_hptbl_find(
const grpc_chttp2_hptbl *tbl, grpc_mdelem *md);
diff --git a/src/core/transport/chttp2/internal.h b/src/core/transport/chttp2/internal.h
index 2d0cb4abdb..3952f8a0e8 100644
--- a/src/core/transport/chttp2/internal.h
+++ b/src/core/transport/chttp2/internal.h
@@ -191,6 +191,9 @@ typedef struct {
copied to next_stream_id in parsing when parsing commences */
gpr_uint32 next_stream_id;
+ /** how far to lookahead in a stream? */
+ gpr_uint32 stream_lookahead;
+
/** last received stream id */
gpr_uint32 last_incoming_stream_id;
@@ -227,14 +230,14 @@ struct grpc_chttp2_transport_parsing {
/** was a goaway frame received? */
gpr_uint8 goaway_received;
+ /** the last sent max_table_size setting */
+ gpr_uint32 last_sent_max_table_size;
+
/** initial window change */
gpr_int64 initial_window_update;
/** data to write later - after parsing */
gpr_slice_buffer qbuf;
- /* metadata object cache */
- grpc_mdstr *str_grpc_timeout;
- grpc_mdelem *elem_grpc_status_ok;
/** parser for headers */
grpc_chttp2_hpack_parser hpack_parser;
/** simple one shot parsers */
@@ -288,7 +291,6 @@ struct grpc_chttp2_transport_parsing {
struct grpc_chttp2_transport {
grpc_transport base; /* must be first */
grpc_endpoint *ep;
- grpc_mdctx *metadata_context;
gpr_refcount refs;
char *peer_string;
@@ -483,7 +485,8 @@ struct grpc_chttp2_stream {
/** Someone is unlocking the transport mutex: check to see if writes
are required, and schedule them if so */
int grpc_chttp2_unlocking_check_writes(grpc_chttp2_transport_global *global,
- grpc_chttp2_transport_writing *writing);
+ grpc_chttp2_transport_writing *writing,
+ int is_parsing);
void grpc_chttp2_perform_writes(
grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_writing *transport_writing,
grpc_endpoint *endpoint);
diff --git a/src/core/transport/chttp2/parsing.c b/src/core/transport/chttp2/parsing.c
index 8cef8fbb77..467e734cc3 100644
--- a/src/core/transport/chttp2/parsing.c
+++ b/src/core/transport/chttp2/parsing.c
@@ -35,14 +35,15 @@
#include <string.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
#include "src/core/profiling/timers.h"
#include "src/core/transport/chttp2/http2_errors.h"
#include "src/core/transport/chttp2/status_conversion.h"
#include "src/core/transport/chttp2/timeout_encoding.h"
-
-#include <grpc/support/alloc.h>
-#include <grpc/support/log.h>
-#include <grpc/support/string_util.h>
+#include "src/core/transport/static_metadata.h"
static int init_frame_parser(grpc_exec_ctx *exec_ctx,
grpc_chttp2_transport_parsing *transport_parsing);
@@ -78,6 +79,9 @@ void grpc_chttp2_prepare_to_read(
GPR_TIMER_BEGIN("grpc_chttp2_prepare_to_read", 0);
transport_parsing->next_stream_id = transport_global->next_stream_id;
+ transport_parsing->last_sent_max_table_size =
+ transport_global->settings[GRPC_SENT_SETTINGS]
+ [GRPC_CHTTP2_SETTINGS_HEADER_TABLE_SIZE];
/* update the parsing view of incoming window */
while (grpc_chttp2_list_pop_unannounced_incoming_window_available(
@@ -127,6 +131,7 @@ void grpc_chttp2_publish_reads(
transport_global->settings[GRPC_SENT_SETTINGS],
GRPC_CHTTP2_NUM_SETTINGS * sizeof(gpr_uint32));
transport_parsing->settings_ack_received = 0;
+ transport_global->sent_local_settings = 0;
}
/* move goaway to the global state if we received one (it will be
@@ -588,13 +593,12 @@ static void on_initial_header(void *tp, grpc_mdelem *md) {
transport_parsing->is_client ? "CLI" : "SVR",
grpc_mdstr_as_c_string(md->key), grpc_mdstr_as_c_string(md->value)));
- if (md->key == transport_parsing->elem_grpc_status_ok->key &&
- md != transport_parsing->elem_grpc_status_ok) {
+ if (md->key == GRPC_MDSTR_GRPC_STATUS && md != GRPC_MDELEM_GRPC_STATUS_0) {
/* TODO(ctiller): check for a status like " 0" */
stream_parsing->seen_error = 1;
}
- if (md->key == transport_parsing->str_grpc_timeout) {
+ if (md->key == GRPC_MDSTR_GRPC_TIMEOUT) {
gpr_timespec *cached_timeout = grpc_mdelem_get_user_data(md, free_timeout);
if (!cached_timeout) {
/* not already parsed: parse it now, and store the result away */
@@ -635,8 +639,7 @@ static void on_trailing_header(void *tp, grpc_mdelem *md) {
transport_parsing->is_client ? "CLI" : "SVR",
grpc_mdstr_as_c_string(md->key), grpc_mdstr_as_c_string(md->value)));
- if (md->key == transport_parsing->elem_grpc_status_ok->key &&
- md != transport_parsing->elem_grpc_status_ok) {
+ if (md->key == GRPC_MDSTR_GRPC_STATUS && md != GRPC_MDELEM_GRPC_STATUS_0) {
/* TODO(ctiller): check for a status like " 0" */
stream_parsing->seen_error = 1;
}
@@ -819,6 +822,9 @@ static int init_settings_frame_parser(
}
if (transport_parsing->incoming_frame_flags & GRPC_CHTTP2_FLAG_ACK) {
transport_parsing->settings_ack_received = 1;
+ grpc_chttp2_hptbl_set_max_bytes(
+ &transport_parsing->hpack_parser.table,
+ transport_parsing->last_sent_max_table_size);
}
transport_parsing->parser = grpc_chttp2_settings_parser_parse;
transport_parsing->parser_data = &transport_parsing->simple.settings;
diff --git a/src/core/transport/chttp2/writing.c b/src/core/transport/chttp2/writing.c
index 353e476e40..805d05222d 100644
--- a/src/core/transport/chttp2/writing.c
+++ b/src/core/transport/chttp2/writing.c
@@ -45,7 +45,7 @@ static void finalize_outbuf(grpc_exec_ctx *exec_ctx,
int grpc_chttp2_unlocking_check_writes(
grpc_chttp2_transport_global *transport_global,
- grpc_chttp2_transport_writing *transport_writing) {
+ grpc_chttp2_transport_writing *transport_writing, int is_parsing) {
grpc_chttp2_stream_global *stream_global;
grpc_chttp2_stream_writing *stream_writing;
@@ -55,8 +55,13 @@ int grpc_chttp2_unlocking_check_writes(
gpr_slice_buffer_swap(&transport_global->qbuf, &transport_writing->outbuf);
GPR_ASSERT(transport_global->qbuf.count == 0);
+ grpc_chttp2_hpack_compressor_set_max_table_size(
+ &transport_writing->hpack_compressor,
+ transport_global->settings[GRPC_PEER_SETTINGS]
+ [GRPC_CHTTP2_SETTINGS_HEADER_TABLE_SIZE]);
+
if (transport_global->dirtied_local_settings &&
- !transport_global->sent_local_settings) {
+ !transport_global->sent_local_settings && !is_parsing) {
gpr_slice_buffer_add(
&transport_writing->outbuf,
grpc_chttp2_settings_create(
@@ -315,6 +320,8 @@ static void finalize_outbuf(grpc_exec_ctx *exec_ctx,
grpc_chttp2_list_add_written_stream(transport_writing, stream_writing);
}
}
+
+ GPR_TIMER_END("finalize_outbuf", 0);
}
void grpc_chttp2_cleanup_writing(
diff --git a/src/core/transport/chttp2_transport.c b/src/core/transport/chttp2_transport.c
index f62294c7c5..97f2a1b75c 100644
--- a/src/core/transport/chttp2_transport.c
+++ b/src/core/transport/chttp2_transport.c
@@ -49,10 +49,10 @@
#include "src/core/transport/chttp2/internal.h"
#include "src/core/transport/chttp2/status_conversion.h"
#include "src/core/transport/chttp2/timeout_encoding.h"
+#include "src/core/transport/static_metadata.h"
#include "src/core/transport/transport_impl.h"
#define DEFAULT_WINDOW 65535
-#define GRPC_CHTTP2_STREAM_LOOKAHEAD DEFAULT_WINDOW
#define DEFAULT_CONNECTION_WINDOW_TARGET (1024 * 1024)
#define MAX_WINDOW 0x7fffffffu
@@ -156,9 +156,6 @@ static void destruct_transport(grpc_exec_ctx *exec_ctx,
grpc_chttp2_hpack_parser_destroy(&t->parsing.hpack_parser);
grpc_chttp2_goaway_parser_destroy(&t->parsing.goaway_parser);
- GRPC_MDSTR_UNREF(t->parsing.str_grpc_timeout);
- GRPC_MDELEM_UNREF(t->parsing.elem_grpc_status_ok);
-
for (i = 0; i < STREAM_LIST_COUNT; i++) {
GPR_ASSERT(t->lists[i].head == NULL);
GPR_ASSERT(t->lists[i].tail == NULL);
@@ -184,8 +181,6 @@ static void destruct_transport(grpc_exec_ctx *exec_ctx,
gpr_free(ping);
}
- grpc_mdctx_unref(t->metadata_context);
-
gpr_free(t->peer_string);
gpr_free(t);
}
@@ -220,8 +215,7 @@ static void ref_transport(grpc_chttp2_transport *t) { gpr_ref(&t->refs); }
static void init_transport(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
const grpc_channel_args *channel_args,
- grpc_endpoint *ep, grpc_mdctx *mdctx,
- gpr_uint8 is_client) {
+ grpc_endpoint *ep, gpr_uint8 is_client) {
size_t i;
int j;
@@ -237,22 +231,17 @@ static void init_transport(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
/* ref is dropped at transport close() */
gpr_ref_init(&t->shutdown_ep_refs, 1);
gpr_mu_init(&t->mu);
- grpc_mdctx_ref(mdctx);
t->peer_string = grpc_endpoint_get_peer(ep);
- t->metadata_context = mdctx;
t->endpoint_reading = 1;
t->global.next_stream_id = is_client ? 1 : 2;
t->global.is_client = is_client;
t->writing.outgoing_window = DEFAULT_WINDOW;
t->parsing.incoming_window = DEFAULT_WINDOW;
+ t->global.stream_lookahead = DEFAULT_WINDOW;
t->global.connection_window_target = DEFAULT_CONNECTION_WINDOW_TARGET;
t->global.ping_counter = 1;
t->global.pings.next = t->global.pings.prev = &t->global.pings;
t->parsing.is_client = is_client;
- t->parsing.str_grpc_timeout =
- grpc_mdstr_from_string(t->metadata_context, "grpc-timeout");
- t->parsing.elem_grpc_status_ok =
- grpc_mdelem_from_strings(t->metadata_context, "grpc-status", "0");
t->parsing.deframe_state =
is_client ? GRPC_DTS_FH_0 : GRPC_DTS_CLIENT_PREFIX_0;
t->writing.is_client = is_client;
@@ -263,12 +252,12 @@ static void init_transport(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
gpr_slice_buffer_init(&t->global.qbuf);
gpr_slice_buffer_init(&t->writing.outbuf);
- grpc_chttp2_hpack_compressor_init(&t->writing.hpack_compressor, mdctx);
+ grpc_chttp2_hpack_compressor_init(&t->writing.hpack_compressor);
grpc_closure_init(&t->writing_action, writing_action, t);
gpr_slice_buffer_init(&t->parsing.qbuf);
grpc_chttp2_goaway_parser_init(&t->parsing.goaway_parser);
- grpc_chttp2_hpack_parser_init(&t->parsing.hpack_parser, t->metadata_context);
+ grpc_chttp2_hpack_parser_init(&t->parsing.hpack_parser);
grpc_closure_init(&t->writing.done_cb, grpc_chttp2_terminate_writing,
&t->writing);
@@ -338,6 +327,43 @@ static void init_transport(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
t->global.next_stream_id =
(gpr_uint32)channel_args->args[i].value.integer;
}
+ } else if (0 == strcmp(channel_args->args[i].key,
+ GRPC_ARG_HTTP2_STREAM_LOOKAHEAD_BYTES)) {
+ if (channel_args->args[i].type != GRPC_ARG_INTEGER) {
+ gpr_log(GPR_ERROR, "%s: must be an integer",
+ GRPC_ARG_HTTP2_STREAM_LOOKAHEAD_BYTES);
+ } else if (channel_args->args[i].value.integer <= 5) {
+ gpr_log(GPR_ERROR, "%s: must be at least 5",
+ GRPC_ARG_HTTP2_STREAM_LOOKAHEAD_BYTES);
+ } else {
+ t->global.stream_lookahead =
+ (gpr_uint32)channel_args->args[i].value.integer;
+ }
+ } else if (0 == strcmp(channel_args->args[i].key,
+ GRPC_ARG_HTTP2_HPACK_TABLE_SIZE_DECODER)) {
+ if (channel_args->args[i].type != GRPC_ARG_INTEGER) {
+ gpr_log(GPR_ERROR, "%s: must be an integer",
+ GRPC_ARG_HTTP2_HPACK_TABLE_SIZE_DECODER);
+ } else if (channel_args->args[i].value.integer < 0) {
+ gpr_log(GPR_DEBUG, "%s: must be non-negative",
+ GRPC_ARG_HTTP2_HPACK_TABLE_SIZE_DECODER);
+ } else {
+ push_setting(t, GRPC_CHTTP2_SETTINGS_HEADER_TABLE_SIZE,
+ (gpr_uint32)channel_args->args[i].value.integer);
+ }
+ } else if (0 == strcmp(channel_args->args[i].key,
+ GRPC_ARG_HTTP2_HPACK_TABLE_SIZE_ENCODER)) {
+ if (channel_args->args[i].type != GRPC_ARG_INTEGER) {
+ gpr_log(GPR_ERROR, "%s: must be an integer",
+ GRPC_ARG_HTTP2_HPACK_TABLE_SIZE_ENCODER);
+ } else if (channel_args->args[i].value.integer < 0) {
+ gpr_log(GPR_DEBUG, "%s: must be non-negative",
+ GRPC_ARG_HTTP2_HPACK_TABLE_SIZE_ENCODER);
+ } else {
+ grpc_chttp2_hpack_compressor_set_max_usable_size(
+ &t->writing.hpack_compressor,
+ (gpr_uint32)channel_args->args[i].value.integer);
+ }
}
}
}
@@ -563,7 +589,8 @@ static void lock(grpc_chttp2_transport *t) { gpr_mu_lock(&t->mu); }
static void unlock(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t) {
GPR_TIMER_BEGIN("unlock", 0);
if (!t->writing_active && !t->closed &&
- grpc_chttp2_unlocking_check_writes(&t->global, &t->writing)) {
+ grpc_chttp2_unlocking_check_writes(&t->global, &t->writing,
+ t->parsing_active)) {
t->writing_active = 1;
REF_TRANSPORT(t, "writing");
grpc_exec_ctx_enqueue(exec_ctx, &t->writing_action, 1);
@@ -724,17 +751,18 @@ void grpc_chttp2_complete_closure_step(grpc_exec_ctx *exec_ctx,
static int contains_non_ok_status(
grpc_chttp2_transport_global *transport_global,
grpc_metadata_batch *batch) {
- grpc_mdelem *ok_elem =
- TRANSPORT_FROM_GLOBAL(transport_global)->parsing.elem_grpc_status_ok;
grpc_linked_mdelem *l;
for (l = batch->list.head; l; l = l->next) {
- if (l->md->key == ok_elem->key && l->md != ok_elem) {
+ if (l->md->key == GRPC_MDSTR_GRPC_STATUS &&
+ l->md != GRPC_MDELEM_GRPC_STATUS_0) {
return 1;
}
}
return 0;
}
+static void do_nothing(grpc_exec_ctx *exec_ctx, void *arg, int success) {}
+
static void perform_stream_op_locked(
grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_global *transport_global,
grpc_chttp2_stream_global *stream_global, grpc_transport_stream_op *op) {
@@ -743,6 +771,9 @@ static void perform_stream_op_locked(
GPR_TIMER_BEGIN("perform_stream_op_locked", 0);
on_complete = op->on_complete;
+ if (on_complete == NULL) {
+ on_complete = grpc_closure_create(do_nothing, NULL);
+ }
/* use final_data as a barrier until enqueue time; the inital counter is
dropped at the end of this function */
on_complete->final_data = 2;
@@ -806,7 +837,7 @@ static void perform_stream_op_locked(
}
if (stream_global->write_closed) {
grpc_chttp2_complete_closure_step(
- exec_ctx, &stream_global->send_trailing_metadata_finished,
+ exec_ctx, &stream_global->send_trailing_metadata_finished,
grpc_metadata_batch_is_empty(op->send_trailing_metadata));
} else if (stream_global->id != 0) {
/* TODO(ctiller): check if there's flow control for any outstanding
@@ -1033,20 +1064,20 @@ void grpc_chttp2_fake_status(grpc_exec_ctx *exec_ctx,
to the upper layers - drop what we've got, and then publish
what we want - which is safe because we haven't told anyone
about the metadata yet */
- if (!stream_global->published_trailing_metadata || stream_global->recv_trailing_metadata_finished != NULL) {
- grpc_mdctx *mdctx =
- TRANSPORT_FROM_GLOBAL(transport_global)->metadata_context;
+ if (!stream_global->published_trailing_metadata ||
+ stream_global->recv_trailing_metadata_finished != NULL) {
char status_string[GPR_LTOA_MIN_BUFSIZE];
gpr_ltoa(status, status_string);
grpc_chttp2_incoming_metadata_buffer_add(
&stream_global->received_trailing_metadata,
- grpc_mdelem_from_strings(mdctx, "grpc-status", status_string));
+ grpc_mdelem_from_metadata_strings(
+ GRPC_MDSTR_GRPC_STATUS, grpc_mdstr_from_string(status_string)));
if (slice) {
grpc_chttp2_incoming_metadata_buffer_add(
&stream_global->received_trailing_metadata,
grpc_mdelem_from_metadata_strings(
- mdctx, grpc_mdstr_from_string(mdctx, "grpc-message"),
- grpc_mdstr_from_slice(mdctx, gpr_slice_ref(*slice))));
+ GRPC_MDSTR_GRPC_MESSAGE,
+ grpc_mdstr_from_slice(gpr_slice_ref(*slice))));
}
stream_global->published_trailing_metadata = 1;
grpc_chttp2_list_add_check_read_ops(transport_global, stream_global);
@@ -1372,8 +1403,8 @@ static void incoming_byte_stream_update_flow_control(
gpr_uint32 max_recv_bytes;
/* clamp max recv hint to an allowable size */
- if (max_size_hint >= GPR_UINT32_MAX - GRPC_CHTTP2_STREAM_LOOKAHEAD) {
- max_recv_bytes = GPR_UINT32_MAX - GRPC_CHTTP2_STREAM_LOOKAHEAD;
+ if (max_size_hint >= GPR_UINT32_MAX - transport_global->stream_lookahead) {
+ max_recv_bytes = GPR_UINT32_MAX - transport_global->stream_lookahead;
} else {
max_recv_bytes = (gpr_uint32)max_size_hint;
}
@@ -1386,8 +1417,9 @@ static void incoming_byte_stream_update_flow_control(
}
/* add some small lookahead to keep pipelines flowing */
- GPR_ASSERT(max_recv_bytes <= GPR_UINT32_MAX - GRPC_CHTTP2_STREAM_LOOKAHEAD);
- max_recv_bytes += GRPC_CHTTP2_STREAM_LOOKAHEAD;
+ GPR_ASSERT(max_recv_bytes <=
+ GPR_UINT32_MAX - transport_global->stream_lookahead);
+ max_recv_bytes += transport_global->stream_lookahead;
if (stream_global->max_recv_bytes < max_recv_bytes) {
gpr_uint32 add_max_recv_bytes =
max_recv_bytes - stream_global->max_recv_bytes;
@@ -1596,9 +1628,9 @@ static const grpc_transport_vtable vtable = {
grpc_transport *grpc_create_chttp2_transport(
grpc_exec_ctx *exec_ctx, const grpc_channel_args *channel_args,
- grpc_endpoint *ep, grpc_mdctx *mdctx, int is_client) {
+ grpc_endpoint *ep, int is_client) {
grpc_chttp2_transport *t = gpr_malloc(sizeof(grpc_chttp2_transport));
- init_transport(exec_ctx, t, channel_args, ep, mdctx, is_client != 0);
+ init_transport(exec_ctx, t, channel_args, ep, is_client != 0);
return &t->base;
}
diff --git a/src/core/transport/chttp2_transport.h b/src/core/transport/chttp2_transport.h
index fce2b680fd..95520501ed 100644
--- a/src/core/transport/chttp2_transport.h
+++ b/src/core/transport/chttp2_transport.h
@@ -42,7 +42,7 @@ extern int grpc_flowctl_trace;
grpc_transport *grpc_create_chttp2_transport(
grpc_exec_ctx *exec_ctx, const grpc_channel_args *channel_args,
- grpc_endpoint *ep, grpc_mdctx *metadata_context, int is_client);
+ grpc_endpoint *ep, int is_client);
void grpc_chttp2_transport_start_reading(grpc_exec_ctx *exec_ctx,
grpc_transport *transport,
diff --git a/src/core/transport/metadata.c b/src/core/transport/metadata.c
index a72dee4399..56e83a0a75 100644
--- a/src/core/transport/metadata.c
+++ b/src/core/transport/metadata.c
@@ -31,20 +31,32 @@
*
*/
-#include "src/core/iomgr/sockaddr.h"
#include "src/core/transport/metadata.h"
#include <assert.h>
#include <stddef.h>
#include <string.h>
+#include <grpc/compression.h>
#include <grpc/support/alloc.h>
#include <grpc/support/atm.h>
#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
#include <grpc/support/time.h>
#include "src/core/profiling/timers.h"
#include "src/core/support/murmur_hash.h"
+#include "src/core/support/string.h"
#include "src/core/transport/chttp2/bin_encoder.h"
+#include "src/core/transport/static_metadata.h"
+
+/* There are two kinds of mdelem and mdstr instances.
+ * Static instances are declared in static_metadata.{h,c} and
+ * are initialized by grpc_mdctx_global_init().
+ * Dynamic instances are stored in hash tables on grpc_mdctx, and are backed
+ * by internal_string and internal_element structures.
+ * Internal helper functions here-in (is_mdstr_static, is_mdelem_static) are
+ * used to determine which kind of element a pointer refers to.
+ */
#define INITIAL_STRTAB_CAPACITY 4
#define INITIAL_MDTAB_CAPACITY 4
@@ -52,107 +64,188 @@
#ifdef GRPC_METADATA_REFCOUNT_DEBUG
#define DEBUG_ARGS , const char *file, int line
#define FWD_DEBUG_ARGS , file, line
-#define INTERNAL_STRING_REF(s) internal_string_ref((s), __FILE__, __LINE__)
-#define INTERNAL_STRING_UNREF(s) internal_string_unref((s), __FILE__, __LINE__)
-#define REF_MD_LOCKED(s) ref_md_locked((s), __FILE__, __LINE__)
+#define REF_MD_LOCKED(shard, s) ref_md_locked((shard), (s), __FILE__, __LINE__)
#else
#define DEBUG_ARGS
#define FWD_DEBUG_ARGS
-#define INTERNAL_STRING_REF(s) internal_string_ref((s))
-#define INTERNAL_STRING_UNREF(s) internal_string_unref((s))
-#define REF_MD_LOCKED(s) ref_md_locked((s))
+#define REF_MD_LOCKED(shard, s) ref_md_locked((shard), (s))
#endif
+#define TABLE_IDX(hash, log2_shards, capacity) \
+ (((hash) >> (log2_shards)) % (capacity))
+#define SHARD_IDX(hash, log2_shards) ((hash) & ((1 << (log2_shards)) - 1))
+
typedef void (*destroy_user_data_func)(void *user_data);
+/* Shadow structure for grpc_mdstr for non-static values */
typedef struct internal_string {
/* must be byte compatible with grpc_mdstr */
gpr_slice slice;
gpr_uint32 hash;
/* private only data */
- gpr_uint32 refs;
+ gpr_atm refcnt;
+
gpr_uint8 has_base64_and_huffman_encoded;
gpr_slice_refcount refcount;
gpr_slice base64_and_huffman;
- grpc_mdctx *context;
-
struct internal_string *bucket_next;
} internal_string;
+/* Shadow structure for grpc_mdelem for non-static elements */
typedef struct internal_metadata {
/* must be byte compatible with grpc_mdelem */
internal_string *key;
internal_string *value;
+ /* private only data */
gpr_atm refcnt;
- /* private only data */
gpr_mu mu_user_data;
gpr_atm destroy_user_data;
gpr_atm user_data;
- grpc_mdctx *context;
struct internal_metadata *bucket_next;
} internal_metadata;
-struct grpc_mdctx {
- gpr_uint32 hash_seed;
- int refs;
-
+typedef struct strtab_shard {
gpr_mu mu;
+ internal_string **strs;
+ size_t count;
+ size_t capacity;
+} strtab_shard;
- internal_string **strtab;
- size_t strtab_count;
- size_t strtab_capacity;
+typedef struct mdtab_shard {
+ gpr_mu mu;
+ internal_metadata **elems;
+ size_t count;
+ size_t capacity;
+ size_t free;
+} mdtab_shard;
+
+#define LOG2_STRTAB_SHARD_COUNT 5
+#define LOG2_MDTAB_SHARD_COUNT 4
+#define STRTAB_SHARD_COUNT ((size_t)(1 << LOG2_STRTAB_SHARD_COUNT))
+#define MDTAB_SHARD_COUNT ((size_t)(1 << LOG2_MDTAB_SHARD_COUNT))
+
+/* hash seed: decided at initialization time */
+static gpr_uint32 g_hash_seed;
+
+/* linearly probed hash tables for static element lookup */
+static grpc_mdstr *g_static_strtab[GRPC_STATIC_MDSTR_COUNT * 2];
+static grpc_mdelem *g_static_mdtab[GRPC_STATIC_MDELEM_COUNT * 2];
+static size_t g_static_strtab_maxprobe;
+static size_t g_static_mdtab_maxprobe;
+
+static strtab_shard g_strtab_shard[STRTAB_SHARD_COUNT];
+static mdtab_shard g_mdtab_shard[MDTAB_SHARD_COUNT];
+
+static void gc_mdtab(mdtab_shard *shard);
+
+void grpc_mdctx_global_init(void) {
+ size_t i, j;
+ g_hash_seed = (gpr_uint32)gpr_now(GPR_CLOCK_REALTIME).tv_nsec;
+ g_static_strtab_maxprobe = 0;
+ g_static_mdtab_maxprobe = 0;
+ /* build static tables */
+ memset(g_static_mdtab, 0, sizeof(g_static_mdtab));
+ memset(g_static_strtab, 0, sizeof(g_static_strtab));
+ for (i = 0; i < GRPC_STATIC_MDSTR_COUNT; i++) {
+ grpc_mdstr *elem = &grpc_static_mdstr_table[i];
+ const char *str = grpc_static_metadata_strings[i];
+ gpr_uint32 hash = gpr_murmur_hash3(str, strlen(str), g_hash_seed);
+ *(gpr_slice *)&elem->slice = gpr_slice_from_static_string(str);
+ *(gpr_uint32 *)&elem->hash = hash;
+ for (j = 0;; j++) {
+ size_t idx = (hash + j) % GPR_ARRAY_SIZE(g_static_strtab);
+ if (g_static_strtab[idx] == NULL) {
+ g_static_strtab[idx] = &grpc_static_mdstr_table[i];
+ break;
+ }
+ }
+ if (j > g_static_strtab_maxprobe) {
+ g_static_strtab_maxprobe = j;
+ }
+ }
+ for (i = 0; i < GRPC_STATIC_MDELEM_COUNT; i++) {
+ grpc_mdelem *elem = &grpc_static_mdelem_table[i];
+ grpc_mdstr *key =
+ &grpc_static_mdstr_table[grpc_static_metadata_elem_indices[2 * i + 0]];
+ grpc_mdstr *value =
+ &grpc_static_mdstr_table[grpc_static_metadata_elem_indices[2 * i + 1]];
+ gpr_uint32 hash = GRPC_MDSTR_KV_HASH(key->hash, value->hash);
+ *(grpc_mdstr **)&elem->key = key;
+ *(grpc_mdstr **)&elem->value = value;
+ for (j = 0;; j++) {
+ size_t idx = (hash + j) % GPR_ARRAY_SIZE(g_static_mdtab);
+ if (g_static_mdtab[idx] == NULL) {
+ g_static_mdtab[idx] = elem;
+ break;
+ }
+ }
+ if (j > g_static_mdtab_maxprobe) {
+ g_static_mdtab_maxprobe = j;
+ }
+ }
+ /* initialize shards */
+ for (i = 0; i < STRTAB_SHARD_COUNT; i++) {
+ strtab_shard *shard = &g_strtab_shard[i];
+ gpr_mu_init(&shard->mu);
+ shard->count = 0;
+ shard->capacity = INITIAL_STRTAB_CAPACITY;
+ shard->strs = gpr_malloc(sizeof(*shard->strs) * shard->capacity);
+ memset(shard->strs, 0, sizeof(*shard->strs) * shard->capacity);
+ }
+ for (i = 0; i < MDTAB_SHARD_COUNT; i++) {
+ mdtab_shard *shard = &g_mdtab_shard[i];
+ gpr_mu_init(&shard->mu);
+ shard->count = 0;
+ shard->free = 0;
+ shard->capacity = INITIAL_MDTAB_CAPACITY;
+ shard->elems = gpr_malloc(sizeof(*shard->elems) * shard->capacity);
+ memset(shard->elems, 0, sizeof(*shard->elems) * shard->capacity);
+ }
+}
- internal_metadata **mdtab;
- size_t mdtab_count;
- size_t mdtab_free;
- size_t mdtab_capacity;
-};
-
-static void internal_string_ref(internal_string *s DEBUG_ARGS);
-static void internal_string_unref(internal_string *s DEBUG_ARGS);
-static void discard_metadata(grpc_mdctx *ctx);
-static void gc_mdtab(grpc_mdctx *ctx);
-static void metadata_context_destroy_locked(grpc_mdctx *ctx);
-
-static void lock(grpc_mdctx *ctx) { gpr_mu_lock(&ctx->mu); }
-
-static void unlock(grpc_mdctx *ctx) {
- /* If the context has been orphaned we'd like to delete it soon. We check
- conditions in unlock as it signals the end of mutations on a context.
-
- We need to ensure all grpc_mdelem and grpc_mdstr elements have been deleted
- first. This is equivalent to saying that both tables have zero counts,
- which is equivalent to saying that strtab_count is zero (as mdelem's MUST
- reference an mdstr for their key and value slots).
-
- To encourage that to happen, we start discarding zero reference count
- mdelems on every unlock (instead of the usual 'I'm too loaded' trigger
- case), since otherwise we can be stuck waiting for a garbage collection
- that will never happen. */
- if (ctx->refs == 0) {
-/* uncomment if you're having trouble diagnosing an mdelem leak to make
- things clearer (slows down destruction a lot, however) */
-#ifdef GRPC_METADATA_REFCOUNT_DEBUG
- gc_mdtab(ctx);
-#endif
- if (ctx->mdtab_count && ctx->mdtab_count == ctx->mdtab_free) {
- discard_metadata(ctx);
+void grpc_mdctx_global_shutdown(void) {
+ size_t i;
+ for (i = 0; i < MDTAB_SHARD_COUNT; i++) {
+ mdtab_shard *shard = &g_mdtab_shard[i];
+ gpr_mu_destroy(&shard->mu);
+ gc_mdtab(shard);
+ /* TODO(ctiller): GPR_ASSERT(shard->count == 0); */
+ if (shard->count != 0) {
+ gpr_log(GPR_DEBUG, "WARNING: %d metadata elements were leaked",
+ shard->count);
}
- if (ctx->strtab_count == 0) {
- metadata_context_destroy_locked(ctx);
- return;
+ gpr_free(shard->elems);
+ }
+ for (i = 0; i < STRTAB_SHARD_COUNT; i++) {
+ strtab_shard *shard = &g_strtab_shard[i];
+ gpr_mu_destroy(&shard->mu);
+ /* TODO(ctiller): GPR_ASSERT(shard->count == 0); */
+ if (shard->count != 0) {
+ gpr_log(GPR_DEBUG, "WARNING: %d metadata strings were leaked",
+ shard->count);
}
+ gpr_free(shard->strs);
}
- gpr_mu_unlock(&ctx->mu);
}
-static void ref_md_locked(internal_metadata *md DEBUG_ARGS) {
+static int is_mdstr_static(grpc_mdstr *s) {
+ return s >= &grpc_static_mdstr_table[0] &&
+ s < &grpc_static_mdstr_table[GRPC_STATIC_MDSTR_COUNT];
+}
+
+static int is_mdelem_static(grpc_mdelem *e) {
+ return e >= &grpc_static_mdelem_table[0] &&
+ e < &grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT];
+}
+
+static void ref_md_locked(mdtab_shard *shard,
+ internal_metadata *md DEBUG_ARGS) {
#ifdef GRPC_METADATA_REFCOUNT_DEBUG
gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
"ELM REF:%p:%d->%d: '%s' = '%s'", md,
@@ -162,96 +255,14 @@ static void ref_md_locked(internal_metadata *md DEBUG_ARGS) {
grpc_mdstr_as_c_string((grpc_mdstr *)md->value));
#endif
if (0 == gpr_atm_no_barrier_fetch_add(&md->refcnt, 2)) {
- md->context->mdtab_free--;
+ shard->free--;
} else {
GPR_ASSERT(1 != gpr_atm_no_barrier_fetch_add(&md->refcnt, -1));
}
}
-grpc_mdctx *grpc_mdctx_create_with_seed(gpr_uint32 seed) {
- grpc_mdctx *ctx = gpr_malloc(sizeof(grpc_mdctx));
-
- ctx->refs = 1;
- ctx->hash_seed = seed;
- gpr_mu_init(&ctx->mu);
- ctx->strtab = gpr_malloc(sizeof(internal_string *) * INITIAL_STRTAB_CAPACITY);
- memset(ctx->strtab, 0, sizeof(grpc_mdstr *) * INITIAL_STRTAB_CAPACITY);
- ctx->strtab_count = 0;
- ctx->strtab_capacity = INITIAL_STRTAB_CAPACITY;
- ctx->mdtab = gpr_malloc(sizeof(internal_metadata *) * INITIAL_MDTAB_CAPACITY);
- memset(ctx->mdtab, 0, sizeof(grpc_mdelem *) * INITIAL_MDTAB_CAPACITY);
- ctx->mdtab_count = 0;
- ctx->mdtab_capacity = INITIAL_MDTAB_CAPACITY;
- ctx->mdtab_free = 0;
-
- return ctx;
-}
-
-grpc_mdctx *grpc_mdctx_create(void) {
- /* This seed is used to prevent remote connections from controlling hash table
- * collisions. It needs to be somewhat unpredictable to a remote connection.
- */
- return grpc_mdctx_create_with_seed(
- (gpr_uint32)gpr_now(GPR_CLOCK_REALTIME).tv_nsec);
-}
-
-static void discard_metadata(grpc_mdctx *ctx) {
- size_t i;
- internal_metadata *next, *cur;
-
- for (i = 0; i < ctx->mdtab_capacity; i++) {
- cur = ctx->mdtab[i];
- while (cur) {
- void *user_data = (void *)gpr_atm_no_barrier_load(&cur->user_data);
- GPR_ASSERT(gpr_atm_acq_load(&cur->refcnt) == 0);
- next = cur->bucket_next;
- INTERNAL_STRING_UNREF(cur->key);
- INTERNAL_STRING_UNREF(cur->value);
- if (user_data != NULL) {
- ((destroy_user_data_func)gpr_atm_no_barrier_load(
- &cur->destroy_user_data))(user_data);
- }
- gpr_mu_destroy(&cur->mu_user_data);
- gpr_free(cur);
- cur = next;
- ctx->mdtab_free--;
- ctx->mdtab_count--;
- }
- ctx->mdtab[i] = NULL;
- }
-}
-
-static void metadata_context_destroy_locked(grpc_mdctx *ctx) {
- GPR_ASSERT(ctx->strtab_count == 0);
- GPR_ASSERT(ctx->mdtab_count == 0);
- GPR_ASSERT(ctx->mdtab_free == 0);
- gpr_free(ctx->strtab);
- gpr_free(ctx->mdtab);
- gpr_mu_unlock(&ctx->mu);
- gpr_mu_destroy(&ctx->mu);
- gpr_free(ctx);
-}
-
-void grpc_mdctx_ref(grpc_mdctx *ctx) {
- GPR_TIMER_BEGIN("grpc_mdctx_ref", 0);
- lock(ctx);
- GPR_ASSERT(ctx->refs > 0);
- ctx->refs++;
- unlock(ctx);
- GPR_TIMER_END("grpc_mdctx_ref", 0);
-}
-
-void grpc_mdctx_unref(grpc_mdctx *ctx) {
- GPR_TIMER_BEGIN("grpc_mdctx_unref", 0);
- lock(ctx);
- GPR_ASSERT(ctx->refs > 0);
- ctx->refs--;
- unlock(ctx);
- GPR_TIMER_END("grpc_mdctx_unref", 0);
-}
-
-static void grow_strtab(grpc_mdctx *ctx) {
- size_t capacity = ctx->strtab_capacity * 2;
+static void grow_strtab(strtab_shard *shard) {
+ size_t capacity = shard->capacity * 2;
size_t i;
internal_string **strtab;
internal_string *s, *next;
@@ -261,105 +272,95 @@ static void grow_strtab(grpc_mdctx *ctx) {
strtab = gpr_malloc(sizeof(internal_string *) * capacity);
memset(strtab, 0, sizeof(internal_string *) * capacity);
- for (i = 0; i < ctx->strtab_capacity; i++) {
- for (s = ctx->strtab[i]; s; s = next) {
+ for (i = 0; i < shard->capacity; i++) {
+ for (s = shard->strs[i]; s; s = next) {
+ size_t idx = TABLE_IDX(s->hash, LOG2_STRTAB_SHARD_COUNT, capacity);
next = s->bucket_next;
- s->bucket_next = strtab[s->hash % capacity];
- strtab[s->hash % capacity] = s;
+ s->bucket_next = strtab[idx];
+ strtab[idx] = s;
}
}
- gpr_free(ctx->strtab);
- ctx->strtab = strtab;
- ctx->strtab_capacity = capacity;
+ gpr_free(shard->strs);
+ shard->strs = strtab;
+ shard->capacity = capacity;
GPR_TIMER_END("grow_strtab", 0);
}
-static void internal_destroy_string(internal_string *is) {
+static void internal_destroy_string(strtab_shard *shard, internal_string *is) {
internal_string **prev_next;
internal_string *cur;
- grpc_mdctx *ctx = is->context;
GPR_TIMER_BEGIN("internal_destroy_string", 0);
if (is->has_base64_and_huffman_encoded) {
gpr_slice_unref(is->base64_and_huffman);
}
- for (prev_next = &ctx->strtab[is->hash % ctx->strtab_capacity],
+ for (prev_next = &shard->strs[TABLE_IDX(is->hash, LOG2_STRTAB_SHARD_COUNT,
+ shard->capacity)],
cur = *prev_next;
cur != is; prev_next = &cur->bucket_next, cur = cur->bucket_next)
;
*prev_next = cur->bucket_next;
- ctx->strtab_count--;
+ shard->count--;
gpr_free(is);
GPR_TIMER_END("internal_destroy_string", 0);
}
-static void internal_string_ref(internal_string *s DEBUG_ARGS) {
-#ifdef GRPC_METADATA_REFCOUNT_DEBUG
- gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "STR REF:%p:%d->%d: '%s'", s,
- s->refs, s->refs + 1, grpc_mdstr_as_c_string((grpc_mdstr *)s));
-#endif
- ++s->refs;
-}
-
-static void internal_string_unref(internal_string *s DEBUG_ARGS) {
-#ifdef GRPC_METADATA_REFCOUNT_DEBUG
- gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "STR UNREF:%p:%d->%d: '%s'", s,
- s->refs, s->refs - 1, grpc_mdstr_as_c_string((grpc_mdstr *)s));
-#endif
- GPR_ASSERT(s->refs > 0);
- if (0 == --s->refs) {
- internal_destroy_string(s);
- }
-}
-
static void slice_ref(void *p) {
internal_string *is =
(internal_string *)((char *)p - offsetof(internal_string, refcount));
- grpc_mdctx *ctx = is->context;
- GPR_TIMER_BEGIN("slice_ref", 0);
- lock(ctx);
- INTERNAL_STRING_REF(is);
- unlock(ctx);
- GPR_TIMER_END("slice_ref", 0);
+ GRPC_MDSTR_REF((grpc_mdstr *)(is));
}
static void slice_unref(void *p) {
internal_string *is =
(internal_string *)((char *)p - offsetof(internal_string, refcount));
- grpc_mdctx *ctx = is->context;
- GPR_TIMER_BEGIN("slice_unref", 0);
- lock(ctx);
- INTERNAL_STRING_UNREF(is);
- unlock(ctx);
- GPR_TIMER_END("slice_unref", 0);
+ GRPC_MDSTR_UNREF((grpc_mdstr *)(is));
}
-grpc_mdstr *grpc_mdstr_from_string(grpc_mdctx *ctx, const char *str) {
- return grpc_mdstr_from_buffer(ctx, (const gpr_uint8 *)str, strlen(str));
+grpc_mdstr *grpc_mdstr_from_string(const char *str) {
+ return grpc_mdstr_from_buffer((const gpr_uint8 *)str, strlen(str));
}
-grpc_mdstr *grpc_mdstr_from_slice(grpc_mdctx *ctx, gpr_slice slice) {
- grpc_mdstr *result = grpc_mdstr_from_buffer(ctx, GPR_SLICE_START_PTR(slice),
+grpc_mdstr *grpc_mdstr_from_slice(gpr_slice slice) {
+ grpc_mdstr *result = grpc_mdstr_from_buffer(GPR_SLICE_START_PTR(slice),
GPR_SLICE_LENGTH(slice));
gpr_slice_unref(slice);
return result;
}
-grpc_mdstr *grpc_mdstr_from_buffer(grpc_mdctx *ctx, const gpr_uint8 *buf,
- size_t length) {
- gpr_uint32 hash = gpr_murmur_hash3(buf, length, ctx->hash_seed);
+grpc_mdstr *grpc_mdstr_from_buffer(const gpr_uint8 *buf, size_t length) {
+ gpr_uint32 hash = gpr_murmur_hash3(buf, length, g_hash_seed);
internal_string *s;
+ strtab_shard *shard =
+ &g_strtab_shard[SHARD_IDX(hash, LOG2_STRTAB_SHARD_COUNT)];
+ size_t i;
+ size_t idx;
GPR_TIMER_BEGIN("grpc_mdstr_from_buffer", 0);
- lock(ctx);
+
+ /* search for a static string */
+ for (i = 0; i <= g_static_strtab_maxprobe; i++) {
+ grpc_mdstr *ss;
+ idx = (hash + i) % GPR_ARRAY_SIZE(g_static_strtab);
+ ss = g_static_strtab[idx];
+ if (ss == NULL) break;
+ if (ss->hash == hash && GPR_SLICE_LENGTH(ss->slice) == length &&
+ 0 == memcmp(buf, GPR_SLICE_START_PTR(ss->slice), length)) {
+ GPR_TIMER_END("grpc_mdstr_from_buffer", 0);
+ return ss;
+ }
+ }
+
+ gpr_mu_lock(&shard->mu);
/* search for an existing string */
- for (s = ctx->strtab[hash % ctx->strtab_capacity]; s; s = s->bucket_next) {
+ idx = TABLE_IDX(hash, LOG2_STRTAB_SHARD_COUNT, shard->capacity);
+ for (s = shard->strs[idx]; s; s = s->bucket_next) {
if (s->hash == hash && GPR_SLICE_LENGTH(s->slice) == length &&
0 == memcmp(buf, GPR_SLICE_START_PTR(s->slice), length)) {
- INTERNAL_STRING_REF(s);
- unlock(ctx);
+ GRPC_MDSTR_REF((grpc_mdstr *)s);
+ gpr_mu_unlock(&shard->mu);
GPR_TIMER_END("grpc_mdstr_from_buffer", 0);
return (grpc_mdstr *)s;
}
@@ -369,7 +370,7 @@ grpc_mdstr *grpc_mdstr_from_buffer(grpc_mdctx *ctx, const gpr_uint8 *buf,
if (length + 1 < GPR_SLICE_INLINED_SIZE) {
/* string data goes directly into the slice */
s = gpr_malloc(sizeof(internal_string));
- s->refs = 1;
+ gpr_atm_rel_store(&s->refcnt, 2);
s->slice.refcount = NULL;
memcpy(s->slice.data.inlined.bytes, buf, length);
s->slice.data.inlined.bytes[length] = 0;
@@ -378,7 +379,7 @@ grpc_mdstr *grpc_mdstr_from_buffer(grpc_mdctx *ctx, const gpr_uint8 *buf,
/* string data goes after the internal_string header, and we +1 for null
terminator */
s = gpr_malloc(sizeof(internal_string) + length + 1);
- s->refs = 1;
+ gpr_atm_rel_store(&s->refcnt, 2);
s->refcount.ref = slice_ref;
s->refcount.unref = slice_unref;
s->slice.refcount = &s->refcount;
@@ -390,44 +391,43 @@ grpc_mdstr *grpc_mdstr_from_buffer(grpc_mdctx *ctx, const gpr_uint8 *buf,
}
s->has_base64_and_huffman_encoded = 0;
s->hash = hash;
- s->context = ctx;
- s->bucket_next = ctx->strtab[hash % ctx->strtab_capacity];
- ctx->strtab[hash % ctx->strtab_capacity] = s;
+ s->bucket_next = shard->strs[idx];
+ shard->strs[idx] = s;
- ctx->strtab_count++;
+ shard->count++;
- if (ctx->strtab_count > ctx->strtab_capacity * 2) {
- grow_strtab(ctx);
+ if (shard->count > shard->capacity * 2) {
+ grow_strtab(shard);
}
- unlock(ctx);
+ gpr_mu_unlock(&shard->mu);
GPR_TIMER_END("grpc_mdstr_from_buffer", 0);
return (grpc_mdstr *)s;
}
-static void gc_mdtab(grpc_mdctx *ctx) {
+static void gc_mdtab(mdtab_shard *shard) {
size_t i;
internal_metadata **prev_next;
internal_metadata *md, *next;
GPR_TIMER_BEGIN("gc_mdtab", 0);
- for (i = 0; i < ctx->mdtab_capacity; i++) {
- prev_next = &ctx->mdtab[i];
- for (md = ctx->mdtab[i]; md; md = next) {
+ for (i = 0; i < shard->capacity; i++) {
+ prev_next = &shard->elems[i];
+ for (md = shard->elems[i]; md; md = next) {
void *user_data = (void *)gpr_atm_no_barrier_load(&md->user_data);
next = md->bucket_next;
if (gpr_atm_acq_load(&md->refcnt) == 0) {
- INTERNAL_STRING_UNREF(md->key);
- INTERNAL_STRING_UNREF(md->value);
+ GRPC_MDSTR_UNREF((grpc_mdstr *)md->key);
+ GRPC_MDSTR_UNREF((grpc_mdstr *)md->value);
if (md->user_data) {
((destroy_user_data_func)gpr_atm_no_barrier_load(
&md->destroy_user_data))(user_data);
}
gpr_free(md);
*prev_next = next;
- ctx->mdtab_free--;
- ctx->mdtab_count--;
+ shard->free--;
+ shard->count--;
} else {
prev_next = &md->bucket_next;
}
@@ -436,8 +436,8 @@ static void gc_mdtab(grpc_mdctx *ctx) {
GPR_TIMER_END("gc_mdtab", 0);
}
-static void grow_mdtab(grpc_mdctx *ctx) {
- size_t capacity = ctx->mdtab_capacity * 2;
+static void grow_mdtab(mdtab_shard *shard) {
+ size_t capacity = shard->capacity * 2;
size_t i;
internal_metadata **mdtab;
internal_metadata *md, *next;
@@ -448,52 +448,67 @@ static void grow_mdtab(grpc_mdctx *ctx) {
mdtab = gpr_malloc(sizeof(internal_metadata *) * capacity);
memset(mdtab, 0, sizeof(internal_metadata *) * capacity);
- for (i = 0; i < ctx->mdtab_capacity; i++) {
- for (md = ctx->mdtab[i]; md; md = next) {
+ for (i = 0; i < shard->capacity; i++) {
+ for (md = shard->elems[i]; md; md = next) {
+ size_t idx;
hash = GRPC_MDSTR_KV_HASH(md->key->hash, md->value->hash);
next = md->bucket_next;
- md->bucket_next = mdtab[hash % capacity];
- mdtab[hash % capacity] = md;
+ idx = TABLE_IDX(hash, LOG2_MDTAB_SHARD_COUNT, capacity);
+ md->bucket_next = mdtab[idx];
+ mdtab[idx] = md;
}
}
- gpr_free(ctx->mdtab);
- ctx->mdtab = mdtab;
- ctx->mdtab_capacity = capacity;
+ gpr_free(shard->elems);
+ shard->elems = mdtab;
+ shard->capacity = capacity;
GPR_TIMER_END("grow_mdtab", 0);
}
-static void rehash_mdtab(grpc_mdctx *ctx) {
- if (ctx->mdtab_free > ctx->mdtab_capacity / 4) {
- gc_mdtab(ctx);
+static void rehash_mdtab(mdtab_shard *shard) {
+ if (shard->free > shard->capacity / 4) {
+ gc_mdtab(shard);
} else {
- grow_mdtab(ctx);
+ grow_mdtab(shard);
}
}
-grpc_mdelem *grpc_mdelem_from_metadata_strings(grpc_mdctx *ctx,
- grpc_mdstr *mkey,
+grpc_mdelem *grpc_mdelem_from_metadata_strings(grpc_mdstr *mkey,
grpc_mdstr *mvalue) {
internal_string *key = (internal_string *)mkey;
internal_string *value = (internal_string *)mvalue;
gpr_uint32 hash = GRPC_MDSTR_KV_HASH(mkey->hash, mvalue->hash);
internal_metadata *md;
-
- GPR_ASSERT(key->context == ctx);
- GPR_ASSERT(value->context == ctx);
+ mdtab_shard *shard = &g_mdtab_shard[SHARD_IDX(hash, LOG2_MDTAB_SHARD_COUNT)];
+ size_t i;
+ size_t idx;
GPR_TIMER_BEGIN("grpc_mdelem_from_metadata_strings", 0);
- lock(ctx);
+ if (is_mdstr_static(mkey) && is_mdstr_static(mvalue)) {
+ for (i = 0; i <= g_static_mdtab_maxprobe; i++) {
+ grpc_mdelem *smd;
+ idx = (hash + i) % GPR_ARRAY_SIZE(g_static_mdtab);
+ smd = g_static_mdtab[idx];
+ if (smd == NULL) break;
+ if (smd->key == mkey && smd->value == mvalue) {
+ GPR_TIMER_END("grpc_mdelem_from_metadata_strings", 0);
+ return smd;
+ }
+ }
+ }
+
+ gpr_mu_lock(&shard->mu);
+ idx = TABLE_IDX(hash, LOG2_MDTAB_SHARD_COUNT, shard->capacity);
/* search for an existing pair */
- for (md = ctx->mdtab[hash % ctx->mdtab_capacity]; md; md = md->bucket_next) {
+ for (md = shard->elems[idx]; md; md = md->bucket_next) {
if (md->key == key && md->value == value) {
- REF_MD_LOCKED(md);
- INTERNAL_STRING_UNREF(key);
- INTERNAL_STRING_UNREF(value);
- unlock(ctx);
+ REF_MD_LOCKED(shard, md);
+ GRPC_MDSTR_UNREF((grpc_mdstr *)key);
+ GRPC_MDSTR_UNREF((grpc_mdstr *)value);
+ gpr_mu_unlock(&shard->mu);
GPR_TIMER_END("grpc_mdelem_from_metadata_strings", 0);
return (grpc_mdelem *)md;
}
@@ -502,12 +517,12 @@ grpc_mdelem *grpc_mdelem_from_metadata_strings(grpc_mdctx *ctx,
/* not found: create a new pair */
md = gpr_malloc(sizeof(internal_metadata));
gpr_atm_rel_store(&md->refcnt, 2);
- md->context = ctx;
md->key = key;
md->value = value;
md->user_data = 0;
md->destroy_user_data = 0;
- md->bucket_next = ctx->mdtab[hash % ctx->mdtab_capacity];
+ md->bucket_next = shard->elems[idx];
+ shard->elems[idx] = md;
gpr_mu_init(&md->mu_user_data);
#ifdef GRPC_METADATA_REFCOUNT_DEBUG
gpr_log(GPR_DEBUG, "ELM NEW:%p:%d: '%s' = '%s'", md,
@@ -515,44 +530,39 @@ grpc_mdelem *grpc_mdelem_from_metadata_strings(grpc_mdctx *ctx,
grpc_mdstr_as_c_string((grpc_mdstr *)md->key),
grpc_mdstr_as_c_string((grpc_mdstr *)md->value));
#endif
- ctx->mdtab[hash % ctx->mdtab_capacity] = md;
- ctx->mdtab_count++;
+ shard->count++;
- if (ctx->mdtab_count > ctx->mdtab_capacity * 2) {
- rehash_mdtab(ctx);
+ if (shard->count > shard->capacity * 2) {
+ rehash_mdtab(shard);
}
- unlock(ctx);
+ gpr_mu_unlock(&shard->mu);
GPR_TIMER_END("grpc_mdelem_from_metadata_strings", 0);
return (grpc_mdelem *)md;
}
-grpc_mdelem *grpc_mdelem_from_strings(grpc_mdctx *ctx, const char *key,
- const char *value) {
- return grpc_mdelem_from_metadata_strings(ctx,
- grpc_mdstr_from_string(ctx, key),
- grpc_mdstr_from_string(ctx, value));
+grpc_mdelem *grpc_mdelem_from_strings(const char *key, const char *value) {
+ return grpc_mdelem_from_metadata_strings(grpc_mdstr_from_string(key),
+ grpc_mdstr_from_string(value));
}
-grpc_mdelem *grpc_mdelem_from_slices(grpc_mdctx *ctx, gpr_slice key,
- gpr_slice value) {
- return grpc_mdelem_from_metadata_strings(ctx, grpc_mdstr_from_slice(ctx, key),
- grpc_mdstr_from_slice(ctx, value));
+grpc_mdelem *grpc_mdelem_from_slices(gpr_slice key, gpr_slice value) {
+ return grpc_mdelem_from_metadata_strings(grpc_mdstr_from_slice(key),
+ grpc_mdstr_from_slice(value));
}
-grpc_mdelem *grpc_mdelem_from_string_and_buffer(grpc_mdctx *ctx,
- const char *key,
+grpc_mdelem *grpc_mdelem_from_string_and_buffer(const char *key,
const gpr_uint8 *value,
size_t value_length) {
return grpc_mdelem_from_metadata_strings(
- ctx, grpc_mdstr_from_string(ctx, key),
- grpc_mdstr_from_buffer(ctx, value, value_length));
+ grpc_mdstr_from_string(key), grpc_mdstr_from_buffer(value, value_length));
}
grpc_mdelem *grpc_mdelem_ref(grpc_mdelem *gmd DEBUG_ARGS) {
internal_metadata *md = (internal_metadata *)gmd;
+ if (is_mdelem_static(gmd)) return gmd;
#ifdef GRPC_METADATA_REFCOUNT_DEBUG
gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
"ELM REF:%p:%d->%d: '%s' = '%s'", md,
@@ -573,6 +583,7 @@ grpc_mdelem *grpc_mdelem_ref(grpc_mdelem *gmd DEBUG_ARGS) {
void grpc_mdelem_unref(grpc_mdelem *gmd DEBUG_ARGS) {
internal_metadata *md = (internal_metadata *)gmd;
if (!md) return;
+ if (is_mdelem_static(gmd)) return;
#ifdef GRPC_METADATA_REFCOUNT_DEBUG
gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG,
"ELM UNREF:%p:%d->%d: '%s' = '%s'", md,
@@ -582,14 +593,16 @@ void grpc_mdelem_unref(grpc_mdelem *gmd DEBUG_ARGS) {
grpc_mdstr_as_c_string((grpc_mdstr *)md->value));
#endif
if (2 == gpr_atm_full_fetch_add(&md->refcnt, -1)) {
- grpc_mdctx *ctx = md->context;
+ gpr_uint32 hash = GRPC_MDSTR_KV_HASH(md->key->hash, md->value->hash);
+ mdtab_shard *shard =
+ &g_mdtab_shard[SHARD_IDX(hash, LOG2_MDTAB_SHARD_COUNT)];
GPR_TIMER_BEGIN("grpc_mdelem_unref.to_zero", 0);
- lock(ctx);
+ gpr_mu_lock(&shard->mu);
if (1 == gpr_atm_no_barrier_load(&md->refcnt)) {
- ctx->mdtab_free++;
+ shard->free++;
gpr_atm_no_barrier_store(&md->refcnt, 0);
}
- unlock(ctx);
+ gpr_mu_unlock(&shard->mu);
GPR_TIMER_END("grpc_mdelem_unref.to_zero", 0);
}
}
@@ -600,36 +613,31 @@ const char *grpc_mdstr_as_c_string(grpc_mdstr *s) {
grpc_mdstr *grpc_mdstr_ref(grpc_mdstr *gs DEBUG_ARGS) {
internal_string *s = (internal_string *)gs;
- grpc_mdctx *ctx = s->context;
- lock(ctx);
- internal_string_ref(s FWD_DEBUG_ARGS);
- unlock(ctx);
+ if (is_mdstr_static(gs)) return gs;
+ GPR_ASSERT(gpr_atm_full_fetch_add(&s->refcnt, 1) != 0);
return gs;
}
void grpc_mdstr_unref(grpc_mdstr *gs DEBUG_ARGS) {
internal_string *s = (internal_string *)gs;
- grpc_mdctx *ctx = s->context;
- lock(ctx);
- internal_string_unref(s FWD_DEBUG_ARGS);
- unlock(ctx);
-}
-
-size_t grpc_mdctx_get_mdtab_capacity_test_only(grpc_mdctx *ctx) {
- return ctx->mdtab_capacity;
-}
-
-size_t grpc_mdctx_get_mdtab_count_test_only(grpc_mdctx *ctx) {
- return ctx->mdtab_count;
-}
-
-size_t grpc_mdctx_get_mdtab_free_test_only(grpc_mdctx *ctx) {
- return ctx->mdtab_free;
+ if (is_mdstr_static(gs)) return;
+ if (2 == gpr_atm_full_fetch_add(&s->refcnt, -1)) {
+ strtab_shard *shard =
+ &g_strtab_shard[SHARD_IDX(s->hash, LOG2_STRTAB_SHARD_COUNT)];
+ gpr_mu_lock(&shard->mu);
+ if (1 == gpr_atm_no_barrier_load(&s->refcnt)) {
+ internal_destroy_string(shard, s);
+ }
+ gpr_mu_unlock(&shard->mu);
+ }
}
void *grpc_mdelem_get_user_data(grpc_mdelem *md, void (*destroy_func)(void *)) {
internal_metadata *im = (internal_metadata *)md;
void *result;
+ if (is_mdelem_static(md)) {
+ return (void *)grpc_static_mdelem_user_data[md - grpc_static_mdelem_table];
+ }
if (gpr_atm_acq_load(&im->destroy_user_data) == (gpr_atm)destroy_func) {
return (void *)gpr_atm_no_barrier_load(&im->user_data);
} else {
@@ -641,6 +649,7 @@ void *grpc_mdelem_get_user_data(grpc_mdelem *md, void (*destroy_func)(void *)) {
void grpc_mdelem_set_user_data(grpc_mdelem *md, void (*destroy_func)(void *),
void *user_data) {
internal_metadata *im = (internal_metadata *)md;
+ GPR_ASSERT(!is_mdelem_static(md));
GPR_ASSERT((user_data == NULL) == (destroy_func == NULL));
gpr_mu_lock(&im->mu_user_data);
if (gpr_atm_no_barrier_load(&im->destroy_user_data)) {
@@ -659,15 +668,16 @@ void grpc_mdelem_set_user_data(grpc_mdelem *md, void (*destroy_func)(void *),
gpr_slice grpc_mdstr_as_base64_encoded_and_huffman_compressed(grpc_mdstr *gs) {
internal_string *s = (internal_string *)gs;
gpr_slice slice;
- grpc_mdctx *ctx = s->context;
- lock(ctx);
+ strtab_shard *shard =
+ &g_strtab_shard[SHARD_IDX(s->hash, LOG2_STRTAB_SHARD_COUNT)];
+ gpr_mu_lock(&shard->mu);
if (!s->has_base64_and_huffman_encoded) {
s->base64_and_huffman =
grpc_chttp2_base64_encode_and_huffman_compress(s->slice);
s->has_base64_and_huffman_encoded = 1;
}
slice = s->base64_and_huffman;
- unlock(ctx);
+ gpr_mu_unlock(&shard->mu);
return slice;
}
diff --git a/src/core/transport/metadata.h b/src/core/transport/metadata.h
index 9a8164037c..c1071e4e16 100644
--- a/src/core/transport/metadata.h
+++ b/src/core/transport/metadata.h
@@ -59,10 +59,15 @@
grpc_mdelem instances MAY live longer than their refcount implies, and are
garbage collected periodically, meaning cached data can easily outlive a
- single request. */
+ single request.
+
+ STATIC METADATA: in static_metadata.h we declare a set of static metadata.
+ These mdelems and mdstrs are available via pre-declared code generated macros
+ and are available to code anywhere between grpc_init() and grpc_shutdown().
+ They are not refcounted, but can be passed to _ref and _unref functions
+ declared here - in which case those functions are effectively no-ops. */
/* Forward declarations */
-typedef struct grpc_mdctx grpc_mdctx;
typedef struct grpc_mdstr grpc_mdstr;
typedef struct grpc_mdelem grpc_mdelem;
@@ -81,25 +86,18 @@ struct grpc_mdelem {
/* there is a private part to this in metadata.c */
};
-/* Create/orphan a metadata context */
-grpc_mdctx *grpc_mdctx_create(void);
-grpc_mdctx *grpc_mdctx_create_with_seed(gpr_uint32 seed);
-void grpc_mdctx_ref(grpc_mdctx *mdctx);
-void grpc_mdctx_unref(grpc_mdctx *mdctx);
-
/* Test only accessors to internal state - only for testing this code - do not
rely on it outside of metadata_test.c */
-size_t grpc_mdctx_get_mdtab_capacity_test_only(grpc_mdctx *mdctx);
-size_t grpc_mdctx_get_mdtab_count_test_only(grpc_mdctx *mdctx);
-size_t grpc_mdctx_get_mdtab_free_test_only(grpc_mdctx *mdctx);
+size_t grpc_mdctx_get_mdtab_capacity_test_only(void);
+size_t grpc_mdctx_get_mdtab_count_test_only(void);
+size_t grpc_mdctx_get_mdtab_free_test_only(void);
/* Constructors for grpc_mdstr instances; take a variety of data types that
clients may have handy */
-grpc_mdstr *grpc_mdstr_from_string(grpc_mdctx *ctx, const char *str);
+grpc_mdstr *grpc_mdstr_from_string(const char *str);
/* Unrefs the slice. */
-grpc_mdstr *grpc_mdstr_from_slice(grpc_mdctx *ctx, gpr_slice slice);
-grpc_mdstr *grpc_mdstr_from_buffer(grpc_mdctx *ctx, const gpr_uint8 *str,
- size_t length);
+grpc_mdstr *grpc_mdstr_from_slice(gpr_slice slice);
+grpc_mdstr *grpc_mdstr_from_buffer(const gpr_uint8 *str, size_t length);
/* Returns a borrowed slice from the mdstr with its contents base64 encoded
and huffman compressed */
@@ -107,15 +105,12 @@ gpr_slice grpc_mdstr_as_base64_encoded_and_huffman_compressed(grpc_mdstr *str);
/* Constructors for grpc_mdelem instances; take a variety of data types that
clients may have handy */
-grpc_mdelem *grpc_mdelem_from_metadata_strings(grpc_mdctx *ctx, grpc_mdstr *key,
+grpc_mdelem *grpc_mdelem_from_metadata_strings(grpc_mdstr *key,
grpc_mdstr *value);
-grpc_mdelem *grpc_mdelem_from_strings(grpc_mdctx *ctx, const char *key,
- const char *value);
+grpc_mdelem *grpc_mdelem_from_strings(const char *key, const char *value);
/* Unrefs the slices. */
-grpc_mdelem *grpc_mdelem_from_slices(grpc_mdctx *ctx, gpr_slice key,
- gpr_slice value);
-grpc_mdelem *grpc_mdelem_from_string_and_buffer(grpc_mdctx *ctx,
- const char *key,
+grpc_mdelem *grpc_mdelem_from_slices(gpr_slice key, gpr_slice value);
+grpc_mdelem *grpc_mdelem_from_string_and_buffer(const char *key,
const gpr_uint8 *value,
size_t value_length);
@@ -157,4 +152,7 @@ int grpc_mdstr_is_bin_suffixed(grpc_mdstr *s);
#define GRPC_MDSTR_KV_HASH(k_hash, v_hash) (GPR_ROTL((k_hash), 2) ^ (v_hash))
+void grpc_mdctx_global_init(void);
+void grpc_mdctx_global_shutdown(void);
+
#endif /* GRPC_INTERNAL_CORE_TRANSPORT_METADATA_H */
diff --git a/src/core/transport/static_metadata.c b/src/core/transport/static_metadata.c
new file mode 100644
index 0000000000..e7aff325c2
--- /dev/null
+++ b/src/core/transport/static_metadata.c
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * WARNING: Auto-generated code.
+ *
+ * To make changes to this file, change
+ * tools/codegen/core/gen_static_metadata.py,
+ * and then re-run it.
+ *
+ * See metadata.h for an explanation of the interface here, and metadata.c for
+ * an
+ * explanation of what's going on.
+ */
+
+#include "src/core/transport/static_metadata.h"
+
+grpc_mdstr grpc_static_mdstr_table[GRPC_STATIC_MDSTR_COUNT];
+
+grpc_mdelem grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT];
+gpr_uintptr grpc_static_mdelem_user_data[GRPC_STATIC_MDELEM_COUNT] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 3, 7, 5, 2, 4, 8, 6, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+const gpr_uint8
+ grpc_static_metadata_elem_indices[GRPC_STATIC_MDELEM_COUNT * 2] = {
+ 11, 33, 10, 33, 12, 33, 12, 47, 13, 33, 14, 33, 15, 33, 16, 33, 17, 33,
+ 19, 33, 20, 33, 21, 33, 22, 33, 23, 33, 24, 33, 25, 33, 26, 33, 27, 33,
+ 28, 18, 28, 33, 29, 33, 30, 33, 34, 33, 35, 33, 36, 33, 37, 33, 40, 31,
+ 40, 32, 40, 46, 40, 51, 40, 52, 40, 53, 40, 54, 41, 31, 41, 46, 41, 51,
+ 44, 0, 44, 1, 44, 2, 48, 33, 55, 33, 56, 33, 57, 33, 58, 33, 59, 33,
+ 60, 33, 61, 33, 62, 33, 63, 33, 64, 38, 64, 66, 65, 76, 65, 77, 67, 33,
+ 68, 33, 69, 33, 70, 33, 71, 33, 72, 33, 73, 39, 73, 49, 73, 50, 74, 33,
+ 75, 33, 78, 3, 78, 4, 78, 5, 78, 6, 78, 7, 78, 8, 78, 9, 79, 33,
+ 80, 81, 82, 33, 83, 33, 84, 33, 85, 33, 86, 33};
+
+const char *const grpc_static_metadata_strings[GRPC_STATIC_MDSTR_COUNT] = {
+ "0",
+ "1",
+ "2",
+ "200",
+ "204",
+ "206",
+ "304",
+ "400",
+ "404",
+ "500",
+ "accept",
+ "accept-charset",
+ "accept-encoding",
+ "accept-language",
+ "accept-ranges",
+ "access-control-allow-origin",
+ "age",
+ "allow",
+ "application/grpc",
+ ":authority",
+ "authorization",
+ "cache-control",
+ "content-disposition",
+ "content-encoding",
+ "content-language",
+ "content-length",
+ "content-location",
+ "content-range",
+ "content-type",
+ "cookie",
+ "date",
+ "deflate",
+ "deflate,gzip",
+ "",
+ "etag",
+ "expect",
+ "expires",
+ "from",
+ "GET",
+ "grpc",
+ "grpc-accept-encoding",
+ "grpc-encoding",
+ "grpc-internal-encoding-request",
+ "grpc-message",
+ "grpc-status",
+ "grpc-timeout",
+ "gzip",
+ "gzip, deflate",
+ "host",
+ "http",
+ "https",
+ "identity",
+ "identity,deflate",
+ "identity,deflate,gzip",
+ "identity,gzip",
+ "if-match",
+ "if-modified-since",
+ "if-none-match",
+ "if-range",
+ "if-unmodified-since",
+ "last-modified",
+ "link",
+ "location",
+ "max-forwards",
+ ":method",
+ ":path",
+ "POST",
+ "proxy-authenticate",
+ "proxy-authorization",
+ "range",
+ "referer",
+ "refresh",
+ "retry-after",
+ ":scheme",
+ "server",
+ "set-cookie",
+ "/",
+ "/index.html",
+ ":status",
+ "strict-transport-security",
+ "te",
+ "trailers",
+ "transfer-encoding",
+ "user-agent",
+ "vary",
+ "via",
+ "www-authenticate"};
+
+const gpr_uint8 grpc_static_accept_encoding_metadata[8] = {0, 29, 26, 30,
+ 28, 32, 27, 31};
diff --git a/src/core/transport/static_metadata.h b/src/core/transport/static_metadata.h
new file mode 100644
index 0000000000..e9055fb45c
--- /dev/null
+++ b/src/core/transport/static_metadata.h
@@ -0,0 +1,402 @@
+/*
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * WARNING: Auto-generated code.
+ *
+ * To make changes to this file, change
+ * tools/codegen/core/gen_static_metadata.py,
+ * and then re-run it.
+ *
+ * See metadata.h for an explanation of the interface here, and metadata.c for
+ * an
+ * explanation of what's going on.
+ */
+
+#ifndef GRPC_INTERNAL_CORE_TRANSPORT_STATIC_METADATA_H
+#define GRPC_INTERNAL_CORE_TRANSPORT_STATIC_METADATA_H
+
+#include "src/core/transport/metadata.h"
+
+#define GRPC_STATIC_MDSTR_COUNT 87
+extern grpc_mdstr grpc_static_mdstr_table[GRPC_STATIC_MDSTR_COUNT];
+/* "0" */
+#define GRPC_MDSTR_0 (&grpc_static_mdstr_table[0])
+/* "1" */
+#define GRPC_MDSTR_1 (&grpc_static_mdstr_table[1])
+/* "2" */
+#define GRPC_MDSTR_2 (&grpc_static_mdstr_table[2])
+/* "200" */
+#define GRPC_MDSTR_200 (&grpc_static_mdstr_table[3])
+/* "204" */
+#define GRPC_MDSTR_204 (&grpc_static_mdstr_table[4])
+/* "206" */
+#define GRPC_MDSTR_206 (&grpc_static_mdstr_table[5])
+/* "304" */
+#define GRPC_MDSTR_304 (&grpc_static_mdstr_table[6])
+/* "400" */
+#define GRPC_MDSTR_400 (&grpc_static_mdstr_table[7])
+/* "404" */
+#define GRPC_MDSTR_404 (&grpc_static_mdstr_table[8])
+/* "500" */
+#define GRPC_MDSTR_500 (&grpc_static_mdstr_table[9])
+/* "accept" */
+#define GRPC_MDSTR_ACCEPT (&grpc_static_mdstr_table[10])
+/* "accept-charset" */
+#define GRPC_MDSTR_ACCEPT_CHARSET (&grpc_static_mdstr_table[11])
+/* "accept-encoding" */
+#define GRPC_MDSTR_ACCEPT_ENCODING (&grpc_static_mdstr_table[12])
+/* "accept-language" */
+#define GRPC_MDSTR_ACCEPT_LANGUAGE (&grpc_static_mdstr_table[13])
+/* "accept-ranges" */
+#define GRPC_MDSTR_ACCEPT_RANGES (&grpc_static_mdstr_table[14])
+/* "access-control-allow-origin" */
+#define GRPC_MDSTR_ACCESS_CONTROL_ALLOW_ORIGIN (&grpc_static_mdstr_table[15])
+/* "age" */
+#define GRPC_MDSTR_AGE (&grpc_static_mdstr_table[16])
+/* "allow" */
+#define GRPC_MDSTR_ALLOW (&grpc_static_mdstr_table[17])
+/* "application/grpc" */
+#define GRPC_MDSTR_APPLICATION_SLASH_GRPC (&grpc_static_mdstr_table[18])
+/* ":authority" */
+#define GRPC_MDSTR_AUTHORITY (&grpc_static_mdstr_table[19])
+/* "authorization" */
+#define GRPC_MDSTR_AUTHORIZATION (&grpc_static_mdstr_table[20])
+/* "cache-control" */
+#define GRPC_MDSTR_CACHE_CONTROL (&grpc_static_mdstr_table[21])
+/* "content-disposition" */
+#define GRPC_MDSTR_CONTENT_DISPOSITION (&grpc_static_mdstr_table[22])
+/* "content-encoding" */
+#define GRPC_MDSTR_CONTENT_ENCODING (&grpc_static_mdstr_table[23])
+/* "content-language" */
+#define GRPC_MDSTR_CONTENT_LANGUAGE (&grpc_static_mdstr_table[24])
+/* "content-length" */
+#define GRPC_MDSTR_CONTENT_LENGTH (&grpc_static_mdstr_table[25])
+/* "content-location" */
+#define GRPC_MDSTR_CONTENT_LOCATION (&grpc_static_mdstr_table[26])
+/* "content-range" */
+#define GRPC_MDSTR_CONTENT_RANGE (&grpc_static_mdstr_table[27])
+/* "content-type" */
+#define GRPC_MDSTR_CONTENT_TYPE (&grpc_static_mdstr_table[28])
+/* "cookie" */
+#define GRPC_MDSTR_COOKIE (&grpc_static_mdstr_table[29])
+/* "date" */
+#define GRPC_MDSTR_DATE (&grpc_static_mdstr_table[30])
+/* "deflate" */
+#define GRPC_MDSTR_DEFLATE (&grpc_static_mdstr_table[31])
+/* "deflate,gzip" */
+#define GRPC_MDSTR_DEFLATE_COMMA_GZIP (&grpc_static_mdstr_table[32])
+/* "" */
+#define GRPC_MDSTR_EMPTY (&grpc_static_mdstr_table[33])
+/* "etag" */
+#define GRPC_MDSTR_ETAG (&grpc_static_mdstr_table[34])
+/* "expect" */
+#define GRPC_MDSTR_EXPECT (&grpc_static_mdstr_table[35])
+/* "expires" */
+#define GRPC_MDSTR_EXPIRES (&grpc_static_mdstr_table[36])
+/* "from" */
+#define GRPC_MDSTR_FROM (&grpc_static_mdstr_table[37])
+/* "GET" */
+#define GRPC_MDSTR_GET (&grpc_static_mdstr_table[38])
+/* "grpc" */
+#define GRPC_MDSTR_GRPC (&grpc_static_mdstr_table[39])
+/* "grpc-accept-encoding" */
+#define GRPC_MDSTR_GRPC_ACCEPT_ENCODING (&grpc_static_mdstr_table[40])
+/* "grpc-encoding" */
+#define GRPC_MDSTR_GRPC_ENCODING (&grpc_static_mdstr_table[41])
+/* "grpc-internal-encoding-request" */
+#define GRPC_MDSTR_GRPC_INTERNAL_ENCODING_REQUEST (&grpc_static_mdstr_table[42])
+/* "grpc-message" */
+#define GRPC_MDSTR_GRPC_MESSAGE (&grpc_static_mdstr_table[43])
+/* "grpc-status" */
+#define GRPC_MDSTR_GRPC_STATUS (&grpc_static_mdstr_table[44])
+/* "grpc-timeout" */
+#define GRPC_MDSTR_GRPC_TIMEOUT (&grpc_static_mdstr_table[45])
+/* "gzip" */
+#define GRPC_MDSTR_GZIP (&grpc_static_mdstr_table[46])
+/* "gzip, deflate" */
+#define GRPC_MDSTR_GZIP_COMMA_DEFLATE (&grpc_static_mdstr_table[47])
+/* "host" */
+#define GRPC_MDSTR_HOST (&grpc_static_mdstr_table[48])
+/* "http" */
+#define GRPC_MDSTR_HTTP (&grpc_static_mdstr_table[49])
+/* "https" */
+#define GRPC_MDSTR_HTTPS (&grpc_static_mdstr_table[50])
+/* "identity" */
+#define GRPC_MDSTR_IDENTITY (&grpc_static_mdstr_table[51])
+/* "identity,deflate" */
+#define GRPC_MDSTR_IDENTITY_COMMA_DEFLATE (&grpc_static_mdstr_table[52])
+/* "identity,deflate,gzip" */
+#define GRPC_MDSTR_IDENTITY_COMMA_DEFLATE_COMMA_GZIP \
+ (&grpc_static_mdstr_table[53])
+/* "identity,gzip" */
+#define GRPC_MDSTR_IDENTITY_COMMA_GZIP (&grpc_static_mdstr_table[54])
+/* "if-match" */
+#define GRPC_MDSTR_IF_MATCH (&grpc_static_mdstr_table[55])
+/* "if-modified-since" */
+#define GRPC_MDSTR_IF_MODIFIED_SINCE (&grpc_static_mdstr_table[56])
+/* "if-none-match" */
+#define GRPC_MDSTR_IF_NONE_MATCH (&grpc_static_mdstr_table[57])
+/* "if-range" */
+#define GRPC_MDSTR_IF_RANGE (&grpc_static_mdstr_table[58])
+/* "if-unmodified-since" */
+#define GRPC_MDSTR_IF_UNMODIFIED_SINCE (&grpc_static_mdstr_table[59])
+/* "last-modified" */
+#define GRPC_MDSTR_LAST_MODIFIED (&grpc_static_mdstr_table[60])
+/* "link" */
+#define GRPC_MDSTR_LINK (&grpc_static_mdstr_table[61])
+/* "location" */
+#define GRPC_MDSTR_LOCATION (&grpc_static_mdstr_table[62])
+/* "max-forwards" */
+#define GRPC_MDSTR_MAX_FORWARDS (&grpc_static_mdstr_table[63])
+/* ":method" */
+#define GRPC_MDSTR_METHOD (&grpc_static_mdstr_table[64])
+/* ":path" */
+#define GRPC_MDSTR_PATH (&grpc_static_mdstr_table[65])
+/* "POST" */
+#define GRPC_MDSTR_POST (&grpc_static_mdstr_table[66])
+/* "proxy-authenticate" */
+#define GRPC_MDSTR_PROXY_AUTHENTICATE (&grpc_static_mdstr_table[67])
+/* "proxy-authorization" */
+#define GRPC_MDSTR_PROXY_AUTHORIZATION (&grpc_static_mdstr_table[68])
+/* "range" */
+#define GRPC_MDSTR_RANGE (&grpc_static_mdstr_table[69])
+/* "referer" */
+#define GRPC_MDSTR_REFERER (&grpc_static_mdstr_table[70])
+/* "refresh" */
+#define GRPC_MDSTR_REFRESH (&grpc_static_mdstr_table[71])
+/* "retry-after" */
+#define GRPC_MDSTR_RETRY_AFTER (&grpc_static_mdstr_table[72])
+/* ":scheme" */
+#define GRPC_MDSTR_SCHEME (&grpc_static_mdstr_table[73])
+/* "server" */
+#define GRPC_MDSTR_SERVER (&grpc_static_mdstr_table[74])
+/* "set-cookie" */
+#define GRPC_MDSTR_SET_COOKIE (&grpc_static_mdstr_table[75])
+/* "/" */
+#define GRPC_MDSTR_SLASH (&grpc_static_mdstr_table[76])
+/* "/index.html" */
+#define GRPC_MDSTR_SLASH_INDEX_DOT_HTML (&grpc_static_mdstr_table[77])
+/* ":status" */
+#define GRPC_MDSTR_STATUS (&grpc_static_mdstr_table[78])
+/* "strict-transport-security" */
+#define GRPC_MDSTR_STRICT_TRANSPORT_SECURITY (&grpc_static_mdstr_table[79])
+/* "te" */
+#define GRPC_MDSTR_TE (&grpc_static_mdstr_table[80])
+/* "trailers" */
+#define GRPC_MDSTR_TRAILERS (&grpc_static_mdstr_table[81])
+/* "transfer-encoding" */
+#define GRPC_MDSTR_TRANSFER_ENCODING (&grpc_static_mdstr_table[82])
+/* "user-agent" */
+#define GRPC_MDSTR_USER_AGENT (&grpc_static_mdstr_table[83])
+/* "vary" */
+#define GRPC_MDSTR_VARY (&grpc_static_mdstr_table[84])
+/* "via" */
+#define GRPC_MDSTR_VIA (&grpc_static_mdstr_table[85])
+/* "www-authenticate" */
+#define GRPC_MDSTR_WWW_AUTHENTICATE (&grpc_static_mdstr_table[86])
+
+#define GRPC_STATIC_MDELEM_COUNT 78
+extern grpc_mdelem grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT];
+extern gpr_uintptr grpc_static_mdelem_user_data[GRPC_STATIC_MDELEM_COUNT];
+/* "accept-charset": "" */
+#define GRPC_MDELEM_ACCEPT_CHARSET_EMPTY (&grpc_static_mdelem_table[0])
+/* "accept": "" */
+#define GRPC_MDELEM_ACCEPT_EMPTY (&grpc_static_mdelem_table[1])
+/* "accept-encoding": "" */
+#define GRPC_MDELEM_ACCEPT_ENCODING_EMPTY (&grpc_static_mdelem_table[2])
+/* "accept-encoding": "gzip, deflate" */
+#define GRPC_MDELEM_ACCEPT_ENCODING_GZIP_COMMA_DEFLATE \
+ (&grpc_static_mdelem_table[3])
+/* "accept-language": "" */
+#define GRPC_MDELEM_ACCEPT_LANGUAGE_EMPTY (&grpc_static_mdelem_table[4])
+/* "accept-ranges": "" */
+#define GRPC_MDELEM_ACCEPT_RANGES_EMPTY (&grpc_static_mdelem_table[5])
+/* "access-control-allow-origin": "" */
+#define GRPC_MDELEM_ACCESS_CONTROL_ALLOW_ORIGIN_EMPTY \
+ (&grpc_static_mdelem_table[6])
+/* "age": "" */
+#define GRPC_MDELEM_AGE_EMPTY (&grpc_static_mdelem_table[7])
+/* "allow": "" */
+#define GRPC_MDELEM_ALLOW_EMPTY (&grpc_static_mdelem_table[8])
+/* ":authority": "" */
+#define GRPC_MDELEM_AUTHORITY_EMPTY (&grpc_static_mdelem_table[9])
+/* "authorization": "" */
+#define GRPC_MDELEM_AUTHORIZATION_EMPTY (&grpc_static_mdelem_table[10])
+/* "cache-control": "" */
+#define GRPC_MDELEM_CACHE_CONTROL_EMPTY (&grpc_static_mdelem_table[11])
+/* "content-disposition": "" */
+#define GRPC_MDELEM_CONTENT_DISPOSITION_EMPTY (&grpc_static_mdelem_table[12])
+/* "content-encoding": "" */
+#define GRPC_MDELEM_CONTENT_ENCODING_EMPTY (&grpc_static_mdelem_table[13])
+/* "content-language": "" */
+#define GRPC_MDELEM_CONTENT_LANGUAGE_EMPTY (&grpc_static_mdelem_table[14])
+/* "content-length": "" */
+#define GRPC_MDELEM_CONTENT_LENGTH_EMPTY (&grpc_static_mdelem_table[15])
+/* "content-location": "" */
+#define GRPC_MDELEM_CONTENT_LOCATION_EMPTY (&grpc_static_mdelem_table[16])
+/* "content-range": "" */
+#define GRPC_MDELEM_CONTENT_RANGE_EMPTY (&grpc_static_mdelem_table[17])
+/* "content-type": "application/grpc" */
+#define GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC \
+ (&grpc_static_mdelem_table[18])
+/* "content-type": "" */
+#define GRPC_MDELEM_CONTENT_TYPE_EMPTY (&grpc_static_mdelem_table[19])
+/* "cookie": "" */
+#define GRPC_MDELEM_COOKIE_EMPTY (&grpc_static_mdelem_table[20])
+/* "date": "" */
+#define GRPC_MDELEM_DATE_EMPTY (&grpc_static_mdelem_table[21])
+/* "etag": "" */
+#define GRPC_MDELEM_ETAG_EMPTY (&grpc_static_mdelem_table[22])
+/* "expect": "" */
+#define GRPC_MDELEM_EXPECT_EMPTY (&grpc_static_mdelem_table[23])
+/* "expires": "" */
+#define GRPC_MDELEM_EXPIRES_EMPTY (&grpc_static_mdelem_table[24])
+/* "from": "" */
+#define GRPC_MDELEM_FROM_EMPTY (&grpc_static_mdelem_table[25])
+/* "grpc-accept-encoding": "deflate" */
+#define GRPC_MDELEM_GRPC_ACCEPT_ENCODING_DEFLATE (&grpc_static_mdelem_table[26])
+/* "grpc-accept-encoding": "deflate,gzip" */
+#define GRPC_MDELEM_GRPC_ACCEPT_ENCODING_DEFLATE_COMMA_GZIP \
+ (&grpc_static_mdelem_table[27])
+/* "grpc-accept-encoding": "gzip" */
+#define GRPC_MDELEM_GRPC_ACCEPT_ENCODING_GZIP (&grpc_static_mdelem_table[28])
+/* "grpc-accept-encoding": "identity" */
+#define GRPC_MDELEM_GRPC_ACCEPT_ENCODING_IDENTITY \
+ (&grpc_static_mdelem_table[29])
+/* "grpc-accept-encoding": "identity,deflate" */
+#define GRPC_MDELEM_GRPC_ACCEPT_ENCODING_IDENTITY_COMMA_DEFLATE \
+ (&grpc_static_mdelem_table[30])
+/* "grpc-accept-encoding": "identity,deflate,gzip" */
+#define GRPC_MDELEM_GRPC_ACCEPT_ENCODING_IDENTITY_COMMA_DEFLATE_COMMA_GZIP \
+ (&grpc_static_mdelem_table[31])
+/* "grpc-accept-encoding": "identity,gzip" */
+#define GRPC_MDELEM_GRPC_ACCEPT_ENCODING_IDENTITY_COMMA_GZIP \
+ (&grpc_static_mdelem_table[32])
+/* "grpc-encoding": "deflate" */
+#define GRPC_MDELEM_GRPC_ENCODING_DEFLATE (&grpc_static_mdelem_table[33])
+/* "grpc-encoding": "gzip" */
+#define GRPC_MDELEM_GRPC_ENCODING_GZIP (&grpc_static_mdelem_table[34])
+/* "grpc-encoding": "identity" */
+#define GRPC_MDELEM_GRPC_ENCODING_IDENTITY (&grpc_static_mdelem_table[35])
+/* "grpc-status": "0" */
+#define GRPC_MDELEM_GRPC_STATUS_0 (&grpc_static_mdelem_table[36])
+/* "grpc-status": "1" */
+#define GRPC_MDELEM_GRPC_STATUS_1 (&grpc_static_mdelem_table[37])
+/* "grpc-status": "2" */
+#define GRPC_MDELEM_GRPC_STATUS_2 (&grpc_static_mdelem_table[38])
+/* "host": "" */
+#define GRPC_MDELEM_HOST_EMPTY (&grpc_static_mdelem_table[39])
+/* "if-match": "" */
+#define GRPC_MDELEM_IF_MATCH_EMPTY (&grpc_static_mdelem_table[40])
+/* "if-modified-since": "" */
+#define GRPC_MDELEM_IF_MODIFIED_SINCE_EMPTY (&grpc_static_mdelem_table[41])
+/* "if-none-match": "" */
+#define GRPC_MDELEM_IF_NONE_MATCH_EMPTY (&grpc_static_mdelem_table[42])
+/* "if-range": "" */
+#define GRPC_MDELEM_IF_RANGE_EMPTY (&grpc_static_mdelem_table[43])
+/* "if-unmodified-since": "" */
+#define GRPC_MDELEM_IF_UNMODIFIED_SINCE_EMPTY (&grpc_static_mdelem_table[44])
+/* "last-modified": "" */
+#define GRPC_MDELEM_LAST_MODIFIED_EMPTY (&grpc_static_mdelem_table[45])
+/* "link": "" */
+#define GRPC_MDELEM_LINK_EMPTY (&grpc_static_mdelem_table[46])
+/* "location": "" */
+#define GRPC_MDELEM_LOCATION_EMPTY (&grpc_static_mdelem_table[47])
+/* "max-forwards": "" */
+#define GRPC_MDELEM_MAX_FORWARDS_EMPTY (&grpc_static_mdelem_table[48])
+/* ":method": "GET" */
+#define GRPC_MDELEM_METHOD_GET (&grpc_static_mdelem_table[49])
+/* ":method": "POST" */
+#define GRPC_MDELEM_METHOD_POST (&grpc_static_mdelem_table[50])
+/* ":path": "/" */
+#define GRPC_MDELEM_PATH_SLASH (&grpc_static_mdelem_table[51])
+/* ":path": "/index.html" */
+#define GRPC_MDELEM_PATH_SLASH_INDEX_DOT_HTML (&grpc_static_mdelem_table[52])
+/* "proxy-authenticate": "" */
+#define GRPC_MDELEM_PROXY_AUTHENTICATE_EMPTY (&grpc_static_mdelem_table[53])
+/* "proxy-authorization": "" */
+#define GRPC_MDELEM_PROXY_AUTHORIZATION_EMPTY (&grpc_static_mdelem_table[54])
+/* "range": "" */
+#define GRPC_MDELEM_RANGE_EMPTY (&grpc_static_mdelem_table[55])
+/* "referer": "" */
+#define GRPC_MDELEM_REFERER_EMPTY (&grpc_static_mdelem_table[56])
+/* "refresh": "" */
+#define GRPC_MDELEM_REFRESH_EMPTY (&grpc_static_mdelem_table[57])
+/* "retry-after": "" */
+#define GRPC_MDELEM_RETRY_AFTER_EMPTY (&grpc_static_mdelem_table[58])
+/* ":scheme": "grpc" */
+#define GRPC_MDELEM_SCHEME_GRPC (&grpc_static_mdelem_table[59])
+/* ":scheme": "http" */
+#define GRPC_MDELEM_SCHEME_HTTP (&grpc_static_mdelem_table[60])
+/* ":scheme": "https" */
+#define GRPC_MDELEM_SCHEME_HTTPS (&grpc_static_mdelem_table[61])
+/* "server": "" */
+#define GRPC_MDELEM_SERVER_EMPTY (&grpc_static_mdelem_table[62])
+/* "set-cookie": "" */
+#define GRPC_MDELEM_SET_COOKIE_EMPTY (&grpc_static_mdelem_table[63])
+/* ":status": "200" */
+#define GRPC_MDELEM_STATUS_200 (&grpc_static_mdelem_table[64])
+/* ":status": "204" */
+#define GRPC_MDELEM_STATUS_204 (&grpc_static_mdelem_table[65])
+/* ":status": "206" */
+#define GRPC_MDELEM_STATUS_206 (&grpc_static_mdelem_table[66])
+/* ":status": "304" */
+#define GRPC_MDELEM_STATUS_304 (&grpc_static_mdelem_table[67])
+/* ":status": "400" */
+#define GRPC_MDELEM_STATUS_400 (&grpc_static_mdelem_table[68])
+/* ":status": "404" */
+#define GRPC_MDELEM_STATUS_404 (&grpc_static_mdelem_table[69])
+/* ":status": "500" */
+#define GRPC_MDELEM_STATUS_500 (&grpc_static_mdelem_table[70])
+/* "strict-transport-security": "" */
+#define GRPC_MDELEM_STRICT_TRANSPORT_SECURITY_EMPTY \
+ (&grpc_static_mdelem_table[71])
+/* "te": "trailers" */
+#define GRPC_MDELEM_TE_TRAILERS (&grpc_static_mdelem_table[72])
+/* "transfer-encoding": "" */
+#define GRPC_MDELEM_TRANSFER_ENCODING_EMPTY (&grpc_static_mdelem_table[73])
+/* "user-agent": "" */
+#define GRPC_MDELEM_USER_AGENT_EMPTY (&grpc_static_mdelem_table[74])
+/* "vary": "" */
+#define GRPC_MDELEM_VARY_EMPTY (&grpc_static_mdelem_table[75])
+/* "via": "" */
+#define GRPC_MDELEM_VIA_EMPTY (&grpc_static_mdelem_table[76])
+/* "www-authenticate": "" */
+#define GRPC_MDELEM_WWW_AUTHENTICATE_EMPTY (&grpc_static_mdelem_table[77])
+
+extern const gpr_uint8
+ grpc_static_metadata_elem_indices[GRPC_STATIC_MDELEM_COUNT * 2];
+extern const char *const grpc_static_metadata_strings[GRPC_STATIC_MDSTR_COUNT];
+extern const gpr_uint8 grpc_static_accept_encoding_metadata[8];
+#define GRPC_MDELEM_ACCEPT_ENCODING_FOR_ALGORITHMS(algs) \
+ (&grpc_static_mdelem_table[grpc_static_accept_encoding_metadata[(algs)]])
+#endif /* GRPC_INTERNAL_CORE_TRANSPORT_STATIC_METADATA_H */