|
|
|
@ -29,25 +29,10 @@ |
|
|
|
|
|
|
|
|
|
namespace grpc_core { |
|
|
|
|
|
|
|
|
|
// Top level interface for parsing a sequence of header, continuation frames.
|
|
|
|
|
class HPackParser { |
|
|
|
|
public: |
|
|
|
|
// 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 |
|
|
|
|
}; |
|
|
|
|
enum class Boundary { None, EndOfHeaders, EndOfStream }; |
|
|
|
|
enum class Priority { None, Included }; |
|
|
|
|
|
|
|
|
|
// User specified structure called for each received header.
|
|
|
|
|
using Sink = std::function<grpc_error_handle(grpc_mdelem)>; |
|
|
|
@ -55,53 +40,189 @@ 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); } |
|
|
|
|
// Parse one slice worth of data
|
|
|
|
|
grpc_error_handle Parse(const grpc_slice& slice, bool is_last); |
|
|
|
|
// Reset state ready for the next BeginFrame
|
|
|
|
|
grpc_error_handle Parse(const grpc_slice& slice); |
|
|
|
|
void FinishFrame(); |
|
|
|
|
|
|
|
|
|
// Retrieve the associated hpack table (for tests, debugging)
|
|
|
|
|
HPackTable* hpack_table() { return &table_; } |
|
|
|
|
// Is the current frame a boundary of some sort
|
|
|
|
|
grpc_chttp2_hptbl* hpack_table() { return &table_; } |
|
|
|
|
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: |
|
|
|
|
// Helper classes: see implementation
|
|
|
|
|
class Parser; |
|
|
|
|
class Input; |
|
|
|
|
class String; |
|
|
|
|
enum class BinaryState { |
|
|
|
|
kNotBinary, |
|
|
|
|
kBinaryBegin, |
|
|
|
|
kBase64Byte0, |
|
|
|
|
kBase64Byte1, |
|
|
|
|
kBase64Byte2, |
|
|
|
|
kBase64Byte3, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
grpc_error_handle ParseInput(Input input, bool is_last); |
|
|
|
|
bool ParseInputInner(Input* input); |
|
|
|
|
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); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
// Callback per header received
|
|
|
|
|
Sink sink_; |
|
|
|
|
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, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
// 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_; |
|
|
|
|
// Buffer priority
|
|
|
|
|
// TODO(ctiller): see if we can move this argument to Parse, and avoid
|
|
|
|
|
// buffering.
|
|
|
|
|
Priority priority_; |
|
|
|
|
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); |
|
|
|
|
|
|
|
|
|
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); |
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
Boundary boundary_; |
|
|
|
|
uint32_t base64_buffer_; |
|
|
|
|
|
|
|
|
|
// hpack table
|
|
|
|
|
HPackTable table_; |
|
|
|
|
grpc_chttp2_hptbl table_; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
} // namespace grpc_core
|
|
|
|
|