HPACK Table --> C++ (#26851)

* Buffer HPACK parsing until the end of a header boundary

HTTP2 headers are sent in (potentially) many frames, but all must be
sent sequentially with no traffic intervening.

This was not clear when I wrote the HPACK parser, and still indeed quite
contentious on the HTTP2 mailing lists.

Now that matter is well settled (years ago!) take advantage of the fact
by delaying parsing until all bytes are available.

A future change will leverage this to avoid having to store and verify
partial parse state, completely eliminating indirect calls within the
parser.

* maybe fixes

* xx

* fix boundary detection

* clang-format

* Revert "xx"

This reverts commit 258d712ed3.

* fix tests

* add missed check

* fixes

* fix

* update tests

* fix benchmark

* properly unref

* optimize final slice refcounting

* cleanup bm_chttp2_hpack

* start

* new parser progress

* refinement

* get it compiling

* bug-fix

* build files

* clang-tidy

* fixes

* fixes

* fixes

* fix-leaks

* clang-tidy

* comments

* fix merge error

* Revert "Buffer HPACK parsing until the end of a header boundary (#26700)"

This reverts commit 8bab3e4bf4.

* streaming hpack parser start

* streaming parser

* clang-format

* Rework HPackTable into C++

* clang-tidy

* fix merge

* actually set the size of the entries array

* better
reviewable/pr26954/r1^2
Craig Tiller 4 years ago committed by GitHub
parent 8e0f8beb1f
commit 83bcb0cf2e
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. 3
      test/core/transport/chttp2/hpack_parser_fuzzer_test.cc
  16. 10
      test/core/transport/chttp2/hpack_parser_test.cc
  17. 141
      test/core/transport/chttp2/hpack_table_test.cc
  18. 4
      test/cpp/microbenchmarks/bm_chttp2_hpack.cc
  19. 2
      tools/doxygen/Doxyfile.c++.internal
  20. 2
      tools/doxygen/Doxyfile.core.internal

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

10
CMakeLists.txt generated

@ -2130,6 +2130,7 @@ target_link_libraries(grpc
absl::inlined_vector
absl::bind_front
absl::statusor
absl::variant
gpr
${_gRPC_SSL_LIBRARIES}
address_sorting
@ -2686,6 +2687,7 @@ target_link_libraries(grpc_unsecure
absl::inlined_vector
absl::bind_front
absl::statusor
absl::variant
gpr
address_sorting
)
@ -16172,7 +16174,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")
@ -16182,7 +16184,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")
@ -16192,7 +16194,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")
@ -16202,7 +16204,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")

@ -744,7 +744,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
@ -1515,6 +1517,7 @@ libs:
- absl/container:inlined_vector
- absl/functional:bind_front
- absl/status:statusor
- absl/types:variant
- gpr
- libssl
- address_sorting
@ -1781,7 +1784,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
@ -2197,6 +2202,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',
@ -557,9 +558,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',
@ -1221,9 +1224,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',
@ -933,10 +934,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',
@ -1809,9 +1812,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

@ -847,10 +847,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

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

2
package.xml generated

@ -827,10 +827,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,7 @@ 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);
GPR_ASSERT(parser->Parse(slices[i], i == nslices - 1) == GRPC_ERROR_NONE);
}
for (i = 0; i < nslices; i++) {
@ -158,8 +158,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 +199,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"

@ -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

@ -1780,10 +1780,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 \

@ -1619,10 +1619,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