diff options
Diffstat (limited to 'grpc/src/core/ext/transport/chttp2/transport/hpack_parser_table.cc')
-rw-r--r-- | grpc/src/core/ext/transport/chttp2/transport/hpack_parser_table.cc | 239 |
1 files changed, 239 insertions, 0 deletions
diff --git a/grpc/src/core/ext/transport/chttp2/transport/hpack_parser_table.cc b/grpc/src/core/ext/transport/chttp2/transport/hpack_parser_table.cc new file mode 100644 index 00000000..17dd0a4d --- /dev/null +++ b/grpc/src/core/ext/transport/chttp2/transport/hpack_parser_table.cc @@ -0,0 +1,239 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <grpc/support/port_platform.h> + +#include "src/core/ext/transport/chttp2/transport/hpack_parser_table.h" + +#include <assert.h> +#include <string.h> + +#include "absl/strings/str_format.h" + +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> + +#include "src/core/ext/transport/chttp2/transport/hpack_constants.h" +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/gpr/murmur_hash.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/surface/validate_metadata.h" + +extern grpc_core::TraceFlag grpc_http_trace; + +namespace grpc_core { + +HPackTable::HPackTable() : static_metadata_(GetStaticMementos()) {} + +HPackTable::~HPackTable() = default; + +/* Evict one element from the table */ +void HPackTable::EvictOne() { + auto first_entry = std::move(entries_[first_entry_]); + GPR_ASSERT(first_entry.transport_size() <= mem_used_); + mem_used_ -= first_entry.transport_size(); + first_entry_ = ((first_entry_ + 1) % entries_.size()); + num_entries_--; +} + +void HPackTable::Rebuild(uint32_t new_cap) { + EntriesVec entries; + entries.resize(new_cap); + for (size_t i = 0; i < num_entries_; i++) { + entries[i] = std::move(entries_[(first_entry_ + i) % entries_.size()]); + } + first_entry_ = 0; + entries_.swap(entries); +} + +void HPackTable::SetMaxBytes(uint32_t max_bytes) { + if (max_bytes_ == max_bytes) { + return; + } + if (GRPC_TRACE_FLAG_ENABLED(grpc_http_trace)) { + gpr_log(GPR_INFO, "Update hpack parser max size to %d", max_bytes); + } + while (mem_used_ > max_bytes) { + EvictOne(); + } + max_bytes_ = max_bytes; +} + +grpc_error_handle HPackTable::SetCurrentTableSize(uint32_t bytes) { + if (current_table_bytes_ == bytes) { + return GRPC_ERROR_NONE; + } + if (bytes > max_bytes_) { + return GRPC_ERROR_CREATE_FROM_CPP_STRING(absl::StrFormat( + "Attempt to make hpack table %d bytes when max is %d bytes", bytes, + max_bytes_)); + } + if (GRPC_TRACE_FLAG_ENABLED(grpc_http_trace)) { + gpr_log(GPR_INFO, "Update hpack parser table size to %d", bytes); + } + while (mem_used_ > bytes) { + EvictOne(); + } + current_table_bytes_ = bytes; + max_entries_ = hpack_constants::EntriesForBytes(bytes); + if (max_entries_ > entries_.size()) { + Rebuild(max_entries_); + } else if (max_entries_ < entries_.size() / 3) { + // TODO(ctiller): move to resource quota system, only shrink under memory + // pressure + uint32_t new_cap = + std::max(max_entries_, static_cast<uint32_t>(kInlineEntries)); + if (new_cap != entries_.size()) { + Rebuild(new_cap); + } + } + return GRPC_ERROR_NONE; +} + +grpc_error_handle HPackTable::Add(Memento md) { + if (current_table_bytes_ > max_bytes_) { + return GRPC_ERROR_CREATE_FROM_CPP_STRING(absl::StrFormat( + "HPACK max table size reduced to %d but not reflected by hpack " + "stream (still at %d)", + max_bytes_, current_table_bytes_)); + } + + // we can't add elements bigger than the max table size + if (md.transport_size() > 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 (num_entries_) { + EvictOne(); + } + return GRPC_ERROR_NONE; + } + + // evict entries to ensure no overflow + while (md.transport_size() > + static_cast<size_t>(current_table_bytes_) - mem_used_) { + EvictOne(); + } + + // copy the finalized entry in + mem_used_ += md.transport_size(); + entries_[(first_entry_ + num_entries_) % entries_.size()] = std::move(md); + + // update accounting values + num_entries_++; + return GRPC_ERROR_NONE; +} + +namespace { +struct StaticTableEntry { + const char* key; + const char* value; +}; + +const StaticTableEntry kStaticTable[hpack_constants::kLastStaticEntry] = { + {":authority", ""}, + {":method", "GET"}, + {":method", "POST"}, + {":path", "/"}, + {":path", "/index.html"}, + {":scheme", "http"}, + {":scheme", "https"}, + {":status", "200"}, + {":status", "204"}, + {":status", "206"}, + {":status", "304"}, + {":status", "400"}, + {":status", "404"}, + {":status", "500"}, + {"accept-charset", ""}, + {"accept-encoding", "gzip, deflate"}, + {"accept-language", ""}, + {"accept-ranges", ""}, + {"accept", ""}, + {"access-control-allow-origin", ""}, + {"age", ""}, + {"allow", ""}, + {"authorization", ""}, + {"cache-control", ""}, + {"content-disposition", ""}, + {"content-encoding", ""}, + {"content-language", ""}, + {"content-length", ""}, + {"content-location", ""}, + {"content-range", ""}, + {"content-type", ""}, + {"cookie", ""}, + {"date", ""}, + {"etag", ""}, + {"expect", ""}, + {"expires", ""}, + {"from", ""}, + {"host", ""}, + {"if-match", ""}, + {"if-modified-since", ""}, + {"if-none-match", ""}, + {"if-range", ""}, + {"if-unmodified-since", ""}, + {"last-modified", ""}, + {"link", ""}, + {"location", ""}, + {"max-forwards", ""}, + {"proxy-authenticate", ""}, + {"proxy-authorization", ""}, + {"range", ""}, + {"referer", ""}, + {"refresh", ""}, + {"retry-after", ""}, + {"server", ""}, + {"set-cookie", ""}, + {"strict-transport-security", ""}, + {"transfer-encoding", ""}, + {"user-agent", ""}, + {"vary", ""}, + {"via", ""}, + {"www-authenticate", ""}, +}; + +GPR_ATTRIBUTE_NOINLINE HPackTable::Memento MakeMemento(size_t i) { + auto sm = kStaticTable[i]; + return grpc_metadata_batch::Parse( + sm.key, Slice::FromStaticString(sm.value), + strlen(sm.key) + strlen(sm.value) + hpack_constants::kEntryOverhead, + [](absl::string_view, const Slice&) { + abort(); // not expecting to see this + }); +} + +} // namespace + +const HPackTable::StaticMementos& HPackTable::GetStaticMementos() { + static const StaticMementos* const static_mementos = new StaticMementos(); + return *static_mementos; +} + +HPackTable::StaticMementos::StaticMementos() { + for (uint32_t i = 0; i < hpack_constants::kLastStaticEntry; i++) { + memento[i] = MakeMemento(i); + } +} + +} // namespace grpc_core |