grpc 1.56.0.pre3 → 1.56.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Makefile +4 -2
- data/include/grpc/grpc_security.h +19 -0
- data/src/core/ext/filters/client_channel/lb_policy/outlier_detection/outlier_detection.cc +1 -1
- data/src/core/ext/filters/client_channel/lb_policy/weighted_round_robin/weighted_round_robin.cc +10 -1
- 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.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 +34 -12
- 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/security/credentials/tls/grpc_tls_credentials_options.cc +8 -0
- data/src/core/lib/security/credentials/tls/grpc_tls_credentials_options.h +5 -1
- data/src/core/lib/security/security_connector/ssl_utils.cc +2 -1
- data/src/core/lib/security/security_connector/ssl_utils.h +1 -1
- data/src/core/lib/security/security_connector/tls/tls_security_connector.cc +1 -1
- data/src/core/lib/surface/validate_metadata.cc +37 -22
- data/src/core/lib/surface/validate_metadata.h +13 -3
- data/src/core/tsi/ssl_transport_security.cc +5 -2
- data/src/core/tsi/ssl_transport_security.h +13 -1
- data/src/ruby/ext/grpc/rb_grpc_imports.generated.c +2 -0
- data/src/ruby/ext/grpc/rb_grpc_imports.generated.h +3 -0
- data/src/ruby/lib/grpc/version.rb +1 -1
- metadata +7 -5
@@ -21,16 +21,28 @@
|
|
21
21
|
|
22
22
|
#include <grpc/support/port_platform.h>
|
23
23
|
|
24
|
+
#include <stddef.h>
|
24
25
|
#include <stdint.h>
|
25
26
|
|
27
|
+
#include <string>
|
28
|
+
#include <utility>
|
26
29
|
#include <vector>
|
27
30
|
|
31
|
+
#include "absl/strings/str_cat.h"
|
32
|
+
#include "absl/strings/string_view.h"
|
33
|
+
#include "absl/types/optional.h"
|
34
|
+
#include "absl/types/span.h"
|
35
|
+
#include "absl/types/variant.h"
|
36
|
+
|
28
37
|
#include <grpc/slice.h>
|
29
38
|
|
30
39
|
#include "src/core/ext/transport/chttp2/transport/frame.h"
|
40
|
+
#include "src/core/ext/transport/chttp2/transport/hpack_parse_result.h"
|
31
41
|
#include "src/core/ext/transport/chttp2/transport/hpack_parser_table.h"
|
32
42
|
#include "src/core/lib/backoff/random_early_detection.h"
|
33
43
|
#include "src/core/lib/iomgr/error.h"
|
44
|
+
#include "src/core/lib/slice/slice.h"
|
45
|
+
#include "src/core/lib/slice/slice_refcount.h"
|
34
46
|
#include "src/core/lib/transport/metadata_batch.h"
|
35
47
|
|
36
48
|
// IWYU pragma: no_include <type_traits>
|
@@ -92,17 +104,150 @@ class HPackParser {
|
|
92
104
|
void FinishFrame();
|
93
105
|
|
94
106
|
// Retrieve the associated hpack table (for tests, debugging)
|
95
|
-
HPackTable* hpack_table() { return &
|
107
|
+
HPackTable* hpack_table() { return &state_.hpack_table; }
|
96
108
|
// Is the current frame a boundary of some sort
|
97
109
|
bool is_boundary() const { return boundary_ != Boundary::None; }
|
98
110
|
// Is the current frame the end of a stream
|
99
111
|
bool is_eof() const { return boundary_ == Boundary::EndOfStream; }
|
100
112
|
|
113
|
+
// How many bytes are buffered (for tests to assert on)
|
114
|
+
size_t buffered_bytes() const { return unparsed_bytes_.size(); }
|
115
|
+
|
101
116
|
private:
|
102
117
|
// Helper classes: see implementation
|
103
118
|
class Parser;
|
104
119
|
class Input;
|
105
|
-
|
120
|
+
|
121
|
+
// Helper to parse a string and turn it into a slice with appropriate memory
|
122
|
+
// management characteristics
|
123
|
+
class String {
|
124
|
+
public:
|
125
|
+
// StringResult carries both a HpackParseStatus and the parsed string
|
126
|
+
struct StringResult;
|
127
|
+
|
128
|
+
String() : value_(absl::Span<const uint8_t>()) {}
|
129
|
+
String(const String&) = delete;
|
130
|
+
String& operator=(const String&) = delete;
|
131
|
+
String(String&& other) noexcept : value_(std::move(other.value_)) {
|
132
|
+
other.value_ = absl::Span<const uint8_t>();
|
133
|
+
}
|
134
|
+
String& operator=(String&& other) noexcept {
|
135
|
+
value_ = std::move(other.value_);
|
136
|
+
other.value_ = absl::Span<const uint8_t>();
|
137
|
+
return *this;
|
138
|
+
}
|
139
|
+
|
140
|
+
// Take the value and leave this empty
|
141
|
+
Slice Take();
|
142
|
+
|
143
|
+
// Return a reference to the value as a string view
|
144
|
+
absl::string_view string_view() const;
|
145
|
+
|
146
|
+
// Parse a non-binary string
|
147
|
+
static StringResult Parse(Input* input, bool is_huff, size_t length);
|
148
|
+
|
149
|
+
// Parse a binary string
|
150
|
+
static StringResult ParseBinary(Input* input, bool is_huff, size_t length);
|
151
|
+
|
152
|
+
private:
|
153
|
+
void AppendBytes(const uint8_t* data, size_t length);
|
154
|
+
explicit String(std::vector<uint8_t> v) : value_(std::move(v)) {}
|
155
|
+
explicit String(absl::Span<const uint8_t> v) : value_(v) {}
|
156
|
+
String(grpc_slice_refcount* r, const uint8_t* begin, const uint8_t* end)
|
157
|
+
: value_(Slice::FromRefcountAndBytes(r, begin, end)) {}
|
158
|
+
|
159
|
+
// Parse some huffman encoded bytes, using output(uint8_t b) to emit each
|
160
|
+
// decoded byte.
|
161
|
+
template <typename Out>
|
162
|
+
static HpackParseStatus ParseHuff(Input* input, uint32_t length,
|
163
|
+
Out output);
|
164
|
+
|
165
|
+
// Parse some uncompressed string bytes.
|
166
|
+
static StringResult ParseUncompressed(Input* input, uint32_t length,
|
167
|
+
uint32_t wire_size);
|
168
|
+
|
169
|
+
// Turn base64 encoded bytes into not base64 encoded bytes.
|
170
|
+
static StringResult Unbase64(String s);
|
171
|
+
|
172
|
+
// Main loop for Unbase64
|
173
|
+
static absl::optional<std::vector<uint8_t>> Unbase64Loop(
|
174
|
+
const uint8_t* cur, const uint8_t* end);
|
175
|
+
|
176
|
+
absl::variant<Slice, absl::Span<const uint8_t>, std::vector<uint8_t>>
|
177
|
+
value_;
|
178
|
+
};
|
179
|
+
|
180
|
+
// Prefix for a string
|
181
|
+
struct StringPrefix {
|
182
|
+
// Number of bytes in input for string
|
183
|
+
uint32_t length;
|
184
|
+
// Is it huffman compressed
|
185
|
+
bool huff;
|
186
|
+
|
187
|
+
std::string ToString() const {
|
188
|
+
return absl::StrCat(length, " bytes ",
|
189
|
+
huff ? "huffman compressed" : "uncompressed");
|
190
|
+
}
|
191
|
+
};
|
192
|
+
|
193
|
+
// Current parse state
|
194
|
+
// ┌───┐
|
195
|
+
// │Top│
|
196
|
+
// └┬─┬┘
|
197
|
+
// │┌▽────────────────┐
|
198
|
+
// ││ParsingKeyLength │
|
199
|
+
// │└┬───────────────┬┘
|
200
|
+
// │┌▽─────────────┐┌▽──────────────┐
|
201
|
+
// ││ParsingKeyBody││SkippingKeyBody│
|
202
|
+
// │└┬─────────────┘└───┬───────────┘
|
203
|
+
// ┌▽─▽────────────────┐┌▽──────────────────┐
|
204
|
+
// │ParsingValueLength ││SkippingValueLength│
|
205
|
+
// └┬─────────────────┬┘└┬──────────────────┘
|
206
|
+
// ┌▽───────────────┐┌▽──▽─────────────┐
|
207
|
+
// │ParsingValueBody││SkippingValueBody│
|
208
|
+
// └────────────────┘└─────────────────┘
|
209
|
+
enum class ParseState : uint8_t {
|
210
|
+
// Start of one opcode
|
211
|
+
kTop,
|
212
|
+
// Parsing a literal keys length
|
213
|
+
kParsingKeyLength,
|
214
|
+
// Parsing a literal key
|
215
|
+
kParsingKeyBody,
|
216
|
+
// Skipping a literal key
|
217
|
+
kSkippingKeyBody,
|
218
|
+
// Parsing a literal value length
|
219
|
+
kParsingValueLength,
|
220
|
+
// Parsing a literal value
|
221
|
+
kParsingValueBody,
|
222
|
+
// Reading a literal value length (so we can skip it)
|
223
|
+
kSkippingValueLength,
|
224
|
+
// Skipping a literal value
|
225
|
+
kSkippingValueBody,
|
226
|
+
};
|
227
|
+
|
228
|
+
// Shared state for Parser instances between slices.
|
229
|
+
struct InterSliceState {
|
230
|
+
HPackTable hpack_table;
|
231
|
+
// Error so far for this frame (set by class Input)
|
232
|
+
HpackParseResult frame_error;
|
233
|
+
// Length of frame so far.
|
234
|
+
uint32_t frame_length = 0;
|
235
|
+
// Length of the string being parsed
|
236
|
+
uint32_t string_length;
|
237
|
+
// How many more dynamic table updates are allowed
|
238
|
+
uint8_t dynamic_table_updates_allowed;
|
239
|
+
// Current parse state
|
240
|
+
ParseState parse_state = ParseState::kTop;
|
241
|
+
// RED for overly large metadata sets
|
242
|
+
RandomEarlyDetection metadata_early_detection;
|
243
|
+
// Should the current header be added to the hpack table?
|
244
|
+
bool add_to_table;
|
245
|
+
// Is the string being parsed huffman compressed?
|
246
|
+
bool is_string_huff_compressed;
|
247
|
+
// Is the value being parsed binary?
|
248
|
+
bool is_binary_header;
|
249
|
+
absl::variant<const HPackTable::Memento*, Slice> key;
|
250
|
+
};
|
106
251
|
|
107
252
|
grpc_error_handle ParseInput(Input input, bool is_last);
|
108
253
|
void ParseInputInner(Input* input);
|
@@ -114,6 +259,8 @@ class HPackParser {
|
|
114
259
|
|
115
260
|
// Bytes that could not be parsed last parsing round
|
116
261
|
std::vector<uint8_t> unparsed_bytes_;
|
262
|
+
// How many bytes would be needed before progress could be made?
|
263
|
+
size_t min_progress_size_ = 0;
|
117
264
|
// Buffer kind of boundary
|
118
265
|
// TODO(ctiller): see if we can move this argument to Parse, and avoid
|
119
266
|
// buffering.
|
@@ -122,15 +269,9 @@ class HPackParser {
|
|
122
269
|
// TODO(ctiller): see if we can move this argument to Parse, and avoid
|
123
270
|
// buffering.
|
124
271
|
Priority priority_;
|
125
|
-
uint8_t dynamic_table_updates_allowed_;
|
126
|
-
// Length of frame so far.
|
127
|
-
uint32_t frame_length_;
|
128
|
-
RandomEarlyDetection metadata_early_detection_;
|
129
272
|
// Information for logging
|
130
273
|
LogInfo log_info_;
|
131
|
-
|
132
|
-
// hpack table
|
133
|
-
HPackTable table_;
|
274
|
+
InterSliceState state_;
|
134
275
|
};
|
135
276
|
|
136
277
|
} // namespace grpc_core
|
@@ -25,16 +25,16 @@
|
|
25
25
|
#include <algorithm>
|
26
26
|
#include <cstddef>
|
27
27
|
#include <cstring>
|
28
|
-
#include <initializer_list>
|
29
28
|
#include <utility>
|
30
29
|
|
31
30
|
#include "absl/status/status.h"
|
32
|
-
#include "absl/strings/
|
31
|
+
#include "absl/strings/str_cat.h"
|
33
32
|
#include "absl/strings/string_view.h"
|
34
33
|
|
35
34
|
#include <grpc/support/log.h>
|
36
35
|
|
37
36
|
#include "src/core/ext/transport/chttp2/transport/hpack_constants.h"
|
37
|
+
#include "src/core/ext/transport/chttp2/transport/hpack_parse_result.h"
|
38
38
|
#include "src/core/ext/transport/chttp2/transport/http_trace.h"
|
39
39
|
#include "src/core/lib/debug/trace.h"
|
40
40
|
#include "src/core/lib/slice/slice.h"
|
@@ -80,6 +80,14 @@ void HPackTable::MementoRingBuffer::Rebuild(uint32_t max_entries) {
|
|
80
80
|
entries_.swap(entries);
|
81
81
|
}
|
82
82
|
|
83
|
+
void HPackTable::MementoRingBuffer::ForEach(
|
84
|
+
absl::FunctionRef<void(uint32_t, const Memento&)> f) const {
|
85
|
+
uint32_t index = 0;
|
86
|
+
while (auto* m = Lookup(index++)) {
|
87
|
+
f(index, *m);
|
88
|
+
}
|
89
|
+
}
|
90
|
+
|
83
91
|
// Evict one element from the table
|
84
92
|
void HPackTable::EvictOne() {
|
85
93
|
auto first_entry = entries_.PopOne();
|
@@ -100,15 +108,9 @@ void HPackTable::SetMaxBytes(uint32_t max_bytes) {
|
|
100
108
|
max_bytes_ = max_bytes;
|
101
109
|
}
|
102
110
|
|
103
|
-
|
104
|
-
if (current_table_bytes_ == bytes)
|
105
|
-
|
106
|
-
}
|
107
|
-
if (bytes > max_bytes_) {
|
108
|
-
return absl::InternalError(absl::StrFormat(
|
109
|
-
"Attempt to make hpack table %d bytes when max is %d bytes", bytes,
|
110
|
-
max_bytes_));
|
111
|
-
}
|
111
|
+
bool HPackTable::SetCurrentTableSize(uint32_t bytes) {
|
112
|
+
if (current_table_bytes_ == bytes) return true;
|
113
|
+
if (bytes > max_bytes_) return false;
|
112
114
|
if (GRPC_TRACE_FLAG_ENABLED(grpc_http_trace)) {
|
113
115
|
gpr_log(GPR_INFO, "Update hpack parser table size to %d", bytes);
|
114
116
|
}
|
@@ -119,30 +121,16 @@ grpc_error_handle HPackTable::SetCurrentTableSize(uint32_t bytes) {
|
|
119
121
|
uint32_t new_cap = std::max(hpack_constants::EntriesForBytes(bytes),
|
120
122
|
hpack_constants::kInitialTableEntries);
|
121
123
|
entries_.Rebuild(new_cap);
|
122
|
-
return
|
124
|
+
return true;
|
123
125
|
}
|
124
126
|
|
125
|
-
|
126
|
-
if (current_table_bytes_ > max_bytes_)
|
127
|
-
return GRPC_ERROR_CREATE(absl::StrFormat(
|
128
|
-
"HPACK max table size reduced to %d but not reflected by hpack "
|
129
|
-
"stream (still at %d)",
|
130
|
-
max_bytes_, current_table_bytes_));
|
131
|
-
}
|
127
|
+
bool HPackTable::Add(Memento md) {
|
128
|
+
if (current_table_bytes_ > max_bytes_) return false;
|
132
129
|
|
133
130
|
// we can't add elements bigger than the max table size
|
134
131
|
if (md.md.transport_size() > current_table_bytes_) {
|
135
|
-
|
136
|
-
|
137
|
-
// size, that entry is added to the table. It is not an error to
|
138
|
-
// attempt to add an entry that is larger than the maximum size; an
|
139
|
-
// attempt to add an entry larger than the entire table causes
|
140
|
-
// the table to be emptied of all existing entries, and results in an
|
141
|
-
// empty table.
|
142
|
-
while (entries_.num_entries()) {
|
143
|
-
EvictOne();
|
144
|
-
}
|
145
|
-
return absl::OkStatus();
|
132
|
+
AddLargerThanCurrentTableSize();
|
133
|
+
return true;
|
146
134
|
}
|
147
135
|
|
148
136
|
// evict entries to ensure no overflow
|
@@ -154,7 +142,33 @@ grpc_error_handle HPackTable::Add(Memento md) {
|
|
154
142
|
// copy the finalized entry in
|
155
143
|
mem_used_ += md.md.transport_size();
|
156
144
|
entries_.Put(std::move(md));
|
157
|
-
return
|
145
|
+
return true;
|
146
|
+
}
|
147
|
+
|
148
|
+
void HPackTable::AddLargerThanCurrentTableSize() {
|
149
|
+
// HPACK draft 10 section 4.4 states:
|
150
|
+
// If the size of the new entry is less than or equal to the maximum
|
151
|
+
// size, that entry is added to the table. It is not an error to
|
152
|
+
// attempt to add an entry that is larger than the maximum size; an
|
153
|
+
// attempt to add an entry larger than the entire table causes
|
154
|
+
// the table to be emptied of all existing entries, and results in an
|
155
|
+
// empty table.
|
156
|
+
while (entries_.num_entries()) {
|
157
|
+
EvictOne();
|
158
|
+
}
|
159
|
+
}
|
160
|
+
|
161
|
+
std::string HPackTable::TestOnlyDynamicTableAsString() const {
|
162
|
+
std::string out;
|
163
|
+
entries_.ForEach([&out](uint32_t i, const Memento& m) {
|
164
|
+
if (m.parse_status.ok()) {
|
165
|
+
absl::StrAppend(&out, i, ": ", m.md.DebugString(), "\n");
|
166
|
+
} else {
|
167
|
+
absl::StrAppend(&out, i, ": ", m.parse_status.Materialize().ToString(),
|
168
|
+
"\n");
|
169
|
+
}
|
170
|
+
});
|
171
|
+
return out;
|
158
172
|
}
|
159
173
|
|
160
174
|
namespace {
|
@@ -236,7 +250,7 @@ HPackTable::Memento MakeMemento(size_t i) {
|
|
236
250
|
[](absl::string_view, const Slice&) {
|
237
251
|
abort(); // not expecting to see this
|
238
252
|
}),
|
239
|
-
|
253
|
+
HpackParseResult()};
|
240
254
|
}
|
241
255
|
|
242
256
|
} // namespace
|
@@ -23,13 +23,14 @@
|
|
23
23
|
|
24
24
|
#include <stdint.h>
|
25
25
|
|
26
|
+
#include <string>
|
26
27
|
#include <vector>
|
27
28
|
|
28
|
-
#include "absl/
|
29
|
+
#include "absl/functional/function_ref.h"
|
29
30
|
|
30
31
|
#include "src/core/ext/transport/chttp2/transport/hpack_constants.h"
|
32
|
+
#include "src/core/ext/transport/chttp2/transport/hpack_parse_result.h"
|
31
33
|
#include "src/core/lib/gprpp/no_destruct.h"
|
32
|
-
#include "src/core/lib/iomgr/error.h"
|
33
34
|
#include "src/core/lib/transport/metadata_batch.h"
|
34
35
|
#include "src/core/lib/transport/parsed_metadata.h"
|
35
36
|
|
@@ -45,11 +46,12 @@ class HPackTable {
|
|
45
46
|
HPackTable& operator=(const HPackTable&) = delete;
|
46
47
|
|
47
48
|
void SetMaxBytes(uint32_t max_bytes);
|
48
|
-
|
49
|
+
bool SetCurrentTableSize(uint32_t bytes);
|
50
|
+
uint32_t current_table_size() { return current_table_bytes_; }
|
49
51
|
|
50
52
|
struct Memento {
|
51
53
|
ParsedMetadata<grpc_metadata_batch> md;
|
52
|
-
|
54
|
+
HpackParseResult parse_status;
|
53
55
|
};
|
54
56
|
|
55
57
|
// Lookup, but don't ref.
|
@@ -68,7 +70,8 @@ class HPackTable {
|
|
68
70
|
}
|
69
71
|
|
70
72
|
// add a table entry to the index
|
71
|
-
|
73
|
+
bool Add(Memento md) GRPC_MUST_USE_RESULT;
|
74
|
+
void AddLargerThanCurrentTableSize();
|
72
75
|
|
73
76
|
// Current entry count in the table.
|
74
77
|
uint32_t num_entries() const { return entries_.num_entries(); }
|
@@ -76,6 +79,13 @@ class HPackTable {
|
|
76
79
|
// Current size of the table.
|
77
80
|
uint32_t test_only_table_size() const { return mem_used_; }
|
78
81
|
|
82
|
+
// Maximum allowed size of the table currently
|
83
|
+
uint32_t max_bytes() const { return max_bytes_; }
|
84
|
+
uint32_t current_table_bytes() const { return current_table_bytes_; }
|
85
|
+
|
86
|
+
// Dynamic table entries, stringified
|
87
|
+
std::string TestOnlyDynamicTableAsString() const;
|
88
|
+
|
79
89
|
private:
|
80
90
|
struct StaticMementos {
|
81
91
|
StaticMementos();
|
@@ -98,6 +108,9 @@ class HPackTable {
|
|
98
108
|
// Lookup the entry at index, or return nullptr if none exists.
|
99
109
|
const Memento* Lookup(uint32_t index) const;
|
100
110
|
|
111
|
+
void ForEach(absl::FunctionRef<void(uint32_t dynamic_index, const Memento&)>
|
112
|
+
f) const;
|
113
|
+
|
101
114
|
uint32_t max_entries() const { return max_entries_; }
|
102
115
|
uint32_t num_entries() const { return num_entries_; }
|
103
116
|
|
@@ -23,7 +23,6 @@
|
|
23
23
|
|
24
24
|
#include <initializer_list>
|
25
25
|
#include <string>
|
26
|
-
#include <utility>
|
27
26
|
|
28
27
|
#include "absl/base/attributes.h"
|
29
28
|
#include "absl/status/status.h"
|
@@ -470,8 +469,8 @@ static HPackParser::LogInfo hpack_parser_log_info(
|
|
470
469
|
}
|
471
470
|
|
472
471
|
static grpc_error_handle init_header_skip_frame_parser(
|
473
|
-
grpc_chttp2_transport* t, HPackParser::Priority priority_type
|
474
|
-
|
472
|
+
grpc_chttp2_transport* t, HPackParser::Priority priority_type,
|
473
|
+
bool is_eoh) {
|
475
474
|
t->parser = grpc_chttp2_transport::Parser{
|
476
475
|
"header", grpc_chttp2_header_parser_parse, &t->hpack_parser};
|
477
476
|
t->hpack_parser.BeginFrame(
|
@@ -595,7 +594,7 @@ static grpc_error_handle init_header_frame_parser(grpc_chttp2_transport* t,
|
|
595
594
|
GRPC_CHTTP2_IF_TRACING(
|
596
595
|
gpr_log(GPR_ERROR,
|
597
596
|
"grpc_chttp2_stream disbanded before CONTINUATION received"));
|
598
|
-
return init_header_skip_frame_parser(t, priority_type);
|
597
|
+
return init_header_skip_frame_parser(t, priority_type, is_eoh);
|
599
598
|
}
|
600
599
|
if (t->is_client) {
|
601
600
|
if (GPR_LIKELY((t->incoming_stream_id & 1) &&
|
@@ -605,7 +604,7 @@ static grpc_error_handle init_header_frame_parser(grpc_chttp2_transport* t,
|
|
605
604
|
GRPC_CHTTP2_IF_TRACING(gpr_log(
|
606
605
|
GPR_ERROR, "ignoring new grpc_chttp2_stream creation on client"));
|
607
606
|
}
|
608
|
-
return init_header_skip_frame_parser(t, priority_type);
|
607
|
+
return init_header_skip_frame_parser(t, priority_type, is_eoh);
|
609
608
|
} else if (GPR_UNLIKELY(t->last_new_stream_id >= t->incoming_stream_id)) {
|
610
609
|
GRPC_CHTTP2_IF_TRACING(gpr_log(
|
611
610
|
GPR_ERROR,
|
@@ -613,13 +612,13 @@ static grpc_error_handle init_header_frame_parser(grpc_chttp2_transport* t,
|
|
613
612
|
"last grpc_chttp2_stream "
|
614
613
|
"id=%d, new grpc_chttp2_stream id=%d",
|
615
614
|
t->last_new_stream_id, t->incoming_stream_id));
|
616
|
-
return init_header_skip_frame_parser(t, priority_type);
|
615
|
+
return init_header_skip_frame_parser(t, priority_type, is_eoh);
|
617
616
|
} else if (GPR_UNLIKELY((t->incoming_stream_id & 1) == 0)) {
|
618
617
|
GRPC_CHTTP2_IF_TRACING(gpr_log(
|
619
618
|
GPR_ERROR,
|
620
619
|
"ignoring grpc_chttp2_stream with non-client generated index %d",
|
621
620
|
t->incoming_stream_id));
|
622
|
-
return init_header_skip_frame_parser(t, priority_type);
|
621
|
+
return init_header_skip_frame_parser(t, priority_type, is_eoh);
|
623
622
|
} else if (GPR_UNLIKELY(
|
624
623
|
grpc_chttp2_stream_map_size(&t->stream_map) >=
|
625
624
|
t->settings[GRPC_ACKED_SETTINGS]
|
@@ -632,7 +631,7 @@ static grpc_error_handle init_header_frame_parser(grpc_chttp2_transport* t,
|
|
632
631
|
"grpc_chttp2_stream request id=%d, last grpc_chttp2_stream id=%d",
|
633
632
|
t, std::string(t->peer_string.as_string_view()).c_str(),
|
634
633
|
t->incoming_stream_id, t->last_new_stream_id));
|
635
|
-
return init_header_skip_frame_parser(t, priority_type);
|
634
|
+
return init_header_skip_frame_parser(t, priority_type, is_eoh);
|
636
635
|
}
|
637
636
|
t->last_new_stream_id = t->incoming_stream_id;
|
638
637
|
s = t->incoming_stream =
|
@@ -640,7 +639,7 @@ static grpc_error_handle init_header_frame_parser(grpc_chttp2_transport* t,
|
|
640
639
|
if (GPR_UNLIKELY(s == nullptr)) {
|
641
640
|
GRPC_CHTTP2_IF_TRACING(
|
642
641
|
gpr_log(GPR_ERROR, "grpc_chttp2_stream not accepted"));
|
643
|
-
return init_header_skip_frame_parser(t, priority_type);
|
642
|
+
return init_header_skip_frame_parser(t, priority_type, is_eoh);
|
644
643
|
}
|
645
644
|
if (t->channelz_socket != nullptr) {
|
646
645
|
t->channelz_socket->RecordStreamStartedFromRemote();
|
@@ -654,7 +653,7 @@ static grpc_error_handle init_header_frame_parser(grpc_chttp2_transport* t,
|
|
654
653
|
GRPC_CHTTP2_IF_TRACING(gpr_log(
|
655
654
|
GPR_ERROR, "skipping already closed grpc_chttp2_stream header"));
|
656
655
|
t->incoming_stream = nullptr;
|
657
|
-
return init_header_skip_frame_parser(t, priority_type);
|
656
|
+
return init_header_skip_frame_parser(t, priority_type, is_eoh);
|
658
657
|
}
|
659
658
|
t->parser = grpc_chttp2_transport::Parser{
|
660
659
|
"header", grpc_chttp2_header_parser_parse, &t->hpack_parser};
|
@@ -687,7 +686,7 @@ static grpc_error_handle init_header_frame_parser(grpc_chttp2_transport* t,
|
|
687
686
|
break;
|
688
687
|
case 2:
|
689
688
|
gpr_log(GPR_ERROR, "too many header frames received");
|
690
|
-
return init_header_skip_frame_parser(t, priority_type);
|
689
|
+
return init_header_skip_frame_parser(t, priority_type, is_eoh);
|
691
690
|
}
|
692
691
|
if (frame_type == HPackParser::LogInfo::kTrailers && !t->header_eof) {
|
693
692
|
return GRPC_ERROR_CREATE(
|
@@ -815,8 +814,9 @@ static grpc_error_handle parse_frame_slice(grpc_chttp2_transport* t,
|
|
815
814
|
&unused)) {
|
816
815
|
grpc_chttp2_parsing_become_skip_parser(t);
|
817
816
|
if (s) {
|
818
|
-
grpc_chttp2_cancel_stream(t, s,
|
817
|
+
grpc_chttp2_cancel_stream(t, s, err);
|
819
818
|
}
|
819
|
+
return absl::OkStatus();
|
820
820
|
}
|
821
821
|
return err;
|
822
822
|
}
|
@@ -43,6 +43,11 @@ class RandomEarlyDetection {
|
|
43
43
|
uint64_t soft_limit() const { return soft_limit_; }
|
44
44
|
uint64_t hard_limit() const { return hard_limit_; }
|
45
45
|
|
46
|
+
void SetLimits(uint64_t soft_limit, uint64_t hard_limit) {
|
47
|
+
soft_limit_ = soft_limit;
|
48
|
+
hard_limit_ = hard_limit;
|
49
|
+
}
|
50
|
+
|
46
51
|
private:
|
47
52
|
// The soft limit is the size at which we start rejecting items with a
|
48
53
|
// probability that increases linearly to 1 as the size approaches the hard
|
@@ -196,6 +196,7 @@ class PosixEventEngine final : public PosixEventEngineWithFdSupport,
|
|
196
196
|
const DNSResolver::ResolverOptions& options) override;
|
197
197
|
void Run(Closure* closure) override;
|
198
198
|
void Run(absl::AnyInvocable<void()> closure) override;
|
199
|
+
// Caution!! The timer implementation cannot create any fds. See #20418.
|
199
200
|
TaskHandle RunAfter(Duration when, Closure* closure) override;
|
200
201
|
TaskHandle RunAfter(Duration when,
|
201
202
|
absl::AnyInvocable<void()> closure) override;
|
@@ -23,7 +23,9 @@
|
|
23
23
|
#include <sys/socket.h> // IWYU pragma: keep
|
24
24
|
#include <unistd.h> // IWYU pragma: keep
|
25
25
|
|
26
|
+
#include <atomic>
|
26
27
|
#include <string>
|
28
|
+
#include <tuple>
|
27
29
|
#include <utility>
|
28
30
|
|
29
31
|
#include "absl/functional/any_invocable.h"
|
@@ -41,6 +43,7 @@
|
|
41
43
|
#include "src/core/lib/event_engine/posix_engine/tcp_socket_utils.h"
|
42
44
|
#include "src/core/lib/event_engine/tcp_socket_utils.h"
|
43
45
|
#include "src/core/lib/gprpp/status_helper.h"
|
46
|
+
#include "src/core/lib/gprpp/time.h"
|
44
47
|
#include "src/core/lib/iomgr/socket_mutator.h"
|
45
48
|
|
46
49
|
namespace grpc_event_engine {
|
@@ -136,6 +139,32 @@ void PosixEngineListenerImpl::AsyncConnectionAcceptor::NotifyOnAccept(
|
|
136
139
|
switch (errno) {
|
137
140
|
case EINTR:
|
138
141
|
continue;
|
142
|
+
case EMFILE:
|
143
|
+
// When the process runs out of fds, accept4() returns EMFILE. When
|
144
|
+
// this happens, the connection is left in the accept queue until
|
145
|
+
// either a read event triggers the on_read callback, or time has
|
146
|
+
// passed and the accept should be re-tried regardless. This callback
|
147
|
+
// is not cancelled, so a spurious wakeup may occur even when there's
|
148
|
+
// nothing to accept. This is not a performant code path, but if an fd
|
149
|
+
// limit has been reached, the system is likely in an unhappy state
|
150
|
+
// regardless.
|
151
|
+
GRPC_LOG_EVERY_N_SEC(1, GPR_ERROR, "%s",
|
152
|
+
"File descriptor limit reached. Retrying.");
|
153
|
+
handle_->NotifyOnRead(notify_on_accept_);
|
154
|
+
// Do not schedule another timer if one is already armed.
|
155
|
+
if (retry_timer_armed_.exchange(true)) return;
|
156
|
+
// Hold a ref while the retry timer is waiting, to prevent listener
|
157
|
+
// destruction and the races that would ensue.
|
158
|
+
Ref();
|
159
|
+
std::ignore =
|
160
|
+
engine_->RunAfter(grpc_core::Duration::Seconds(1), [this]() {
|
161
|
+
retry_timer_armed_.store(false);
|
162
|
+
if (!handle_->IsHandleShutdown()) {
|
163
|
+
handle_->SetReadable();
|
164
|
+
}
|
165
|
+
Unref();
|
166
|
+
});
|
167
|
+
return;
|
139
168
|
case EAGAIN:
|
140
169
|
case ECONNABORTED:
|
141
170
|
handle_->NotifyOnRead(notify_on_accept_);
|
@@ -121,6 +121,9 @@ class PosixEngineListenerImpl
|
|
121
121
|
ListenerSocketsContainer::ListenerSocket socket_;
|
122
122
|
EventHandle* handle_;
|
123
123
|
PosixEngineClosure* notify_on_accept_;
|
124
|
+
// Tracks the status of a backup timer to retry accept4 calls after file
|
125
|
+
// descriptor exhaustion.
|
126
|
+
std::atomic<bool> retry_timer_armed_{false};
|
124
127
|
};
|
125
128
|
class ListenerAsyncAcceptors : public ListenerSocketsContainer {
|
126
129
|
public:
|
@@ -18,6 +18,8 @@
|
|
18
18
|
|
19
19
|
#include <grpc/support/port_platform.h>
|
20
20
|
|
21
|
+
#include <utility>
|
22
|
+
|
21
23
|
#include <grpc/support/atm.h>
|
22
24
|
|
23
25
|
// FIXME: "posix" files shouldn't be depending on _GNU_SOURCE
|
@@ -78,6 +80,8 @@
|
|
78
80
|
#include "src/core/lib/transport/error_utils.h"
|
79
81
|
|
80
82
|
static std::atomic<int64_t> num_dropped_connections{0};
|
83
|
+
static constexpr grpc_core::Duration kRetryAcceptWaitTime{
|
84
|
+
grpc_core::Duration::Seconds(1)};
|
81
85
|
|
82
86
|
using ::grpc_event_engine::experimental::EndpointConfig;
|
83
87
|
using ::grpc_event_engine::experimental::EventEngine;
|
@@ -361,22 +365,38 @@ static void on_read(void* arg, grpc_error_handle err) {
|
|
361
365
|
if (fd < 0) {
|
362
366
|
if (errno == EINTR) {
|
363
367
|
continue;
|
364
|
-
}
|
365
|
-
|
368
|
+
}
|
369
|
+
// When the process runs out of fds, accept4() returns EMFILE. When this
|
370
|
+
// happens, the connection is left in the accept queue until either a
|
371
|
+
// read event triggers the on_read callback, or time has passed and the
|
372
|
+
// accept should be re-tried regardless. This callback is not cancelled,
|
373
|
+
// so a spurious wakeup may occur even when there's nothing to accept.
|
374
|
+
// This is not a performant code path, but if an fd limit has been
|
375
|
+
// reached, the system is likely in an unhappy state regardless.
|
376
|
+
if (errno == EMFILE) {
|
377
|
+
GRPC_LOG_EVERY_N_SEC(1, GPR_ERROR, "%s",
|
378
|
+
"File descriptor limit reached. Retrying.");
|
379
|
+
grpc_fd_notify_on_read(sp->emfd, &sp->read_closure);
|
380
|
+
if (gpr_atm_full_xchg(&sp->retry_timer_armed, true)) return;
|
381
|
+
grpc_timer_init(&sp->retry_timer,
|
382
|
+
grpc_core::Timestamp::Now() + kRetryAcceptWaitTime,
|
383
|
+
&sp->retry_closure);
|
384
|
+
return;
|
385
|
+
}
|
386
|
+
if (errno == EAGAIN || errno == ECONNABORTED || errno == EWOULDBLOCK) {
|
366
387
|
grpc_fd_notify_on_read(sp->emfd, &sp->read_closure);
|
367
388
|
return;
|
389
|
+
}
|
390
|
+
gpr_mu_lock(&sp->server->mu);
|
391
|
+
if (!sp->server->shutdown_listeners) {
|
392
|
+
gpr_log(GPR_ERROR, "Failed accept4: %s",
|
393
|
+
grpc_core::StrError(errno).c_str());
|
368
394
|
} else {
|
369
|
-
|
370
|
-
|
371
|
-
gpr_log(GPR_ERROR, "Failed accept4: %s",
|
372
|
-
grpc_core::StrError(errno).c_str());
|
373
|
-
} else {
|
374
|
-
// if we have shutdown listeners, accept4 could fail, and we
|
375
|
-
// needn't notify users
|
376
|
-
}
|
377
|
-
gpr_mu_unlock(&sp->server->mu);
|
378
|
-
goto error;
|
395
|
+
// if we have shutdown listeners, accept4 could fail, and we
|
396
|
+
// needn't notify users
|
379
397
|
}
|
398
|
+
gpr_mu_unlock(&sp->server->mu);
|
399
|
+
goto error;
|
380
400
|
}
|
381
401
|
|
382
402
|
if (sp->server->memory_quota->IsMemoryPressureHigh()) {
|
@@ -569,6 +589,7 @@ static grpc_error_handle clone_port(grpc_tcp_listener* listener,
|
|
569
589
|
sp->port_index = listener->port_index;
|
570
590
|
sp->fd_index = listener->fd_index + count - i;
|
571
591
|
GPR_ASSERT(sp->emfd);
|
592
|
+
grpc_tcp_server_listener_initialize_retry_timer(sp);
|
572
593
|
while (listener->server->tail->next != nullptr) {
|
573
594
|
listener->server->tail = listener->server->tail->next;
|
574
595
|
}
|
@@ -817,6 +838,7 @@ static void tcp_server_shutdown_listeners(grpc_tcp_server* s) {
|
|
817
838
|
if (s->active_ports) {
|
818
839
|
grpc_tcp_listener* sp;
|
819
840
|
for (sp = s->head; sp; sp = sp->next) {
|
841
|
+
grpc_timer_cancel(&sp->retry_timer);
|
820
842
|
grpc_fd_shutdown(sp->emfd, GRPC_ERROR_CREATE("Server shutdown"));
|
821
843
|
}
|
822
844
|
}
|