grpc 1.53.0 → 1.53.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/Makefile +4 -2
  3. data/include/grpc/impl/grpc_types.h +11 -2
  4. data/src/core/ext/filters/client_channel/http_proxy.cc +1 -1
  5. data/src/core/ext/filters/client_channel/lb_policy/rls/rls.cc +1 -1
  6. data/src/core/ext/transport/chttp2/transport/bin_encoder.cc +12 -8
  7. data/src/core/ext/transport/chttp2/transport/bin_encoder.h +5 -1
  8. data/src/core/ext/transport/chttp2/transport/chttp2_transport.cc +33 -2
  9. data/src/core/ext/transport/chttp2/transport/hpack_encoder.cc +118 -222
  10. data/src/core/ext/transport/chttp2/transport/hpack_encoder.h +295 -113
  11. data/src/core/ext/transport/chttp2/transport/hpack_encoder_table.cc +2 -0
  12. data/src/core/ext/transport/chttp2/transport/hpack_encoder_table.h +2 -0
  13. data/src/core/ext/transport/chttp2/transport/hpack_parser.cc +466 -273
  14. data/src/core/ext/transport/chttp2/transport/hpack_parser.h +7 -3
  15. data/src/core/ext/transport/chttp2/transport/hpack_parser_table.cc +14 -12
  16. data/src/core/ext/transport/chttp2/transport/hpack_parser_table.h +9 -1
  17. data/src/core/ext/transport/chttp2/transport/internal.h +2 -0
  18. data/src/core/ext/transport/chttp2/transport/parsing.cc +6 -0
  19. data/src/core/lib/backoff/random_early_detection.cc +31 -0
  20. data/src/core/lib/backoff/random_early_detection.h +59 -0
  21. data/src/core/lib/event_engine/posix_engine/posix_engine.h +1 -0
  22. data/src/core/lib/event_engine/posix_engine/posix_engine_listener.cc +29 -0
  23. data/src/core/lib/event_engine/posix_engine/posix_engine_listener.h +3 -0
  24. data/src/core/lib/iomgr/endpoint_pair.h +2 -2
  25. data/src/core/lib/iomgr/endpoint_pair_posix.cc +2 -2
  26. data/src/core/lib/iomgr/endpoint_pair_windows.cc +1 -1
  27. data/src/core/lib/iomgr/tcp_server_posix.cc +39 -14
  28. data/src/core/lib/iomgr/tcp_server_utils_posix.h +12 -0
  29. data/src/core/lib/iomgr/tcp_server_utils_posix_common.cc +21 -0
  30. data/src/core/lib/surface/validate_metadata.cc +43 -42
  31. data/src/core/lib/surface/validate_metadata.h +9 -0
  32. data/src/core/lib/transport/metadata_batch.cc +4 -4
  33. data/src/core/lib/transport/metadata_batch.h +153 -15
  34. data/src/core/lib/transport/parsed_metadata.h +19 -9
  35. data/src/ruby/lib/grpc/version.rb +1 -1
  36. metadata +5 -3
@@ -29,6 +29,7 @@
29
29
 
30
30
  #include "src/core/ext/transport/chttp2/transport/frame.h"
31
31
  #include "src/core/ext/transport/chttp2/transport/hpack_parser_table.h"
32
+ #include "src/core/lib/backoff/random_early_detection.h"
32
33
  #include "src/core/lib/iomgr/error.h"
33
34
  #include "src/core/lib/transport/metadata_batch.h"
34
35
 
@@ -80,7 +81,8 @@ class HPackParser {
80
81
  // Begin parsing a new frame
81
82
  // Sink receives each parsed header,
82
83
  void BeginFrame(grpc_metadata_batch* metadata_buffer,
83
- uint32_t metadata_size_limit, Boundary boundary,
84
+ uint32_t metadata_size_soft_limit,
85
+ uint32_t metadata_size_hard_limit, Boundary boundary,
84
86
  Priority priority, LogInfo log_info);
85
87
  // Start throwing away any received headers after parsing them.
86
88
  void StopBufferingFrame() { metadata_buffer_ = nullptr; }
@@ -103,7 +105,9 @@ class HPackParser {
103
105
  class String;
104
106
 
105
107
  grpc_error_handle ParseInput(Input input, bool is_last);
106
- bool ParseInputInner(Input* input);
108
+ void ParseInputInner(Input* input);
109
+ GPR_ATTRIBUTE_NOINLINE
110
+ void HandleMetadataSoftSizeLimitExceeded(Input* input);
107
111
 
108
112
  // Target metadata buffer
109
113
  grpc_metadata_batch* metadata_buffer_ = nullptr;
@@ -121,7 +125,7 @@ class HPackParser {
121
125
  uint8_t dynamic_table_updates_allowed_;
122
126
  // Length of frame so far.
123
127
  uint32_t frame_length_;
124
- uint32_t metadata_size_limit_;
128
+ RandomEarlyDetection metadata_early_detection_;
125
129
  // Information for logging
126
130
  LogInfo log_info_;
127
131
 
@@ -82,8 +82,8 @@ void HPackTable::MementoRingBuffer::Rebuild(uint32_t max_entries) {
82
82
  // Evict one element from the table
83
83
  void HPackTable::EvictOne() {
84
84
  auto first_entry = entries_.PopOne();
85
- GPR_ASSERT(first_entry.transport_size() <= mem_used_);
86
- mem_used_ -= first_entry.transport_size();
85
+ GPR_ASSERT(first_entry.md.transport_size() <= mem_used_);
86
+ mem_used_ -= first_entry.md.transport_size();
87
87
  }
88
88
 
89
89
  void HPackTable::SetMaxBytes(uint32_t max_bytes) {
@@ -104,7 +104,7 @@ grpc_error_handle HPackTable::SetCurrentTableSize(uint32_t bytes) {
104
104
  return absl::OkStatus();
105
105
  }
106
106
  if (bytes > max_bytes_) {
107
- return GRPC_ERROR_CREATE(absl::StrFormat(
107
+ return absl::InternalError(absl::StrFormat(
108
108
  "Attempt to make hpack table %d bytes when max is %d bytes", bytes,
109
109
  max_bytes_));
110
110
  }
@@ -130,7 +130,7 @@ grpc_error_handle HPackTable::Add(Memento md) {
130
130
  }
131
131
 
132
132
  // we can't add elements bigger than the max table size
133
- if (md.transport_size() > current_table_bytes_) {
133
+ if (md.md.transport_size() > current_table_bytes_) {
134
134
  // HPACK draft 10 section 4.4 states:
135
135
  // If the size of the new entry is less than or equal to the maximum
136
136
  // size, that entry is added to the table. It is not an error to
@@ -145,13 +145,13 @@ grpc_error_handle HPackTable::Add(Memento md) {
145
145
  }
146
146
 
147
147
  // evict entries to ensure no overflow
148
- while (md.transport_size() >
148
+ while (md.md.transport_size() >
149
149
  static_cast<size_t>(current_table_bytes_) - mem_used_) {
150
150
  EvictOne();
151
151
  }
152
152
 
153
153
  // copy the finalized entry in
154
- mem_used_ += md.transport_size();
154
+ mem_used_ += md.md.transport_size();
155
155
  entries_.Put(std::move(md));
156
156
  return absl::OkStatus();
157
157
  }
@@ -228,12 +228,14 @@ const StaticTableEntry kStaticTable[hpack_constants::kLastStaticEntry] = {
228
228
 
229
229
  HPackTable::Memento MakeMemento(size_t i) {
230
230
  auto sm = kStaticTable[i];
231
- return grpc_metadata_batch::Parse(
232
- sm.key, Slice::FromStaticString(sm.value),
233
- strlen(sm.key) + strlen(sm.value) + hpack_constants::kEntryOverhead,
234
- [](absl::string_view, const Slice&) {
235
- abort(); // not expecting to see this
236
- });
231
+ return HPackTable::Memento{
232
+ grpc_metadata_batch::Parse(
233
+ sm.key, Slice::FromStaticString(sm.value),
234
+ strlen(sm.key) + strlen(sm.value) + hpack_constants::kEntryOverhead,
235
+ [](absl::string_view, const Slice&) {
236
+ abort(); // not expecting to see this
237
+ }),
238
+ absl::OkStatus()};
237
239
  }
238
240
 
239
241
  } // namespace
@@ -25,6 +25,8 @@
25
25
 
26
26
  #include <vector>
27
27
 
28
+ #include "absl/status/status.h"
29
+
28
30
  #include "src/core/ext/transport/chttp2/transport/hpack_constants.h"
29
31
  #include "src/core/lib/gprpp/no_destruct.h"
30
32
  #include "src/core/lib/iomgr/error.h"
@@ -45,7 +47,10 @@ class HPackTable {
45
47
  void SetMaxBytes(uint32_t max_bytes);
46
48
  grpc_error_handle SetCurrentTableSize(uint32_t bytes);
47
49
 
48
- using Memento = ParsedMetadata<grpc_metadata_batch>;
50
+ struct Memento {
51
+ ParsedMetadata<grpc_metadata_batch> md;
52
+ absl::Status parse_status;
53
+ };
49
54
 
50
55
  // Lookup, but don't ref.
51
56
  const Memento* Lookup(uint32_t index) const {
@@ -68,6 +73,9 @@ class HPackTable {
68
73
  // Current entry count in the table.
69
74
  uint32_t num_entries() const { return entries_.num_entries(); }
70
75
 
76
+ // Current size of the table.
77
+ uint32_t test_only_table_size() const { return mem_used_; }
78
+
71
79
  private:
72
80
  struct StaticMementos {
73
81
  StaticMementos();
@@ -457,6 +457,8 @@ struct grpc_chttp2_transport
457
457
  bool keepalive_ping_started = false;
458
458
  /// keep-alive state machine state
459
459
  grpc_chttp2_keepalive_state keepalive_state;
460
+ // Soft limit on max header size.
461
+ uint32_t max_header_list_size_soft_limit = 0;
460
462
  grpc_core::ContextList* cl = nullptr;
461
463
  grpc_core::RefCountedPtr<grpc_core::channelz::SocketNode> channelz_socket;
462
464
  uint32_t num_messages_in_next_write = 0;
@@ -475,6 +475,9 @@ static grpc_error_handle init_header_skip_frame_parser(
475
475
  "header", grpc_chttp2_header_parser_parse, &t->hpack_parser};
476
476
  t->hpack_parser.BeginFrame(
477
477
  nullptr,
478
+ /*metadata_size_soft_limit=*/
479
+ t->max_header_list_size_soft_limit,
480
+ /*metadata_size_hard_limit=*/
478
481
  t->settings[GRPC_ACKED_SETTINGS]
479
482
  [GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE],
480
483
  hpack_boundary_type(t, is_eoh), priority_type,
@@ -691,6 +694,9 @@ static grpc_error_handle init_header_frame_parser(grpc_chttp2_transport* t,
691
694
  }
692
695
  t->hpack_parser.BeginFrame(
693
696
  incoming_metadata_buffer,
697
+ /*metadata_size_soft_limit=*/
698
+ t->max_header_list_size_soft_limit,
699
+ /*metadata_size_hard_limit=*/
694
700
  t->settings[GRPC_ACKED_SETTINGS]
695
701
  [GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE],
696
702
  hpack_boundary_type(t, is_eoh), priority_type,
@@ -0,0 +1,31 @@
1
+ // Copyright 2023 gRPC authors.
2
+ //
3
+ // Licensed under the Apache License, Version 2.0 (the "License");
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+
15
+ #include <grpc/support/port_platform.h>
16
+
17
+ #include "src/core/lib/backoff/random_early_detection.h"
18
+
19
+ namespace grpc_core {
20
+
21
+ bool RandomEarlyDetection::Reject(uint64_t size) {
22
+ if (size <= soft_limit_) return false;
23
+ if (size < hard_limit_) {
24
+ return absl::Bernoulli(bitgen_,
25
+ static_cast<double>(size - soft_limit_) /
26
+ static_cast<double>(hard_limit_ - soft_limit_));
27
+ }
28
+ return true;
29
+ }
30
+
31
+ } // namespace grpc_core
@@ -0,0 +1,59 @@
1
+ // Copyright 2023 gRPC authors.
2
+ //
3
+ // Licensed under the Apache License, Version 2.0 (the "License");
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+
15
+ #ifndef GRPC_SRC_CORE_LIB_BACKOFF_RANDOM_EARLY_DETECTION_H
16
+ #define GRPC_SRC_CORE_LIB_BACKOFF_RANDOM_EARLY_DETECTION_H
17
+
18
+ #include <grpc/support/port_platform.h>
19
+
20
+ #include <limits.h>
21
+
22
+ #include <cstdint>
23
+
24
+ #include "absl/random/random.h"
25
+
26
+ namespace grpc_core {
27
+
28
+ // Implements the random early detection algorithm - allows items to be rejected
29
+ // or accepted based upon their size.
30
+ class RandomEarlyDetection {
31
+ public:
32
+ RandomEarlyDetection() : soft_limit_(INT_MAX), hard_limit_(INT_MAX) {}
33
+ RandomEarlyDetection(uint64_t soft_limit, uint64_t hard_limit)
34
+ : soft_limit_(soft_limit), hard_limit_(hard_limit) {}
35
+
36
+ // Returns true if the size is greater than or equal to the hard limit - ie if
37
+ // this item must be rejected.
38
+ bool MustReject(uint64_t size) { return size >= hard_limit_; }
39
+
40
+ // Returns true if the item should be rejected.
41
+ bool Reject(uint64_t size);
42
+
43
+ uint64_t soft_limit() const { return soft_limit_; }
44
+ uint64_t hard_limit() const { return hard_limit_; }
45
+
46
+ private:
47
+ // The soft limit is the size at which we start rejecting items with a
48
+ // probability that increases linearly to 1 as the size approaches the hard
49
+ // limit.
50
+ uint64_t soft_limit_;
51
+ // The hard limit is the size at which we reject all items.
52
+ uint64_t hard_limit_;
53
+ // The bit generator used to generate random numbers.
54
+ absl::InsecureBitGen bitgen_;
55
+ };
56
+
57
+ } // namespace grpc_core
58
+
59
+ #endif // GRPC_SRC_CORE_LIB_BACKOFF_RANDOM_EARLY_DETECTION_H
@@ -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 {
@@ -133,6 +136,32 @@ void PosixEngineListenerImpl::AsyncConnectionAcceptor::NotifyOnAccept(
133
136
  switch (errno) {
134
137
  case EINTR:
135
138
  continue;
139
+ case EMFILE:
140
+ // When the process runs out of fds, accept4() returns EMFILE. When
141
+ // this happens, the connection is left in the accept queue until
142
+ // either a read event triggers the on_read callback, or time has
143
+ // passed and the accept should be re-tried regardless. This callback
144
+ // is not cancelled, so a spurious wakeup may occur even when there's
145
+ // nothing to accept. This is not a performant code path, but if an fd
146
+ // limit has been reached, the system is likely in an unhappy state
147
+ // regardless.
148
+ GRPC_LOG_EVERY_N_SEC(1, "%s",
149
+ "File descriptor limit reached. Retrying.");
150
+ handle_->NotifyOnRead(notify_on_accept_);
151
+ // Do not schedule another timer if one is already armed.
152
+ if (retry_timer_armed_.exchange(true)) return;
153
+ // Hold a ref while the retry timer is waiting, to prevent listener
154
+ // destruction and the races that would ensue.
155
+ Ref();
156
+ std::ignore =
157
+ engine_->RunAfter(grpc_core::Duration::Seconds(1), [this]() {
158
+ retry_timer_armed_.store(false);
159
+ if (!handle_->IsHandleShutdown()) {
160
+ handle_->SetReadable();
161
+ }
162
+ Unref();
163
+ });
164
+ return;
136
165
  case EAGAIN:
137
166
  case ECONNABORTED:
138
167
  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:
@@ -28,7 +28,7 @@ struct grpc_endpoint_pair {
28
28
  grpc_endpoint* server;
29
29
  };
30
30
 
31
- grpc_endpoint_pair grpc_iomgr_create_endpoint_pair(const char* name,
32
- grpc_channel_args* args);
31
+ grpc_endpoint_pair grpc_iomgr_create_endpoint_pair(
32
+ const char* name, const grpc_channel_args* args);
33
33
 
34
34
  #endif // GRPC_SRC_CORE_LIB_IOMGR_ENDPOINT_PAIR_H
@@ -55,8 +55,8 @@ static void create_sockets(int sv[2]) {
55
55
  GPR_ASSERT(grpc_set_socket_no_sigpipe_if_possible(sv[1]) == absl::OkStatus());
56
56
  }
57
57
 
58
- grpc_endpoint_pair grpc_iomgr_create_endpoint_pair(const char* name,
59
- grpc_channel_args* args) {
58
+ grpc_endpoint_pair grpc_iomgr_create_endpoint_pair(
59
+ const char* name, const grpc_channel_args* args) {
60
60
  int sv[2];
61
61
  grpc_endpoint_pair p;
62
62
  create_sockets(sv);
@@ -80,7 +80,7 @@ static void create_sockets(SOCKET sv[2]) {
80
80
  }
81
81
 
82
82
  grpc_endpoint_pair grpc_iomgr_create_endpoint_pair(
83
- const char*, grpc_channel_args* channel_args) {
83
+ const char*, const grpc_channel_args* /* channel_args */) {
84
84
  SOCKET sv[2];
85
85
  grpc_endpoint_pair p;
86
86
  create_sockets(sv);
@@ -16,13 +16,17 @@
16
16
  //
17
17
  //
18
18
 
19
+ #include <grpc/support/port_platform.h>
20
+
21
+ #include <utility>
22
+
23
+ #include <grpc/support/atm.h>
24
+
19
25
  // FIXME: "posix" files shouldn't be depending on _GNU_SOURCE
20
26
  #ifndef _GNU_SOURCE
21
27
  #define _GNU_SOURCE
22
28
  #endif
23
29
 
24
- #include <grpc/support/port_platform.h>
25
-
26
30
  #include "src/core/lib/iomgr/port.h"
27
31
 
28
32
  #ifdef GRPC_POSIX_SOCKET_TCP_SERVER
@@ -45,6 +49,7 @@
45
49
 
46
50
  #include <grpc/byte_buffer.h>
47
51
  #include <grpc/event_engine/endpoint_config.h>
52
+ #include <grpc/event_engine/event_engine.h>
48
53
  #include <grpc/support/alloc.h>
49
54
  #include <grpc/support/log.h>
50
55
  #include <grpc/support/sync.h>
@@ -74,6 +79,8 @@
74
79
  #include "src/core/lib/transport/error_utils.h"
75
80
 
76
81
  static std::atomic<int64_t> num_dropped_connections{0};
82
+ static constexpr grpc_core::Duration kRetryAcceptWaitTime{
83
+ grpc_core::Duration::Seconds(1)};
77
84
 
78
85
  using ::grpc_event_engine::experimental::EndpointConfig;
79
86
  using ::grpc_event_engine::experimental::EventEngine;
@@ -350,22 +357,38 @@ static void on_read(void* arg, grpc_error_handle err) {
350
357
  if (fd < 0) {
351
358
  if (errno == EINTR) {
352
359
  continue;
353
- } else if (errno == EAGAIN || errno == ECONNABORTED ||
354
- errno == EWOULDBLOCK) {
360
+ }
361
+ // When the process runs out of fds, accept4() returns EMFILE. When this
362
+ // happens, the connection is left in the accept queue until either a
363
+ // read event triggers the on_read callback, or time has passed and the
364
+ // accept should be re-tried regardless. This callback is not cancelled,
365
+ // so a spurious wakeup may occur even when there's nothing to accept.
366
+ // This is not a performant code path, but if an fd limit has been
367
+ // reached, the system is likely in an unhappy state regardless.
368
+ if (errno == EMFILE) {
369
+ GRPC_LOG_EVERY_N_SEC(1, "%s",
370
+ "File descriptor limit reached. Retrying.");
371
+ grpc_fd_notify_on_read(sp->emfd, &sp->read_closure);
372
+ if (gpr_atm_full_xchg(&sp->retry_timer_armed, true)) return;
373
+ grpc_timer_init(&sp->retry_timer,
374
+ grpc_core::Timestamp::Now() + kRetryAcceptWaitTime,
375
+ &sp->retry_closure);
376
+ return;
377
+ }
378
+ if (errno == EAGAIN || errno == ECONNABORTED || errno == EWOULDBLOCK) {
355
379
  grpc_fd_notify_on_read(sp->emfd, &sp->read_closure);
356
380
  return;
381
+ }
382
+ gpr_mu_lock(&sp->server->mu);
383
+ if (!sp->server->shutdown_listeners) {
384
+ gpr_log(GPR_ERROR, "Failed accept4: %s",
385
+ grpc_core::StrError(errno).c_str());
357
386
  } else {
358
- gpr_mu_lock(&sp->server->mu);
359
- if (!sp->server->shutdown_listeners) {
360
- gpr_log(GPR_ERROR, "Failed accept4: %s",
361
- grpc_core::StrError(errno).c_str());
362
- } else {
363
- // if we have shutdown listeners, accept4 could fail, and we
364
- // needn't notify users
365
- }
366
- gpr_mu_unlock(&sp->server->mu);
367
- goto error;
387
+ // if we have shutdown listeners, accept4 could fail, and we
388
+ // needn't notify users
368
389
  }
390
+ gpr_mu_unlock(&sp->server->mu);
391
+ goto error;
369
392
  }
370
393
 
371
394
  if (sp->server->memory_quota->IsMemoryPressureHigh()) {
@@ -558,6 +581,7 @@ static grpc_error_handle clone_port(grpc_tcp_listener* listener,
558
581
  sp->port_index = listener->port_index;
559
582
  sp->fd_index = listener->fd_index + count - i;
560
583
  GPR_ASSERT(sp->emfd);
584
+ grpc_tcp_server_listener_initialize_retry_timer(sp);
561
585
  while (listener->server->tail->next != nullptr) {
562
586
  listener->server->tail = listener->server->tail->next;
563
587
  }
@@ -791,6 +815,7 @@ static void tcp_server_shutdown_listeners(grpc_tcp_server* s) {
791
815
  if (s->active_ports) {
792
816
  grpc_tcp_listener* sp;
793
817
  for (sp = s->head; sp; sp = sp->next) {
818
+ grpc_timer_cancel(&sp->retry_timer);
794
819
  grpc_fd_shutdown(sp->emfd, GRPC_ERROR_CREATE("Server shutdown"));
795
820
  }
796
821
  }
@@ -30,6 +30,7 @@
30
30
  #include "src/core/lib/iomgr/resolve_address.h"
31
31
  #include "src/core/lib/iomgr/socket_utils_posix.h"
32
32
  #include "src/core/lib/iomgr/tcp_server.h"
33
+ #include "src/core/lib/iomgr/timer.h"
33
34
  #include "src/core/lib/resource_quota/memory_quota.h"
34
35
 
35
36
  // one listening port
@@ -52,6 +53,11 @@ typedef struct grpc_tcp_listener {
52
53
  // identified while iterating through 'next'.
53
54
  struct grpc_tcp_listener* sibling;
54
55
  int is_sibling;
56
+ // If an accept4() call fails, a timer is started to drain the accept queue in
57
+ // case no further connection attempts reach the gRPC server.
58
+ grpc_closure retry_closure;
59
+ grpc_timer retry_timer;
60
+ gpr_atm retry_timer_armed;
55
61
  } grpc_tcp_listener;
56
62
 
57
63
  // the overall server
@@ -139,4 +145,10 @@ grpc_error_handle grpc_tcp_server_prepare_socket(
139
145
  // Ruturn true if the platform supports ifaddrs
140
146
  bool grpc_tcp_server_have_ifaddrs(void);
141
147
 
148
+ // Initialize (but don't start) the timer and callback to retry accept4() on a
149
+ // listening socket after file descriptors have been exhausted. This must be
150
+ // called when creating a new listener.
151
+ void grpc_tcp_server_listener_initialize_retry_timer(
152
+ grpc_tcp_listener* listener);
153
+
142
154
  #endif // GRPC_SRC_CORE_LIB_IOMGR_TCP_SERVER_UTILS_POSIX_H
@@ -18,6 +18,8 @@
18
18
 
19
19
  #include <grpc/support/port_platform.h>
20
20
 
21
+ #include <grpc/support/atm.h>
22
+
21
23
  #include "src/core/lib/iomgr/port.h"
22
24
 
23
25
  #ifdef GRPC_POSIX_SOCKET_TCP_SERVER_UTILS_COMMON
@@ -81,6 +83,24 @@ static int get_max_accept_queue_size(void) {
81
83
  return s_max_accept_queue_size;
82
84
  }
83
85
 
86
+ static void listener_retry_timer_cb(void* arg, grpc_error_handle err) {
87
+ // Do nothing if cancelled.
88
+ if (!err.ok()) return;
89
+ grpc_tcp_listener* listener = static_cast<grpc_tcp_listener*>(arg);
90
+ gpr_atm_no_barrier_store(&listener->retry_timer_armed, false);
91
+ if (!grpc_fd_is_shutdown(listener->emfd)) {
92
+ grpc_fd_set_readable(listener->emfd);
93
+ }
94
+ }
95
+
96
+ void grpc_tcp_server_listener_initialize_retry_timer(
97
+ grpc_tcp_listener* listener) {
98
+ gpr_atm_no_barrier_store(&listener->retry_timer_armed, false);
99
+ grpc_timer_init_unset(&listener->retry_timer);
100
+ GRPC_CLOSURE_INIT(&listener->retry_closure, listener_retry_timer_cb, listener,
101
+ grpc_schedule_on_exec_ctx);
102
+ }
103
+
84
104
  static grpc_error_handle add_socket_to_server(grpc_tcp_server* s, int fd,
85
105
  const grpc_resolved_address* addr,
86
106
  unsigned port_index,
@@ -112,6 +132,7 @@ static grpc_error_handle add_socket_to_server(grpc_tcp_server* s, int fd,
112
132
  sp->server = s;
113
133
  sp->fd = fd;
114
134
  sp->emfd = grpc_fd_create(fd, name.c_str(), true);
135
+ grpc_tcp_server_listener_initialize_retry_timer(sp);
115
136
 
116
137
  // Check and set fd as prellocated
117
138
  if (grpc_tcp_server_pre_allocated_fd(s) == fd) {
@@ -21,46 +21,20 @@
21
21
  #include "src/core/lib/surface/validate_metadata.h"
22
22
 
23
23
  #include "absl/status/status.h"
24
+ #include "absl/strings/escaping.h"
25
+ #include "absl/strings/str_cat.h"
24
26
  #include "absl/strings/string_view.h"
25
27
 
26
28
  #include <grpc/grpc.h>
27
29
 
28
- #include "src/core/lib/gpr/string.h"
29
30
  #include "src/core/lib/gprpp/bitset.h"
30
- #include "src/core/lib/gprpp/memory.h"
31
- #include "src/core/lib/gprpp/status_helper.h"
32
31
  #include "src/core/lib/iomgr/error.h"
32
+ #include "src/core/lib/slice/slice_internal.h"
33
33
 
34
- static grpc_error_handle conforms_to(const grpc_slice& slice,
35
- const grpc_core::BitSet<256>& legal_bits,
36
- const char* err_desc) {
37
- const uint8_t* p = GRPC_SLICE_START_PTR(slice);
38
- const uint8_t* e = GRPC_SLICE_END_PTR(slice);
39
- for (; p != e; p++) {
40
- if (!legal_bits.is_set(*p)) {
41
- size_t len;
42
- grpc_core::UniquePtr<char> ptr(gpr_dump_return_len(
43
- reinterpret_cast<const char*> GRPC_SLICE_START_PTR(slice),
44
- GRPC_SLICE_LENGTH(slice), GPR_DUMP_HEX | GPR_DUMP_ASCII, &len));
45
- grpc_error_handle error = grpc_error_set_str(
46
- grpc_error_set_int(GRPC_ERROR_CREATE(err_desc),
47
- grpc_core::StatusIntProperty::kOffset,
48
- p - GRPC_SLICE_START_PTR(slice)),
49
- grpc_core::StatusStrProperty::kRawBytes,
50
- absl::string_view(ptr.get(), len));
51
- return error;
52
- }
53
- }
54
- return absl::OkStatus();
55
- }
56
-
57
- static int error2int(grpc_error_handle error) {
58
- int r = (error.ok());
59
- return r;
60
- }
34
+ namespace grpc_core {
61
35
 
62
36
  namespace {
63
- class LegalHeaderKeyBits : public grpc_core::BitSet<256> {
37
+ class LegalHeaderKeyBits : public BitSet<256> {
64
38
  public:
65
39
  constexpr LegalHeaderKeyBits() {
66
40
  for (int i = 'a'; i <= 'z'; i++) set(i);
@@ -71,19 +45,45 @@ class LegalHeaderKeyBits : public grpc_core::BitSet<256> {
71
45
  }
72
46
  };
73
47
  constexpr LegalHeaderKeyBits g_legal_header_key_bits;
74
- } // namespace
75
48
 
76
- grpc_error_handle grpc_validate_header_key_is_legal(const grpc_slice& slice) {
77
- if (GRPC_SLICE_LENGTH(slice) == 0) {
78
- return GRPC_ERROR_CREATE("Metadata keys cannot be zero length");
49
+ GPR_ATTRIBUTE_NOINLINE
50
+ absl::Status DoesNotConformTo(absl::string_view x, const char* err_desc) {
51
+ return absl::InternalError(absl::StrCat(err_desc, ": ", x, " (hex ",
52
+ absl::BytesToHexString(x), ")"));
53
+ }
54
+
55
+ absl::Status ConformsTo(absl::string_view x, const BitSet<256>& legal_bits,
56
+ const char* err_desc) {
57
+ for (uint8_t c : x) {
58
+ if (!legal_bits.is_set(c)) {
59
+ return DoesNotConformTo(x, err_desc);
60
+ }
79
61
  }
80
- if (GRPC_SLICE_LENGTH(slice) > UINT32_MAX) {
81
- return GRPC_ERROR_CREATE("Metadata keys cannot be larger than UINT32_MAX");
62
+ return absl::OkStatus();
63
+ }
64
+ } // namespace
65
+
66
+ absl::Status ValidateHeaderKeyIsLegal(absl::string_view key) {
67
+ if (key.empty()) {
68
+ return absl::InternalError("Metadata keys cannot be zero length");
82
69
  }
83
- if (GRPC_SLICE_START_PTR(slice)[0] == ':') {
84
- return GRPC_ERROR_CREATE("Metadata keys cannot start with :");
70
+ if (key.size() > UINT32_MAX) {
71
+ return absl::InternalError(
72
+ "Metadata keys cannot be larger than UINT32_MAX");
85
73
  }
86
- return conforms_to(slice, g_legal_header_key_bits, "Illegal header key");
74
+ return ConformsTo(key, g_legal_header_key_bits, "Illegal header key");
75
+ }
76
+
77
+ } // namespace grpc_core
78
+
79
+ static int error2int(grpc_error_handle error) {
80
+ int r = (error.ok());
81
+ return r;
82
+ }
83
+
84
+ grpc_error_handle grpc_validate_header_key_is_legal(const grpc_slice& slice) {
85
+ return grpc_core::ValidateHeaderKeyIsLegal(
86
+ grpc_core::StringViewFromSlice(slice));
87
87
  }
88
88
 
89
89
  int grpc_header_key_is_legal(grpc_slice slice) {
@@ -104,8 +104,9 @@ constexpr LegalHeaderNonBinValueBits g_legal_header_non_bin_value_bits;
104
104
 
105
105
  grpc_error_handle grpc_validate_header_nonbin_value_is_legal(
106
106
  const grpc_slice& slice) {
107
- return conforms_to(slice, g_legal_header_non_bin_value_bits,
108
- "Illegal header value");
107
+ return grpc_core::ConformsTo(grpc_core::StringViewFromSlice(slice),
108
+ g_legal_header_non_bin_value_bits,
109
+ "Illegal header value");
109
110
  }
110
111
 
111
112
  int grpc_header_nonbin_value_is_legal(grpc_slice slice) {