1 // 2 // 3 // Copyright 2015 gRPC authors. 4 // 5 // Licensed under the Apache License, Version 2.0 (the "License"); 6 // you may not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, software 12 // distributed under the License is distributed on an "AS IS" BASIS, 13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 // See the License for the specific language governing permissions and 15 // limitations under the License. 16 // 17 // 18 19 #ifndef GRPC_SRC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_PARSER_H 20 #define GRPC_SRC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_PARSER_H 21 22 #include <grpc/support/port_platform.h> 23 24 #include <stddef.h> 25 #include <stdint.h> 26 27 #include <string> 28 #include <utility> 29 #include <vector> 30 31 #include "absl/strings/str_cat.h" 32 #include "absl/strings/string_view.h" 33 #include "absl/types/optional.h" 34 #include "absl/types/span.h" 35 #include "absl/types/variant.h" 36 37 #include <grpc/slice.h> 38 39 #include "src/core/ext/transport/chttp2/transport/frame.h" 40 #include "src/core/ext/transport/chttp2/transport/hpack_parse_result.h" 41 #include "src/core/ext/transport/chttp2/transport/hpack_parser_table.h" 42 #include "src/core/lib/backoff/random_early_detection.h" 43 #include "src/core/lib/iomgr/error.h" 44 #include "src/core/lib/slice/slice.h" 45 #include "src/core/lib/slice/slice_refcount.h" 46 #include "src/core/lib/transport/metadata_batch.h" 47 48 // IWYU pragma: no_include <type_traits> 49 50 namespace grpc_core { 51 52 // Top level interface for parsing a sequence of header, continuation frames. 53 class HPackParser { 54 public: 55 // What kind of stream boundary is provided by this frame? 56 enum class Boundary : uint8_t { 57 // More continuations are expected 58 None, 59 // This marks the end of headers, so data frames should follow 60 EndOfHeaders, 61 // This marks the end of headers *and* the end of the stream 62 EndOfStream 63 }; 64 // What kind of priority is represented in the next frame 65 enum class Priority : uint8_t { 66 // No priority field 67 None, 68 // Yes there's a priority field 69 Included 70 }; 71 // Details about a frame we only need to know for logging 72 struct LogInfo { 73 // The stream ID 74 uint32_t stream_id; 75 // Headers or trailers? 76 enum Type : uint8_t { 77 kHeaders, 78 kTrailers, 79 kDontKnow, 80 }; 81 Type type; 82 // Client or server? 83 bool is_client; 84 }; 85 86 HPackParser(); 87 ~HPackParser(); 88 89 // Non-copyable/movable 90 HPackParser(const HPackParser&) = delete; 91 HPackParser& operator=(const HPackParser&) = delete; 92 93 // Begin parsing a new frame 94 // Sink receives each parsed header, 95 void BeginFrame(grpc_metadata_batch* metadata_buffer, 96 uint32_t metadata_size_soft_limit, 97 uint32_t metadata_size_hard_limit, Boundary boundary, 98 Priority priority, LogInfo log_info); 99 // Start throwing away any received headers after parsing them. StopBufferingFrame()100 void StopBufferingFrame() { metadata_buffer_ = nullptr; } 101 // Parse one slice worth of data 102 grpc_error_handle Parse(const grpc_slice& slice, bool is_last); 103 // Reset state ready for the next BeginFrame 104 void FinishFrame(); 105 106 // Retrieve the associated hpack table (for tests, debugging) hpack_table()107 HPackTable* hpack_table() { return &state_.hpack_table; } 108 // Is the current frame a boundary of some sort is_boundary()109 bool is_boundary() const { return boundary_ != Boundary::None; } 110 // Is the current frame the end of a stream is_eof()111 bool is_eof() const { return boundary_ == Boundary::EndOfStream; } 112 113 // How many bytes are buffered (for tests to assert on) buffered_bytes()114 size_t buffered_bytes() const { return unparsed_bytes_.size(); } 115 116 private: 117 // Helper classes: see implementation 118 class Parser; 119 class Input; 120 121 // Helper to parse a string and turn it into a slice with appropriate memory 122 // management characteristics 123 class String { 124 public: 125 // StringResult carries both a HpackParseStatus and the parsed string 126 struct StringResult; 127 String()128 String() : value_(absl::Span<const uint8_t>()) {} 129 String(const String&) = delete; 130 String& operator=(const String&) = delete; String(String && other)131 String(String&& other) noexcept : value_(std::move(other.value_)) { 132 other.value_ = absl::Span<const uint8_t>(); 133 } 134 String& operator=(String&& other) noexcept { 135 value_ = std::move(other.value_); 136 other.value_ = absl::Span<const uint8_t>(); 137 return *this; 138 } 139 140 // Take the value and leave this empty 141 Slice Take(); 142 143 // Return a reference to the value as a string view 144 absl::string_view string_view() const; 145 146 // Parse a non-binary string 147 static StringResult Parse(Input* input, bool is_huff, size_t length); 148 149 // Parse a binary string 150 static StringResult ParseBinary(Input* input, bool is_huff, size_t length); 151 152 private: 153 void AppendBytes(const uint8_t* data, size_t length); String(std::vector<uint8_t> v)154 explicit String(std::vector<uint8_t> v) : value_(std::move(v)) {} String(absl::Span<const uint8_t> v)155 explicit String(absl::Span<const uint8_t> v) : value_(v) {} String(grpc_slice_refcount * r,const uint8_t * begin,const uint8_t * end)156 String(grpc_slice_refcount* r, const uint8_t* begin, const uint8_t* end) 157 : value_(Slice::FromRefcountAndBytes(r, begin, end)) {} 158 159 // Parse some huffman encoded bytes, using output(uint8_t b) to emit each 160 // decoded byte. 161 template <typename Out> 162 static HpackParseStatus ParseHuff(Input* input, uint32_t length, 163 Out output); 164 165 // Parse some uncompressed string bytes. 166 static StringResult ParseUncompressed(Input* input, uint32_t length, 167 uint32_t wire_size); 168 169 // Turn base64 encoded bytes into not base64 encoded bytes. 170 static StringResult Unbase64(String s); 171 172 // Main loop for Unbase64 173 static absl::optional<std::vector<uint8_t>> Unbase64Loop( 174 const uint8_t* cur, const uint8_t* end); 175 176 absl::variant<Slice, absl::Span<const uint8_t>, std::vector<uint8_t>> 177 value_; 178 }; 179 180 // Prefix for a string 181 struct StringPrefix { 182 // Number of bytes in input for string 183 uint32_t length; 184 // Is it huffman compressed 185 bool huff; 186 ToStringStringPrefix187 std::string ToString() const { 188 return absl::StrCat(length, " bytes ", 189 huff ? "huffman compressed" : "uncompressed"); 190 } 191 }; 192 193 // Current parse state 194 // ┌───┐ 195 // │Top│ 196 // └┬─┬┘ 197 // │┌▽────────────────┐ 198 // ││ParsingKeyLength │ 199 // │└┬───────────────┬┘ 200 // │┌▽─────────────┐┌▽──────────────┐ 201 // ││ParsingKeyBody││SkippingKeyBody│ 202 // │└┬─────────────┘└───┬───────────┘ 203 // ┌▽─▽────────────────┐┌▽──────────────────┐ 204 // │ParsingValueLength ││SkippingValueLength│ 205 // └┬─────────────────┬┘└┬──────────────────┘ 206 // ┌▽───────────────┐┌▽──▽─────────────┐ 207 // │ParsingValueBody││SkippingValueBody│ 208 // └────────────────┘└─────────────────┘ 209 enum class ParseState : uint8_t { 210 // Start of one opcode 211 kTop, 212 // Parsing a literal keys length 213 kParsingKeyLength, 214 // Parsing a literal key 215 kParsingKeyBody, 216 // Skipping a literal key 217 kSkippingKeyBody, 218 // Parsing a literal value length 219 kParsingValueLength, 220 // Parsing a literal value 221 kParsingValueBody, 222 // Reading a literal value length (so we can skip it) 223 kSkippingValueLength, 224 // Skipping a literal value 225 kSkippingValueBody, 226 }; 227 228 // Shared state for Parser instances between slices. 229 struct InterSliceState { 230 HPackTable hpack_table; 231 // Error so far for this frame (set by class Input) 232 HpackParseResult frame_error; 233 // Length of frame so far. 234 uint32_t frame_length = 0; 235 // Length of the string being parsed 236 uint32_t string_length; 237 // How many more dynamic table updates are allowed 238 uint8_t dynamic_table_updates_allowed; 239 // Current parse state 240 ParseState parse_state = ParseState::kTop; 241 // RED for overly large metadata sets 242 RandomEarlyDetection metadata_early_detection; 243 // Should the current header be added to the hpack table? 244 bool add_to_table; 245 // Is the string being parsed huffman compressed? 246 bool is_string_huff_compressed; 247 // Is the value being parsed binary? 248 bool is_binary_header; 249 absl::variant<const HPackTable::Memento*, Slice> key; 250 }; 251 252 grpc_error_handle ParseInput(Input input, bool is_last); 253 void ParseInputInner(Input* input); 254 GPR_ATTRIBUTE_NOINLINE 255 void HandleMetadataSoftSizeLimitExceeded(Input* input); 256 257 // Target metadata buffer 258 grpc_metadata_batch* metadata_buffer_ = nullptr; 259 260 // Bytes that could not be parsed last parsing round 261 std::vector<uint8_t> unparsed_bytes_; 262 // How many bytes would be needed before progress could be made? 263 size_t min_progress_size_ = 0; 264 // Buffer kind of boundary 265 // TODO(ctiller): see if we can move this argument to Parse, and avoid 266 // buffering. 267 Boundary boundary_; 268 // Buffer priority 269 // TODO(ctiller): see if we can move this argument to Parse, and avoid 270 // buffering. 271 Priority priority_; 272 // Information for logging 273 LogInfo log_info_; 274 InterSliceState state_; 275 }; 276 277 } // namespace grpc_core 278 279 // wraps grpc_chttp2_hpack_parser_parse to provide a frame level parser for 280 // the transport 281 grpc_error_handle grpc_chttp2_header_parser_parse(void* hpack_parser, 282 grpc_chttp2_transport* t, 283 grpc_chttp2_stream* s, 284 const grpc_slice& slice, 285 int is_last); 286 287 #endif // GRPC_SRC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_PARSER_H 288