diff options
Diffstat (limited to 'src/core')
-rw-r--r-- | src/core/transport/chttp2/bin_encoder.c | 20 | ||||
-rw-r--r-- | src/core/transport/chttp2/bin_encoder.h | 2 | ||||
-rw-r--r-- | src/core/transport/chttp2/gen_hpack_tables.c | 19 | ||||
-rw-r--r-- | src/core/transport/chttp2/hpack_parser.c | 260 | ||||
-rw-r--r-- | src/core/transport/chttp2/hpack_parser.h | 3 | ||||
-rw-r--r-- | src/core/transport/chttp2/stream_encoder.c | 85 | ||||
-rw-r--r-- | src/core/transport/metadata.c | 1 |
7 files changed, 310 insertions, 80 deletions
diff --git a/src/core/transport/chttp2/bin_encoder.c b/src/core/transport/chttp2/bin_encoder.c index 39a303bce9..e3b5fe9fcf 100644 --- a/src/core/transport/chttp2/bin_encoder.c +++ b/src/core/transport/chttp2/bin_encoder.c @@ -32,6 +32,9 @@ */ #include "src/core/transport/chttp2/bin_encoder.h" + +#include <string.h> + #include "src/core/transport/chttp2/huffsyms.h" #include <grpc/support/log.h> @@ -123,7 +126,7 @@ gpr_slice grpc_chttp2_base64_encode(gpr_slice input) { /* encode full triplets */ for (i = 0; i < input_triplets; i++) { out[0] = alphabet[in[0] >> 2]; - out[1] = alphabet[((in[0] & 0x2) << 4) | (in[1] >> 4)]; + out[1] = alphabet[((in[0] & 0x3) << 4) | (in[1] >> 4)]; out[2] = alphabet[((in[1] & 0xf) << 2) | (in[2] >> 6)]; out[3] = alphabet[in[2] & 0x3f]; out += 4; @@ -136,13 +139,13 @@ gpr_slice grpc_chttp2_base64_encode(gpr_slice input) { break; case 1: out[0] = alphabet[in[0] >> 2]; - out[1] = alphabet[(in[0] & 0x2) << 4]; + out[1] = alphabet[(in[0] & 0x3) << 4]; out += 2; in += 1; break; case 2: out[0] = alphabet[in[0] >> 2]; - out[1] = alphabet[((in[0] & 0x2) << 4) | (in[1] >> 4)]; + out[1] = alphabet[((in[0] & 0x3) << 4) | (in[1] >> 4)]; out[2] = alphabet[(in[1] & 0xf) << 2]; out += 3; in += 2; @@ -238,7 +241,7 @@ gpr_slice grpc_chttp2_base64_encode_and_huffman_compress(gpr_slice input) { /* encode full triplets */ for (i = 0; i < input_triplets; i++) { - enc_add2(&out, in[0] >> 2, ((in[0] & 0x2) << 4) | (in[1] >> 4)); + enc_add2(&out, in[0] >> 2, ((in[0] & 0x3) << 4) | (in[1] >> 4)); enc_add2(&out, ((in[1] & 0xf) << 2) | (in[2] >> 6), in[2] & 0x3f); in += 3; } @@ -248,11 +251,11 @@ gpr_slice grpc_chttp2_base64_encode_and_huffman_compress(gpr_slice input) { case 0: break; case 1: - enc_add2(&out, in[0] >> 2, (in[0] & 0x2) << 4); + enc_add2(&out, in[0] >> 2, (in[0] & 0x3) << 4); in += 1; break; case 2: - enc_add2(&out, in[0] >> 2, ((in[0] & 0x2) << 4) | (in[1] >> 4)); + enc_add2(&out, in[0] >> 2, ((in[0] & 0x3) << 4) | (in[1] >> 4)); enc_add1(&out, (in[1] & 0xf) << 2); in += 2; break; @@ -269,3 +272,8 @@ gpr_slice grpc_chttp2_base64_encode_and_huffman_compress(gpr_slice input) { GPR_ASSERT(in == GPR_SLICE_END_PTR(input)); return output; } + +int grpc_is_binary_header(const char *key, size_t length) { + if (length < 5) return 0; + return 0 == memcmp(key + length - 4, "-bin", 4); +} diff --git a/src/core/transport/chttp2/bin_encoder.h b/src/core/transport/chttp2/bin_encoder.h index 86031137dc..2310f841f8 100644 --- a/src/core/transport/chttp2/bin_encoder.h +++ b/src/core/transport/chttp2/bin_encoder.h @@ -51,4 +51,6 @@ gpr_slice grpc_chttp2_huffman_compress(gpr_slice input); return y; */ gpr_slice grpc_chttp2_base64_encode_and_huffman_compress(gpr_slice input); +int grpc_is_binary_header(const char *key, size_t length); + #endif /* __GRPC_INTERNAL_TRANSPORT_CHTTP2_BIN_ENCODER_H_ */ diff --git a/src/core/transport/chttp2/gen_hpack_tables.c b/src/core/transport/chttp2/gen_hpack_tables.c index 301b79ef2c..3b9e8ed4dc 100644 --- a/src/core/transport/chttp2/gen_hpack_tables.c +++ b/src/core/transport/chttp2/gen_hpack_tables.c @@ -332,10 +332,29 @@ static void generate_base64_huff_encoder_table() { printf("};\n"); } +static void generate_base64_inverse_table() { + static const char alphabet[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; + unsigned char inverse[256]; + int i; + + memset(inverse, 255, sizeof(inverse)); + for (i = 0; i < strlen(alphabet); i++) { + inverse[(unsigned char)alphabet[i]] = i; + } + + printf("static const gpr_uint8 inverse_base64[256] = {"); + for (i = 0; i < 256; i++) { + printf("%d,", inverse[i]); + } + printf("};\n"); +} + int main(void) { generate_huff_tables(); generate_first_byte_lut(); generate_base64_huff_encoder_table(); + generate_base64_inverse_table(); return 0; } diff --git a/src/core/transport/chttp2/hpack_parser.c b/src/core/transport/chttp2/hpack_parser.c index 33588a73d4..246935fd79 100644 --- a/src/core/transport/chttp2/hpack_parser.c +++ b/src/core/transport/chttp2/hpack_parser.c @@ -37,13 +37,21 @@ #include <string.h> #include <assert.h> -#include "src/core/support/murmur_hash.h" +#include "src/core/transport/chttp2/bin_encoder.h" #include <grpc/support/alloc.h> #include <grpc/support/log.h> #include <grpc/support/port_platform.h> #include <grpc/support/string.h> #include <grpc/support/useful.h> +typedef enum { + NOT_BINARY, + B64_BYTE0, + B64_BYTE1, + B64_BYTE2, + B64_BYTE3 +} binary_state; + /* How parsing works: The parser object keeps track of a function pointer which represents the @@ -68,8 +76,12 @@ static int parse_string_prefix(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, const gpr_uint8 *end); static int parse_key_string(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, const gpr_uint8 *end); -static int parse_value_string(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, - const gpr_uint8 *end); +static int parse_value_string_with_indexed_key(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, + const gpr_uint8 *end); +static int parse_value_string_with_literal_key(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, + const gpr_uint8 *end); static int parse_value0(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, const gpr_uint8 *end); @@ -582,6 +594,27 @@ static const gpr_int16 emit_sub_tbl[249 * 16] = { 13, 22, 22, 22, 22, 256, 256, 256, 256, }; +static const gpr_uint8 inverse_base64[256] = { + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, + 255, 255, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, + 255, 64, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 255, 255, 255, 255, 255, 255, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, +}; + /* emission helpers */ static void on_hdr(grpc_chttp2_hpack_parser *p, grpc_mdelem *md, int add_to_table) { @@ -724,7 +757,7 @@ static int finish_lithdr_incidx_v(grpc_chttp2_hpack_parser *p, static int parse_lithdr_incidx(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, const gpr_uint8 *end) { static const grpc_chttp2_hpack_parser_state and_then[] = { - parse_value_string, finish_lithdr_incidx}; + parse_value_string_with_indexed_key, finish_lithdr_incidx}; p->next_state = and_then; p->index = (*cur) & 0x3f; return parse_string_prefix(p, cur + 1, end); @@ -734,7 +767,8 @@ static int parse_lithdr_incidx(grpc_chttp2_hpack_parser *p, static int parse_lithdr_incidx_x(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, const gpr_uint8 *end) { static const grpc_chttp2_hpack_parser_state and_then[] = { - parse_string_prefix, parse_value_string, finish_lithdr_incidx}; + parse_string_prefix, parse_value_string_with_indexed_key, + finish_lithdr_incidx}; p->next_state = and_then; p->index = 0x3f; p->parsing.value = &p->index; @@ -745,8 +779,8 @@ static int parse_lithdr_incidx_x(grpc_chttp2_hpack_parser *p, static int parse_lithdr_incidx_v(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, const gpr_uint8 *end) { static const grpc_chttp2_hpack_parser_state and_then[] = { - parse_key_string, parse_string_prefix, parse_value_string, - finish_lithdr_incidx_v}; + parse_key_string, parse_string_prefix, + parse_value_string_with_literal_key, finish_lithdr_incidx_v}; p->next_state = and_then; return parse_string_prefix(p, cur + 1, end); } @@ -776,7 +810,7 @@ static int finish_lithdr_notidx_v(grpc_chttp2_hpack_parser *p, static int parse_lithdr_notidx(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, const gpr_uint8 *end) { static const grpc_chttp2_hpack_parser_state and_then[] = { - parse_value_string, finish_lithdr_notidx}; + parse_value_string_with_indexed_key, finish_lithdr_notidx}; p->next_state = and_then; p->index = (*cur) & 0xf; return parse_string_prefix(p, cur + 1, end); @@ -786,7 +820,8 @@ static int parse_lithdr_notidx(grpc_chttp2_hpack_parser *p, static int parse_lithdr_notidx_x(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, const gpr_uint8 *end) { static const grpc_chttp2_hpack_parser_state and_then[] = { - parse_string_prefix, parse_value_string, finish_lithdr_notidx}; + parse_string_prefix, parse_value_string_with_indexed_key, + finish_lithdr_notidx}; p->next_state = and_then; p->index = 0xf; p->parsing.value = &p->index; @@ -797,8 +832,8 @@ static int parse_lithdr_notidx_x(grpc_chttp2_hpack_parser *p, static int parse_lithdr_notidx_v(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, const gpr_uint8 *end) { static const grpc_chttp2_hpack_parser_state and_then[] = { - parse_key_string, parse_string_prefix, parse_value_string, - finish_lithdr_notidx_v}; + parse_key_string, parse_string_prefix, + parse_value_string_with_literal_key, finish_lithdr_notidx_v}; p->next_state = and_then; return parse_string_prefix(p, cur + 1, end); } @@ -828,7 +863,7 @@ static int finish_lithdr_nvridx_v(grpc_chttp2_hpack_parser *p, static int parse_lithdr_nvridx(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, const gpr_uint8 *end) { static const grpc_chttp2_hpack_parser_state and_then[] = { - parse_value_string, finish_lithdr_nvridx}; + parse_value_string_with_indexed_key, finish_lithdr_nvridx}; p->next_state = and_then; p->index = (*cur) & 0xf; return parse_string_prefix(p, cur + 1, end); @@ -838,7 +873,8 @@ static int parse_lithdr_nvridx(grpc_chttp2_hpack_parser *p, static int parse_lithdr_nvridx_x(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, const gpr_uint8 *end) { static const grpc_chttp2_hpack_parser_state and_then[] = { - parse_string_prefix, parse_value_string, finish_lithdr_nvridx}; + parse_string_prefix, parse_value_string_with_indexed_key, + finish_lithdr_nvridx}; p->next_state = and_then; p->index = 0xf; p->parsing.value = &p->index; @@ -849,8 +885,8 @@ static int parse_lithdr_nvridx_x(grpc_chttp2_hpack_parser *p, static int parse_lithdr_nvridx_v(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, const gpr_uint8 *end) { static const grpc_chttp2_hpack_parser_state and_then[] = { - parse_key_string, parse_string_prefix, parse_value_string, - finish_lithdr_nvridx_v}; + parse_key_string, parse_string_prefix, + parse_value_string_with_literal_key, finish_lithdr_nvridx_v}; p->next_state = and_then; return parse_string_prefix(p, cur + 1, end); } @@ -1043,8 +1079,8 @@ static int parse_string_prefix(grpc_chttp2_hpack_parser *p, } /* append some bytes to a string */ -static void append_string(grpc_chttp2_hpack_parser_string *str, - const gpr_uint8 *data, size_t length) { +static void append_bytes(grpc_chttp2_hpack_parser_string *str, + const gpr_uint8 *data, size_t length) { if (length + str->length > str->capacity) { str->capacity = str->length + length; str->str = gpr_realloc(str->str, str->capacity); @@ -1053,45 +1089,156 @@ static void append_string(grpc_chttp2_hpack_parser_string *str, str->length += length; } +static int append_string(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end) { + grpc_chttp2_hpack_parser_string *str = p->parsing.str; + gpr_uint32 bits; + gpr_uint8 decoded[3]; + switch ((binary_state)p->binary) { + case NOT_BINARY: + append_bytes(str, cur, end - cur); + return 1; + b64_byte0: + case B64_BYTE0: + if (cur == end) { + p->binary = B64_BYTE0; + return 1; + } + bits = inverse_base64[*cur]; + ++cur; + if (bits == 255) + return 0; + else if (bits == 64) + goto b64_byte0; + p->base64_buffer = bits << 18; + /* fallthrough */ + b64_byte1: + case B64_BYTE1: + if (cur == end) { + p->binary = B64_BYTE1; + return 1; + } + bits = inverse_base64[*cur]; + ++cur; + if (bits == 255) + return 0; + else if (bits == 64) + goto b64_byte1; + p->base64_buffer |= bits << 12; + /* fallthrough */ + b64_byte2: + case B64_BYTE2: + if (cur == end) { + p->binary = B64_BYTE2; + return 1; + } + bits = inverse_base64[*cur]; + ++cur; + if (bits == 255) + return 0; + else if (bits == 64) + goto b64_byte2; + p->base64_buffer |= bits << 6; + /* fallthrough */ + b64_byte3: + case B64_BYTE3: + if (cur == end) { + p->binary = B64_BYTE3; + return 1; + } + bits = inverse_base64[*cur]; + ++cur; + if (bits == 255) + return 0; + else if (bits == 64) + goto b64_byte3; + p->base64_buffer |= bits; + bits = p->base64_buffer; + decoded[0] = bits >> 16; + decoded[1] = bits >> 8; + decoded[2] = bits; + append_bytes(str, decoded, 3); + goto b64_byte0; + } + gpr_log(GPR_ERROR, "should never reach here"); + abort(); + return 1; +} + /* append a null terminator to a string */ -static void finish_str(grpc_chttp2_hpack_parser_string *str) { +static int finish_str(grpc_chttp2_hpack_parser *p) { gpr_uint8 terminator = 0; - append_string(str, &terminator, 1); - str->length--; /* don't actually count the null terminator */ + gpr_uint8 decoded[2]; + gpr_uint32 bits; + grpc_chttp2_hpack_parser_string *str = p->parsing.str; + switch ((binary_state)p->binary) { + case NOT_BINARY: + break; + case B64_BYTE0: + break; + case B64_BYTE1: + gpr_log(GPR_ERROR, "illegal base64 encoding"); + return 0; /* illegal encoding */ + case B64_BYTE2: + bits = p->base64_buffer; + if (bits & 0xffff) { + gpr_log(GPR_ERROR, "trailing bits in base64 encoding: 0x%04x", + bits & 0xffff); + return 0; + } + decoded[0] = bits >> 16; + append_bytes(str, decoded, 1); + break; + case B64_BYTE3: + bits = p->base64_buffer; + if (bits & 0xff) { + gpr_log(GPR_ERROR, "trailing bits in base64 encoding: 0x%02x", + bits & 0xff); + return 0; + } + decoded[0] = bits >> 16; + decoded[1] = bits >> 8; + append_bytes(str, decoded, 2); + break; + } + append_bytes(str, &terminator, 1); + p->parsing.str->length--; /* don't actually count the null terminator */ + return 1; } /* decode a nibble from a huffman encoded stream */ -static void huff_nibble(grpc_chttp2_hpack_parser *p, gpr_uint8 nibble) { +static int huff_nibble(grpc_chttp2_hpack_parser *p, gpr_uint8 nibble) { gpr_int16 emit = emit_sub_tbl[16 * emit_tbl[p->huff_state] + nibble]; gpr_int16 next = next_sub_tbl[16 * next_tbl[p->huff_state] + nibble]; if (emit != -1) { if (emit >= 0 && emit < 256) { gpr_uint8 c = emit; - append_string(p->parsing.str, &c, 1); + if (!append_string(p, &c, (&c) + 1)) return 0; } else { assert(emit == 256); } } p->huff_state = next; + return 1; } /* decode full bytes from a huffman encoded stream */ -static void add_huff_bytes(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, - const gpr_uint8 *end) { +static int add_huff_bytes(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end) { for (; cur != end; ++cur) { - huff_nibble(p, *cur >> 4); - huff_nibble(p, *cur & 0xf); + if (!huff_nibble(p, *cur >> 4) || !huff_nibble(p, *cur & 0xf)) return 0; } + return 1; } /* decode some string bytes based on the current decoding mode (huffman or not) */ -static void add_str_bytes(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, - const gpr_uint8 *end) { +static int add_str_bytes(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, + const gpr_uint8 *end) { if (p->huff) { - add_huff_bytes(p, cur, end); + return add_huff_bytes(p, cur, end); } else { - append_string(p->parsing.str, cur, end - cur); + return append_string(p, cur, end); } } @@ -1101,11 +1248,10 @@ static int parse_string(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, size_t remaining = p->strlen - p->strgot; size_t given = end - cur; if (remaining <= given) { - add_str_bytes(p, cur, cur + remaining); - finish_str(p->parsing.str); - return parse_next(p, cur + remaining, end); + return add_str_bytes(p, cur, cur + remaining) && finish_str(p) && + parse_next(p, cur + remaining, end); } else { - add_str_bytes(p, cur, cur + given); + if (!add_str_bytes(p, cur, cur + given)) return 0; p->strgot += given; p->state = parse_string; return 1; @@ -1114,25 +1260,63 @@ static int parse_string(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, /* begin parsing a string - performs setup, calls parse_string */ static int begin_parse_string(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, - const gpr_uint8 *end, + const gpr_uint8 *end, gpr_uint8 binary, grpc_chttp2_hpack_parser_string *str) { p->strgot = 0; str->length = 0; p->parsing.str = str; p->huff_state = 0; + p->binary = binary; return parse_string(p, cur, end); } /* parse the key string */ static int parse_key_string(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, const gpr_uint8 *end) { - return begin_parse_string(p, cur, end, &p->key); + return begin_parse_string(p, cur, end, NOT_BINARY, &p->key); +} + +/* check if a key represents a binary header or not */ +typedef enum { BINARY_HEADER, PLAINTEXT_HEADER, ERROR_HEADER } is_binary_header; + +static is_binary_header is_binary_literal_header(grpc_chttp2_hpack_parser *p) { + return grpc_is_binary_header(p->key.str, p->key.length) ? BINARY_HEADER + : PLAINTEXT_HEADER; +} + +static is_binary_header is_binary_indexed_header(grpc_chttp2_hpack_parser *p) { + grpc_mdelem *elem = grpc_chttp2_hptbl_lookup(&p->table, p->index); + if (!elem) return ERROR_HEADER; + return grpc_is_binary_header( + (const char *)GPR_SLICE_START_PTR(elem->key->slice), + GPR_SLICE_LENGTH(elem->key->slice)) + ? BINARY_HEADER + : PLAINTEXT_HEADER; } /* parse the value string */ static int parse_value_string(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, - const gpr_uint8 *end) { - return begin_parse_string(p, cur, end, &p->value); + const gpr_uint8 *end, is_binary_header type) { + switch (type) { + case BINARY_HEADER: + return begin_parse_string(p, cur, end, B64_BYTE0, &p->value); + case PLAINTEXT_HEADER: + return begin_parse_string(p, cur, end, NOT_BINARY, &p->value); + case ERROR_HEADER: + return 0; + } +} + +static int parse_value_string_with_indexed_key(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, + const gpr_uint8 *end) { + return parse_value_string(p, cur, end, is_binary_indexed_header(p)); +} + +static int parse_value_string_with_literal_key(grpc_chttp2_hpack_parser *p, + const gpr_uint8 *cur, + const gpr_uint8 *end) { + return parse_value_string(p, cur, end, is_binary_literal_header(p)); } /* PUBLIC INTERFACE */ diff --git a/src/core/transport/chttp2/hpack_parser.h b/src/core/transport/chttp2/hpack_parser.h index cf68042fbd..799513e7ff 100644 --- a/src/core/transport/chttp2/hpack_parser.h +++ b/src/core/transport/chttp2/hpack_parser.h @@ -78,12 +78,15 @@ struct grpc_chttp2_hpack_parser { gpr_uint32 strgot; /* huffman decoding state */ gpr_uint16 huff_state; + /* is the string being decoded binary? */ + gpr_uint8 binary; /* is the current string huffman encoded? */ gpr_uint8 huff; /* set by higher layers, used by grpc_chttp2_header_parser_parse to signal it should append a metadata boundary at the end of frame */ gpr_uint8 is_boundary; gpr_uint8 is_eof; + gpr_uint32 base64_buffer; /* hpack table */ grpc_chttp2_hptbl table; diff --git a/src/core/transport/chttp2/stream_encoder.c b/src/core/transport/chttp2/stream_encoder.c index d46366d8b2..07eeb0a474 100644 --- a/src/core/transport/chttp2/stream_encoder.c +++ b/src/core/transport/chttp2/stream_encoder.c @@ -38,6 +38,7 @@ #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" @@ -269,62 +270,79 @@ static void emit_indexed(grpc_chttp2_hpack_compressor *c, gpr_uint32 index, GRPC_CHTTP2_WRITE_VARINT(index, 1, 0x80, add_tiny_header_data(st, len), len); } +static gpr_slice get_wire_value(grpc_mdelem *elem, gpr_uint8 *huffman_prefix) { + if (grpc_is_binary_header((const char *)GPR_SLICE_START_PTR(elem->key->slice), + GPR_SLICE_LENGTH(elem->key->slice))) { + *huffman_prefix = 0x80; + return grpc_mdstr_as_base64_encoded_and_huffman_compressed(elem->value); + } + /* TODO(ctiller): opportunistically compress non-binary headers */ + *huffman_prefix = 0x00; + return elem->value->slice; +} + static void emit_lithdr_incidx(grpc_chttp2_hpack_compressor *c, - gpr_uint32 key_index, grpc_mdstr *value, + gpr_uint32 key_index, grpc_mdelem *elem, framer_state *st) { int len_pfx = GRPC_CHTTP2_VARINT_LENGTH(key_index, 2); - int len_val = GPR_SLICE_LENGTH(value->slice); + gpr_uint8 huffman_prefix; + gpr_slice value_slice = get_wire_value(elem, &huffman_prefix); + int len_val = GPR_SLICE_LENGTH(value_slice); int len_val_len = GRPC_CHTTP2_VARINT_LENGTH(len_val, 1); GRPC_CHTTP2_WRITE_VARINT(key_index, 2, 0x40, add_tiny_header_data(st, len_pfx), len_pfx); GRPC_CHTTP2_WRITE_VARINT(len_val, 1, 0x00, add_tiny_header_data(st, len_val_len), len_val_len); - add_header_data(st, gpr_slice_ref(value->slice)); + add_header_data(st, gpr_slice_ref(value_slice)); } static void emit_lithdr_noidx(grpc_chttp2_hpack_compressor *c, - gpr_uint32 key_index, grpc_mdstr *value, + gpr_uint32 key_index, grpc_mdelem *elem, framer_state *st) { int len_pfx = GRPC_CHTTP2_VARINT_LENGTH(key_index, 4); - int len_val = GPR_SLICE_LENGTH(value->slice); + gpr_uint8 huffman_prefix; + gpr_slice value_slice = get_wire_value(elem, &huffman_prefix); + int len_val = GPR_SLICE_LENGTH(value_slice); int len_val_len = GRPC_CHTTP2_VARINT_LENGTH(len_val, 1); GRPC_CHTTP2_WRITE_VARINT(key_index, 4, 0x00, add_tiny_header_data(st, len_pfx), len_pfx); GRPC_CHTTP2_WRITE_VARINT(len_val, 1, 0x00, add_tiny_header_data(st, len_val_len), len_val_len); - add_header_data(st, gpr_slice_ref(value->slice)); + add_header_data(st, gpr_slice_ref(value_slice)); } static void emit_lithdr_incidx_v(grpc_chttp2_hpack_compressor *c, - grpc_mdstr *key, grpc_mdstr *value, - framer_state *st) { - int len_key = GPR_SLICE_LENGTH(key->slice); - int len_val = GPR_SLICE_LENGTH(value->slice); + grpc_mdelem *elem, framer_state *st) { + int len_key = GPR_SLICE_LENGTH(elem->key->slice); + gpr_uint8 huffman_prefix; + gpr_slice value_slice = get_wire_value(elem, &huffman_prefix); + int len_val = GPR_SLICE_LENGTH(value_slice); int len_key_len = GRPC_CHTTP2_VARINT_LENGTH(len_key, 1); int len_val_len = GRPC_CHTTP2_VARINT_LENGTH(len_val, 1); *add_tiny_header_data(st, 1) = 0x40; GRPC_CHTTP2_WRITE_VARINT(len_key, 1, 0x00, add_tiny_header_data(st, len_key_len), len_key_len); - add_header_data(st, gpr_slice_ref(key->slice)); - GRPC_CHTTP2_WRITE_VARINT(len_val, 1, 0x00, + add_header_data(st, gpr_slice_ref(elem->key->slice)); + GRPC_CHTTP2_WRITE_VARINT(len_val, 1, huffman_prefix, add_tiny_header_data(st, len_val_len), len_val_len); - add_header_data(st, gpr_slice_ref(value->slice)); + add_header_data(st, gpr_slice_ref(value_slice)); } static void emit_lithdr_noidx_v(grpc_chttp2_hpack_compressor *c, - grpc_mdstr *key, grpc_mdstr *value, - framer_state *st) { - int len_key = GPR_SLICE_LENGTH(key->slice); - int len_val = GPR_SLICE_LENGTH(value->slice); + grpc_mdelem *elem, framer_state *st) { + int len_key = GPR_SLICE_LENGTH(elem->key->slice); + gpr_uint8 huffman_prefix; + gpr_slice value_slice = get_wire_value(elem, &huffman_prefix); + int len_val = GPR_SLICE_LENGTH(value_slice); int len_key_len = GRPC_CHTTP2_VARINT_LENGTH(len_key, 1); int len_val_len = GRPC_CHTTP2_VARINT_LENGTH(len_val, 1); *add_tiny_header_data(st, 1) = 0x00; GRPC_CHTTP2_WRITE_VARINT(len_key, 1, 0x00, add_tiny_header_data(st, len_key_len), len_key_len); - add_header_data(st, gpr_slice_ref(key->slice)); - GRPC_CHTTP2_WRITE_VARINT(len_val, 1, 0x00, + add_header_data(st, gpr_slice_ref(elem->key->slice)); + GRPC_CHTTP2_WRITE_VARINT(len_val, 1, huffman_prefix, add_tiny_header_data(st, len_val_len), len_val_len); - add_header_data(st, gpr_slice_ref(value->slice)); + add_header_data(st, gpr_slice_ref(value_slice)); } static gpr_uint32 dynidx(grpc_chttp2_hpack_compressor *c, gpr_uint32 index) { @@ -338,6 +356,7 @@ static void hpack_enc(grpc_chttp2_hpack_compressor *c, grpc_mdelem *elem, gpr_uint32 key_hash = elem->key->hash; gpr_uint32 elem_hash = key_hash ^ elem->value->hash; size_t decoder_space_usage; + gpr_uint32 indices_key; int should_add_elem; inc_filter(HASH_FRAGMENT_1(elem_hash), &c->filter_elems_sum, c->filter_elems); @@ -371,35 +390,29 @@ static void hpack_enc(grpc_chttp2_hpack_compressor *c, grpc_mdelem *elem, /* no hits for the elem... maybe there's a key? */ + indices_key = c->indices_keys[HASH_FRAGMENT_2(key_hash)]; if (c->entries_keys[HASH_FRAGMENT_2(key_hash)] == elem->key && - c->indices_keys[HASH_FRAGMENT_2(key_hash)] > c->tail_remote_index) { + indices_key > c->tail_remote_index) { /* HIT: key (first cuckoo hash) */ if (should_add_elem) { - emit_lithdr_incidx(c, - dynidx(c, c->indices_keys[HASH_FRAGMENT_2(key_hash)]), - elem->value, st); + emit_lithdr_incidx(c, dynidx(c, indices_key), elem, st); add_elem(c, elem); } else { - emit_lithdr_noidx(c, - dynidx(c, c->indices_keys[HASH_FRAGMENT_2(key_hash)]), - elem->value, st); + emit_lithdr_noidx(c, dynidx(c, indices_key), elem, st); grpc_mdelem_unref(elem); } return; } + indices_key = c->indices_keys[HASH_FRAGMENT_3(key_hash)]; if (c->entries_keys[HASH_FRAGMENT_3(key_hash)] == elem->key && - c->indices_keys[HASH_FRAGMENT_3(key_hash)] > c->tail_remote_index) { + indices_key > c->tail_remote_index) { /* HIT: key (first cuckoo hash) */ if (should_add_elem) { - emit_lithdr_incidx(c, - dynidx(c, c->indices_keys[HASH_FRAGMENT_3(key_hash)]), - elem->value, st); + emit_lithdr_incidx(c, dynidx(c, indices_key), elem, st); add_elem(c, elem); } else { - emit_lithdr_noidx(c, - dynidx(c, c->indices_keys[HASH_FRAGMENT_3(key_hash)]), - elem->value, st); + emit_lithdr_noidx(c, dynidx(c, indices_key), elem, st); grpc_mdelem_unref(elem); } return; @@ -408,10 +421,10 @@ static void hpack_enc(grpc_chttp2_hpack_compressor *c, grpc_mdelem *elem, /* no elem, key in the table... fall back to literal emission */ if (should_add_elem) { - emit_lithdr_incidx_v(c, elem->key, elem->value, st); + emit_lithdr_incidx_v(c, elem, st); add_elem(c, elem); } else { - emit_lithdr_noidx_v(c, elem->key, elem->value, st); + emit_lithdr_noidx_v(c, elem, st); grpc_mdelem_unref(elem); } } diff --git a/src/core/transport/metadata.c b/src/core/transport/metadata.c index 9edfd5bfd1..1a869cfbc5 100644 --- a/src/core/transport/metadata.c +++ b/src/core/transport/metadata.c @@ -540,6 +540,7 @@ gpr_slice grpc_mdstr_as_base64_encoded_and_huffman_compressed(grpc_mdstr *gs) { 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); |