grpc 1.55.0 → 1.55.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Makefile +4 -2
- data/src/core/ext/filters/client_channel/client_channel.cc +4 -6
- data/src/core/ext/filters/client_channel/client_channel.h +2 -0
- data/src/core/ext/transport/chttp2/transport/hpack_parse_result.cc +176 -0
- data/src/core/ext/transport/chttp2/transport/hpack_parse_result.h +325 -0
- data/src/core/ext/transport/chttp2/transport/hpack_parser.cc +567 -543
- data/src/core/ext/transport/chttp2/transport/hpack_parser.h +150 -9
- data/src/core/ext/transport/chttp2/transport/hpack_parser_table.cc +46 -32
- data/src/core/ext/transport/chttp2/transport/hpack_parser_table.h +18 -5
- data/src/core/ext/transport/chttp2/transport/parsing.cc +12 -12
- data/src/core/lib/backoff/random_early_detection.h +5 -0
- data/src/core/lib/event_engine/posix_engine/posix_engine.cc +30 -17
- data/src/core/lib/event_engine/posix_engine/posix_engine.h +1 -0
- data/src/core/lib/event_engine/posix_engine/posix_engine_listener.cc +29 -0
- data/src/core/lib/event_engine/posix_engine/posix_engine_listener.h +3 -0
- data/src/core/lib/iomgr/tcp_server_posix.cc +39 -14
- data/src/core/lib/iomgr/tcp_server_utils_posix.h +12 -0
- data/src/core/lib/iomgr/tcp_server_utils_posix_common.cc +21 -0
- data/src/core/lib/surface/validate_metadata.cc +37 -22
- data/src/core/lib/surface/validate_metadata.h +13 -3
- data/src/ruby/lib/grpc/version.rb +1 -1
- metadata +5 -3
@@ -24,7 +24,6 @@
|
|
24
24
|
#include <stdlib.h>
|
25
25
|
|
26
26
|
#include <algorithm>
|
27
|
-
#include <initializer_list>
|
28
27
|
#include <string>
|
29
28
|
#include <utility>
|
30
29
|
|
@@ -32,21 +31,22 @@
|
|
32
31
|
#include "absl/status/status.h"
|
33
32
|
#include "absl/strings/match.h"
|
34
33
|
#include "absl/strings/str_cat.h"
|
35
|
-
#include "absl/strings/str_format.h"
|
36
34
|
#include "absl/strings/string_view.h"
|
37
35
|
#include "absl/types/optional.h"
|
38
36
|
#include "absl/types/span.h"
|
39
37
|
#include "absl/types/variant.h"
|
40
38
|
|
39
|
+
#include <grpc/slice.h>
|
41
40
|
#include <grpc/support/log.h>
|
42
41
|
|
43
42
|
#include "src/core/ext/transport/chttp2/transport/decode_huff.h"
|
44
43
|
#include "src/core/ext/transport/chttp2/transport/hpack_constants.h"
|
44
|
+
#include "src/core/ext/transport/chttp2/transport/hpack_parse_result.h"
|
45
|
+
#include "src/core/ext/transport/chttp2/transport/hpack_parser_table.h"
|
45
46
|
#include "src/core/lib/debug/stats.h"
|
46
47
|
#include "src/core/lib/debug/stats_data.h"
|
47
48
|
#include "src/core/lib/debug/trace.h"
|
48
|
-
#include "src/core/lib/gprpp/
|
49
|
-
#include "src/core/lib/gprpp/status_helper.h"
|
49
|
+
#include "src/core/lib/gprpp/match.h"
|
50
50
|
#include "src/core/lib/slice/slice.h"
|
51
51
|
#include "src/core/lib/slice/slice_refcount.h"
|
52
52
|
#include "src/core/lib/surface/validate_metadata.h"
|
@@ -82,39 +82,6 @@ struct Base64InverseTable {
|
|
82
82
|
|
83
83
|
constexpr Base64InverseTable kBase64InverseTable;
|
84
84
|
|
85
|
-
absl::Status EnsureStreamError(absl::Status error) {
|
86
|
-
if (error.ok()) return error;
|
87
|
-
return grpc_error_set_int(std::move(error), StatusIntProperty::kStreamId, 0);
|
88
|
-
}
|
89
|
-
|
90
|
-
bool IsStreamError(const absl::Status& status) {
|
91
|
-
intptr_t stream_id;
|
92
|
-
return grpc_error_get_int(status, StatusIntProperty::kStreamId, &stream_id);
|
93
|
-
}
|
94
|
-
|
95
|
-
class MetadataSizeLimitExceededEncoder {
|
96
|
-
public:
|
97
|
-
explicit MetadataSizeLimitExceededEncoder(std::string& summary)
|
98
|
-
: summary_(summary) {}
|
99
|
-
|
100
|
-
void Encode(const Slice& key, const Slice& value) {
|
101
|
-
AddToSummary(key.as_string_view(), value.size());
|
102
|
-
}
|
103
|
-
|
104
|
-
template <typename Key, typename Value>
|
105
|
-
void Encode(Key, const Value& value) {
|
106
|
-
AddToSummary(Key::key(), EncodedSizeOfKey(Key(), value));
|
107
|
-
}
|
108
|
-
|
109
|
-
private:
|
110
|
-
void AddToSummary(absl::string_view key,
|
111
|
-
size_t value_length) GPR_ATTRIBUTE_NOINLINE {
|
112
|
-
absl::StrAppend(&summary_, " ", key, ":",
|
113
|
-
hpack_constants::SizeForEntry(key.size(), value_length),
|
114
|
-
"B");
|
115
|
-
}
|
116
|
-
std::string& summary_;
|
117
|
-
};
|
118
85
|
} // namespace
|
119
86
|
|
120
87
|
// Input tracks the current byte through the input data and provides it
|
@@ -122,11 +89,12 @@ class MetadataSizeLimitExceededEncoder {
|
|
122
89
|
class HPackParser::Input {
|
123
90
|
public:
|
124
91
|
Input(grpc_slice_refcount* current_slice_refcount, const uint8_t* begin,
|
125
|
-
const uint8_t* end)
|
92
|
+
const uint8_t* end, HpackParseResult& error)
|
126
93
|
: current_slice_refcount_(current_slice_refcount),
|
127
94
|
begin_(begin),
|
128
95
|
end_(end),
|
129
|
-
frontier_(begin)
|
96
|
+
frontier_(begin),
|
97
|
+
error_(error) {}
|
130
98
|
|
131
99
|
// If input is backed by a slice, retrieve its refcount. If not, return
|
132
100
|
// nullptr.
|
@@ -156,7 +124,7 @@ class HPackParser::Input {
|
|
156
124
|
// of stream
|
157
125
|
absl::optional<uint8_t> Next() {
|
158
126
|
if (end_of_stream()) {
|
159
|
-
UnexpectedEOF();
|
127
|
+
UnexpectedEOF(/*min_progress_size=*/1);
|
160
128
|
return absl::optional<uint8_t>();
|
161
129
|
}
|
162
130
|
return *begin_++;
|
@@ -202,9 +170,16 @@ class HPackParser::Input {
|
|
202
170
|
|
203
171
|
// Spec weirdness: we can add an infinite stream of 0x80 at the end of a
|
204
172
|
// varint and still end up with a correctly encoded varint.
|
173
|
+
// We allow up to 16 just for kicks, but any more and we'll assume the
|
174
|
+
// sender is being malicious.
|
175
|
+
int num_redundant_0x80 = 0;
|
205
176
|
do {
|
206
177
|
cur = Next();
|
207
178
|
if (!cur.has_value()) return {};
|
179
|
+
++num_redundant_0x80;
|
180
|
+
if (num_redundant_0x80 == 16) {
|
181
|
+
return ParseVarintMaliciousEncoding();
|
182
|
+
}
|
208
183
|
} while (*cur == 0x80);
|
209
184
|
|
210
185
|
// BUT... the last byte needs to be 0x00 or we'll overflow dramatically!
|
@@ -212,14 +187,6 @@ class HPackParser::Input {
|
|
212
187
|
return ParseVarintOutOfRange(value, *cur);
|
213
188
|
}
|
214
189
|
|
215
|
-
// Prefix for a string
|
216
|
-
struct StringPrefix {
|
217
|
-
// Number of bytes in input for string
|
218
|
-
uint32_t length;
|
219
|
-
// Is it huffman compressed
|
220
|
-
bool huff;
|
221
|
-
};
|
222
|
-
|
223
190
|
// Parse a string prefix
|
224
191
|
absl::optional<StringPrefix> ParseStringPrefix() {
|
225
192
|
auto cur = Next();
|
@@ -243,17 +210,13 @@ class HPackParser::Input {
|
|
243
210
|
return StringPrefix{strlen, huff};
|
244
211
|
}
|
245
212
|
|
246
|
-
// Check if we saw an EOF
|
213
|
+
// Check if we saw an EOF
|
247
214
|
bool eof_error() const {
|
248
|
-
return
|
215
|
+
return min_progress_size_ != 0 || error_.connection_error();
|
249
216
|
}
|
250
217
|
|
251
|
-
//
|
252
|
-
|
253
|
-
grpc_error_handle out = error_;
|
254
|
-
error_ = absl::OkStatus();
|
255
|
-
return out;
|
256
|
-
}
|
218
|
+
// Minimum number of bytes to unstuck the current parse
|
219
|
+
size_t min_progress_size() const { return min_progress_size_; }
|
257
220
|
|
258
221
|
bool has_error() const { return !error_.ok(); }
|
259
222
|
|
@@ -261,31 +224,55 @@ class HPackParser::Input {
|
|
261
224
|
// chttp2 does not close the connection.
|
262
225
|
// Intended for errors that are specific to a stream and recoverable.
|
263
226
|
// Callers should ensure that any hpack table updates happen.
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
// StreamId is used as a signal to skip this stream but keep the connection
|
268
|
-
// alive
|
269
|
-
SetError(EnsureStreamError(std::move(error)));
|
227
|
+
void SetErrorAndContinueParsing(HpackParseResult error) {
|
228
|
+
GPR_DEBUG_ASSERT(error.stream_error());
|
229
|
+
SetError(std::move(error));
|
270
230
|
}
|
271
231
|
|
272
232
|
// Set the current error, and skip past remaining bytes.
|
273
233
|
// Intended for unrecoverable errors, with the expectation that they will
|
274
234
|
// close the connection on return to chttp2.
|
275
|
-
|
276
|
-
|
235
|
+
void SetErrorAndStopParsing(HpackParseResult error) {
|
236
|
+
GPR_DEBUG_ASSERT(error.connection_error());
|
277
237
|
SetError(std::move(error));
|
278
238
|
begin_ = end_;
|
279
239
|
}
|
280
240
|
|
281
|
-
// Set the error to an unexpected eof
|
282
|
-
|
283
|
-
|
284
|
-
|
241
|
+
// Set the error to an unexpected eof.
|
242
|
+
// min_progress_size: how many bytes beyond the current frontier do we need to
|
243
|
+
// read prior to being able to get further in this parse.
|
244
|
+
void UnexpectedEOF(size_t min_progress_size) {
|
245
|
+
GPR_ASSERT(min_progress_size > 0);
|
246
|
+
if (min_progress_size_ != 0 || error_.connection_error()) {
|
247
|
+
GPR_DEBUG_ASSERT(eof_error());
|
248
|
+
return;
|
249
|
+
}
|
250
|
+
// Set min progress size, taking into account bytes parsed already but not
|
251
|
+
// consumed.
|
252
|
+
min_progress_size_ = min_progress_size + (begin_ - frontier_);
|
253
|
+
GPR_DEBUG_ASSERT(eof_error());
|
285
254
|
}
|
286
255
|
|
287
256
|
// Update the frontier - signifies we've successfully parsed another element
|
288
|
-
void UpdateFrontier() {
|
257
|
+
void UpdateFrontier() {
|
258
|
+
GPR_DEBUG_ASSERT(skip_bytes_ == 0);
|
259
|
+
frontier_ = begin_;
|
260
|
+
}
|
261
|
+
|
262
|
+
void UpdateFrontierAndSkipBytes(size_t skip_bytes) {
|
263
|
+
UpdateFrontier();
|
264
|
+
size_t remaining = end_ - begin_;
|
265
|
+
if (skip_bytes >= remaining) {
|
266
|
+
// If we have more bytes to skip than we have remaining in this buffer
|
267
|
+
// then we skip over what's there and stash that we need to skip some
|
268
|
+
// more.
|
269
|
+
skip_bytes_ = skip_bytes - remaining;
|
270
|
+
frontier_ = end_;
|
271
|
+
} else {
|
272
|
+
// Otherwise we zoom through some bytes and continue parsing.
|
273
|
+
frontier_ += skip_bytes_;
|
274
|
+
}
|
275
|
+
}
|
289
276
|
|
290
277
|
// Get the frontier - for buffering should we fail due to eof
|
291
278
|
const uint8_t* frontier() const { return frontier_; }
|
@@ -294,19 +281,23 @@ class HPackParser::Input {
|
|
294
281
|
// Helper to set the error to out of range for ParseVarint
|
295
282
|
absl::optional<uint32_t> ParseVarintOutOfRange(uint32_t value,
|
296
283
|
uint8_t last_byte) {
|
297
|
-
SetErrorAndStopParsing(
|
298
|
-
|
299
|
-
|
300
|
-
|
284
|
+
SetErrorAndStopParsing(
|
285
|
+
HpackParseResult::VarintOutOfRangeError(value, last_byte));
|
286
|
+
return absl::optional<uint32_t>();
|
287
|
+
}
|
288
|
+
|
289
|
+
// Helper to set the error in the case of a malicious encoding
|
290
|
+
absl::optional<uint32_t> ParseVarintMaliciousEncoding() {
|
291
|
+
SetErrorAndStopParsing(HpackParseResult::MaliciousVarintEncodingError());
|
301
292
|
return absl::optional<uint32_t>();
|
302
293
|
}
|
303
294
|
|
304
295
|
// If no error is set, set it to the given error (i.e. first error wins)
|
305
296
|
// Do not use this directly, instead use SetErrorAndContinueParsing or
|
306
297
|
// SetErrorAndStopParsing.
|
307
|
-
void SetError(
|
308
|
-
if (!error_.ok() ||
|
309
|
-
if (
|
298
|
+
void SetError(HpackParseResult error) {
|
299
|
+
if (!error_.ok() || min_progress_size_ > 0) {
|
300
|
+
if (error.connection_error() && !error_.connection_error()) {
|
310
301
|
error_ = std::move(error); // connection errors dominate
|
311
302
|
}
|
312
303
|
return;
|
@@ -323,211 +314,156 @@ class HPackParser::Input {
|
|
323
314
|
// Frontier denotes the first byte past successfully processed input
|
324
315
|
const uint8_t* frontier_;
|
325
316
|
// Current error
|
326
|
-
|
327
|
-
// If the error was EOF, we flag it here
|
328
|
-
|
317
|
+
HpackParseResult& error_;
|
318
|
+
// If the error was EOF, we flag it here by noting how many more bytes would
|
319
|
+
// be needed to make progress
|
320
|
+
size_t min_progress_size_ = 0;
|
321
|
+
// Number of bytes that should be skipped before parsing resumes.
|
322
|
+
// (We've failed parsing a request for whatever reason, but we're still
|
323
|
+
// continuing the connection so we need to see future opcodes after this bit).
|
324
|
+
size_t skip_bytes_ = 0;
|
329
325
|
};
|
330
326
|
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
kOk,
|
341
|
-
// Parse reached end of the current frame
|
342
|
-
kEof,
|
343
|
-
// Parse failed due to a huffman decode error
|
344
|
-
kParseHuffFailed,
|
345
|
-
// Parse failed due to a base64 decode error
|
346
|
-
kUnbase64Failed,
|
347
|
-
};
|
348
|
-
|
349
|
-
String() : value_(absl::Span<const uint8_t>()) {}
|
350
|
-
String(const String&) = delete;
|
351
|
-
String& operator=(const String&) = delete;
|
352
|
-
String(String&& other) noexcept : value_(std::move(other.value_)) {
|
353
|
-
other.value_ = absl::Span<const uint8_t>();
|
354
|
-
}
|
355
|
-
String& operator=(String&& other) noexcept {
|
356
|
-
value_ = std::move(other.value_);
|
357
|
-
other.value_ = absl::Span<const uint8_t>();
|
358
|
-
return *this;
|
327
|
+
absl::string_view HPackParser::String::string_view() const {
|
328
|
+
if (auto* p = absl::get_if<Slice>(&value_)) {
|
329
|
+
return p->as_string_view();
|
330
|
+
} else if (auto* p = absl::get_if<absl::Span<const uint8_t>>(&value_)) {
|
331
|
+
return absl::string_view(reinterpret_cast<const char*>(p->data()),
|
332
|
+
p->size());
|
333
|
+
} else if (auto* p = absl::get_if<std::vector<uint8_t>>(&value_)) {
|
334
|
+
return absl::string_view(reinterpret_cast<const char*>(p->data()),
|
335
|
+
p->size());
|
359
336
|
}
|
337
|
+
GPR_UNREACHABLE_CODE(return absl::string_view());
|
338
|
+
}
|
360
339
|
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
//
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
} else if (auto* p = absl::get_if<absl::Span<const uint8_t>>(&value_)) {
|
369
|
-
return absl::string_view(reinterpret_cast<const char*>(p->data()),
|
370
|
-
p->size());
|
371
|
-
} else if (auto* p = absl::get_if<std::vector<uint8_t>>(&value_)) {
|
372
|
-
return absl::string_view(reinterpret_cast<const char*>(p->data()),
|
373
|
-
p->size());
|
374
|
-
}
|
375
|
-
GPR_UNREACHABLE_CODE(return absl::string_view());
|
340
|
+
template <typename Out>
|
341
|
+
HpackParseStatus HPackParser::String::ParseHuff(Input* input, uint32_t length,
|
342
|
+
Out output) {
|
343
|
+
// If there's insufficient bytes remaining, return now.
|
344
|
+
if (input->remaining() < length) {
|
345
|
+
input->UnexpectedEOF(/*min_progress_size=*/length);
|
346
|
+
return HpackParseStatus::kEof;
|
376
347
|
}
|
348
|
+
// Grab the byte range, and iterate through it.
|
349
|
+
const uint8_t* p = input->cur_ptr();
|
350
|
+
input->Advance(length);
|
351
|
+
return HuffDecoder<Out>(output, p, p + length).Run()
|
352
|
+
? HpackParseStatus::kOk
|
353
|
+
: HpackParseStatus::kParseHuffFailed;
|
354
|
+
}
|
377
355
|
|
378
|
-
|
379
|
-
|
356
|
+
struct HPackParser::String::StringResult {
|
357
|
+
StringResult() = delete;
|
358
|
+
StringResult(HpackParseStatus status, size_t wire_size, String value)
|
359
|
+
: status(status), wire_size(wire_size), value(std::move(value)) {}
|
360
|
+
HpackParseStatus status;
|
361
|
+
size_t wire_size;
|
362
|
+
String value;
|
363
|
+
};
|
380
364
|
|
381
|
-
|
382
|
-
|
365
|
+
HPackParser::String::StringResult HPackParser::String::ParseUncompressed(
|
366
|
+
Input* input, uint32_t length, uint32_t wire_size) {
|
367
|
+
// Check there's enough bytes
|
368
|
+
if (input->remaining() < length) {
|
369
|
+
input->UnexpectedEOF(/*min_progress_size=*/length);
|
370
|
+
GPR_DEBUG_ASSERT(input->eof_error());
|
371
|
+
return StringResult{HpackParseStatus::kEof, wire_size, String{}};
|
372
|
+
}
|
373
|
+
auto* refcount = input->slice_refcount();
|
374
|
+
auto* p = input->cur_ptr();
|
375
|
+
input->Advance(length);
|
376
|
+
if (refcount != nullptr) {
|
377
|
+
return StringResult{HpackParseStatus::kOk, wire_size,
|
378
|
+
String(refcount, p, p + length)};
|
379
|
+
} else {
|
380
|
+
return StringResult{HpackParseStatus::kOk, wire_size,
|
381
|
+
String(absl::Span<const uint8_t>(p, length))};
|
382
|
+
}
|
383
|
+
}
|
383
384
|
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
String(grpc_slice_refcount* r, const uint8_t* begin, const uint8_t* end)
|
389
|
-
: value_(Slice::FromRefcountAndBytes(r, begin, end)) {}
|
390
|
-
|
391
|
-
// Parse some huffman encoded bytes, using output(uint8_t b) to emit each
|
392
|
-
// decoded byte.
|
393
|
-
template <typename Out>
|
394
|
-
static ParseStatus ParseHuff(Input* input, uint32_t length, Out output) {
|
395
|
-
// If there's insufficient bytes remaining, return now.
|
396
|
-
if (input->remaining() < length) {
|
397
|
-
input->UnexpectedEOF();
|
398
|
-
GPR_DEBUG_ASSERT(input->eof_error());
|
399
|
-
return ParseStatus::kEof;
|
400
|
-
}
|
401
|
-
// Grab the byte range, and iterate through it.
|
402
|
-
const uint8_t* p = input->cur_ptr();
|
403
|
-
input->Advance(length);
|
404
|
-
return HuffDecoder<Out>(output, p, p + length).Run()
|
405
|
-
? ParseStatus::kOk
|
406
|
-
: ParseStatus::kParseHuffFailed;
|
385
|
+
absl::optional<std::vector<uint8_t>> HPackParser::String::Unbase64Loop(
|
386
|
+
const uint8_t* cur, const uint8_t* end) {
|
387
|
+
while (cur != end && end[-1] == '=') {
|
388
|
+
--end;
|
407
389
|
}
|
408
390
|
|
409
|
-
|
410
|
-
|
411
|
-
|
391
|
+
std::vector<uint8_t> out;
|
392
|
+
out.reserve(3 * (end - cur) / 4 + 3);
|
393
|
+
|
394
|
+
// Decode 4 bytes at a time while we can
|
395
|
+
while (end - cur >= 4) {
|
396
|
+
uint32_t bits = kBase64InverseTable.table[*cur];
|
397
|
+
if (bits > 63) return {};
|
398
|
+
uint32_t buffer = bits << 18;
|
399
|
+
++cur;
|
400
|
+
|
401
|
+
bits = kBase64InverseTable.table[*cur];
|
402
|
+
if (bits > 63) return {};
|
403
|
+
buffer |= bits << 12;
|
404
|
+
++cur;
|
405
|
+
|
406
|
+
bits = kBase64InverseTable.table[*cur];
|
407
|
+
if (bits > 63) return {};
|
408
|
+
buffer |= bits << 6;
|
409
|
+
++cur;
|
410
|
+
|
411
|
+
bits = kBase64InverseTable.table[*cur];
|
412
|
+
if (bits > 63) return {};
|
413
|
+
buffer |= bits;
|
414
|
+
++cur;
|
415
|
+
|
416
|
+
out.insert(out.end(), {static_cast<uint8_t>(buffer >> 16),
|
417
|
+
static_cast<uint8_t>(buffer >> 8),
|
418
|
+
static_cast<uint8_t>(buffer)});
|
419
|
+
}
|
420
|
+
// Deal with the last 0, 1, 2, or 3 bytes.
|
421
|
+
switch (end - cur) {
|
422
|
+
case 0:
|
423
|
+
return out;
|
424
|
+
case 1:
|
425
|
+
return {};
|
426
|
+
case 2: {
|
427
|
+
uint32_t bits = kBase64InverseTable.table[*cur];
|
428
|
+
if (bits > 63) return {};
|
429
|
+
uint32_t buffer = bits << 18;
|
412
430
|
|
413
|
-
|
414
|
-
|
431
|
+
++cur;
|
432
|
+
bits = kBase64InverseTable.table[*cur];
|
433
|
+
if (bits > 63) return {};
|
434
|
+
buffer |= bits << 12;
|
415
435
|
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
while (cur != end && end[-1] == '=') {
|
420
|
-
--end;
|
436
|
+
if (buffer & 0xffff) return {};
|
437
|
+
out.push_back(static_cast<uint8_t>(buffer >> 16));
|
438
|
+
return out;
|
421
439
|
}
|
422
|
-
|
423
|
-
std::vector<uint8_t> out;
|
424
|
-
out.reserve(3 * (end - cur) / 4 + 3);
|
425
|
-
|
426
|
-
// Decode 4 bytes at a time while we can
|
427
|
-
while (end - cur >= 4) {
|
440
|
+
case 3: {
|
428
441
|
uint32_t bits = kBase64InverseTable.table[*cur];
|
429
442
|
if (bits > 63) return {};
|
430
443
|
uint32_t buffer = bits << 18;
|
431
|
-
++cur;
|
432
444
|
|
445
|
+
++cur;
|
433
446
|
bits = kBase64InverseTable.table[*cur];
|
434
447
|
if (bits > 63) return {};
|
435
448
|
buffer |= bits << 12;
|
436
|
-
++cur;
|
437
449
|
|
450
|
+
++cur;
|
438
451
|
bits = kBase64InverseTable.table[*cur];
|
439
452
|
if (bits > 63) return {};
|
440
453
|
buffer |= bits << 6;
|
441
|
-
++cur;
|
442
454
|
|
443
|
-
bits = kBase64InverseTable.table[*cur];
|
444
|
-
if (bits > 63) return {};
|
445
|
-
buffer |= bits;
|
446
455
|
++cur;
|
447
|
-
|
448
|
-
out.
|
449
|
-
|
450
|
-
|
456
|
+
if (buffer & 0xff) return {};
|
457
|
+
out.push_back(static_cast<uint8_t>(buffer >> 16));
|
458
|
+
out.push_back(static_cast<uint8_t>(buffer >> 8));
|
459
|
+
return out;
|
451
460
|
}
|
452
|
-
// Deal with the last 0, 1, 2, or 3 bytes.
|
453
|
-
switch (end - cur) {
|
454
|
-
case 0:
|
455
|
-
return out;
|
456
|
-
case 1:
|
457
|
-
return {};
|
458
|
-
case 2: {
|
459
|
-
uint32_t bits = kBase64InverseTable.table[*cur];
|
460
|
-
if (bits > 63) return {};
|
461
|
-
uint32_t buffer = bits << 18;
|
462
|
-
|
463
|
-
++cur;
|
464
|
-
bits = kBase64InverseTable.table[*cur];
|
465
|
-
if (bits > 63) return {};
|
466
|
-
buffer |= bits << 12;
|
467
|
-
|
468
|
-
if (buffer & 0xffff) return {};
|
469
|
-
out.push_back(static_cast<uint8_t>(buffer >> 16));
|
470
|
-
return out;
|
471
|
-
}
|
472
|
-
case 3: {
|
473
|
-
uint32_t bits = kBase64InverseTable.table[*cur];
|
474
|
-
if (bits > 63) return {};
|
475
|
-
uint32_t buffer = bits << 18;
|
476
|
-
|
477
|
-
++cur;
|
478
|
-
bits = kBase64InverseTable.table[*cur];
|
479
|
-
if (bits > 63) return {};
|
480
|
-
buffer |= bits << 12;
|
481
|
-
|
482
|
-
++cur;
|
483
|
-
bits = kBase64InverseTable.table[*cur];
|
484
|
-
if (bits > 63) return {};
|
485
|
-
buffer |= bits << 6;
|
486
|
-
|
487
|
-
++cur;
|
488
|
-
if (buffer & 0xff) return {};
|
489
|
-
out.push_back(static_cast<uint8_t>(buffer >> 16));
|
490
|
-
out.push_back(static_cast<uint8_t>(buffer >> 8));
|
491
|
-
return out;
|
492
|
-
}
|
493
|
-
}
|
494
|
-
|
495
|
-
GPR_UNREACHABLE_CODE(return out;);
|
496
461
|
}
|
497
462
|
|
498
|
-
|
499
|
-
};
|
500
|
-
|
501
|
-
struct HPackParser::String::ParseResult {
|
502
|
-
ParseResult() = delete;
|
503
|
-
ParseResult(ParseStatus status, size_t wire_size, String value)
|
504
|
-
: status(status), wire_size(wire_size), value(std::move(value)) {}
|
505
|
-
ParseStatus status;
|
506
|
-
size_t wire_size;
|
507
|
-
String value;
|
508
|
-
};
|
509
|
-
|
510
|
-
HPackParser::String::ParseResult HPackParser::String::ParseUncompressed(
|
511
|
-
Input* input, uint32_t length, uint32_t wire_size) {
|
512
|
-
// Check there's enough bytes
|
513
|
-
if (input->remaining() < length) {
|
514
|
-
input->UnexpectedEOF();
|
515
|
-
GPR_DEBUG_ASSERT(input->eof_error());
|
516
|
-
return ParseResult{ParseStatus::kEof, wire_size, String{}};
|
517
|
-
}
|
518
|
-
auto* refcount = input->slice_refcount();
|
519
|
-
auto* p = input->cur_ptr();
|
520
|
-
input->Advance(length);
|
521
|
-
if (refcount != nullptr) {
|
522
|
-
return ParseResult{ParseStatus::kOk, wire_size,
|
523
|
-
String(refcount, p, p + length)};
|
524
|
-
} else {
|
525
|
-
return ParseResult{ParseStatus::kOk, wire_size,
|
526
|
-
String(absl::Span<const uint8_t>(p, length))};
|
527
|
-
}
|
463
|
+
GPR_UNREACHABLE_CODE(return out;);
|
528
464
|
}
|
529
465
|
|
530
|
-
HPackParser::String::
|
466
|
+
HPackParser::String::StringResult HPackParser::String::Unbase64(String s) {
|
531
467
|
absl::optional<std::vector<uint8_t>> result;
|
532
468
|
if (auto* p = absl::get_if<Slice>(&s.value_)) {
|
533
469
|
result = Unbase64Loop(p->begin(), p->end());
|
@@ -539,46 +475,38 @@ HPackParser::String::ParseResult HPackParser::String::Unbase64(String s) {
|
|
539
475
|
result = Unbase64Loop(p->data(), p->data() + p->size());
|
540
476
|
}
|
541
477
|
if (!result.has_value()) {
|
542
|
-
return
|
543
|
-
|
478
|
+
return StringResult{HpackParseStatus::kUnbase64Failed,
|
479
|
+
s.string_view().length(), String{}};
|
544
480
|
}
|
545
|
-
return
|
546
|
-
|
481
|
+
return StringResult{HpackParseStatus::kOk, s.string_view().length(),
|
482
|
+
String(std::move(*result))};
|
547
483
|
}
|
548
484
|
|
549
|
-
HPackParser::String::
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
return ParseResult{ParseStatus::kEof, 0, String{}};
|
554
|
-
}
|
555
|
-
if (pfx->huff) {
|
485
|
+
HPackParser::String::StringResult HPackParser::String::Parse(Input* input,
|
486
|
+
bool is_huff,
|
487
|
+
size_t length) {
|
488
|
+
if (is_huff) {
|
556
489
|
// Huffman coded
|
557
490
|
std::vector<uint8_t> output;
|
558
|
-
|
559
|
-
|
491
|
+
HpackParseStatus sts =
|
492
|
+
ParseHuff(input, length, [&output](uint8_t c) { output.push_back(c); });
|
560
493
|
size_t wire_len = output.size();
|
561
|
-
return
|
494
|
+
return StringResult{sts, wire_len, String(std::move(output))};
|
562
495
|
}
|
563
|
-
return ParseUncompressed(input,
|
496
|
+
return ParseUncompressed(input, length, length);
|
564
497
|
}
|
565
498
|
|
566
|
-
HPackParser::String::
|
567
|
-
Input* input) {
|
568
|
-
|
569
|
-
|
570
|
-
GPR_DEBUG_ASSERT(input->eof_error());
|
571
|
-
return ParseResult{ParseStatus::kEof, 0, String{}};
|
572
|
-
}
|
573
|
-
if (!pfx->huff) {
|
574
|
-
if (pfx->length > 0 && input->peek() == 0) {
|
499
|
+
HPackParser::String::StringResult HPackParser::String::ParseBinary(
|
500
|
+
Input* input, bool is_huff, size_t length) {
|
501
|
+
if (!is_huff) {
|
502
|
+
if (length > 0 && input->peek() == 0) {
|
575
503
|
// 'true-binary'
|
576
504
|
input->Advance(1);
|
577
|
-
return ParseUncompressed(input,
|
505
|
+
return ParseUncompressed(input, length - 1, length);
|
578
506
|
}
|
579
507
|
// Base64 encoded... pull out the string, then unbase64 it
|
580
|
-
auto base64 = ParseUncompressed(input,
|
581
|
-
if (base64.status !=
|
508
|
+
auto base64 = ParseUncompressed(input, length, length);
|
509
|
+
if (base64.status != HpackParseStatus::kOk) return base64;
|
582
510
|
return Unbase64(std::move(base64.value));
|
583
511
|
} else {
|
584
512
|
// Huffman encoded...
|
@@ -587,36 +515,35 @@ HPackParser::String::ParseResult HPackParser::String::ParseBinary(
|
|
587
515
|
// and what is it.
|
588
516
|
enum class State { kUnsure, kBinary, kBase64 };
|
589
517
|
State state = State::kUnsure;
|
590
|
-
auto sts =
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
return ParseResult{sts, 0, String{}};
|
518
|
+
auto sts = ParseHuff(input, length, [&state, &decompressed](uint8_t c) {
|
519
|
+
if (state == State::kUnsure) {
|
520
|
+
// First byte... if it's zero it's binary
|
521
|
+
if (c == 0) {
|
522
|
+
// Save the type, and skip the zero
|
523
|
+
state = State::kBinary;
|
524
|
+
return;
|
525
|
+
} else {
|
526
|
+
// Flag base64, store this value
|
527
|
+
state = State::kBase64;
|
528
|
+
}
|
529
|
+
}
|
530
|
+
// Non-first byte, or base64 first byte
|
531
|
+
decompressed.push_back(c);
|
532
|
+
});
|
533
|
+
if (sts != HpackParseStatus::kOk) {
|
534
|
+
return StringResult{sts, 0, String{}};
|
608
535
|
}
|
609
536
|
switch (state) {
|
610
537
|
case State::kUnsure:
|
611
538
|
// No bytes, empty span
|
612
|
-
return
|
613
|
-
|
539
|
+
return StringResult{HpackParseStatus::kOk, 0,
|
540
|
+
String(absl::Span<const uint8_t>())};
|
614
541
|
case State::kBinary:
|
615
542
|
// Binary, we're done
|
616
543
|
{
|
617
544
|
size_t wire_len = decompressed.size();
|
618
|
-
return
|
619
|
-
|
545
|
+
return StringResult{HpackParseStatus::kOk, wire_len,
|
546
|
+
String(std::move(decompressed))};
|
620
547
|
}
|
621
548
|
case State::kBase64:
|
622
549
|
// Base64 - unpack it
|
@@ -629,28 +556,38 @@ HPackParser::String::ParseResult HPackParser::String::ParseBinary(
|
|
629
556
|
// Parser parses one key/value pair from a byte stream.
|
630
557
|
class HPackParser::Parser {
|
631
558
|
public:
|
632
|
-
Parser(Input* input, grpc_metadata_batch
|
633
|
-
|
634
|
-
RandomEarlyDetection* metadata_early_detection, LogInfo log_info)
|
559
|
+
Parser(Input* input, grpc_metadata_batch*& metadata_buffer,
|
560
|
+
InterSliceState& state, LogInfo log_info)
|
635
561
|
: input_(input),
|
636
562
|
metadata_buffer_(metadata_buffer),
|
637
|
-
|
638
|
-
dynamic_table_updates_allowed_(dynamic_table_updates_allowed),
|
639
|
-
frame_length_(frame_length),
|
640
|
-
metadata_early_detection_(metadata_early_detection),
|
563
|
+
state_(state),
|
641
564
|
log_info_(log_info) {}
|
642
565
|
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
566
|
+
bool Parse() {
|
567
|
+
switch (state_.parse_state) {
|
568
|
+
case ParseState::kTop:
|
569
|
+
return ParseTop();
|
570
|
+
case ParseState::kParsingKeyLength:
|
571
|
+
return ParseKeyLength();
|
572
|
+
case ParseState::kParsingKeyBody:
|
573
|
+
return ParseKeyBody();
|
574
|
+
case ParseState::kSkippingKeyBody:
|
575
|
+
return SkipKeyBody();
|
576
|
+
case ParseState::kParsingValueLength:
|
577
|
+
return ParseValueLength();
|
578
|
+
case ParseState::kParsingValueBody:
|
579
|
+
return ParseValueBody();
|
580
|
+
case ParseState::kSkippingValueLength:
|
581
|
+
return SkipValueLength();
|
582
|
+
case ParseState::kSkippingValueBody:
|
583
|
+
return SkipValueBody();
|
648
584
|
}
|
649
|
-
|
650
|
-
return true;
|
585
|
+
GPR_UNREACHABLE_CODE(return false);
|
651
586
|
}
|
652
587
|
|
653
|
-
|
588
|
+
private:
|
589
|
+
bool ParseTop() {
|
590
|
+
GPR_DEBUG_ASSERT(state_.parse_state == ParseState::kTop);
|
654
591
|
auto cur = *input_->Next();
|
655
592
|
switch (cur >> 4) {
|
656
593
|
// Literal header not indexed - First byte format: 0000xxxx
|
@@ -663,11 +600,11 @@ class HPackParser::Parser {
|
|
663
600
|
case 1:
|
664
601
|
switch (cur & 0xf) {
|
665
602
|
case 0: // literal key
|
666
|
-
return
|
603
|
+
return StartParseLiteralKey(false);
|
667
604
|
case 0xf: // varint encoded key index
|
668
|
-
return
|
605
|
+
return StartVarIdxKey(0xf, false);
|
669
606
|
default: // inline encoded key index
|
670
|
-
return
|
607
|
+
return StartIdxKey(cur & 0xf, false);
|
671
608
|
}
|
672
609
|
// Update max table size.
|
673
610
|
// First byte format: 001xxxxx
|
@@ -694,20 +631,20 @@ class HPackParser::Parser {
|
|
694
631
|
case 4:
|
695
632
|
if (cur == 0x40) {
|
696
633
|
// literal key
|
697
|
-
return
|
634
|
+
return StartParseLiteralKey(true);
|
698
635
|
}
|
699
636
|
ABSL_FALLTHROUGH_INTENDED;
|
700
637
|
case 5:
|
701
638
|
case 6:
|
702
639
|
// inline encoded key index
|
703
|
-
return
|
640
|
+
return StartIdxKey(cur & 0x3f, true);
|
704
641
|
case 7:
|
705
642
|
if (cur == 0x7f) {
|
706
643
|
// varint encoded key index
|
707
|
-
return
|
644
|
+
return StartVarIdxKey(0x3f, true);
|
708
645
|
} else {
|
709
646
|
// inline encoded key index
|
710
|
-
return
|
647
|
+
return StartIdxKey(cur & 0x3f, true);
|
711
648
|
}
|
712
649
|
// Indexed Header Field Representation
|
713
650
|
// First byte format: 1xxxxxxx
|
@@ -719,7 +656,7 @@ class HPackParser::Parser {
|
|
719
656
|
if (cur == 0x80) {
|
720
657
|
// illegal value.
|
721
658
|
input_->SetErrorAndStopParsing(
|
722
|
-
|
659
|
+
HpackParseResult::IllegalHpackOpCode());
|
723
660
|
return false;
|
724
661
|
}
|
725
662
|
ABSL_FALLTHROUGH_INTENDED;
|
@@ -743,7 +680,6 @@ class HPackParser::Parser {
|
|
743
680
|
GPR_UNREACHABLE_CODE(abort());
|
744
681
|
}
|
745
682
|
|
746
|
-
private:
|
747
683
|
void GPR_ATTRIBUTE_NOINLINE LogHeader(const HPackTable::Memento& memento) {
|
748
684
|
const char* type;
|
749
685
|
switch (log_info_.type) {
|
@@ -757,46 +693,48 @@ class HPackParser::Parser {
|
|
757
693
|
type = "???";
|
758
694
|
break;
|
759
695
|
}
|
760
|
-
gpr_log(
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
696
|
+
gpr_log(
|
697
|
+
GPR_DEBUG, "HTTP:%d:%s:%s: %s%s", log_info_.stream_id, type,
|
698
|
+
log_info_.is_client ? "CLI" : "SVR", memento.md.DebugString().c_str(),
|
699
|
+
memento.parse_status.ok()
|
700
|
+
? ""
|
701
|
+
: absl::StrCat(" (parse error: ",
|
702
|
+
memento.parse_status.Materialize().ToString(), ")")
|
703
|
+
.c_str());
|
768
704
|
}
|
769
705
|
|
770
706
|
void EmitHeader(const HPackTable::Memento& md) {
|
771
707
|
// Pass up to the transport
|
772
|
-
|
773
|
-
if (!input_->has_error() &&
|
774
|
-
metadata_early_detection_->MustReject(*frame_length_)) {
|
775
|
-
// Reject any requests above hard metadata limit.
|
776
|
-
HandleMetadataHardSizeLimitExceeded(md);
|
777
|
-
}
|
708
|
+
state_.frame_length += md.md.transport_size();
|
778
709
|
if (!md.parse_status.ok()) {
|
779
710
|
// Reject any requests with invalid metadata.
|
780
|
-
|
711
|
+
input_->SetErrorAndContinueParsing(md.parse_status);
|
781
712
|
}
|
782
713
|
if (GPR_LIKELY(metadata_buffer_ != nullptr)) {
|
783
714
|
metadata_buffer_->Set(md.md);
|
784
715
|
}
|
716
|
+
if (state_.metadata_early_detection.MustReject(state_.frame_length)) {
|
717
|
+
// Reject any requests above hard metadata limit.
|
718
|
+
input_->SetErrorAndContinueParsing(
|
719
|
+
HpackParseResult::HardMetadataLimitExceededError(
|
720
|
+
std::exchange(metadata_buffer_, nullptr), state_.frame_length,
|
721
|
+
state_.metadata_early_detection.hard_limit()));
|
722
|
+
}
|
785
723
|
}
|
786
724
|
|
787
|
-
bool FinishHeaderAndAddToTable(
|
788
|
-
// Allow higher code to just pass in failures ... simplifies things a bit.
|
789
|
-
if (!md.has_value()) return false;
|
725
|
+
bool FinishHeaderAndAddToTable(HPackTable::Memento md) {
|
790
726
|
// Log if desired
|
791
727
|
if (GRPC_TRACE_FLAG_ENABLED(grpc_trace_chttp2_hpack_parser)) {
|
792
|
-
LogHeader(
|
728
|
+
LogHeader(md);
|
793
729
|
}
|
794
730
|
// Emit whilst we own the metadata.
|
795
|
-
EmitHeader(
|
731
|
+
EmitHeader(md);
|
796
732
|
// Add to the hpack table
|
797
|
-
|
798
|
-
|
799
|
-
|
733
|
+
if (GPR_UNLIKELY(!state_.hpack_table.Add(std::move(md)))) {
|
734
|
+
input_->SetErrorAndStopParsing(
|
735
|
+
HpackParseResult::AddBeforeTableSizeUpdated(
|
736
|
+
state_.hpack_table.current_table_bytes(),
|
737
|
+
state_.hpack_table.max_bytes()));
|
800
738
|
return false;
|
801
739
|
};
|
802
740
|
return true;
|
@@ -817,136 +755,269 @@ class HPackParser::Parser {
|
|
817
755
|
EmitHeader(md);
|
818
756
|
}
|
819
757
|
|
820
|
-
//
|
821
|
-
|
822
|
-
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
auto ErrorHandler() {
|
829
|
-
return [this](absl::string_view error, const Slice&) {
|
830
|
-
auto message =
|
831
|
-
absl::StrCat("Error parsing '", key_string_,
|
832
|
-
"' metadata: error=", error, " key=", key_string_);
|
833
|
-
gpr_log(GPR_ERROR, "%s", message.c_str());
|
834
|
-
if (status_.ok()) {
|
835
|
-
status_ = absl::InternalError(message);
|
836
|
-
}
|
837
|
-
};
|
758
|
+
// Parse an index encoded key and a string encoded value
|
759
|
+
bool StartIdxKey(uint32_t index, bool add_to_table) {
|
760
|
+
GPR_DEBUG_ASSERT(state_.parse_state == ParseState::kTop);
|
761
|
+
input_->UpdateFrontier();
|
762
|
+
const auto* elem = state_.hpack_table.Lookup(index);
|
763
|
+
if (GPR_UNLIKELY(elem == nullptr)) {
|
764
|
+
InvalidHPackIndexError(index);
|
765
|
+
return false;
|
838
766
|
}
|
767
|
+
state_.parse_state = ParseState::kParsingValueLength;
|
768
|
+
state_.is_binary_header = elem->md.is_binary_header();
|
769
|
+
state_.key.emplace<const HPackTable::Memento*>(elem);
|
770
|
+
state_.add_to_table = add_to_table;
|
771
|
+
return ParseValueLength();
|
772
|
+
};
|
839
773
|
|
840
|
-
|
841
|
-
|
842
|
-
|
774
|
+
// Parse a varint index encoded key and a string encoded value
|
775
|
+
bool StartVarIdxKey(uint32_t offset, bool add_to_table) {
|
776
|
+
GPR_DEBUG_ASSERT(state_.parse_state == ParseState::kTop);
|
777
|
+
auto index = input_->ParseVarint(offset);
|
778
|
+
if (GPR_UNLIKELY(!index.has_value())) return false;
|
779
|
+
return StartIdxKey(*index, add_to_table);
|
780
|
+
}
|
843
781
|
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
|
850
|
-
|
851
|
-
if (status_.ok()) status_ = this_error;
|
852
|
-
input_->SetErrorAndContinueParsing(std::move(this_error));
|
853
|
-
};
|
854
|
-
switch (status) {
|
855
|
-
case String::ParseStatus::kOk:
|
856
|
-
return true;
|
857
|
-
case String::ParseStatus::kParseHuffFailed:
|
858
|
-
input_->SetErrorAndStopParsing(
|
859
|
-
absl::InternalError("Huffman decoding failed"));
|
860
|
-
return false;
|
861
|
-
case String::ParseStatus::kUnbase64Failed:
|
862
|
-
continuable("illegal base64 encoding");
|
863
|
-
return true;
|
864
|
-
case String::ParseStatus::kEof:
|
865
|
-
GPR_DEBUG_ASSERT(input_->eof_error());
|
866
|
-
return false;
|
867
|
-
}
|
868
|
-
GPR_UNREACHABLE_CODE(return false);
|
869
|
-
}
|
782
|
+
bool StartParseLiteralKey(bool add_to_table) {
|
783
|
+
GPR_DEBUG_ASSERT(state_.parse_state == ParseState::kTop);
|
784
|
+
state_.add_to_table = add_to_table;
|
785
|
+
state_.parse_state = ParseState::kParsingKeyLength;
|
786
|
+
input_->UpdateFrontier();
|
787
|
+
return ParseKeyLength();
|
788
|
+
}
|
870
789
|
|
871
|
-
|
872
|
-
|
873
|
-
|
874
|
-
|
875
|
-
|
790
|
+
bool ShouldSkipParsingString(uint64_t string_length) const {
|
791
|
+
// We skip parsing if the string is longer than the current table size, and
|
792
|
+
// if we would have to reject the string due to metadata length limits
|
793
|
+
// regardless of what else was in the metadata batch.
|
794
|
+
//
|
795
|
+
// Why longer than the current table size? - it simplifies the logic at the
|
796
|
+
// end of skipping the string (and possibly a second if this is a key).
|
797
|
+
// If a key/value pair longer than the current table size is added to the
|
798
|
+
// hpack table we're forced to clear the entire table - this is a
|
799
|
+
// predictable operation that's easy to encode and doesn't need any state
|
800
|
+
// other than "skipping" to be carried forward.
|
801
|
+
// If we did not do this, we could end up in a situation where even though
|
802
|
+
// the metadata would overflow the current limit, it might not overflow the
|
803
|
+
// current hpack table size, and so we could not skip in on the off chance
|
804
|
+
// that we'd need to add it to the hpack table *and* reject the batch as a
|
805
|
+
// whole.
|
806
|
+
// That would be a mess, we're not doing it.
|
807
|
+
//
|
808
|
+
// These rules will end up having us parse some things that ultimately get
|
809
|
+
// rejected, and that's ok: the important thing is to have a bounded maximum
|
810
|
+
// so we can't be forced to infinitely buffer - not to have a perfect
|
811
|
+
// computation here.
|
812
|
+
return string_length > state_.hpack_table.current_table_size() &&
|
813
|
+
state_.metadata_early_detection.MustReject(
|
814
|
+
string_length + hpack_constants::kEntryOverhead);
|
815
|
+
}
|
816
|
+
|
817
|
+
bool ParseKeyLength() {
|
818
|
+
GPR_DEBUG_ASSERT(state_.parse_state == ParseState::kParsingKeyLength);
|
819
|
+
auto pfx = input_->ParseStringPrefix();
|
820
|
+
if (!pfx.has_value()) return false;
|
821
|
+
state_.is_string_huff_compressed = pfx->huff;
|
822
|
+
state_.string_length = pfx->length;
|
823
|
+
input_->UpdateFrontier();
|
824
|
+
if (ShouldSkipParsingString(state_.string_length)) {
|
825
|
+
input_->SetErrorAndContinueParsing(
|
826
|
+
HpackParseResult::HardMetadataLimitExceededByKeyError(
|
827
|
+
state_.string_length,
|
828
|
+
state_.metadata_early_detection.hard_limit()));
|
829
|
+
metadata_buffer_ = nullptr;
|
830
|
+
state_.parse_state = ParseState::kSkippingKeyBody;
|
831
|
+
return SkipKeyBody();
|
832
|
+
} else {
|
833
|
+
state_.parse_state = ParseState::kParsingKeyBody;
|
834
|
+
return ParseKeyBody();
|
835
|
+
}
|
836
|
+
}
|
876
837
|
|
877
|
-
|
878
|
-
|
879
|
-
auto key = String::Parse(input_
|
838
|
+
bool ParseKeyBody() {
|
839
|
+
GPR_DEBUG_ASSERT(state_.parse_state == ParseState::kParsingKeyBody);
|
840
|
+
auto key = String::Parse(input_, state_.is_string_huff_compressed,
|
841
|
+
state_.string_length);
|
880
842
|
switch (key.status) {
|
881
|
-
case
|
843
|
+
case HpackParseStatus::kOk:
|
882
844
|
break;
|
883
|
-
case
|
884
|
-
input_->SetErrorAndStopParsing(
|
885
|
-
absl::InternalError("Huffman decoding failed"));
|
886
|
-
return absl::nullopt;
|
887
|
-
case String::ParseStatus::kUnbase64Failed:
|
888
|
-
Crash("unreachable");
|
889
|
-
case String::ParseStatus::kEof:
|
845
|
+
case HpackParseStatus::kEof:
|
890
846
|
GPR_DEBUG_ASSERT(input_->eof_error());
|
891
|
-
return
|
847
|
+
return false;
|
848
|
+
default:
|
849
|
+
input_->SetErrorAndStopParsing(
|
850
|
+
HpackParseResult::FromStatus(key.status));
|
851
|
+
return false;
|
892
852
|
}
|
893
|
-
|
894
|
-
|
895
|
-
|
896
|
-
|
897
|
-
|
898
|
-
auto value_slice = value.value.Take();
|
899
|
-
const auto transport_size =
|
900
|
-
key_string.size() + value.wire_size + hpack_constants::kEntryOverhead;
|
901
|
-
return builder.Build(
|
902
|
-
grpc_metadata_batch::Parse(key_string, std::move(value_slice),
|
903
|
-
transport_size, builder.ErrorHandler()));
|
853
|
+
input_->UpdateFrontier();
|
854
|
+
state_.parse_state = ParseState::kParsingValueLength;
|
855
|
+
state_.is_binary_header = absl::EndsWith(key.value.string_view(), "-bin");
|
856
|
+
state_.key.emplace<Slice>(key.value.Take());
|
857
|
+
return ParseValueLength();
|
904
858
|
}
|
905
859
|
|
906
|
-
|
907
|
-
|
908
|
-
|
909
|
-
|
910
|
-
return
|
860
|
+
bool SkipStringBody() {
|
861
|
+
auto remaining = input_->remaining();
|
862
|
+
if (remaining >= state_.string_length) {
|
863
|
+
input_->Advance(state_.string_length);
|
864
|
+
return true;
|
865
|
+
} else {
|
866
|
+
input_->Advance(remaining);
|
867
|
+
input_->UpdateFrontier();
|
868
|
+
state_.string_length -= remaining;
|
869
|
+
// The default action of our outer loop is to buffer up to
|
870
|
+
// min_progress_size bytes.
|
871
|
+
// We know we need to do nothing up to the string length, so it would be
|
872
|
+
// legal to pass that here - however that would cause a client selected
|
873
|
+
// large buffer size to be accumulated, which would be an attack vector.
|
874
|
+
// We could also pass 1 here, and we'd be called to parse potentially
|
875
|
+
// every byte, which would give clients a way to consume substantial CPU -
|
876
|
+
// again not great.
|
877
|
+
// So we pick some tradeoff number - big enough to amortize wakeups, but
|
878
|
+
// probably not big enough to cause excessive memory use on the receiver.
|
879
|
+
input_->UnexpectedEOF(
|
880
|
+
/*min_progress_size=*/std::min(state_.string_length, 1024u));
|
881
|
+
return false;
|
911
882
|
}
|
912
|
-
return ValidateHeaderKeyIsLegal(key);
|
913
883
|
}
|
914
884
|
|
915
|
-
|
916
|
-
|
917
|
-
|
918
|
-
|
919
|
-
|
920
|
-
|
885
|
+
bool SkipKeyBody() {
|
886
|
+
GPR_DEBUG_ASSERT(state_.parse_state == ParseState::kSkippingKeyBody);
|
887
|
+
if (!SkipStringBody()) return false;
|
888
|
+
input_->UpdateFrontier();
|
889
|
+
state_.parse_state = ParseState::kSkippingValueLength;
|
890
|
+
return SkipValueLength();
|
891
|
+
}
|
892
|
+
|
893
|
+
bool SkipValueLength() {
|
894
|
+
GPR_DEBUG_ASSERT(state_.parse_state == ParseState::kSkippingValueLength);
|
895
|
+
auto pfx = input_->ParseStringPrefix();
|
896
|
+
if (!pfx.has_value()) return false;
|
897
|
+
state_.string_length = pfx->length;
|
898
|
+
input_->UpdateFrontier();
|
899
|
+
state_.parse_state = ParseState::kSkippingValueBody;
|
900
|
+
return SkipValueBody();
|
901
|
+
}
|
902
|
+
|
903
|
+
bool SkipValueBody() {
|
904
|
+
GPR_DEBUG_ASSERT(state_.parse_state == ParseState::kSkippingValueBody);
|
905
|
+
if (!SkipStringBody()) return false;
|
906
|
+
input_->UpdateFrontier();
|
907
|
+
state_.parse_state = ParseState::kTop;
|
908
|
+
if (state_.add_to_table) {
|
909
|
+
state_.hpack_table.AddLargerThanCurrentTableSize();
|
921
910
|
}
|
922
|
-
|
923
|
-
|
924
|
-
if (!builder.HandleParseResult(value.status)) return absl::nullopt;
|
925
|
-
return builder.Build(elem->md.WithNewValue(
|
926
|
-
value.value.Take(), value.wire_size, builder.ErrorHandler()));
|
927
|
-
};
|
911
|
+
return true;
|
912
|
+
}
|
928
913
|
|
929
|
-
|
930
|
-
|
931
|
-
auto
|
932
|
-
if (
|
933
|
-
|
914
|
+
bool ParseValueLength() {
|
915
|
+
GPR_DEBUG_ASSERT(state_.parse_state == ParseState::kParsingValueLength);
|
916
|
+
auto pfx = input_->ParseStringPrefix();
|
917
|
+
if (!pfx.has_value()) return false;
|
918
|
+
state_.is_string_huff_compressed = pfx->huff;
|
919
|
+
state_.string_length = pfx->length;
|
920
|
+
input_->UpdateFrontier();
|
921
|
+
if (ShouldSkipParsingString(state_.string_length)) {
|
922
|
+
input_->SetErrorAndContinueParsing(
|
923
|
+
HpackParseResult::HardMetadataLimitExceededByValueError(
|
924
|
+
Match(
|
925
|
+
state_.key, [](const Slice& s) { return s.as_string_view(); },
|
926
|
+
[](const HPackTable::Memento* m) { return m->md.key(); }),
|
927
|
+
state_.string_length,
|
928
|
+
state_.metadata_early_detection.hard_limit()));
|
929
|
+
metadata_buffer_ = nullptr;
|
930
|
+
state_.parse_state = ParseState::kSkippingValueBody;
|
931
|
+
return SkipValueBody();
|
932
|
+
} else {
|
933
|
+
state_.parse_state = ParseState::kParsingValueBody;
|
934
|
+
return ParseValueBody();
|
935
|
+
}
|
934
936
|
}
|
935
937
|
|
936
|
-
|
937
|
-
|
938
|
-
|
939
|
-
|
938
|
+
bool ParseValueBody() {
|
939
|
+
GPR_DEBUG_ASSERT(state_.parse_state == ParseState::kParsingValueBody);
|
940
|
+
auto value =
|
941
|
+
state_.is_binary_header
|
942
|
+
? String::ParseBinary(input_, state_.is_string_huff_compressed,
|
943
|
+
state_.string_length)
|
944
|
+
: String::Parse(input_, state_.is_string_huff_compressed,
|
945
|
+
state_.string_length);
|
946
|
+
HpackParseResult& status = state_.frame_error;
|
947
|
+
absl::string_view key_string;
|
948
|
+
if (auto* s = absl::get_if<Slice>(&state_.key)) {
|
949
|
+
key_string = s->as_string_view();
|
950
|
+
if (status.ok()) {
|
951
|
+
auto r = ValidateKey(key_string);
|
952
|
+
if (r != ValidateMetadataResult::kOk) {
|
953
|
+
input_->SetErrorAndContinueParsing(
|
954
|
+
HpackParseResult::InvalidMetadataError(r, key_string));
|
955
|
+
}
|
956
|
+
}
|
940
957
|
} else {
|
941
|
-
|
958
|
+
const auto* memento = absl::get<const HPackTable::Memento*>(state_.key);
|
959
|
+
key_string = memento->md.key();
|
960
|
+
if (status.ok() && !memento->parse_status.ok()) {
|
961
|
+
input_->SetErrorAndContinueParsing(memento->parse_status);
|
962
|
+
}
|
963
|
+
}
|
964
|
+
switch (value.status) {
|
965
|
+
case HpackParseStatus::kOk:
|
966
|
+
break;
|
967
|
+
case HpackParseStatus::kEof:
|
968
|
+
GPR_DEBUG_ASSERT(input_->eof_error());
|
969
|
+
return false;
|
970
|
+
default: {
|
971
|
+
auto result =
|
972
|
+
HpackParseResult::FromStatusWithKey(value.status, key_string);
|
973
|
+
if (result.stream_error()) {
|
974
|
+
input_->SetErrorAndContinueParsing(std::move(result));
|
975
|
+
break;
|
976
|
+
} else {
|
977
|
+
input_->SetErrorAndStopParsing(std::move(result));
|
978
|
+
return false;
|
979
|
+
}
|
980
|
+
}
|
981
|
+
}
|
982
|
+
auto value_slice = value.value.Take();
|
983
|
+
const auto transport_size =
|
984
|
+
key_string.size() + value.wire_size + hpack_constants::kEntryOverhead;
|
985
|
+
auto md = grpc_metadata_batch::Parse(
|
986
|
+
key_string, std::move(value_slice), transport_size,
|
987
|
+
[key_string, &status, this](absl::string_view message, const Slice&) {
|
988
|
+
if (!status.ok()) return;
|
989
|
+
input_->SetErrorAndContinueParsing(
|
990
|
+
HpackParseResult::MetadataParseError(key_string));
|
991
|
+
gpr_log(GPR_ERROR, "Error parsing '%s' metadata: %s",
|
992
|
+
std::string(key_string).c_str(),
|
993
|
+
std::string(message).c_str());
|
994
|
+
});
|
995
|
+
HPackTable::Memento memento{std::move(md),
|
996
|
+
status.PersistentStreamErrorOrOk()};
|
997
|
+
input_->UpdateFrontier();
|
998
|
+
state_.parse_state = ParseState::kTop;
|
999
|
+
if (state_.add_to_table) {
|
1000
|
+
return FinishHeaderAndAddToTable(std::move(memento));
|
1001
|
+
} else {
|
1002
|
+
FinishHeaderOmitFromTable(memento);
|
1003
|
+
return true;
|
942
1004
|
}
|
943
1005
|
}
|
944
1006
|
|
1007
|
+
ValidateMetadataResult ValidateKey(absl::string_view key) {
|
1008
|
+
if (key == HttpSchemeMetadata::key() || key == HttpMethodMetadata::key() ||
|
1009
|
+
key == HttpAuthorityMetadata::key() || key == HttpPathMetadata::key() ||
|
1010
|
+
key == HttpStatusMetadata::key()) {
|
1011
|
+
return ValidateMetadataResult::kOk;
|
1012
|
+
}
|
1013
|
+
return ValidateHeaderKeyIsLegal(key);
|
1014
|
+
}
|
1015
|
+
|
945
1016
|
// Emit an indexed field
|
946
1017
|
bool FinishIndexed(absl::optional<uint32_t> index) {
|
947
|
-
|
1018
|
+
state_.dynamic_table_updates_allowed = 0;
|
948
1019
|
if (!index.has_value()) return false;
|
949
|
-
const auto* elem =
|
1020
|
+
const auto* elem = state_.hpack_table.Lookup(*index);
|
950
1021
|
if (GPR_UNLIKELY(elem == nullptr)) {
|
951
1022
|
InvalidHPackIndexError(*index);
|
952
1023
|
return false;
|
@@ -958,15 +1029,16 @@ class HPackParser::Parser {
|
|
958
1029
|
// finish parsing a max table size change
|
959
1030
|
bool FinishMaxTableSize(absl::optional<uint32_t> size) {
|
960
1031
|
if (!size.has_value()) return false;
|
961
|
-
if (
|
962
|
-
input_->SetErrorAndStopParsing(
|
963
|
-
|
1032
|
+
if (state_.dynamic_table_updates_allowed == 0) {
|
1033
|
+
input_->SetErrorAndStopParsing(
|
1034
|
+
HpackParseResult::TooManyDynamicTableSizeChangesError());
|
964
1035
|
return false;
|
965
1036
|
}
|
966
|
-
|
967
|
-
|
968
|
-
|
969
|
-
|
1037
|
+
state_.dynamic_table_updates_allowed--;
|
1038
|
+
if (!state_.hpack_table.SetCurrentTableSize(*size)) {
|
1039
|
+
input_->SetErrorAndStopParsing(
|
1040
|
+
HpackParseResult::IllegalTableSizeChangeError(
|
1041
|
+
*size, state_.hpack_table.max_bytes()));
|
970
1042
|
return false;
|
971
1043
|
}
|
972
1044
|
return true;
|
@@ -975,51 +1047,13 @@ class HPackParser::Parser {
|
|
975
1047
|
// Set an invalid hpack index error if no error has been set. Returns result
|
976
1048
|
// unmodified.
|
977
1049
|
void InvalidHPackIndexError(uint32_t index) {
|
978
|
-
input_->SetErrorAndStopParsing(
|
979
|
-
|
980
|
-
StatusIntProperty::kIndex,
|
981
|
-
static_cast<intptr_t>(index)),
|
982
|
-
StatusIntProperty::kSize,
|
983
|
-
static_cast<intptr_t>(this->table_->num_entries())));
|
984
|
-
}
|
985
|
-
|
986
|
-
GPR_ATTRIBUTE_NOINLINE
|
987
|
-
void HandleMetadataParseError(const absl::Status& status) {
|
988
|
-
if (metadata_buffer_ != nullptr) {
|
989
|
-
metadata_buffer_->Clear();
|
990
|
-
metadata_buffer_ = nullptr;
|
991
|
-
}
|
992
|
-
// StreamId is used as a signal to skip this stream but keep the connection
|
993
|
-
// alive
|
994
|
-
input_->SetErrorAndContinueParsing(status);
|
995
|
-
}
|
996
|
-
|
997
|
-
GPR_ATTRIBUTE_NOINLINE
|
998
|
-
void HandleMetadataHardSizeLimitExceeded(const HPackTable::Memento& md) {
|
999
|
-
// Collect a summary of sizes so far for debugging
|
1000
|
-
// Do not collect contents, for fear of exposing PII.
|
1001
|
-
std::string summary;
|
1002
|
-
std::string error_message;
|
1003
|
-
if (metadata_buffer_ != nullptr) {
|
1004
|
-
MetadataSizeLimitExceededEncoder encoder(summary);
|
1005
|
-
metadata_buffer_->Encode(&encoder);
|
1006
|
-
}
|
1007
|
-
summary = absl::StrCat("; adding ", md.md.key(), " (length ",
|
1008
|
-
md.md.transport_size(), "B)",
|
1009
|
-
summary.empty() ? "" : " to ", summary);
|
1010
|
-
error_message = absl::StrCat(
|
1011
|
-
"received metadata size exceeds hard limit (", *frame_length_, " vs. ",
|
1012
|
-
metadata_early_detection_->hard_limit(), ")", summary);
|
1013
|
-
HandleMetadataParseError(absl::ResourceExhaustedError(error_message));
|
1050
|
+
input_->SetErrorAndStopParsing(
|
1051
|
+
HpackParseResult::InvalidHpackIndexError(index));
|
1014
1052
|
}
|
1015
1053
|
|
1016
1054
|
Input* const input_;
|
1017
|
-
grpc_metadata_batch
|
1018
|
-
|
1019
|
-
uint8_t* const dynamic_table_updates_allowed_;
|
1020
|
-
uint32_t* const frame_length_;
|
1021
|
-
// Random early detection of metadata size limits.
|
1022
|
-
RandomEarlyDetection* metadata_early_detection_;
|
1055
|
+
grpc_metadata_batch*& metadata_buffer_;
|
1056
|
+
InterSliceState& state_;
|
1023
1057
|
const LogInfo log_info_;
|
1024
1058
|
};
|
1025
1059
|
|
@@ -1051,9 +1085,8 @@ void HPackParser::BeginFrame(grpc_metadata_batch* metadata_buffer,
|
|
1051
1085
|
}
|
1052
1086
|
boundary_ = boundary;
|
1053
1087
|
priority_ = priority;
|
1054
|
-
|
1055
|
-
|
1056
|
-
metadata_early_detection_ = RandomEarlyDetection(
|
1088
|
+
state_.dynamic_table_updates_allowed = 2;
|
1089
|
+
state_.metadata_early_detection.SetLimits(
|
1057
1090
|
/*soft_limit=*/metadata_size_soft_limit,
|
1058
1091
|
/*hard_limit=*/metadata_size_hard_limit);
|
1059
1092
|
log_info_ = log_info;
|
@@ -1061,36 +1094,43 @@ void HPackParser::BeginFrame(grpc_metadata_batch* metadata_buffer,
|
|
1061
1094
|
|
1062
1095
|
grpc_error_handle HPackParser::Parse(const grpc_slice& slice, bool is_last) {
|
1063
1096
|
if (GPR_UNLIKELY(!unparsed_bytes_.empty())) {
|
1097
|
+
unparsed_bytes_.insert(unparsed_bytes_.end(), GRPC_SLICE_START_PTR(slice),
|
1098
|
+
GRPC_SLICE_END_PTR(slice));
|
1099
|
+
if (!(is_last && is_boundary()) &&
|
1100
|
+
unparsed_bytes_.size() < min_progress_size_) {
|
1101
|
+
// We wouldn't make progress anyway, skip out.
|
1102
|
+
return absl::OkStatus();
|
1103
|
+
}
|
1064
1104
|
std::vector<uint8_t> buffer = std::move(unparsed_bytes_);
|
1065
|
-
|
1066
|
-
|
1067
|
-
|
1068
|
-
Input(nullptr, buffer.data(), buffer.data() + buffer.size()), is_last);
|
1105
|
+
return ParseInput(Input(nullptr, buffer.data(),
|
1106
|
+
buffer.data() + buffer.size(), state_.frame_error),
|
1107
|
+
is_last);
|
1069
1108
|
}
|
1070
1109
|
return ParseInput(Input(slice.refcount, GRPC_SLICE_START_PTR(slice),
|
1071
|
-
GRPC_SLICE_END_PTR(slice)),
|
1110
|
+
GRPC_SLICE_END_PTR(slice), state_.frame_error),
|
1072
1111
|
is_last);
|
1073
1112
|
}
|
1074
1113
|
|
1075
1114
|
grpc_error_handle HPackParser::ParseInput(Input input, bool is_last) {
|
1076
1115
|
ParseInputInner(&input);
|
1077
|
-
if (is_last) {
|
1078
|
-
if (
|
1116
|
+
if (is_last && is_boundary()) {
|
1117
|
+
if (state_.metadata_early_detection.Reject(state_.frame_length)) {
|
1079
1118
|
HandleMetadataSoftSizeLimitExceeded(&input);
|
1080
1119
|
}
|
1081
|
-
global_stats().IncrementHttp2MetadataSize(
|
1082
|
-
|
1083
|
-
|
1084
|
-
|
1085
|
-
|
1086
|
-
|
1087
|
-
|
1088
|
-
|
1120
|
+
global_stats().IncrementHttp2MetadataSize(state_.frame_length);
|
1121
|
+
if (!state_.frame_error.connection_error() &&
|
1122
|
+
(input.eof_error() || state_.parse_state != ParseState::kTop)) {
|
1123
|
+
state_.frame_error = HpackParseResult::IncompleteHeaderAtBoundaryError();
|
1124
|
+
}
|
1125
|
+
state_.frame_length = 0;
|
1126
|
+
return std::exchange(state_.frame_error, HpackParseResult()).Materialize();
|
1127
|
+
} else {
|
1128
|
+
if (input.eof_error() && !state_.frame_error.connection_error()) {
|
1129
|
+
unparsed_bytes_ = std::vector<uint8_t>(input.frontier(), input.end_ptr());
|
1130
|
+
min_progress_size_ = input.min_progress_size();
|
1089
1131
|
}
|
1090
|
-
|
1091
|
-
return input.TakeError();
|
1132
|
+
return state_.frame_error.Materialize();
|
1092
1133
|
}
|
1093
|
-
return input.TakeError();
|
1094
1134
|
}
|
1095
1135
|
|
1096
1136
|
void HPackParser::ParseInputInner(Input* input) {
|
@@ -1099,7 +1139,7 @@ void HPackParser::ParseInputInner(Input* input) {
|
|
1099
1139
|
break;
|
1100
1140
|
case Priority::Included: {
|
1101
1141
|
if (input->remaining() < 5) {
|
1102
|
-
input->UnexpectedEOF();
|
1142
|
+
input->UnexpectedEOF(/*min_progress_size=*/5);
|
1103
1143
|
return;
|
1104
1144
|
}
|
1105
1145
|
input->Advance(5);
|
@@ -1108,10 +1148,8 @@ void HPackParser::ParseInputInner(Input* input) {
|
|
1108
1148
|
}
|
1109
1149
|
}
|
1110
1150
|
while (!input->end_of_stream()) {
|
1111
|
-
if (GPR_UNLIKELY(
|
1112
|
-
|
1113
|
-
&metadata_early_detection_, log_info_)
|
1114
|
-
.Parse())) {
|
1151
|
+
if (GPR_UNLIKELY(
|
1152
|
+
!Parser(input, metadata_buffer_, state_, log_info_).Parse())) {
|
1115
1153
|
return;
|
1116
1154
|
}
|
1117
1155
|
input->UpdateFrontier();
|
@@ -1121,24 +1159,10 @@ void HPackParser::ParseInputInner(Input* input) {
|
|
1121
1159
|
void HPackParser::FinishFrame() { metadata_buffer_ = nullptr; }
|
1122
1160
|
|
1123
1161
|
void HPackParser::HandleMetadataSoftSizeLimitExceeded(Input* input) {
|
1124
|
-
// Collect a summary of sizes so far for debugging
|
1125
|
-
// Do not collect contents, for fear of exposing PII.
|
1126
|
-
std::string summary;
|
1127
|
-
std::string error_message;
|
1128
|
-
if (metadata_buffer_ != nullptr) {
|
1129
|
-
MetadataSizeLimitExceededEncoder encoder(summary);
|
1130
|
-
metadata_buffer_->Encode(&encoder);
|
1131
|
-
}
|
1132
|
-
error_message = absl::StrCat(
|
1133
|
-
"received metadata size exceeds soft limit (", frame_length_, " vs. ",
|
1134
|
-
metadata_early_detection_.soft_limit(),
|
1135
|
-
"), rejecting requests with some random probability", summary);
|
1136
|
-
if (metadata_buffer_ != nullptr) {
|
1137
|
-
metadata_buffer_->Clear();
|
1138
|
-
metadata_buffer_ = nullptr;
|
1139
|
-
}
|
1140
1162
|
input->SetErrorAndContinueParsing(
|
1141
|
-
|
1163
|
+
HpackParseResult::SoftMetadataLimitExceededError(
|
1164
|
+
std::exchange(metadata_buffer_, nullptr), state_.frame_length,
|
1165
|
+
state_.metadata_early_detection.soft_limit()));
|
1142
1166
|
}
|
1143
1167
|
|
1144
1168
|
} // namespace grpc_core
|