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.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/Makefile +4 -2
  3. data/include/grpc/grpc_security.h +19 -0
  4. data/src/core/ext/filters/client_channel/lb_policy/outlier_detection/outlier_detection.cc +1 -1
  5. data/src/core/ext/filters/client_channel/lb_policy/weighted_round_robin/weighted_round_robin.cc +10 -1
  6. data/src/core/ext/transport/chttp2/transport/hpack_parse_result.cc +176 -0
  7. data/src/core/ext/transport/chttp2/transport/hpack_parse_result.h +325 -0
  8. data/src/core/ext/transport/chttp2/transport/hpack_parser.cc +567 -543
  9. data/src/core/ext/transport/chttp2/transport/hpack_parser.h +150 -9
  10. data/src/core/ext/transport/chttp2/transport/hpack_parser_table.cc +46 -32
  11. data/src/core/ext/transport/chttp2/transport/hpack_parser_table.h +18 -5
  12. data/src/core/ext/transport/chttp2/transport/parsing.cc +12 -12
  13. data/src/core/lib/backoff/random_early_detection.h +5 -0
  14. data/src/core/lib/event_engine/posix_engine/posix_engine.h +1 -0
  15. data/src/core/lib/event_engine/posix_engine/posix_engine_listener.cc +29 -0
  16. data/src/core/lib/event_engine/posix_engine/posix_engine_listener.h +3 -0
  17. data/src/core/lib/iomgr/tcp_server_posix.cc +34 -12
  18. data/src/core/lib/iomgr/tcp_server_utils_posix.h +12 -0
  19. data/src/core/lib/iomgr/tcp_server_utils_posix_common.cc +21 -0
  20. data/src/core/lib/security/credentials/tls/grpc_tls_credentials_options.cc +8 -0
  21. data/src/core/lib/security/credentials/tls/grpc_tls_credentials_options.h +5 -1
  22. data/src/core/lib/security/security_connector/ssl_utils.cc +2 -1
  23. data/src/core/lib/security/security_connector/ssl_utils.h +1 -1
  24. data/src/core/lib/security/security_connector/tls/tls_security_connector.cc +1 -1
  25. data/src/core/lib/surface/validate_metadata.cc +37 -22
  26. data/src/core/lib/surface/validate_metadata.h +13 -3
  27. data/src/core/tsi/ssl_transport_security.cc +5 -2
  28. data/src/core/tsi/ssl_transport_security.h +13 -1
  29. data/src/ruby/ext/grpc/rb_grpc_imports.generated.c +2 -0
  30. data/src/ruby/ext/grpc/rb_grpc_imports.generated.h +3 -0
  31. data/src/ruby/lib/grpc/version.rb +1 -1
  32. 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 &table_; }
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
- class String;
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/str_format.h"
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
- grpc_error_handle HPackTable::SetCurrentTableSize(uint32_t bytes) {
104
- if (current_table_bytes_ == bytes) {
105
- return absl::OkStatus();
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 absl::OkStatus();
124
+ return true;
123
125
  }
124
126
 
125
- grpc_error_handle HPackTable::Add(Memento md) {
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
- // HPACK draft 10 section 4.4 states:
136
- // If the size of the new entry is less than or equal to the maximum
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 absl::OkStatus();
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
- absl::OkStatus()};
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/status/status.h"
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
- grpc_error_handle SetCurrentTableSize(uint32_t bytes);
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
- absl::Status parse_status;
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
- grpc_error_handle Add(Memento md) GRPC_MUST_USE_RESULT;
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
- bool is_eoh = t->expect_continuation_stream_id != 0;
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, std::exchange(err, absl::OkStatus()));
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
- } else if (errno == EAGAIN || errno == ECONNABORTED ||
365
- errno == EWOULDBLOCK) {
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
- gpr_mu_lock(&sp->server->mu);
370
- if (!sp->server->shutdown_listeners) {
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
  }