Reland HPACK parsing changes (#26997)

* Revert "Revert "HPACK Table --> C++ (#26851)" (#26995)"

This reverts commit 840bcce9c4.

* fix bad parsing of trailing === in binary metadata
pull/26973/head^2
Craig Tiller 3 years ago committed by GitHub
parent c8f7d33953
commit bbea27de46
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      BUILD
  2. 10
      CMakeLists.txt
  3. 6
      build_autogenerated.yaml
  4. 5
      gRPC-C++.podspec
  5. 5
      gRPC-Core.podspec
  6. 2
      grpc.gemspec
  7. 2
      grpc.gyp
  8. 2
      package.xml
  9. 7
      src/core/ext/transport/chttp2/transport/hpack_encoder.cc
  10. 1813
      src/core/ext/transport/chttp2/transport/hpack_parser.cc
  11. 215
      src/core/ext/transport/chttp2/transport/hpack_parser.h
  12. 201
      src/core/ext/transport/chttp2/transport/hpack_table.cc
  13. 198
      src/core/ext/transport/chttp2/transport/hpack_table.h
  14. 3
      src/core/ext/transport/chttp2/transport/parsing.cc
  15. 2
      test/core/transport/chttp2/binary-metadata.headers
  16. 3
      test/core/transport/chttp2/hpack_parser_fuzzer_test.cc
  17. 26
      test/core/transport/chttp2/hpack_parser_test.cc
  18. 141
      test/core/transport/chttp2/hpack_table_test.cc
  19. 4
      test/cpp/microbenchmarks/bm_chttp2_hpack.cc
  20. 2
      tools/doxygen/Doxyfile.c++.internal
  21. 2
      tools/doxygen/Doxyfile.core.internal

@ -2647,6 +2647,7 @@ grpc_cc_library(
"grpc_http_filters",
"grpc_trace",
"grpc_transport_chttp2_alpn",
"match",
"popularity_count",
],
)

10
CMakeLists.txt generated

@ -2132,6 +2132,7 @@ target_link_libraries(grpc
absl::inlined_vector
absl::bind_front
absl::statusor
absl::variant
gpr
${_gRPC_SSL_LIBRARIES}
address_sorting
@ -2688,6 +2689,7 @@ target_link_libraries(grpc_unsecure
absl::inlined_vector
absl::bind_front
absl::statusor
absl::variant
gpr
address_sorting
)
@ -16283,7 +16285,7 @@ generate_pkgconfig(
"gRPC"
"high performance general RPC framework"
"${gRPC_CORE_VERSION}"
"gpr openssl absl_base absl_bind_front absl_cord absl_core_headers absl_flat_hash_map absl_inlined_vector absl_memory absl_optional absl_status absl_statusor absl_str_format absl_strings absl_synchronization absl_time"
"gpr openssl absl_base absl_bind_front absl_cord absl_core_headers absl_flat_hash_map absl_inlined_vector absl_memory absl_optional absl_status absl_statusor absl_str_format absl_strings absl_synchronization absl_time absl_variant"
"-lgrpc -laddress_sorting -lre2 -lupb -lcares -lz"
""
"grpc.pc")
@ -16293,7 +16295,7 @@ generate_pkgconfig(
"gRPC unsecure"
"high performance general RPC framework without SSL"
"${gRPC_CORE_VERSION}"
"gpr absl_base absl_bind_front absl_cord absl_core_headers absl_flat_hash_map absl_inlined_vector absl_memory absl_optional absl_status absl_statusor absl_str_format absl_strings absl_synchronization absl_time"
"gpr absl_base absl_bind_front absl_cord absl_core_headers absl_flat_hash_map absl_inlined_vector absl_memory absl_optional absl_status absl_statusor absl_str_format absl_strings absl_synchronization absl_time absl_variant"
"-lgrpc_unsecure"
""
"grpc_unsecure.pc")
@ -16303,7 +16305,7 @@ generate_pkgconfig(
"gRPC++"
"C++ wrapper for gRPC"
"${gRPC_CPP_VERSION}"
"grpc absl_base absl_bind_front absl_cord absl_core_headers absl_flat_hash_map absl_inlined_vector absl_memory absl_optional absl_status absl_statusor absl_str_format absl_strings absl_synchronization absl_time"
"grpc absl_base absl_bind_front absl_cord absl_core_headers absl_flat_hash_map absl_inlined_vector absl_memory absl_optional absl_status absl_statusor absl_str_format absl_strings absl_synchronization absl_time absl_variant"
"-lgrpc++"
""
"grpc++.pc")
@ -16313,7 +16315,7 @@ generate_pkgconfig(
"gRPC++ unsecure"
"C++ wrapper for gRPC without SSL"
"${gRPC_CPP_VERSION}"
"grpc_unsecure absl_base absl_bind_front absl_cord absl_core_headers absl_flat_hash_map absl_inlined_vector absl_memory absl_optional absl_status absl_statusor absl_str_format absl_strings absl_synchronization absl_time"
"grpc_unsecure absl_base absl_bind_front absl_cord absl_core_headers absl_flat_hash_map absl_inlined_vector absl_memory absl_optional absl_status absl_statusor absl_str_format absl_strings absl_synchronization absl_time absl_variant"
"-lgrpc++_unsecure"
""
"grpc++_unsecure.pc")

@ -739,7 +739,9 @@ libs:
- src/core/lib/event_engine/sockaddr.h
- src/core/lib/gprpp/atomic.h
- src/core/lib/gprpp/dual_ref_counted.h
- src/core/lib/gprpp/match.h
- src/core/lib/gprpp/orphanable.h
- src/core/lib/gprpp/overload.h
- src/core/lib/gprpp/ref_counted.h
- src/core/lib/gprpp/ref_counted_ptr.h
- src/core/lib/http/format_request.h
@ -1510,6 +1512,7 @@ libs:
- absl/container:inlined_vector
- absl/functional:bind_front
- absl/status:statusor
- absl/types:variant
- gpr
- libssl
- address_sorting
@ -1776,7 +1779,9 @@ libs:
- src/core/lib/event_engine/sockaddr.h
- src/core/lib/gprpp/atomic.h
- src/core/lib/gprpp/dual_ref_counted.h
- src/core/lib/gprpp/match.h
- src/core/lib/gprpp/orphanable.h
- src/core/lib/gprpp/overload.h
- src/core/lib/gprpp/ref_counted.h
- src/core/lib/gprpp/ref_counted_ptr.h
- src/core/lib/http/format_request.h
@ -2192,6 +2197,7 @@ libs:
- absl/container:inlined_vector
- absl/functional:bind_front
- absl/status:statusor
- absl/types:variant
- gpr
- address_sorting
baselib: true

5
gRPC-C++.podspec generated

@ -202,6 +202,7 @@ Pod::Spec.new do |s|
ss.dependency 'abseil/synchronization/synchronization', abseil_version
ss.dependency 'abseil/time/time', abseil_version
ss.dependency 'abseil/types/optional', abseil_version
ss.dependency 'abseil/types/variant', abseil_version
ss.source_files = 'src/core/ext/filters/client_channel/backend_metric.h',
'src/core/ext/filters/client_channel/backup_poller.h',
@ -553,9 +554,11 @@ Pod::Spec.new do |s|
'src/core/lib/gprpp/global_config_generic.h',
'src/core/lib/gprpp/host_port.h',
'src/core/lib/gprpp/manual_constructor.h',
'src/core/lib/gprpp/match.h',
'src/core/lib/gprpp/memory.h',
'src/core/lib/gprpp/mpscq.h',
'src/core/lib/gprpp/orphanable.h',
'src/core/lib/gprpp/overload.h',
'src/core/lib/gprpp/ref_counted.h',
'src/core/lib/gprpp/ref_counted_ptr.h',
'src/core/lib/gprpp/stat.h',
@ -1213,9 +1216,11 @@ Pod::Spec.new do |s|
'src/core/lib/gprpp/global_config_generic.h',
'src/core/lib/gprpp/host_port.h',
'src/core/lib/gprpp/manual_constructor.h',
'src/core/lib/gprpp/match.h',
'src/core/lib/gprpp/memory.h',
'src/core/lib/gprpp/mpscq.h',
'src/core/lib/gprpp/orphanable.h',
'src/core/lib/gprpp/overload.h',
'src/core/lib/gprpp/ref_counted.h',
'src/core/lib/gprpp/ref_counted_ptr.h',
'src/core/lib/gprpp/stat.h',

5
gRPC-Core.podspec generated

@ -192,6 +192,7 @@ Pod::Spec.new do |s|
ss.dependency 'abseil/synchronization/synchronization', abseil_version
ss.dependency 'abseil/time/time', abseil_version
ss.dependency 'abseil/types/optional', abseil_version
ss.dependency 'abseil/types/variant', abseil_version
ss.compiler_flags = '-DBORINGSSL_PREFIX=GRPC -Wno-unreachable-code -Wno-shorten-64-to-32'
ss.source_files = 'src/core/ext/filters/census/grpc_context.cc',
@ -928,10 +929,12 @@ Pod::Spec.new do |s|
'src/core/lib/gprpp/host_port.cc',
'src/core/lib/gprpp/host_port.h',
'src/core/lib/gprpp/manual_constructor.h',
'src/core/lib/gprpp/match.h',
'src/core/lib/gprpp/memory.h',
'src/core/lib/gprpp/mpscq.cc',
'src/core/lib/gprpp/mpscq.h',
'src/core/lib/gprpp/orphanable.h',
'src/core/lib/gprpp/overload.h',
'src/core/lib/gprpp/ref_counted.h',
'src/core/lib/gprpp/ref_counted_ptr.h',
'src/core/lib/gprpp/stat.h',
@ -1800,9 +1803,11 @@ Pod::Spec.new do |s|
'src/core/lib/gprpp/global_config_generic.h',
'src/core/lib/gprpp/host_port.h',
'src/core/lib/gprpp/manual_constructor.h',
'src/core/lib/gprpp/match.h',
'src/core/lib/gprpp/memory.h',
'src/core/lib/gprpp/mpscq.h',
'src/core/lib/gprpp/orphanable.h',
'src/core/lib/gprpp/overload.h',
'src/core/lib/gprpp/ref_counted.h',
'src/core/lib/gprpp/ref_counted_ptr.h',
'src/core/lib/gprpp/stat.h',

2
grpc.gemspec generated

@ -842,10 +842,12 @@ Gem::Specification.new do |s|
s.files += %w( src/core/lib/gprpp/host_port.cc )
s.files += %w( src/core/lib/gprpp/host_port.h )
s.files += %w( src/core/lib/gprpp/manual_constructor.h )
s.files += %w( src/core/lib/gprpp/match.h )
s.files += %w( src/core/lib/gprpp/memory.h )
s.files += %w( src/core/lib/gprpp/mpscq.cc )
s.files += %w( src/core/lib/gprpp/mpscq.h )
s.files += %w( src/core/lib/gprpp/orphanable.h )
s.files += %w( src/core/lib/gprpp/overload.h )
s.files += %w( src/core/lib/gprpp/ref_counted.h )
s.files += %w( src/core/lib/gprpp/ref_counted_ptr.h )
s.files += %w( src/core/lib/gprpp/stat.h )

2
grpc.gyp generated

@ -473,6 +473,7 @@
'absl/container:inlined_vector',
'absl/functional:bind_front',
'absl/status:statusor',
'absl/types:variant',
'gpr',
'address_sorting',
],
@ -1132,6 +1133,7 @@
'absl/container:inlined_vector',
'absl/functional:bind_front',
'absl/status:statusor',
'absl/types:variant',
'gpr',
'address_sorting',
],

2
package.xml generated

@ -822,10 +822,12 @@
<file baseinstalldir="/" name="src/core/lib/gprpp/host_port.cc" role="src" />
<file baseinstalldir="/" name="src/core/lib/gprpp/host_port.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/gprpp/manual_constructor.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/gprpp/match.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/gprpp/memory.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/gprpp/mpscq.cc" role="src" />
<file baseinstalldir="/" name="src/core/lib/gprpp/mpscq.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/gprpp/orphanable.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/gprpp/overload.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/gprpp/ref_counted.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/gprpp/ref_counted_ptr.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/gprpp/stat.h" role="src" />

@ -586,7 +586,7 @@ static void GPR_ATTRIBUTE_NOINLINE hpack_enc_log(grpc_mdelem elem) {
}
static uint32_t dynidx(grpc_chttp2_hpack_compressor* c, uint32_t elem_index) {
return 1 + GRPC_CHTTP2_LAST_STATIC_ENTRY + c->tail_remote_index +
return 1 + grpc_core::HPackTable::kLastStaticEntry + c->tail_remote_index +
c->table_elems - elem_index;
}
@ -833,7 +833,7 @@ void grpc_chttp2_encode_header(grpc_chttp2_hpack_compressor* c,
if (is_static &&
(static_index =
reinterpret_cast<grpc_core::StaticMetadata*>(GRPC_MDELEM_DATA(md))
->StaticIndex()) < GRPC_CHTTP2_LAST_STATIC_ENTRY) {
->StaticIndex()) < grpc_core::HPackTable::kLastStaticEntry) {
emit_indexed(c, static_cast<uint32_t>(static_index + 1), &st);
} else {
hpack_enc(c, md, &st);
@ -847,7 +847,8 @@ void grpc_chttp2_encode_header(grpc_chttp2_hpack_compressor* c,
if (is_static &&
(static_index = reinterpret_cast<grpc_core::StaticMetadata*>(
GRPC_MDELEM_DATA(l->md))
->StaticIndex()) < GRPC_CHTTP2_LAST_STATIC_ENTRY) {
->StaticIndex()) <
grpc_core::HPackTable::kLastStaticEntry) {
emit_indexed(c, static_cast<uint32_t>(static_index + 1), &st);
} else {
hpack_enc(c, l->md, &st);

File diff suppressed because it is too large Load Diff

@ -29,10 +29,25 @@
namespace grpc_core {
// Top level interface for parsing a sequence of header, continuation frames.
class HPackParser {
public:
enum class Boundary { None, EndOfHeaders, EndOfStream };
enum class Priority { None, Included };
// What kind of stream boundary is provided by this frame?
enum class Boundary : uint8_t {
// More continuations are expected
None,
// This marks the end of headers, so data frames should follow
EndOfHeaders,
// This marks the end of headers *and* the end of the stream
EndOfStream
};
// What kind of priority is represented in the next frame
enum class Priority : uint8_t {
// No priority field
None,
// Yes there's a priority field
Included
};
// User specified structure called for each received header.
using Sink = std::function<grpc_error_handle(grpc_mdelem)>;
@ -40,189 +55,53 @@ class HPackParser {
HPackParser();
~HPackParser();
// Non-copyable/movable
HPackParser(const HPackParser&) = delete;
HPackParser& operator=(const HPackParser&) = delete;
// Begin parsing a new frame
// Sink receives each parsed header,
void BeginFrame(Sink sink, Boundary boundary, Priority priority);
// Change the header sink mid parse
void ResetSink(Sink sink) { sink_ = std::move(sink); }
grpc_error_handle Parse(const grpc_slice& slice);
// Parse one slice worth of data
grpc_error_handle Parse(const grpc_slice& slice, bool is_last);
// Reset state ready for the next BeginFrame
void FinishFrame();
grpc_chttp2_hptbl* hpack_table() { return &table_; }
// Retrieve the associated hpack table (for tests, debugging)
HPackTable* hpack_table() { return &table_; }
// Is the current frame a boundary of some sort
bool is_boundary() const { return boundary_ != Boundary::None; }
// Is the current frame the end of a stream
bool is_eof() const { return boundary_ == Boundary::EndOfStream; }
bool is_in_begin_state() const { return state_ == &HPackParser::parse_begin; }
private:
enum class BinaryState {
kNotBinary,
kBinaryBegin,
kBase64Byte0,
kBase64Byte1,
kBase64Byte2,
kBase64Byte3,
};
struct String {
bool copied_;
struct {
grpc_slice referenced;
struct {
char* str;
uint32_t length;
uint32_t capacity;
} copied;
} data_;
UnmanagedMemorySlice TakeExtern();
ManagedMemorySlice TakeIntern();
void AppendBytes(const uint8_t* data, size_t length);
};
using State = grpc_error_handle (HPackParser::*)(const uint8_t* beg,
const uint8_t* end);
// Forward declarations for parsing states.
// These are keeping their old (C-style) names until a future refactor where
// they will be eliminated.
grpc_error_handle parse_next(const uint8_t* cur, const uint8_t* end);
grpc_error_handle parse_begin(const uint8_t* cur, const uint8_t* end);
grpc_error_handle parse_error(const uint8_t* cur, const uint8_t* end,
grpc_error_handle error);
grpc_error_handle still_parse_error(const uint8_t* cur, const uint8_t* end);
grpc_error_handle parse_illegal_op(const uint8_t* cur, const uint8_t* end);
grpc_error_handle parse_string_prefix(const uint8_t* cur, const uint8_t* end);
grpc_error_handle parse_key_string(const uint8_t* cur, const uint8_t* end);
grpc_error_handle parse_value_string(const uint8_t* cur, const uint8_t* end,
bool is_binary);
grpc_error_handle parse_value_string_with_indexed_key(const uint8_t* cur,
const uint8_t* end);
grpc_error_handle parse_value_string_with_literal_key(const uint8_t* cur,
const uint8_t* end);
grpc_error_handle parse_stream_weight(const uint8_t* cur, const uint8_t* end);
grpc_error_handle parse_value0(const uint8_t* cur, const uint8_t* end);
grpc_error_handle parse_value1(const uint8_t* cur, const uint8_t* end);
grpc_error_handle parse_value2(const uint8_t* cur, const uint8_t* end);
grpc_error_handle parse_value3(const uint8_t* cur, const uint8_t* end);
grpc_error_handle parse_value4(const uint8_t* cur, const uint8_t* end);
grpc_error_handle parse_value5up(const uint8_t* cur, const uint8_t* end);
grpc_error_handle parse_stream_dep0(const uint8_t* cur, const uint8_t* end);
grpc_error_handle parse_stream_dep1(const uint8_t* cur, const uint8_t* end);
grpc_error_handle parse_stream_dep2(const uint8_t* cur, const uint8_t* end);
grpc_error_handle parse_stream_dep3(const uint8_t* cur, const uint8_t* end);
grpc_error_handle parse_indexed_field(const uint8_t* cur, const uint8_t* end);
grpc_error_handle parse_indexed_field_x(const uint8_t* cur,
const uint8_t* end);
grpc_error_handle parse_lithdr_incidx(const uint8_t* cur, const uint8_t* end);
grpc_error_handle parse_lithdr_incidx_x(const uint8_t* cur,
const uint8_t* end);
grpc_error_handle parse_lithdr_incidx_v(const uint8_t* cur,
const uint8_t* end);
grpc_error_handle parse_lithdr_notidx(const uint8_t* cur, const uint8_t* end);
grpc_error_handle parse_lithdr_notidx_x(const uint8_t* cur,
const uint8_t* end);
grpc_error_handle parse_lithdr_notidx_v(const uint8_t* cur,
const uint8_t* end);
grpc_error_handle parse_lithdr_nvridx(const uint8_t* cur, const uint8_t* end);
grpc_error_handle parse_lithdr_nvridx_x(const uint8_t* cur,
const uint8_t* end);
grpc_error_handle parse_lithdr_nvridx_v(const uint8_t* cur,
const uint8_t* end);
grpc_error_handle parse_max_tbl_size(const uint8_t* cur, const uint8_t* end);
grpc_error_handle parse_max_tbl_size_x(const uint8_t* cur,
const uint8_t* end);
grpc_error_handle parse_string(const uint8_t* cur, const uint8_t* end);
grpc_error_handle begin_parse_string(const uint8_t* cur, const uint8_t* end,
BinaryState binary, String* str);
grpc_error_handle finish_indexed_field(const uint8_t* cur,
const uint8_t* end);
grpc_error_handle finish_lithdr_incidx(const uint8_t* cur,
const uint8_t* end);
grpc_error_handle finish_lithdr_incidx_v(const uint8_t* cur,
const uint8_t* end);
grpc_error_handle finish_lithdr_notidx(const uint8_t* cur,
const uint8_t* end);
grpc_error_handle finish_lithdr_notidx_v(const uint8_t* cur,
const uint8_t* end);
grpc_error_handle finish_lithdr_nvridx(const uint8_t* cur,
const uint8_t* end);
grpc_error_handle finish_lithdr_nvridx_v(const uint8_t* cur,
const uint8_t* end);
grpc_error_handle finish_max_tbl_size(const uint8_t* cur, const uint8_t* end);
grpc_error_handle finish_str(const uint8_t* cur, const uint8_t* end);
enum class TableAction {
kAddToTable,
kOmitFromTable,
};
GPR_ATTRIBUTE_NOINLINE grpc_error_handle InvalidHPackIndexError();
GPR_ATTRIBUTE_NOINLINE void LogHeader(grpc_mdelem md);
grpc_error_handle AddHeaderToTable(grpc_mdelem md);
template <TableAction table_action>
grpc_error_handle FinishHeader(grpc_mdelem md);
grpc_mdelem GetPrecomputedMDForIndex();
void SetPrecomputedMDIndex(grpc_mdelem md);
bool IsBinaryLiteralHeader();
grpc_error_handle IsBinaryIndexedHeader(bool* is);
// Helper classes: see implementation
class Parser;
class Input;
class String;
grpc_error_handle AppendString(const uint8_t* cur, const uint8_t* end);
grpc_error_handle AppendHuffNibble(uint8_t nibble);
grpc_error_handle AppendHuffBytes(const uint8_t* cur, const uint8_t* end);
grpc_error_handle AppendStrBytes(const uint8_t* cur, const uint8_t* end);
grpc_error_handle ParseInput(Input input, bool is_last);
bool ParseInputInner(Input* input);
// Callback per header received
Sink sink_;
grpc_error_handle last_error_;
// current parse state - or a function that implements it
State state_;
// future states dependent on the opening op code
const State* next_state_;
// what to do after skipping prioritization data
State after_prioritization_;
// the refcount of the slice that we're currently parsing
grpc_slice_refcount* current_slice_refcount_;
// the value we're currently parsing
union {
uint32_t* value;
String* str;
} parsing_;
// string parameters for each chunk
String key_;
String value_;
// parsed index
uint32_t index_;
// When we parse a value string, we determine the metadata element for a
// specific index, which we need again when we're finishing up with that
// header. To avoid calculating the metadata element for that index a second
// time at that stage, we cache (and invalidate) the element here.
grpc_mdelem md_for_index_;
#ifndef NDEBUG
int64_t precomputed_md_index_;
#endif
// length of source bytes for the currently parsing string
uint32_t strlen_;
// number of source bytes read for the currently parsing string
uint32_t strgot_;
// huffman decoding state
int16_t huff_state_;
// is the string being decoded binary?
BinaryState binary_;
// is the current string huffman encoded?
bool huff_;
// is a dynamic table update allowed?
uint8_t dynamic_table_updates_allowed_;
// set by higher layers, used by grpc_chttp2_header_parser_parse to signal
// it should append a metadata boundary at the end of frame
// Bytes that could not be parsed last parsing round
std::vector<uint8_t> unparsed_bytes_;
// Buffer kind of boundary
// TODO(ctiller): see if we can move this argument to Parse, and avoid
// buffering.
Boundary boundary_;
uint32_t base64_buffer_;
// Buffer priority
// TODO(ctiller): see if we can move this argument to Parse, and avoid
// buffering.
Priority priority_;
uint8_t dynamic_table_updates_allowed_;
// hpack table
grpc_chttp2_hptbl table_;
HPackTable table_;
};
} // namespace grpc_core

@ -36,191 +36,132 @@
extern grpc_core::TraceFlag grpc_http_trace;
void grpc_chttp2_hptbl_destroy(grpc_chttp2_hptbl* tbl) {
size_t 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);
tbl->ents = nullptr;
}
namespace grpc_core {
template <bool take_ref>
static grpc_mdelem lookup_dynamic_index(const grpc_chttp2_hptbl* tbl,
uint32_t tbl_index) {
/* Not static - 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;
grpc_mdelem md = tbl->ents[offset];
if (take_ref) {
GRPC_MDELEM_REF(md);
}
return md;
}
/* Invalid entry: return error */
return GRPC_MDNULL;
}
using hpack_table_detail::EntriesForBytes;
using hpack_table_detail::kInlineEntries;
grpc_mdelem grpc_chttp2_hptbl_lookup_dynamic_index(const grpc_chttp2_hptbl* tbl,
uint32_t tbl_index) {
return lookup_dynamic_index<false>(tbl, tbl_index);
}
HPackTable::HPackTable() : entries_(kInlineEntries) {}
grpc_mdelem grpc_chttp2_hptbl_lookup_ref_dynamic_index(
const grpc_chttp2_hptbl* tbl, uint32_t tbl_index) {
return lookup_dynamic_index<true>(tbl, tbl_index);
HPackTable::~HPackTable() {
for (size_t i = 0; i < num_entries_; i++) {
GRPC_MDELEM_UNREF(entries_[(first_entry_ + i) % entries_.size()]);
}
}
/* 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 = GRPC_SLICE_LENGTH(GRPC_MDKEY(first_ent)) +
GRPC_SLICE_LENGTH(GRPC_MDVALUE(first_ent)) +
GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD;
GPR_ASSERT(elem_bytes <= tbl->mem_used);
tbl->mem_used -= static_cast<uint32_t>(elem_bytes);
tbl->first_ent = ((tbl->first_ent + 1) % tbl->cap_entries);
tbl->num_ents--;
GRPC_MDELEM_UNREF(first_ent);
void HPackTable::EvictOne() {
grpc_mdelem first_entry = entries_[first_entry_];
size_t elem_bytes = GRPC_SLICE_LENGTH(GRPC_MDKEY(first_entry)) +
GRPC_SLICE_LENGTH(GRPC_MDVALUE(first_entry)) +
kEntryOverhead;
GPR_ASSERT(elem_bytes <= mem_used_);
mem_used_ -= static_cast<uint32_t>(elem_bytes);
first_entry_ = ((first_entry_ + 1) % entries_.size());
num_entries_--;
GRPC_MDELEM_UNREF(first_entry);
}
static void rebuild_ents(grpc_chttp2_hptbl* tbl, uint32_t new_cap) {
grpc_mdelem* ents =
static_cast<grpc_mdelem*>(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];
void HPackTable::Rebuild(uint32_t new_cap) {
EntriesVec entries;
entries.resize(new_cap);
for (size_t i = 0; i < num_entries_; i++) {
entries[i] = entries_[(first_entry_ + i) % entries_.size()];
}
gpr_free(tbl->ents);
tbl->ents = ents;
tbl->cap_entries = new_cap;
tbl->first_ent = 0;
first_entry_ = 0;
entries_.swap(entries);
}
void grpc_chttp2_hptbl_set_max_bytes(grpc_chttp2_hptbl* tbl,
uint32_t max_bytes) {
if (tbl->max_bytes == max_bytes) {
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 (tbl->mem_used > max_bytes) {
evict1(tbl);
while (mem_used_ > max_bytes) {
EvictOne();
}
tbl->max_bytes = max_bytes;
max_bytes_ = max_bytes;
}
grpc_error_handle grpc_chttp2_hptbl_set_current_table_size(
grpc_chttp2_hptbl* tbl, uint32_t bytes) {
if (tbl->current_table_bytes == bytes) {
grpc_error_handle HPackTable::SetCurrentTableSize(uint32_t bytes) {
if (current_table_bytes_ == bytes) {
return GRPC_ERROR_NONE;
}
if (bytes > tbl->max_bytes) {
if (bytes > max_bytes_) {
return GRPC_ERROR_CREATE_FROM_COPIED_STRING(
absl::StrFormat(
"Attempt to make hpack table %d bytes when max is %d bytes", bytes,
tbl->max_bytes)
max_bytes_)
.c_str());
}
if (GRPC_TRACE_FLAG_ENABLED(grpc_http_trace)) {
gpr_log(GPR_INFO, "Update hpack parser table size to %d", bytes);
}
while (tbl->mem_used > bytes) {
evict1(tbl);
}
tbl->current_table_bytes = bytes;
tbl->max_entries = grpc_chttp2_hptbl::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);
while (mem_used_ > bytes) {
EvictOne();
}
current_table_bytes_ = bytes;
max_entries_ = 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_, kInlineEntries);
if (new_cap != entries_.size()) {
Rebuild(new_cap);
}
}
return GRPC_ERROR_NONE;
}
grpc_error_handle grpc_chttp2_hptbl_add(grpc_chttp2_hptbl* tbl,
grpc_mdelem md) {
grpc_error_handle HPackTable::Add(grpc_mdelem md) {
/* determine how many bytes of buffer this entry represents */
size_t elem_bytes = GRPC_SLICE_LENGTH(GRPC_MDKEY(md)) +
GRPC_SLICE_LENGTH(GRPC_MDVALUE(md)) +
GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD;
GRPC_SLICE_LENGTH(GRPC_MDVALUE(md)) + kEntryOverhead;
if (tbl->current_table_bytes > tbl->max_bytes) {
if (current_table_bytes_ > max_bytes_) {
return GRPC_ERROR_CREATE_FROM_COPIED_STRING(
absl::StrFormat(
"HPACK max table size reduced to %d but not reflected by hpack "
"stream (still at %d)",
tbl->max_bytes, tbl->current_table_bytes)
max_bytes_, current_table_bytes_)
.c_str());
}
/* 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);
// we can't add elements bigger than the max table size
if (elem_bytes > 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 (elem_bytes >
static_cast<size_t>(tbl->current_table_bytes) - tbl->mem_used) {
evict1(tbl);
// evict entries to ensure no overflow
while (elem_bytes > static_cast<size_t>(current_table_bytes_) - mem_used_) {
EvictOne();
}
/* copy the finalized entry in */
tbl->ents[(tbl->first_ent + tbl->num_ents) % tbl->cap_entries] =
// copy the finalized entry in
entries_[(first_entry_ + num_entries_) % entries_.size()] =
GRPC_MDELEM_REF(md);
/* update accounting values */
tbl->num_ents++;
tbl->mem_used += static_cast<uint32_t>(elem_bytes);
// update accounting values
num_entries_++;
mem_used_ += static_cast<uint32_t>(elem_bytes);
return GRPC_ERROR_NONE;
}
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 = grpc_static_mdelem_manifested()[i];
if (!grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDKEY(ent))) continue;
r.index = i + 1u;
r.has_value = grpc_slice_eq(GRPC_MDVALUE(md), GRPC_MDVALUE(ent));
if (r.has_value) return r;
}
/* Scan the dynamic table */
for (i = 0; i < tbl->num_ents; i++) {
uint32_t idx = static_cast<uint32_t>(tbl->num_ents - i +
GRPC_CHTTP2_LAST_STATIC_ENTRY);
grpc_mdelem ent = tbl->ents[(tbl->first_ent + i) % tbl->cap_entries];
if (!grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDKEY(ent))) continue;
r.index = idx;
r.has_value = grpc_slice_eq(GRPC_MDVALUE(md), GRPC_MDVALUE(ent));
if (r.has_value) return r;
}
return r;
}
} // namespace grpc_core
static size_t get_base64_encoded_size(size_t raw_length) {
static const uint8_t tail_xtra[3] = {0, 2, 3};

@ -27,98 +27,111 @@
#include "src/core/lib/transport/metadata.h"
#include "src/core/lib/transport/static_metadata.h"
/* HPACK header table */
/* last index in the static table */
#define GRPC_CHTTP2_LAST_STATIC_ENTRY 61
/* Initial table size as per the spec */
#define GRPC_CHTTP2_INITIAL_HPACK_TABLE_SIZE 4096
/* Maximum table size that we'll use */
#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 */
struct grpc_chttp2_hptbl {
static uint32_t entries_for_bytes(uint32_t bytes) {
return (bytes + GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD - 1) /
GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD;
}
static constexpr uint32_t kInitialCapacity =
(GRPC_CHTTP2_INITIAL_HPACK_TABLE_SIZE + GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD -
1) /
GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD;
grpc_chttp2_hptbl() {
GPR_DEBUG_ASSERT(!ents);
constexpr uint32_t AllocSize = sizeof(*ents) * kInitialCapacity;
ents = static_cast<grpc_mdelem*>(gpr_malloc(AllocSize));
memset(ents, 0, AllocSize);
}
namespace grpc_core {
/* the first used entry in ents */
uint32_t first_ent = 0;
/* how many entries are in the table */
uint32_t num_ents = 0;
/* the amount of memory used by the table, according to the hpack algorithm */
uint32_t mem_used = 0;
/* the max memory allowed to be used by the table, according to the hpack
algorithm */
uint32_t max_bytes = GRPC_CHTTP2_INITIAL_HPACK_TABLE_SIZE;
/* the currently agreed size of the table, according to the hpack algorithm */
uint32_t current_table_bytes = GRPC_CHTTP2_INITIAL_HPACK_TABLE_SIZE;
/* Maximum number of entries we could possibly fit in the table, given defined
overheads */
uint32_t max_entries = kInitialCapacity;
/* Number of entries allocated in ents */
uint32_t cap_entries = kInitialCapacity;
/* 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 = nullptr;
};
namespace hpack_table_detail {
// Per entry overhead bytes as per the spec
static constexpr uint32_t kEntryOverhead = 32;
// Initial table size as per the spec
static constexpr uint32_t kInitialTableSize = 4096;
static constexpr uint32_t EntriesForBytes(uint32_t bytes) noexcept {
return (bytes + kEntryOverhead - 1) / kEntryOverhead;
}
static constexpr uint32_t kInlineEntries = EntriesForBytes(kInitialTableSize);
} // namespace hpack_table_detail
// HPACK header table
class HPackTable {
public:
// last index in the static table
static constexpr uint32_t kLastStaticEntry = 61;
// Initial table size as per the spec
static constexpr uint32_t kInitialTableSize =
hpack_table_detail::kInitialTableSize;
// Per entry overhead bytes as per the spec
static constexpr uint32_t kEntryOverhead = hpack_table_detail::kEntryOverhead;
HPackTable();
~HPackTable();
HPackTable(const HPackTable&);
HPackTable& operator=(const HPackTable&);
void SetMaxBytes(uint32_t max_bytes);
grpc_error_handle SetCurrentTableSize(uint32_t bytes);
// Lookup, but don't ref.
grpc_mdelem Peek(uint32_t index) const { return Lookup<false>(index); }
// Lookup, taking a ref if found.
grpc_mdelem Fetch(uint32_t index) const { return Lookup<true>(index); }
// add a table entry to the index
grpc_error_handle Add(grpc_mdelem md) GRPC_MUST_USE_RESULT;
void grpc_chttp2_hptbl_destroy(grpc_chttp2_hptbl* tbl);
void grpc_chttp2_hptbl_set_max_bytes(grpc_chttp2_hptbl* tbl,
uint32_t max_bytes);
grpc_error_handle grpc_chttp2_hptbl_set_current_table_size(
grpc_chttp2_hptbl* tbl, uint32_t bytes);
/* lookup a table entry based on its hpack index */
grpc_mdelem grpc_chttp2_hptbl_lookup_dynamic_index(const grpc_chttp2_hptbl* tbl,
uint32_t tbl_index);
grpc_mdelem grpc_chttp2_hptbl_lookup_ref_dynamic_index(
const grpc_chttp2_hptbl* tbl, uint32_t tbl_index);
template <bool take_ref = false>
inline grpc_mdelem grpc_chttp2_hptbl_lookup(const grpc_chttp2_hptbl* tbl,
uint32_t index) {
/* Static table comes first, just return an entry from it.
NB: This imposes the constraint that the first
GRPC_CHTTP2_LAST_STATIC_ENTRY entries in the core static metadata table
must follow the hpack standard. If that changes, we *must* not rely on
reading the core static metadata table here; at that point we'd need our
own singleton static metadata in the correct order. */
if (index <= GRPC_CHTTP2_LAST_STATIC_ENTRY) {
return grpc_static_mdelem_manifested()[index - 1];
} else {
if (take_ref) {
return grpc_chttp2_hptbl_lookup_ref_dynamic_index(tbl, index);
// Current entry count in the table.
uint32_t num_entries() const { return num_entries_; }
private:
using EntriesVec =
absl::InlinedVector<grpc_mdelem, hpack_table_detail::kInlineEntries>;
/* lookup a table entry based on its hpack index */
template <bool take_ref>
grpc_mdelem Lookup(uint32_t index) const {
// Static table comes first, just return an entry from it.
// NB: This imposes the constraint that the first
// GRPC_CHTTP2_LAST_STATIC_ENTRY entries in the core static metadata table
// must follow the hpack standard. If that changes, we *must* not rely on
// reading the core static metadata table here; at that point we'd need our
// own singleton static metadata in the correct order.
if (index <= kLastStaticEntry) {
return grpc_static_mdelem_manifested()[index - 1];
} else {
return grpc_chttp2_hptbl_lookup_dynamic_index(tbl, index);
return LookupDynamic<take_ref>(index);
}
}
}
/* add a table entry to the index */
grpc_error_handle grpc_chttp2_hptbl_add(grpc_chttp2_hptbl* tbl,
grpc_mdelem md) GRPC_MUST_USE_RESULT;
template <bool take_ref>
grpc_mdelem LookupDynamic(uint32_t index) const {
// Not static - find the value in the list of valid entries
const uint32_t tbl_index = index - (kLastStaticEntry + 1);
if (tbl_index < num_entries_) {
uint32_t offset =
(num_entries_ - 1u - tbl_index + first_entry_) % entries_.size();
grpc_mdelem md = entries_[offset];
if (take_ref) {
GRPC_MDELEM_REF(md);
}
return md;
}
// Invalid entry: return error
return GRPC_MDNULL;
}
void EvictOne();
void Rebuild(uint32_t new_cap);
// The first used entry in ents.
uint32_t first_entry_ = 0;
// How many entries are in the table.
uint32_t num_entries_ = 0;
// The amount of memory used by the table, according to the hpack algorithm
uint32_t mem_used_ = 0;
// The max memory allowed to be used by the table, according to the hpack
// algorithm.
uint32_t max_bytes_ = kInitialTableSize;
// The currently agreed size of the table, according to the hpack algorithm.
uint32_t current_table_bytes_ = kInitialTableSize;
// Maximum number of entries we could possibly fit in the table, given defined
// overheads.
uint32_t max_entries_ = hpack_table_detail::kInlineEntries;
// HPack table entries
EntriesVec entries_;
};
} // namespace grpc_core
size_t grpc_chttp2_get_size_in_hpack_table(grpc_mdelem elem,
bool use_true_binary_metadata);
@ -130,19 +143,10 @@ inline uintptr_t grpc_chttp2_get_static_hpack_table_index(grpc_mdelem md) {
uintptr_t index =
reinterpret_cast<grpc_core::StaticMetadata*>(GRPC_MDELEM_DATA(md)) -
grpc_static_mdelem_table();
if (index < GRPC_CHTTP2_LAST_STATIC_ENTRY) {
if (index < grpc_core::HPackTable::kLastStaticEntry) {
return index + 1; // Hpack static metadata element indices start at 1
}
return 0;
}
/* 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 */
struct grpc_chttp2_hptbl_find_result {
uint32_t index;
int has_value;
};
grpc_chttp2_hptbl_find_result grpc_chttp2_hptbl_find(
const grpc_chttp2_hptbl* tbl, grpc_mdelem md);
#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_TABLE_H */

@ -767,8 +767,7 @@ static grpc_error_handle init_settings_frame_parser(grpc_chttp2_transport* t) {
if (t->incoming_frame_flags & GRPC_CHTTP2_FLAG_ACK) {
memcpy(t->settings[GRPC_ACKED_SETTINGS], t->settings[GRPC_SENT_SETTINGS],
GRPC_CHTTP2_NUM_SETTINGS * sizeof(uint32_t));
grpc_chttp2_hptbl_set_max_bytes(
t->hpack_parser.hpack_table(),
t->hpack_parser.hpack_table()->SetMaxBytes(
t->settings[GRPC_ACKED_SETTINGS]
[GRPC_CHTTP2_SETTINGS_HEADER_TABLE_SIZE]);
t->sent_local_settings = false;

@ -45,7 +45,8 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
grpc_core::HPackParser parser;
parser.BeginFrame(onhdr, grpc_core::HPackParser::Boundary::None,
grpc_core::HPackParser::Priority::None);
GRPC_ERROR_UNREF(parser.Parse(grpc_slice_from_static_buffer(data, size)));
GRPC_ERROR_UNREF(
parser.Parse(grpc_slice_from_static_buffer(data, size), true));
}
grpc_shutdown();
return 0;

@ -65,7 +65,11 @@ static void test_vector(grpc_core::HPackParser* parser,
for (i = 0; i < nslices; i++) {
grpc_core::ExecCtx exec_ctx;
GPR_ASSERT(parser->Parse(slices[i]) == GRPC_ERROR_NONE);
auto err = parser->Parse(slices[i], i == nslices - 1);
if (err != GRPC_ERROR_NONE) {
gpr_log(GPR_ERROR, "Unexpected parse error: %s", grpc_error_string(err));
abort();
}
}
for (i = 0; i < nslices; i++) {
@ -158,8 +162,8 @@ static void test_vectors(grpc_slice_split_mode mode) {
{
grpc_core::HPackParser parser;
grpc_chttp2_hptbl_set_max_bytes(parser.hpack_table(), 256);
grpc_chttp2_hptbl_set_current_table_size(parser.hpack_table(), 256);
parser.hpack_table()->SetMaxBytes(256);
parser.hpack_table()->SetCurrentTableSize(256);
/* D.5.1 */
test_vector(&parser, mode,
"4803 3330 3258 0770 7269 7661 7465 611d"
@ -199,8 +203,8 @@ static void test_vectors(grpc_slice_split_mode mode) {
{
grpc_core::HPackParser parser;
grpc_chttp2_hptbl_set_max_bytes(parser.hpack_table(), 256);
grpc_chttp2_hptbl_set_current_table_size(parser.hpack_table(), 256);
parser.hpack_table()->SetMaxBytes(256);
parser.hpack_table()->SetCurrentTableSize(256);
/* D.6.1 */
test_vector(&parser, mode,
"4882 6402 5885 aec3 771a 4b61 96d0 7abe"
@ -234,6 +238,18 @@ static void test_vectors(grpc_slice_split_mode mode) {
"set-cookie",
"foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1")});
}
{
grpc_core::HPackParser parser;
// Binary metadata: created using:
// tools/codegen/core/gen_header_frame.py
// --compression inc --no_framing --hex
// < test/core/transport/chttp2/binary-metadata.headers
test_vector(&parser, mode,
"40 09 61 2e 62 2e 63 2d 62 69 6e 0c 62 32 31 6e 4d 6a 41 79 "
"4d 51 3d 3d",
{std::make_pair("a.b.c-bin", "omg2021")});
}
}
int main(int argc, char** argv) {

@ -36,21 +36,23 @@
#define LOG_TEST(x) gpr_log(GPR_INFO, "%s", x)
static void assert_str(const grpc_chttp2_hptbl* /*tbl*/, grpc_slice mdstr,
using grpc_core::HPackTable;
static void assert_str(const HPackTable* /*tbl*/, grpc_slice mdstr,
const char* str) {
GPR_ASSERT(grpc_slice_str_cmp(mdstr, str) == 0);
}
static void assert_index(const grpc_chttp2_hptbl* tbl, uint32_t idx,
const char* key, const char* value) {
grpc_mdelem md = grpc_chttp2_hptbl_lookup(tbl, idx);
static void assert_index(const HPackTable* tbl, uint32_t idx, const char* key,
const char* value) {
grpc_mdelem md = tbl->Peek(idx);
assert_str(tbl, GRPC_MDKEY(md), key);
assert_str(tbl, GRPC_MDVALUE(md), value);
}
static void test_static_lookup(void) {
grpc_core::ExecCtx exec_ctx;
grpc_chttp2_hptbl tbl;
HPackTable tbl;
LOG_TEST("test_static_lookup");
assert_index(&tbl, 1, ":authority", "");
@ -114,12 +116,10 @@ static void test_static_lookup(void) {
assert_index(&tbl, 59, "vary", "");
assert_index(&tbl, 60, "via", "");
assert_index(&tbl, 61, "www-authenticate", "");
grpc_chttp2_hptbl_destroy(&tbl);
}
static void test_many_additions(void) {
grpc_chttp2_hptbl tbl;
HPackTable tbl;
int i;
LOG_TEST("test_many_additions");
@ -132,135 +132,17 @@ static void test_many_additions(void) {
std::string value = absl::StrCat("VALUE:", i);
elem = grpc_mdelem_from_slices(grpc_slice_from_cpp_string(key),
grpc_slice_from_cpp_string(value));
GPR_ASSERT(grpc_chttp2_hptbl_add(&tbl, elem) == GRPC_ERROR_NONE);
GPR_ASSERT(tbl.Add(elem) == GRPC_ERROR_NONE);
GRPC_MDELEM_UNREF(elem);
assert_index(&tbl, 1 + GRPC_CHTTP2_LAST_STATIC_ENTRY, key.c_str(),
assert_index(&tbl, 1 + HPackTable::kLastStaticEntry, key.c_str(),
value.c_str());
if (i) {
std::string key = absl::StrCat("K:", i - 1);
std::string value = absl::StrCat("VALUE:", i - 1);
assert_index(&tbl, 2 + GRPC_CHTTP2_LAST_STATIC_ENTRY, key.c_str(),
assert_index(&tbl, 2 + HPackTable::kLastStaticEntry, key.c_str(),
value.c_str());
}
}
grpc_chttp2_hptbl_destroy(&tbl);
}
static grpc_chttp2_hptbl_find_result find_simple(grpc_chttp2_hptbl* tbl,
const char* key,
const char* value) {
grpc_core::ExecCtx exec_ctx;
grpc_mdelem md = grpc_mdelem_from_slices(
grpc_slice_from_copied_string(key), grpc_slice_from_copied_string(value));
grpc_chttp2_hptbl_find_result r = grpc_chttp2_hptbl_find(tbl, md);
GRPC_MDELEM_UNREF(md);
return r;
}
static void test_find(void) {
grpc_core::ExecCtx exec_ctx;
grpc_chttp2_hptbl tbl;
uint32_t i;
char buffer[32];
grpc_mdelem elem;
grpc_chttp2_hptbl_find_result r;
LOG_TEST("test_find");
elem = grpc_mdelem_from_slices(grpc_slice_from_static_string("abc"),
grpc_slice_from_static_string("xyz"));
GPR_ASSERT(grpc_chttp2_hptbl_add(&tbl, elem) == GRPC_ERROR_NONE);
GRPC_MDELEM_UNREF(elem);
elem = grpc_mdelem_from_slices(grpc_slice_from_static_string("abc"),
grpc_slice_from_static_string("123"));
GPR_ASSERT(grpc_chttp2_hptbl_add(&tbl, elem) == GRPC_ERROR_NONE);
GRPC_MDELEM_UNREF(elem);
elem = grpc_mdelem_from_slices(grpc_slice_from_static_string("x"),
grpc_slice_from_static_string("1"));
GPR_ASSERT(grpc_chttp2_hptbl_add(&tbl, elem) == GRPC_ERROR_NONE);
GRPC_MDELEM_UNREF(elem);
r = find_simple(&tbl, "abc", "123");
GPR_ASSERT(r.index == 2 + GRPC_CHTTP2_LAST_STATIC_ENTRY);
GPR_ASSERT(r.has_value == 1);
r = find_simple(&tbl, "abc", "xyz");
GPR_ASSERT(r.index == 3 + GRPC_CHTTP2_LAST_STATIC_ENTRY);
GPR_ASSERT(r.has_value == 1);
r = find_simple(&tbl, "x", "1");
GPR_ASSERT(r.index == 1 + GRPC_CHTTP2_LAST_STATIC_ENTRY);
GPR_ASSERT(r.has_value == 1);
r = find_simple(&tbl, "x", "2");
GPR_ASSERT(r.index == 1 + GRPC_CHTTP2_LAST_STATIC_ENTRY);
GPR_ASSERT(r.has_value == 0);
r = find_simple(&tbl, "vary", "some-vary-arg");
GPR_ASSERT(r.index == 59);
GPR_ASSERT(r.has_value == 0);
r = find_simple(&tbl, "accept-encoding", "gzip, deflate");
GPR_ASSERT(r.index == 16);
GPR_ASSERT(r.has_value == 1);
r = find_simple(&tbl, "accept-encoding", "gzip");
GPR_ASSERT(r.index == 16);
GPR_ASSERT(r.has_value == 0);
r = find_simple(&tbl, ":method", "GET");
GPR_ASSERT(r.index == 2);
GPR_ASSERT(r.has_value == 1);
r = find_simple(&tbl, ":method", "POST");
GPR_ASSERT(r.index == 3);
GPR_ASSERT(r.has_value == 1);
r = find_simple(&tbl, ":method", "PUT");
GPR_ASSERT(r.index == 2 || r.index == 3);
GPR_ASSERT(r.has_value == 0);
r = find_simple(&tbl, "this-does-not-exist", "");
GPR_ASSERT(r.index == 0);
GPR_ASSERT(r.has_value == 0);
/* overflow the string buffer, check find still works */
for (i = 0; i < 10000; i++) {
int64_ttoa(i, buffer);
elem = grpc_mdelem_from_slices(grpc_slice_from_static_string("test"),
grpc_slice_from_copied_string(buffer));
GPR_ASSERT(grpc_chttp2_hptbl_add(&tbl, elem) == GRPC_ERROR_NONE);
GRPC_MDELEM_UNREF(elem);
}
r = find_simple(&tbl, "abc", "123");
GPR_ASSERT(r.index == 0);
GPR_ASSERT(r.has_value == 0);
r = find_simple(&tbl, "test", "9999");
GPR_ASSERT(r.index == 1 + GRPC_CHTTP2_LAST_STATIC_ENTRY);
GPR_ASSERT(r.has_value == 1);
r = find_simple(&tbl, "test", "9998");
GPR_ASSERT(r.index == 2 + GRPC_CHTTP2_LAST_STATIC_ENTRY);
GPR_ASSERT(r.has_value == 1);
for (i = 0; i < tbl.num_ents; i++) {
uint32_t expect = 9999 - i;
int64_ttoa(expect, buffer);
r = find_simple(&tbl, "test", buffer);
GPR_ASSERT(r.index == i + 1 + GRPC_CHTTP2_LAST_STATIC_ENTRY);
GPR_ASSERT(r.has_value == 1);
}
r = find_simple(&tbl, "test", "10000");
GPR_ASSERT(r.index != 0);
GPR_ASSERT(r.has_value == 0);
grpc_chttp2_hptbl_destroy(&tbl);
}
int main(int argc, char** argv) {
@ -268,7 +150,6 @@ int main(int argc, char** argv) {
grpc_init();
test_static_lookup();
test_many_additions();
test_find();
grpc_shutdown();
return 0;
}

@ -461,11 +461,11 @@ static void BM_HpackParserParseHeader(benchmark::State& state) {
grpc_core::HPackParser::Boundary::None,
grpc_core::HPackParser::Priority::None);
for (auto slice : init_slices) {
GPR_ASSERT(GRPC_ERROR_NONE == p.Parse(slice));
GPR_ASSERT(GRPC_ERROR_NONE == p.Parse(slice, false));
}
while (state.KeepRunning()) {
for (auto slice : benchmark_slices) {
GPR_ASSERT(GRPC_ERROR_NONE == p.Parse(slice));
GPR_ASSERT(GRPC_ERROR_NONE == p.Parse(slice, false));
}
grpc_core::ExecCtx::Get()->Flush();
// Recreate arena every 4k iterations to avoid oom

@ -1775,10 +1775,12 @@ src/core/lib/gprpp/global_config_generic.h \
src/core/lib/gprpp/host_port.cc \
src/core/lib/gprpp/host_port.h \
src/core/lib/gprpp/manual_constructor.h \
src/core/lib/gprpp/match.h \
src/core/lib/gprpp/memory.h \
src/core/lib/gprpp/mpscq.cc \
src/core/lib/gprpp/mpscq.h \
src/core/lib/gprpp/orphanable.h \
src/core/lib/gprpp/overload.h \
src/core/lib/gprpp/ref_counted.h \
src/core/lib/gprpp/ref_counted_ptr.h \
src/core/lib/gprpp/stat.h \

@ -1614,10 +1614,12 @@ src/core/lib/gprpp/global_config_generic.h \
src/core/lib/gprpp/host_port.cc \
src/core/lib/gprpp/host_port.h \
src/core/lib/gprpp/manual_constructor.h \
src/core/lib/gprpp/match.h \
src/core/lib/gprpp/memory.h \
src/core/lib/gprpp/mpscq.cc \
src/core/lib/gprpp/mpscq.h \
src/core/lib/gprpp/orphanable.h \
src/core/lib/gprpp/overload.h \
src/core/lib/gprpp/ref_counted.h \
src/core/lib/gprpp/ref_counted_ptr.h \
src/core/lib/gprpp/stat.h \

Loading…
Cancel
Save