grpc 1.54.0 → 1.54.2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of grpc might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/Makefile +2 -2
- data/src/core/ext/filters/client_channel/lb_policy/rls/rls.cc +1 -1
- data/src/core/ext/gcp/metadata_query.cc +5 -10
- data/src/core/ext/gcp/metadata_query.h +10 -5
- data/src/core/ext/transport/chttp2/transport/bin_encoder.cc +12 -8
- data/src/core/ext/transport/chttp2/transport/bin_encoder.h +5 -1
- data/src/core/ext/transport/chttp2/transport/hpack_encoder.cc +118 -222
- data/src/core/ext/transport/chttp2/transport/hpack_encoder.h +295 -113
- data/src/core/ext/transport/chttp2/transport/hpack_encoder_table.cc +2 -0
- data/src/core/ext/transport/chttp2/transport/hpack_encoder_table.h +2 -0
- data/src/core/ext/transport/chttp2/transport/hpack_parser.cc +451 -277
- data/src/core/ext/transport/chttp2/transport/hpack_parser.h +3 -1
- data/src/core/ext/transport/chttp2/transport/hpack_parser_table.cc +14 -12
- data/src/core/ext/transport/chttp2/transport/hpack_parser_table.h +9 -1
- data/src/core/lib/event_engine/event_engine.cc +4 -4
- data/src/core/lib/surface/validate_metadata.cc +43 -42
- data/src/core/lib/surface/validate_metadata.h +9 -0
- data/src/core/lib/transport/metadata_batch.h +110 -2
- data/src/core/lib/transport/parsed_metadata.h +16 -6
- data/src/ruby/lib/grpc/version.rb +1 -1
- metadata +2 -2
@@ -38,7 +38,6 @@
|
|
38
38
|
#include "absl/types/span.h"
|
39
39
|
#include "absl/types/variant.h"
|
40
40
|
|
41
|
-
#include <grpc/status.h>
|
42
41
|
#include <grpc/support/log.h>
|
43
42
|
|
44
43
|
#include "src/core/ext/transport/chttp2/transport/decode_huff.h"
|
@@ -46,9 +45,11 @@
|
|
46
45
|
#include "src/core/lib/debug/stats.h"
|
47
46
|
#include "src/core/lib/debug/stats_data.h"
|
48
47
|
#include "src/core/lib/debug/trace.h"
|
48
|
+
#include "src/core/lib/gprpp/crash.h"
|
49
49
|
#include "src/core/lib/gprpp/status_helper.h"
|
50
50
|
#include "src/core/lib/slice/slice.h"
|
51
51
|
#include "src/core/lib/slice/slice_refcount.h"
|
52
|
+
#include "src/core/lib/surface/validate_metadata.h"
|
52
53
|
#include "src/core/lib/transport/parsed_metadata.h"
|
53
54
|
|
54
55
|
// IWYU pragma: no_include <type_traits>
|
@@ -80,6 +81,40 @@ struct Base64InverseTable {
|
|
80
81
|
};
|
81
82
|
|
82
83
|
constexpr Base64InverseTable kBase64InverseTable;
|
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
|
+
};
|
83
118
|
} // namespace
|
84
119
|
|
85
120
|
// Input tracks the current byte through the input data and provides it
|
@@ -121,7 +156,8 @@ class HPackParser::Input {
|
|
121
156
|
// of stream
|
122
157
|
absl::optional<uint8_t> Next() {
|
123
158
|
if (end_of_stream()) {
|
124
|
-
|
159
|
+
UnexpectedEOF();
|
160
|
+
return absl::optional<uint8_t>();
|
125
161
|
}
|
126
162
|
return *begin_++;
|
127
163
|
}
|
@@ -187,7 +223,10 @@ class HPackParser::Input {
|
|
187
223
|
// Parse a string prefix
|
188
224
|
absl::optional<StringPrefix> ParseStringPrefix() {
|
189
225
|
auto cur = Next();
|
190
|
-
if (!cur.has_value())
|
226
|
+
if (!cur.has_value()) {
|
227
|
+
GPR_DEBUG_ASSERT(eof_error());
|
228
|
+
return {};
|
229
|
+
}
|
191
230
|
// Huffman if the top bit is 1
|
192
231
|
const bool huff = (*cur & 0x80) != 0;
|
193
232
|
// String length
|
@@ -195,14 +234,19 @@ class HPackParser::Input {
|
|
195
234
|
if (strlen == 0x7f) {
|
196
235
|
// all ones ==> varint string length
|
197
236
|
auto v = ParseVarint(0x7f);
|
198
|
-
if (!v.has_value())
|
237
|
+
if (!v.has_value()) {
|
238
|
+
GPR_DEBUG_ASSERT(eof_error());
|
239
|
+
return {};
|
240
|
+
}
|
199
241
|
strlen = *v;
|
200
242
|
}
|
201
243
|
return StringPrefix{strlen, huff};
|
202
244
|
}
|
203
245
|
|
204
246
|
// Check if we saw an EOF.. must be verified before looking at TakeError
|
205
|
-
bool eof_error() const {
|
247
|
+
bool eof_error() const {
|
248
|
+
return eof_error_ || (!error_.ok() && !IsStreamError(error_));
|
249
|
+
}
|
206
250
|
|
207
251
|
// Extract the parse error, leaving the current error as NONE.
|
208
252
|
grpc_error_handle TakeError() {
|
@@ -211,34 +255,33 @@ class HPackParser::Input {
|
|
211
255
|
return out;
|
212
256
|
}
|
213
257
|
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
258
|
+
bool has_error() const { return !error_.ok(); }
|
259
|
+
|
260
|
+
// Set the current error - tweaks the error to include a stream id so that
|
261
|
+
// chttp2 does not close the connection.
|
262
|
+
// Intended for errors that are specific to a stream and recoverable.
|
263
|
+
// Callers should ensure that any hpack table updates happen.
|
264
|
+
GPR_ATTRIBUTE_NOINLINE void SetErrorAndContinueParsing(
|
265
|
+
grpc_error_handle error) {
|
266
|
+
GPR_ASSERT(!error.ok());
|
267
|
+
// StreamId is used as a signal to skip this stream but keep the connection
|
268
|
+
// alive
|
269
|
+
SetError(EnsureStreamError(std::move(error)));
|
222
270
|
}
|
223
271
|
|
224
|
-
//
|
225
|
-
//
|
226
|
-
|
227
|
-
GPR_ATTRIBUTE_NOINLINE
|
228
|
-
|
229
|
-
|
230
|
-
error_ = error_factory();
|
272
|
+
// Set the current error, and skip past remaining bytes.
|
273
|
+
// Intended for unrecoverable errors, with the expectation that they will
|
274
|
+
// close the connection on return to chttp2.
|
275
|
+
GPR_ATTRIBUTE_NOINLINE void SetErrorAndStopParsing(grpc_error_handle error) {
|
276
|
+
GPR_ASSERT(!error.ok());
|
277
|
+
SetError(std::move(error));
|
231
278
|
begin_ = end_;
|
232
|
-
return return_value;
|
233
279
|
}
|
234
280
|
|
235
|
-
// Set the error to an unexpected eof
|
236
|
-
|
237
|
-
|
238
|
-
T UnexpectedEOF(T return_value) {
|
239
|
-
if (!error_.ok()) return return_value;
|
281
|
+
// Set the error to an unexpected eof
|
282
|
+
void UnexpectedEOF() {
|
283
|
+
if (!error_.ok() && !IsStreamError(error_)) return;
|
240
284
|
eof_error_ = true;
|
241
|
-
return return_value;
|
242
285
|
}
|
243
286
|
|
244
287
|
// Update the frontier - signifies we've successfully parsed another element
|
@@ -251,14 +294,24 @@ class HPackParser::Input {
|
|
251
294
|
// Helper to set the error to out of range for ParseVarint
|
252
295
|
absl::optional<uint32_t> ParseVarintOutOfRange(uint32_t value,
|
253
296
|
uint8_t last_byte) {
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
297
|
+
SetErrorAndStopParsing(absl::InternalError(absl::StrFormat(
|
298
|
+
"integer overflow in hpack integer decoding: have 0x%08x, "
|
299
|
+
"got byte 0x%02x on byte 5",
|
300
|
+
value, last_byte)));
|
301
|
+
return absl::optional<uint32_t>();
|
302
|
+
}
|
303
|
+
|
304
|
+
// If no error is set, set it to the given error (i.e. first error wins)
|
305
|
+
// Do not use this directly, instead use SetErrorAndContinueParsing or
|
306
|
+
// SetErrorAndStopParsing.
|
307
|
+
void SetError(grpc_error_handle error) {
|
308
|
+
if (!error_.ok() || eof_error_) {
|
309
|
+
if (!IsStreamError(error) && IsStreamError(error_)) {
|
310
|
+
error_ = std::move(error); // connection errors dominate
|
311
|
+
}
|
312
|
+
return;
|
313
|
+
}
|
314
|
+
error_ = std::move(error);
|
262
315
|
}
|
263
316
|
|
264
317
|
// Refcount if we are backed by a slice
|
@@ -279,6 +332,21 @@ class HPackParser::Input {
|
|
279
332
|
// management characteristics
|
280
333
|
class HPackParser::String {
|
281
334
|
public:
|
335
|
+
// ParseResult carries both a ParseStatus and the parsed string
|
336
|
+
struct ParseResult;
|
337
|
+
// Result of parsing a string
|
338
|
+
enum class ParseStatus {
|
339
|
+
// Parsed OK
|
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>()) {}
|
282
350
|
String(const String&) = delete;
|
283
351
|
String& operator=(const String&) = delete;
|
284
352
|
String(String&& other) noexcept : value_(std::move(other.value_)) {
|
@@ -308,72 +376,10 @@ class HPackParser::String {
|
|
308
376
|
}
|
309
377
|
|
310
378
|
// Parse a non-binary string
|
311
|
-
static
|
312
|
-
auto pfx = input->ParseStringPrefix();
|
313
|
-
if (!pfx.has_value()) return {};
|
314
|
-
if (pfx->huff) {
|
315
|
-
// Huffman coded
|
316
|
-
std::vector<uint8_t> output;
|
317
|
-
auto v = ParseHuff(input, pfx->length,
|
318
|
-
[&output](uint8_t c) { output.push_back(c); });
|
319
|
-
if (!v) return {};
|
320
|
-
return String(std::move(output));
|
321
|
-
}
|
322
|
-
return ParseUncompressed(input, pfx->length);
|
323
|
-
}
|
379
|
+
static ParseResult Parse(Input* input);
|
324
380
|
|
325
381
|
// Parse a binary string
|
326
|
-
static
|
327
|
-
auto pfx = input->ParseStringPrefix();
|
328
|
-
if (!pfx.has_value()) return {};
|
329
|
-
if (!pfx->huff) {
|
330
|
-
if (pfx->length > 0 && input->peek() == 0) {
|
331
|
-
// 'true-binary'
|
332
|
-
input->Advance(1);
|
333
|
-
return ParseUncompressed(input, pfx->length - 1);
|
334
|
-
}
|
335
|
-
// Base64 encoded... pull out the string, then unbase64 it
|
336
|
-
auto base64 = ParseUncompressed(input, pfx->length);
|
337
|
-
if (!base64.has_value()) return {};
|
338
|
-
return Unbase64(input, std::move(*base64));
|
339
|
-
} else {
|
340
|
-
// Huffman encoded...
|
341
|
-
std::vector<uint8_t> decompressed;
|
342
|
-
// State here says either we don't know if it's base64 or binary, or we do
|
343
|
-
// and what is it.
|
344
|
-
enum class State { kUnsure, kBinary, kBase64 };
|
345
|
-
State state = State::kUnsure;
|
346
|
-
auto decompressed_ok =
|
347
|
-
ParseHuff(input, pfx->length, [&state, &decompressed](uint8_t c) {
|
348
|
-
if (state == State::kUnsure) {
|
349
|
-
// First byte... if it's zero it's binary
|
350
|
-
if (c == 0) {
|
351
|
-
// Save the type, and skip the zero
|
352
|
-
state = State::kBinary;
|
353
|
-
return;
|
354
|
-
} else {
|
355
|
-
// Flag base64, store this value
|
356
|
-
state = State::kBase64;
|
357
|
-
}
|
358
|
-
}
|
359
|
-
// Non-first byte, or base64 first byte
|
360
|
-
decompressed.push_back(c);
|
361
|
-
});
|
362
|
-
if (!decompressed_ok) return {};
|
363
|
-
switch (state) {
|
364
|
-
case State::kUnsure:
|
365
|
-
// No bytes, empty span
|
366
|
-
return String(absl::Span<const uint8_t>());
|
367
|
-
case State::kBinary:
|
368
|
-
// Binary, we're done
|
369
|
-
return String(std::move(decompressed));
|
370
|
-
case State::kBase64:
|
371
|
-
// Base64 - unpack it
|
372
|
-
return Unbase64(input, String(std::move(decompressed)));
|
373
|
-
}
|
374
|
-
GPR_UNREACHABLE_CODE(abort(););
|
375
|
-
}
|
376
|
-
}
|
382
|
+
static ParseResult ParseBinary(Input* input);
|
377
383
|
|
378
384
|
private:
|
379
385
|
void AppendBytes(const uint8_t* data, size_t length);
|
@@ -385,54 +391,27 @@ class HPackParser::String {
|
|
385
391
|
// Parse some huffman encoded bytes, using output(uint8_t b) to emit each
|
386
392
|
// decoded byte.
|
387
393
|
template <typename Out>
|
388
|
-
static
|
394
|
+
static ParseStatus ParseHuff(Input* input, uint32_t length, Out output) {
|
389
395
|
// If there's insufficient bytes remaining, return now.
|
390
396
|
if (input->remaining() < length) {
|
391
|
-
|
397
|
+
input->UnexpectedEOF();
|
398
|
+
GPR_DEBUG_ASSERT(input->eof_error());
|
399
|
+
return ParseStatus::kEof;
|
392
400
|
}
|
393
401
|
// Grab the byte range, and iterate through it.
|
394
402
|
const uint8_t* p = input->cur_ptr();
|
395
403
|
input->Advance(length);
|
396
|
-
return HuffDecoder<Out>(output, p, p + length).Run()
|
404
|
+
return HuffDecoder<Out>(output, p, p + length).Run()
|
405
|
+
? ParseStatus::kOk
|
406
|
+
: ParseStatus::kParseHuffFailed;
|
397
407
|
}
|
398
408
|
|
399
409
|
// Parse some uncompressed string bytes.
|
400
|
-
static
|
401
|
-
|
402
|
-
// Check there's enough bytes
|
403
|
-
if (input->remaining() < length) {
|
404
|
-
return input->UnexpectedEOF(absl::optional<String>());
|
405
|
-
}
|
406
|
-
auto* refcount = input->slice_refcount();
|
407
|
-
auto* p = input->cur_ptr();
|
408
|
-
input->Advance(length);
|
409
|
-
if (refcount != nullptr) {
|
410
|
-
return String(refcount, p, p + length);
|
411
|
-
} else {
|
412
|
-
return String(absl::Span<const uint8_t>(p, length));
|
413
|
-
}
|
414
|
-
}
|
410
|
+
static ParseResult ParseUncompressed(Input* input, uint32_t length,
|
411
|
+
uint32_t wire_size);
|
415
412
|
|
416
413
|
// Turn base64 encoded bytes into not base64 encoded bytes.
|
417
|
-
|
418
|
-
static absl::optional<String> Unbase64(Input* input, String s) {
|
419
|
-
absl::optional<std::vector<uint8_t>> result;
|
420
|
-
if (auto* p = absl::get_if<Slice>(&s.value_)) {
|
421
|
-
result = Unbase64Loop(p->begin(), p->end());
|
422
|
-
}
|
423
|
-
if (auto* p = absl::get_if<absl::Span<const uint8_t>>(&s.value_)) {
|
424
|
-
result = Unbase64Loop(p->begin(), p->end());
|
425
|
-
}
|
426
|
-
if (auto* p = absl::get_if<std::vector<uint8_t>>(&s.value_)) {
|
427
|
-
result = Unbase64Loop(p->data(), p->data() + p->size());
|
428
|
-
}
|
429
|
-
if (!result.has_value()) {
|
430
|
-
return input->MaybeSetErrorAndReturn(
|
431
|
-
[] { return GRPC_ERROR_CREATE("illegal base64 encoding"); },
|
432
|
-
absl::optional<String>());
|
433
|
-
}
|
434
|
-
return String(std::move(*result));
|
435
|
-
}
|
414
|
+
static ParseResult Unbase64(String s);
|
436
415
|
|
437
416
|
// Main loop for Unbase64
|
438
417
|
static absl::optional<std::vector<uint8_t>> Unbase64Loop(const uint8_t* cur,
|
@@ -519,25 +498,154 @@ class HPackParser::String {
|
|
519
498
|
absl::variant<Slice, absl::Span<const uint8_t>, std::vector<uint8_t>> value_;
|
520
499
|
};
|
521
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
|
+
}
|
528
|
+
}
|
529
|
+
|
530
|
+
HPackParser::String::ParseResult HPackParser::String::Unbase64(String s) {
|
531
|
+
absl::optional<std::vector<uint8_t>> result;
|
532
|
+
if (auto* p = absl::get_if<Slice>(&s.value_)) {
|
533
|
+
result = Unbase64Loop(p->begin(), p->end());
|
534
|
+
}
|
535
|
+
if (auto* p = absl::get_if<absl::Span<const uint8_t>>(&s.value_)) {
|
536
|
+
result = Unbase64Loop(p->begin(), p->end());
|
537
|
+
}
|
538
|
+
if (auto* p = absl::get_if<std::vector<uint8_t>>(&s.value_)) {
|
539
|
+
result = Unbase64Loop(p->data(), p->data() + p->size());
|
540
|
+
}
|
541
|
+
if (!result.has_value()) {
|
542
|
+
return ParseResult{ParseStatus::kUnbase64Failed, s.string_view().length(),
|
543
|
+
String{}};
|
544
|
+
}
|
545
|
+
return ParseResult{ParseStatus::kOk, s.string_view().length(),
|
546
|
+
String(std::move(*result))};
|
547
|
+
}
|
548
|
+
|
549
|
+
HPackParser::String::ParseResult HPackParser::String::Parse(Input* input) {
|
550
|
+
auto pfx = input->ParseStringPrefix();
|
551
|
+
if (!pfx.has_value()) {
|
552
|
+
GPR_DEBUG_ASSERT(input->eof_error());
|
553
|
+
return ParseResult{ParseStatus::kEof, 0, String{}};
|
554
|
+
}
|
555
|
+
if (pfx->huff) {
|
556
|
+
// Huffman coded
|
557
|
+
std::vector<uint8_t> output;
|
558
|
+
ParseStatus sts = ParseHuff(input, pfx->length,
|
559
|
+
[&output](uint8_t c) { output.push_back(c); });
|
560
|
+
size_t wire_len = output.size();
|
561
|
+
return ParseResult{sts, wire_len, String(std::move(output))};
|
562
|
+
}
|
563
|
+
return ParseUncompressed(input, pfx->length, pfx->length);
|
564
|
+
}
|
565
|
+
|
566
|
+
HPackParser::String::ParseResult HPackParser::String::ParseBinary(
|
567
|
+
Input* input) {
|
568
|
+
auto pfx = input->ParseStringPrefix();
|
569
|
+
if (!pfx.has_value()) {
|
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) {
|
575
|
+
// 'true-binary'
|
576
|
+
input->Advance(1);
|
577
|
+
return ParseUncompressed(input, pfx->length - 1, pfx->length);
|
578
|
+
}
|
579
|
+
// Base64 encoded... pull out the string, then unbase64 it
|
580
|
+
auto base64 = ParseUncompressed(input, pfx->length, pfx->length);
|
581
|
+
if (base64.status != ParseStatus::kOk) return base64;
|
582
|
+
return Unbase64(std::move(base64.value));
|
583
|
+
} else {
|
584
|
+
// Huffman encoded...
|
585
|
+
std::vector<uint8_t> decompressed;
|
586
|
+
// State here says either we don't know if it's base64 or binary, or we do
|
587
|
+
// and what is it.
|
588
|
+
enum class State { kUnsure, kBinary, kBase64 };
|
589
|
+
State state = State::kUnsure;
|
590
|
+
auto sts =
|
591
|
+
ParseHuff(input, pfx->length, [&state, &decompressed](uint8_t c) {
|
592
|
+
if (state == State::kUnsure) {
|
593
|
+
// First byte... if it's zero it's binary
|
594
|
+
if (c == 0) {
|
595
|
+
// Save the type, and skip the zero
|
596
|
+
state = State::kBinary;
|
597
|
+
return;
|
598
|
+
} else {
|
599
|
+
// Flag base64, store this value
|
600
|
+
state = State::kBase64;
|
601
|
+
}
|
602
|
+
}
|
603
|
+
// Non-first byte, or base64 first byte
|
604
|
+
decompressed.push_back(c);
|
605
|
+
});
|
606
|
+
if (sts != ParseStatus::kOk) {
|
607
|
+
return ParseResult{sts, 0, String{}};
|
608
|
+
}
|
609
|
+
switch (state) {
|
610
|
+
case State::kUnsure:
|
611
|
+
// No bytes, empty span
|
612
|
+
return ParseResult{ParseStatus::kOk, 0,
|
613
|
+
String(absl::Span<const uint8_t>())};
|
614
|
+
case State::kBinary:
|
615
|
+
// Binary, we're done
|
616
|
+
{
|
617
|
+
size_t wire_len = decompressed.size();
|
618
|
+
return ParseResult{ParseStatus::kOk, wire_len,
|
619
|
+
String(std::move(decompressed))};
|
620
|
+
}
|
621
|
+
case State::kBase64:
|
622
|
+
// Base64 - unpack it
|
623
|
+
return Unbase64(String(std::move(decompressed)));
|
624
|
+
}
|
625
|
+
GPR_UNREACHABLE_CODE(abort(););
|
626
|
+
}
|
627
|
+
}
|
628
|
+
|
522
629
|
// Parser parses one key/value pair from a byte stream.
|
523
630
|
class HPackParser::Parser {
|
524
631
|
public:
|
525
632
|
Parser(Input* input, grpc_metadata_batch* metadata_buffer, HPackTable* table,
|
526
633
|
uint8_t* dynamic_table_updates_allowed, uint32_t* frame_length,
|
527
|
-
RandomEarlyDetection* metadata_early_detection,
|
528
|
-
LogInfo log_info)
|
634
|
+
RandomEarlyDetection* metadata_early_detection, LogInfo log_info)
|
529
635
|
: input_(input),
|
530
636
|
metadata_buffer_(metadata_buffer),
|
531
637
|
table_(table),
|
532
638
|
dynamic_table_updates_allowed_(dynamic_table_updates_allowed),
|
533
639
|
frame_length_(frame_length),
|
534
640
|
metadata_early_detection_(metadata_early_detection),
|
535
|
-
is_last_(is_last),
|
536
641
|
log_info_(log_info) {}
|
537
642
|
|
538
643
|
// Skip any priority bits, or return false on failure
|
539
644
|
bool SkipPriority() {
|
540
|
-
if (input_->remaining() < 5)
|
645
|
+
if (input_->remaining() < 5) {
|
646
|
+
input_->UnexpectedEOF();
|
647
|
+
return false;
|
648
|
+
}
|
541
649
|
input_->Advance(5);
|
542
650
|
return true;
|
543
651
|
}
|
@@ -610,8 +718,9 @@ class HPackParser::Parser {
|
|
610
718
|
case 8:
|
611
719
|
if (cur == 0x80) {
|
612
720
|
// illegal value.
|
613
|
-
|
614
|
-
|
721
|
+
input_->SetErrorAndStopParsing(
|
722
|
+
absl::InternalError("Illegal hpack op code"));
|
723
|
+
return false;
|
615
724
|
}
|
616
725
|
ABSL_FALLTHROUGH_INTENDED;
|
617
726
|
case 9:
|
@@ -648,24 +757,31 @@ class HPackParser::Parser {
|
|
648
757
|
type = "???";
|
649
758
|
break;
|
650
759
|
}
|
651
|
-
gpr_log(GPR_DEBUG, "HTTP:%d:%s:%s: %s", log_info_.stream_id, type,
|
652
|
-
log_info_.is_client ? "CLI" : "SVR",
|
760
|
+
gpr_log(GPR_DEBUG, "HTTP:%d:%s:%s: %s%s", log_info_.stream_id, type,
|
761
|
+
log_info_.is_client ? "CLI" : "SVR",
|
762
|
+
memento.md.DebugString().c_str(),
|
763
|
+
memento.parse_status.ok()
|
764
|
+
? ""
|
765
|
+
: absl::StrCat(
|
766
|
+
" (parse error: ", memento.parse_status.ToString(), ")")
|
767
|
+
.c_str());
|
653
768
|
}
|
654
769
|
|
655
|
-
|
770
|
+
void EmitHeader(const HPackTable::Memento& md) {
|
656
771
|
// Pass up to the transport
|
657
|
-
|
658
|
-
|
659
|
-
|
772
|
+
*frame_length_ += md.md.transport_size();
|
773
|
+
if (!input_->has_error() &&
|
774
|
+
metadata_early_detection_->MustReject(*frame_length_)) {
|
660
775
|
// Reject any requests above hard metadata limit.
|
661
|
-
|
662
|
-
}
|
663
|
-
|
664
|
-
|
776
|
+
HandleMetadataHardSizeLimitExceeded(md);
|
777
|
+
}
|
778
|
+
if (!md.parse_status.ok()) {
|
779
|
+
// Reject any requests with invalid metadata.
|
780
|
+
HandleMetadataParseError(md.parse_status);
|
781
|
+
}
|
782
|
+
if (GPR_LIKELY(metadata_buffer_ != nullptr)) {
|
783
|
+
metadata_buffer_->Set(md.md);
|
665
784
|
}
|
666
|
-
|
667
|
-
metadata_buffer_->Set(md);
|
668
|
-
return true;
|
669
785
|
}
|
670
786
|
|
671
787
|
bool FinishHeaderAndAddToTable(absl::optional<HPackTable::Memento> md) {
|
@@ -676,73 +792,149 @@ class HPackParser::Parser {
|
|
676
792
|
LogHeader(*md);
|
677
793
|
}
|
678
794
|
// Emit whilst we own the metadata.
|
679
|
-
|
795
|
+
EmitHeader(*md);
|
680
796
|
// Add to the hpack table
|
681
797
|
grpc_error_handle err = table_->Add(std::move(*md));
|
682
798
|
if (GPR_UNLIKELY(!err.ok())) {
|
683
|
-
input_->
|
799
|
+
input_->SetErrorAndStopParsing(std::move(err));
|
684
800
|
return false;
|
685
801
|
};
|
686
|
-
return
|
802
|
+
return true;
|
687
803
|
}
|
688
804
|
|
689
805
|
bool FinishHeaderOmitFromTable(absl::optional<HPackTable::Memento> md) {
|
690
806
|
// Allow higher code to just pass in failures ... simplifies things a bit.
|
691
807
|
if (!md.has_value()) return false;
|
692
|
-
|
808
|
+
FinishHeaderOmitFromTable(*md);
|
809
|
+
return true;
|
693
810
|
}
|
694
811
|
|
695
|
-
|
812
|
+
void FinishHeaderOmitFromTable(const HPackTable::Memento& md) {
|
696
813
|
// Log if desired
|
697
814
|
if (GRPC_TRACE_FLAG_ENABLED(grpc_trace_chttp2_hpack_parser)) {
|
698
815
|
LogHeader(md);
|
699
816
|
}
|
700
|
-
|
817
|
+
EmitHeader(md);
|
701
818
|
}
|
702
819
|
|
820
|
+
// Helper type to build a memento from a key & value, and to consolidate some
|
821
|
+
// tricky error path code.
|
822
|
+
class MementoBuilder {
|
823
|
+
public:
|
824
|
+
explicit MementoBuilder(Input* input, absl::string_view key_string,
|
825
|
+
absl::Status status = absl::OkStatus())
|
826
|
+
: input_(input), key_string_(key_string), status_(std::move(status)) {}
|
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
|
+
};
|
838
|
+
}
|
839
|
+
|
840
|
+
HPackTable::Memento Build(ParsedMetadata<grpc_metadata_batch> memento) {
|
841
|
+
return HPackTable::Memento{std::move(memento), std::move(status_)};
|
842
|
+
}
|
843
|
+
|
844
|
+
// Handle the result of parsing a value.
|
845
|
+
// Returns true if parsing should continue, false if it should stop.
|
846
|
+
// Stores an error on the input if necessary.
|
847
|
+
bool HandleParseResult(String::ParseStatus status) {
|
848
|
+
auto continuable = [this](absl::string_view error) {
|
849
|
+
auto this_error = absl::InternalError(absl::StrCat(
|
850
|
+
"Error parsing '", key_string_, "' metadata: error=", error));
|
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
|
+
}
|
870
|
+
|
871
|
+
private:
|
872
|
+
Input* input_;
|
873
|
+
absl::string_view key_string_;
|
874
|
+
absl::Status status_;
|
875
|
+
};
|
876
|
+
|
703
877
|
// Parse a string encoded key and a string encoded value
|
704
878
|
absl::optional<HPackTable::Memento> ParseLiteralKey() {
|
705
879
|
auto key = String::Parse(input_);
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
|
880
|
+
switch (key.status) {
|
881
|
+
case String::ParseStatus::kOk:
|
882
|
+
break;
|
883
|
+
case String::ParseStatus::kParseHuffFailed:
|
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:
|
890
|
+
GPR_DEBUG_ASSERT(input_->eof_error());
|
891
|
+
return absl::nullopt;
|
710
892
|
}
|
711
|
-
auto key_string = key
|
712
|
-
auto
|
713
|
-
|
714
|
-
|
715
|
-
return
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
893
|
+
auto key_string = key.value.string_view();
|
894
|
+
auto value = ParseValueString(absl::EndsWith(key_string, "-bin"));
|
895
|
+
MementoBuilder builder(input_, key_string,
|
896
|
+
EnsureStreamError(ValidateKey(key_string)));
|
897
|
+
if (!builder.HandleParseResult(value.status)) return absl::nullopt;
|
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()));
|
904
|
+
}
|
905
|
+
|
906
|
+
absl::Status ValidateKey(absl::string_view key) {
|
907
|
+
if (key == HttpSchemeMetadata::key() || key == HttpMethodMetadata::key() ||
|
908
|
+
key == HttpAuthorityMetadata::key() || key == HttpPathMetadata::key() ||
|
909
|
+
key == HttpStatusMetadata::key()) {
|
910
|
+
return absl::OkStatus();
|
911
|
+
}
|
912
|
+
return ValidateHeaderKeyIsLegal(key);
|
720
913
|
}
|
721
914
|
|
722
915
|
// Parse an index encoded key and a string encoded value
|
723
916
|
absl::optional<HPackTable::Memento> ParseIdxKey(uint32_t index) {
|
724
917
|
const auto* elem = table_->Lookup(index);
|
725
918
|
if (GPR_UNLIKELY(elem == nullptr)) {
|
726
|
-
|
727
|
-
|
728
|
-
}
|
729
|
-
|
730
|
-
|
731
|
-
return
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
}
|
919
|
+
InvalidHPackIndexError(index);
|
920
|
+
return absl::optional<HPackTable::Memento>();
|
921
|
+
}
|
922
|
+
MementoBuilder builder(input_, elem->md.key(), elem->parse_status);
|
923
|
+
auto value = ParseValueString(elem->md.is_binary_header());
|
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
|
+
};
|
736
928
|
|
737
929
|
// Parse a varint index encoded key and a string encoded value
|
738
930
|
absl::optional<HPackTable::Memento> ParseVarIdxKey(uint32_t offset) {
|
739
931
|
auto index = input_->ParseVarint(offset);
|
740
|
-
if (GPR_UNLIKELY(!index.has_value())) return
|
932
|
+
if (GPR_UNLIKELY(!index.has_value())) return absl::nullopt;
|
741
933
|
return ParseIdxKey(*index);
|
742
934
|
}
|
743
935
|
|
744
936
|
// Parse a string, figuring out if it's binary or not by the key name.
|
745
|
-
|
937
|
+
String::ParseResult ParseValueString(bool is_binary) {
|
746
938
|
if (is_binary) {
|
747
939
|
return String::ParseBinary(input_);
|
748
940
|
} else {
|
@@ -756,26 +948,25 @@ class HPackParser::Parser {
|
|
756
948
|
if (!index.has_value()) return false;
|
757
949
|
const auto* elem = table_->Lookup(*index);
|
758
950
|
if (GPR_UNLIKELY(elem == nullptr)) {
|
759
|
-
|
951
|
+
InvalidHPackIndexError(*index);
|
952
|
+
return false;
|
760
953
|
}
|
761
|
-
|
954
|
+
FinishHeaderOmitFromTable(*elem);
|
955
|
+
return true;
|
762
956
|
}
|
763
957
|
|
764
958
|
// finish parsing a max table size change
|
765
959
|
bool FinishMaxTableSize(absl::optional<uint32_t> size) {
|
766
960
|
if (!size.has_value()) return false;
|
767
961
|
if (*dynamic_table_updates_allowed_ == 0) {
|
768
|
-
|
769
|
-
|
770
|
-
|
771
|
-
"More than two max table size changes in a single frame");
|
772
|
-
},
|
773
|
-
false);
|
962
|
+
input_->SetErrorAndStopParsing(absl::InternalError(
|
963
|
+
"More than two max table size changes in a single frame"));
|
964
|
+
return false;
|
774
965
|
}
|
775
966
|
(*dynamic_table_updates_allowed_)--;
|
776
967
|
grpc_error_handle err = table_->SetCurrentTableSize(*size);
|
777
968
|
if (!err.ok()) {
|
778
|
-
input_->
|
969
|
+
input_->SetErrorAndStopParsing(std::move(err));
|
779
970
|
return false;
|
780
971
|
}
|
781
972
|
return true;
|
@@ -783,47 +974,28 @@ class HPackParser::Parser {
|
|
783
974
|
|
784
975
|
// Set an invalid hpack index error if no error has been set. Returns result
|
785
976
|
// unmodified.
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
794
|
-
StatusIntProperty::kSize,
|
795
|
-
static_cast<intptr_t>(this->table_->num_entries()));
|
796
|
-
},
|
797
|
-
std::move(result));
|
798
|
-
}
|
799
|
-
|
800
|
-
class MetadataSizeLimitExceededEncoder {
|
801
|
-
public:
|
802
|
-
explicit MetadataSizeLimitExceededEncoder(std::string& summary)
|
803
|
-
: summary_(summary) {}
|
804
|
-
|
805
|
-
void Encode(const Slice& key, const Slice& value) {
|
806
|
-
AddToSummary(key.as_string_view(), value.size());
|
807
|
-
}
|
808
|
-
|
809
|
-
template <typename Key, typename Value>
|
810
|
-
void Encode(Key, const Value& value) {
|
811
|
-
AddToSummary(Key::key(), EncodedSizeOfKey(Key(), value));
|
812
|
-
}
|
977
|
+
void InvalidHPackIndexError(uint32_t index) {
|
978
|
+
input_->SetErrorAndStopParsing(grpc_error_set_int(
|
979
|
+
grpc_error_set_int(absl::InternalError("Invalid HPACK index received"),
|
980
|
+
StatusIntProperty::kIndex,
|
981
|
+
static_cast<intptr_t>(index)),
|
982
|
+
StatusIntProperty::kSize,
|
983
|
+
static_cast<intptr_t>(this->table_->num_entries())));
|
984
|
+
}
|
813
985
|
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
"B");
|
986
|
+
GPR_ATTRIBUTE_NOINLINE
|
987
|
+
void HandleMetadataParseError(const absl::Status& status) {
|
988
|
+
if (metadata_buffer_ != nullptr) {
|
989
|
+
metadata_buffer_->Clear();
|
990
|
+
metadata_buffer_ = nullptr;
|
820
991
|
}
|
821
|
-
|
822
|
-
|
992
|
+
// StreamId is used as a signal to skip this stream but keep the connection
|
993
|
+
// alive
|
994
|
+
input_->SetErrorAndContinueParsing(status);
|
995
|
+
}
|
823
996
|
|
824
997
|
GPR_ATTRIBUTE_NOINLINE
|
825
|
-
|
826
|
-
bool exceeded_hard_limit) {
|
998
|
+
void HandleMetadataHardSizeLimitExceeded(const HPackTable::Memento& md) {
|
827
999
|
// Collect a summary of sizes so far for debugging
|
828
1000
|
// Do not collect contents, for fear of exposing PII.
|
829
1001
|
std::string summary;
|
@@ -832,49 +1004,22 @@ class HPackParser::Parser {
|
|
832
1004
|
MetadataSizeLimitExceededEncoder encoder(summary);
|
833
1005
|
metadata_buffer_->Encode(&encoder);
|
834
1006
|
}
|
835
|
-
summary =
|
836
|
-
|
837
|
-
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
|
842
|
-
} else {
|
843
|
-
error_message = absl::StrCat(
|
844
|
-
"received initial metadata size exceeds soft limit (", *frame_length_,
|
845
|
-
" vs. ", metadata_early_detection_->soft_limit(),
|
846
|
-
"), rejecting requests with some random probability", summary);
|
847
|
-
}
|
848
|
-
if (metadata_buffer_ != nullptr) metadata_buffer_->Clear();
|
849
|
-
// StreamId is used as a signal to skip this stream but keep the connection
|
850
|
-
// alive
|
851
|
-
return input_->MaybeSetErrorAndReturn(
|
852
|
-
[error_message = std::move(error_message)] {
|
853
|
-
return grpc_error_set_int(
|
854
|
-
grpc_error_set_int(GRPC_ERROR_CREATE(error_message),
|
855
|
-
StatusIntProperty::kRpcStatus,
|
856
|
-
GRPC_STATUS_RESOURCE_EXHAUSTED),
|
857
|
-
StatusIntProperty::kStreamId, 0);
|
858
|
-
},
|
859
|
-
false);
|
860
|
-
}
|
861
|
-
|
862
|
-
static void ReportMetadataParseError(absl::string_view key,
|
863
|
-
absl::string_view error,
|
864
|
-
absl::string_view value) {
|
865
|
-
gpr_log(
|
866
|
-
GPR_ERROR, "Error parsing metadata: %s",
|
867
|
-
absl::StrCat("error=", error, " key=", key, " value=", value).c_str());
|
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));
|
868
1014
|
}
|
869
1015
|
|
870
1016
|
Input* const input_;
|
871
|
-
grpc_metadata_batch*
|
1017
|
+
grpc_metadata_batch* metadata_buffer_;
|
872
1018
|
HPackTable* const table_;
|
873
1019
|
uint8_t* const dynamic_table_updates_allowed_;
|
874
1020
|
uint32_t* const frame_length_;
|
875
1021
|
// Random early detection of metadata size limits.
|
876
1022
|
RandomEarlyDetection* metadata_early_detection_;
|
877
|
-
bool is_last_; // Whether this is the last frame.
|
878
1023
|
const LogInfo log_info_;
|
879
1024
|
};
|
880
1025
|
|
@@ -928,26 +1073,35 @@ grpc_error_handle HPackParser::Parse(const grpc_slice& slice, bool is_last) {
|
|
928
1073
|
}
|
929
1074
|
|
930
1075
|
grpc_error_handle HPackParser::ParseInput(Input input, bool is_last) {
|
931
|
-
|
932
|
-
if (is_last)
|
933
|
-
|
1076
|
+
ParseInputInner(&input);
|
1077
|
+
if (is_last) {
|
1078
|
+
if (metadata_early_detection_.Reject(frame_length_)) {
|
1079
|
+
HandleMetadataSoftSizeLimitExceeded(&input);
|
1080
|
+
}
|
1081
|
+
global_stats().IncrementHttp2MetadataSize(frame_length_);
|
1082
|
+
}
|
934
1083
|
if (input.eof_error()) {
|
935
1084
|
if (GPR_UNLIKELY(is_last && is_boundary())) {
|
936
|
-
|
1085
|
+
auto err = input.TakeError();
|
1086
|
+
if (!err.ok() && !IsStreamError(err)) return err;
|
1087
|
+
return absl::InternalError(
|
937
1088
|
"Incomplete header at the end of a header/continuation sequence");
|
938
1089
|
}
|
939
1090
|
unparsed_bytes_ = std::vector<uint8_t>(input.frontier(), input.end_ptr());
|
940
|
-
return
|
1091
|
+
return input.TakeError();
|
941
1092
|
}
|
942
1093
|
return input.TakeError();
|
943
1094
|
}
|
944
1095
|
|
945
|
-
|
1096
|
+
void HPackParser::ParseInputInner(Input* input) {
|
946
1097
|
switch (priority_) {
|
947
1098
|
case Priority::None:
|
948
1099
|
break;
|
949
1100
|
case Priority::Included: {
|
950
|
-
if (input->remaining() < 5)
|
1101
|
+
if (input->remaining() < 5) {
|
1102
|
+
input->UnexpectedEOF();
|
1103
|
+
return;
|
1104
|
+
}
|
951
1105
|
input->Advance(5);
|
952
1106
|
input->UpdateFrontier();
|
953
1107
|
priority_ = Priority::None;
|
@@ -956,15 +1110,35 @@ bool HPackParser::ParseInputInner(Input* input, bool is_last) {
|
|
956
1110
|
while (!input->end_of_stream()) {
|
957
1111
|
if (GPR_UNLIKELY(!Parser(input, metadata_buffer_, &table_,
|
958
1112
|
&dynamic_table_updates_allowed_, &frame_length_,
|
959
|
-
&metadata_early_detection_,
|
1113
|
+
&metadata_early_detection_, log_info_)
|
960
1114
|
.Parse())) {
|
961
|
-
return
|
1115
|
+
return;
|
962
1116
|
}
|
963
1117
|
input->UpdateFrontier();
|
964
1118
|
}
|
965
|
-
return true;
|
966
1119
|
}
|
967
1120
|
|
968
1121
|
void HPackParser::FinishFrame() { metadata_buffer_ = nullptr; }
|
969
1122
|
|
1123
|
+
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
|
+
input->SetErrorAndContinueParsing(
|
1141
|
+
absl::ResourceExhaustedError(error_message));
|
1142
|
+
}
|
1143
|
+
|
970
1144
|
} // namespace grpc_core
|