Index: third_party/grpc/src/core/transport/chttp2/hpack_table.c |
diff --git a/third_party/grpc/src/core/transport/chttp2/hpack_table.c b/third_party/grpc/src/core/transport/chttp2/hpack_table.c |
new file mode 100644 |
index 0000000000000000000000000000000000000000..f1ce3b84fdccae84ac1495e8e22ef4d8a862cffc |
--- /dev/null |
+++ b/third_party/grpc/src/core/transport/chttp2/hpack_table.c |
@@ -0,0 +1,361 @@ |
+/* |
+ * |
+ * 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. |
+ * |
+ */ |
+ |
+#include "src/core/transport/chttp2/hpack_table.h" |
+ |
+#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 { |
+ const char *key; |
+ const char *value; |
+} static_table[] = { |
+ /* 0: */ |
+ {NULL, NULL}, |
+ /* 1: */ |
+ {":authority", ""}, |
+ /* 2: */ |
+ {":method", "GET"}, |
+ /* 3: */ |
+ {":method", "POST"}, |
+ /* 4: */ |
+ {":path", "/"}, |
+ /* 5: */ |
+ {":path", "/index.html"}, |
+ /* 6: */ |
+ {":scheme", "http"}, |
+ /* 7: */ |
+ {":scheme", "https"}, |
+ /* 8: */ |
+ {":status", "200"}, |
+ /* 9: */ |
+ {":status", "204"}, |
+ /* 10: */ |
+ {":status", "206"}, |
+ /* 11: */ |
+ {":status", "304"}, |
+ /* 12: */ |
+ {":status", "400"}, |
+ /* 13: */ |
+ {":status", "404"}, |
+ /* 14: */ |
+ {":status", "500"}, |
+ /* 15: */ |
+ {"accept-charset", ""}, |
+ /* 16: */ |
+ {"accept-encoding", "gzip, deflate"}, |
+ /* 17: */ |
+ {"accept-language", ""}, |
+ /* 18: */ |
+ {"accept-ranges", ""}, |
+ /* 19: */ |
+ {"accept", ""}, |
+ /* 20: */ |
+ {"access-control-allow-origin", ""}, |
+ /* 21: */ |
+ {"age", ""}, |
+ /* 22: */ |
+ {"allow", ""}, |
+ /* 23: */ |
+ {"authorization", ""}, |
+ /* 24: */ |
+ {"cache-control", ""}, |
+ /* 25: */ |
+ {"content-disposition", ""}, |
+ /* 26: */ |
+ {"content-encoding", ""}, |
+ /* 27: */ |
+ {"content-language", ""}, |
+ /* 28: */ |
+ {"content-length", ""}, |
+ /* 29: */ |
+ {"content-location", ""}, |
+ /* 30: */ |
+ {"content-range", ""}, |
+ /* 31: */ |
+ {"content-type", ""}, |
+ /* 32: */ |
+ {"cookie", ""}, |
+ /* 33: */ |
+ {"date", ""}, |
+ /* 34: */ |
+ {"etag", ""}, |
+ /* 35: */ |
+ {"expect", ""}, |
+ /* 36: */ |
+ {"expires", ""}, |
+ /* 37: */ |
+ {"from", ""}, |
+ /* 38: */ |
+ {"host", ""}, |
+ /* 39: */ |
+ {"if-match", ""}, |
+ /* 40: */ |
+ {"if-modified-since", ""}, |
+ /* 41: */ |
+ {"if-none-match", ""}, |
+ /* 42: */ |
+ {"if-range", ""}, |
+ /* 43: */ |
+ {"if-unmodified-since", ""}, |
+ /* 44: */ |
+ {"last-modified", ""}, |
+ /* 45: */ |
+ {"link", ""}, |
+ /* 46: */ |
+ {"location", ""}, |
+ /* 47: */ |
+ {"max-forwards", ""}, |
+ /* 48: */ |
+ {"proxy-authenticate", ""}, |
+ /* 49: */ |
+ {"proxy-authorization", ""}, |
+ /* 50: */ |
+ {"range", ""}, |
+ /* 51: */ |
+ {"referer", ""}, |
+ /* 52: */ |
+ {"refresh", ""}, |
+ /* 53: */ |
+ {"retry-after", ""}, |
+ /* 54: */ |
+ {"server", ""}, |
+ /* 55: */ |
+ {"set-cookie", ""}, |
+ /* 56: */ |
+ {"strict-transport-security", ""}, |
+ /* 57: */ |
+ {"transfer-encoding", ""}, |
+ /* 58: */ |
+ {"user-agent", ""}, |
+ /* 59: */ |
+ {"vary", ""}, |
+ /* 60: */ |
+ {"via", ""}, |
+ /* 61: */ |
+ {"www-authenticate", ""}, |
+}; |
+ |
+static uint32_t entries_for_bytes(uint32_t 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->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(static_table[i].key, static_table[i].value); |
+ } |
+} |
+ |
+void grpc_chttp2_hptbl_destroy(grpc_chttp2_hptbl *tbl) { |
+ size_t i; |
+ for (i = 0; i < GRPC_CHTTP2_LAST_STATIC_ENTRY; i++) { |
+ GRPC_MDELEM_UNREF(tbl->static_ents[i]); |
+ } |
+ for (i = 0; i < tbl->num_ents; i++) { |
+ 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, |
+ uint32_t tbl_index) { |
+ /* Static table comes first, just return an entry from it */ |
+ if (tbl_index <= GRPC_CHTTP2_LAST_STATIC_ENTRY) { |
+ return tbl->static_ents[tbl_index - 1]; |
+ } |
+ /* Otherwise, find the value in the list of valid entries */ |
+ tbl_index -= (GRPC_CHTTP2_LAST_STATIC_ENTRY + 1); |
+ if (tbl_index < tbl->num_ents) { |
+ uint32_t offset = |
+ (tbl->num_ents - 1u - tbl_index + tbl->first_ent) % tbl->cap_entries; |
+ return tbl->ents[offset]; |
+ } |
+ /* Invalid entry: return error */ |
+ return NULL; |
+} |
+ |
+/* Evict one element from the table */ |
+static void evict1(grpc_chttp2_hptbl *tbl) { |
+ grpc_mdelem *first_ent = tbl->ents[tbl->first_ent]; |
+ size_t elem_bytes = GPR_SLICE_LENGTH(first_ent->key->slice) + |
+ GPR_SLICE_LENGTH(first_ent->value->slice) + |
+ GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD; |
+ GPR_ASSERT(elem_bytes <= tbl->mem_used); |
+ tbl->mem_used -= (uint32_t)elem_bytes; |
+ tbl->first_ent = ((tbl->first_ent + 1) % tbl->cap_entries); |
+ tbl->num_ents--; |
+ GRPC_MDELEM_UNREF(first_ent); |
+} |
+ |
+static void rebuild_ents(grpc_chttp2_hptbl *tbl, uint32_t new_cap) { |
+ grpc_mdelem **ents = gpr_malloc(sizeof(*ents) * new_cap); |
+ uint32_t 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, |
+ uint32_t 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, |
+ uint32_t 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) { |
+ uint32_t 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->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 |
+ * attempt to add an entry that is larger than the maximum size; an |
+ * attempt to add an entry larger than the entire table causes |
+ * the table |
+ * to be emptied of all existing entries, and results in an |
+ * empty table. |
+ */ |
+ while (tbl->num_ents) { |
+ evict1(tbl); |
+ } |
+ return 1; |
+ } |
+ |
+ /* evict entries to ensure no overflow */ |
+ while (elem_bytes > (size_t)tbl->current_table_bytes - tbl->mem_used) { |
+ evict1(tbl); |
+ } |
+ |
+ /* copy the finalized entry in */ |
+ tbl->ents[(tbl->first_ent + tbl->num_ents) % tbl->cap_entries] = |
+ GRPC_MDELEM_REF(md); |
+ |
+ /* update accounting values */ |
+ tbl->num_ents++; |
+ tbl->mem_used += (uint32_t)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}; |
+ uint32_t 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 = 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++) { |
+ uint32_t idx = |
+ (uint32_t)(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; |
+ if (r.has_value) return r; |
+ } |
+ |
+ return r; |
+} |