grpc 1.59.0 → 1.59.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 (26) hide show
  1. checksums.yaml +4 -4
  2. data/Makefile +7 -1
  3. data/src/core/ext/filters/http/server/http_server_filter.cc +21 -17
  4. data/src/core/ext/transport/chttp2/transport/chttp2_transport.cc +504 -361
  5. data/src/core/ext/transport/chttp2/transport/frame_ping.cc +11 -1
  6. data/src/core/ext/transport/chttp2/transport/frame_rst_stream.cc +9 -0
  7. data/src/core/ext/transport/chttp2/transport/internal.h +92 -28
  8. data/src/core/ext/transport/chttp2/transport/max_concurrent_streams_policy.cc +44 -0
  9. data/src/core/ext/transport/chttp2/transport/max_concurrent_streams_policy.h +67 -0
  10. data/src/core/ext/transport/chttp2/transport/parsing.cc +103 -14
  11. data/src/core/ext/transport/chttp2/transport/ping_callbacks.cc +108 -0
  12. data/src/core/ext/transport/chttp2/transport/ping_callbacks.h +115 -0
  13. data/src/core/ext/transport/chttp2/transport/ping_rate_policy.cc +26 -4
  14. data/src/core/ext/transport/chttp2/transport/ping_rate_policy.h +16 -1
  15. data/src/core/ext/transport/chttp2/transport/write_size_policy.cc +60 -0
  16. data/src/core/ext/transport/chttp2/transport/write_size_policy.h +66 -0
  17. data/src/core/ext/transport/chttp2/transport/writing.cc +149 -77
  18. data/src/core/lib/channel/promise_based_filter.cc +9 -4
  19. data/src/core/lib/channel/promise_based_filter.h +2 -1
  20. data/src/core/lib/experiments/experiments.cc +222 -0
  21. data/src/core/lib/experiments/experiments.h +135 -0
  22. data/src/core/lib/iomgr/combiner.cc +3 -0
  23. data/src/core/lib/transport/metadata_batch.h +11 -1
  24. data/src/core/lib/transport/transport.h +6 -0
  25. data/src/ruby/lib/grpc/version.rb +1 -1
  26. metadata +9 -3
@@ -24,7 +24,9 @@
24
24
  #include <string.h>
25
25
 
26
26
  #include <algorithm>
27
+ #include <atomic>
27
28
  #include <initializer_list>
29
+ #include <limits>
28
30
  #include <memory>
29
31
  #include <new>
30
32
  #include <string>
@@ -36,12 +38,14 @@
36
38
  #include "absl/container/flat_hash_map.h"
37
39
  #include "absl/hash/hash.h"
38
40
  #include "absl/meta/type_traits.h"
41
+ #include "absl/random/random.h"
39
42
  #include "absl/status/status.h"
40
43
  #include "absl/strings/cord.h"
41
44
  #include "absl/strings/str_cat.h"
42
45
  #include "absl/strings/str_format.h"
43
46
  #include "absl/strings/string_view.h"
44
47
  #include "absl/types/optional.h"
48
+ #include "absl/types/variant.h"
45
49
 
46
50
  #include <grpc/event_engine/event_engine.h>
47
51
  #include <grpc/grpc.h>
@@ -63,9 +67,12 @@
63
67
  #include "src/core/ext/transport/chttp2/transport/http_trace.h"
64
68
  #include "src/core/ext/transport/chttp2/transport/internal.h"
65
69
  #include "src/core/ext/transport/chttp2/transport/legacy_frame.h"
70
+ #include "src/core/ext/transport/chttp2/transport/max_concurrent_streams_policy.h"
66
71
  #include "src/core/ext/transport/chttp2/transport/ping_abuse_policy.h"
72
+ #include "src/core/ext/transport/chttp2/transport/ping_callbacks.h"
67
73
  #include "src/core/ext/transport/chttp2/transport/ping_rate_policy.h"
68
74
  #include "src/core/ext/transport/chttp2/transport/varint.h"
75
+ #include "src/core/ext/transport/chttp2/transport/write_size_policy.h"
69
76
  #include "src/core/lib/channel/call_tracer.h"
70
77
  #include "src/core/lib/channel/channel_args.h"
71
78
  #include "src/core/lib/channel/context.h"
@@ -116,6 +123,9 @@
116
123
 
117
124
  #define DEFAULT_MAX_PENDING_INDUCED_FRAMES 10000
118
125
 
126
+ #define GRPC_ARG_HTTP2_PING_ON_RST_STREAM_PERCENT \
127
+ "grpc.http2.ping_on_rst_stream_percent"
128
+
119
129
  static grpc_core::Duration g_default_client_keepalive_time =
120
130
  grpc_core::Duration::Infinity();
121
131
  static grpc_core::Duration g_default_client_keepalive_timeout =
@@ -127,6 +137,11 @@ static grpc_core::Duration g_default_server_keepalive_timeout =
127
137
  static bool g_default_client_keepalive_permit_without_calls = false;
128
138
  static bool g_default_server_keepalive_permit_without_calls = false;
129
139
 
140
+ // EXPERIMENTAL: control tarpitting in chttp2
141
+ #define GRPC_ARG_HTTP_ALLOW_TARPIT "grpc.http.tarpit"
142
+ #define GRPC_ARG_HTTP_TARPIT_MIN_DURATION_MS "grpc.http.tarpit_min_duration_ms"
143
+ #define GRPC_ARG_HTTP_TARPIT_MAX_DURATION_MS "grpc.http.tarpit_max_duration_ms"
144
+
130
145
  #define MAX_CLIENT_STREAM_ID 0x7fffffffu
131
146
  grpc_core::TraceFlag grpc_keepalive_trace(false, "http_keepalive");
132
147
  grpc_core::DebugOnlyTraceFlag grpc_trace_chttp2_refcount(false,
@@ -153,7 +168,7 @@ static void queue_setting_update(grpc_chttp2_transport* t,
153
168
  grpc_chttp2_setting_id id, uint32_t value);
154
169
 
155
170
  static void close_from_api(grpc_chttp2_transport* t, grpc_chttp2_stream* s,
156
- grpc_error_handle error);
171
+ grpc_error_handle error, bool tarpit);
157
172
 
158
173
  // Start new streams that have been created if we can
159
174
  static void maybe_start_some_streams(grpc_chttp2_transport* t);
@@ -202,21 +217,18 @@ static void init_keepalive_ping(
202
217
  static void init_keepalive_ping_locked(
203
218
  grpc_core::RefCountedPtr<grpc_chttp2_transport> t,
204
219
  GRPC_UNUSED grpc_error_handle error);
205
- static void start_keepalive_ping(
206
- grpc_core::RefCountedPtr<grpc_chttp2_transport> t, grpc_error_handle error);
207
220
  static void finish_keepalive_ping(
208
221
  grpc_core::RefCountedPtr<grpc_chttp2_transport> t, grpc_error_handle error);
209
- static void start_keepalive_ping_locked(
210
- grpc_core::RefCountedPtr<grpc_chttp2_transport> t, grpc_error_handle error);
211
222
  static void finish_keepalive_ping_locked(
212
223
  grpc_core::RefCountedPtr<grpc_chttp2_transport> t, grpc_error_handle error);
213
- static void keepalive_watchdog_fired(
214
- grpc_core::RefCountedPtr<grpc_chttp2_transport> t);
215
- static void keepalive_watchdog_fired_locked(
216
- grpc_core::RefCountedPtr<grpc_chttp2_transport> t,
217
- GRPC_UNUSED grpc_error_handle error);
218
224
  static void maybe_reset_keepalive_ping_timer_locked(grpc_chttp2_transport* t);
219
225
 
226
+ static void send_goaway(grpc_chttp2_transport* t, grpc_error_handle error,
227
+ bool immediate_disconnect_hint);
228
+
229
+ // Timeout for getting an ack back on settings changes
230
+ #define GRPC_ARG_SETTINGS_TIMEOUT "grpc.http2.settings_timeout"
231
+
220
232
  namespace {
221
233
  grpc_core::CallTracerInterface* CallTracerIfEnabled(grpc_chttp2_stream* s) {
222
234
  if (s->context == nullptr || !grpc_core::IsTraceRecordCallopsEnabled()) {
@@ -317,6 +329,8 @@ void ForEachContextListEntryExecute(void* arg, Timestamps* ts,
317
329
  grpc_chttp2_transport::~grpc_chttp2_transport() {
318
330
  size_t i;
319
331
 
332
+ cancel_pings(this, GRPC_ERROR_CREATE("Transport destroyed"));
333
+
320
334
  event_engine.reset();
321
335
 
322
336
  if (channelz_socket != nullptr) {
@@ -327,8 +341,6 @@ grpc_chttp2_transport::~grpc_chttp2_transport() {
327
341
 
328
342
  grpc_slice_buffer_destroy(&qbuf);
329
343
 
330
- grpc_slice_buffer_destroy(&outbuf);
331
-
332
344
  grpc_error_handle error = GRPC_ERROR_CREATE("Transport destroyed");
333
345
  // ContextList::Execute follows semantics of a callback function and does not
334
346
  // take a ref on error
@@ -348,8 +360,6 @@ grpc_chttp2_transport::~grpc_chttp2_transport() {
348
360
  GPR_ASSERT(stream_map.empty());
349
361
  GRPC_COMBINER_UNREF(combiner, "chttp2_transport");
350
362
 
351
- cancel_pings(this, GRPC_ERROR_CREATE("Transport destroyed"));
352
-
353
363
  while (write_cb_pool) {
354
364
  grpc_chttp2_write_cb* next = write_cb_pool->next;
355
365
  gpr_free(write_cb_pool);
@@ -396,8 +406,16 @@ static void read_channel_args(grpc_chttp2_transport* t,
396
406
  t->keepalive_timeout = std::max(
397
407
  grpc_core::Duration::Zero(),
398
408
  channel_args.GetDurationFromIntMillis(GRPC_ARG_KEEPALIVE_TIMEOUT_MS)
399
- .value_or(t->is_client ? g_default_client_keepalive_timeout
400
- : g_default_server_keepalive_timeout));
409
+ .value_or(t->keepalive_time == grpc_core::Duration::Infinity()
410
+ ? grpc_core::Duration::Infinity()
411
+ : (t->is_client ? g_default_client_keepalive_timeout
412
+ : g_default_server_keepalive_timeout)));
413
+ t->ping_timeout = std::max(
414
+ grpc_core::Duration::Zero(),
415
+ channel_args.GetDurationFromIntMillis(GRPC_ARG_PING_TIMEOUT_MS)
416
+ .value_or(t->keepalive_time == grpc_core::Duration::Infinity()
417
+ ? grpc_core::Duration::Infinity()
418
+ : grpc_core::Duration::Minutes(1)));
401
419
  if (t->is_client) {
402
420
  t->keepalive_permit_without_calls =
403
421
  channel_args.GetBool(GRPC_ARG_KEEPALIVE_PERMIT_WITHOUT_CALLS)
@@ -412,6 +430,11 @@ static void read_channel_args(grpc_chttp2_transport* t,
412
430
  : false);
413
431
  }
414
432
 
433
+ t->settings_timeout =
434
+ channel_args.GetDurationFromIntMillis(GRPC_ARG_SETTINGS_TIMEOUT)
435
+ .value_or(std::max(t->keepalive_timeout * 2,
436
+ grpc_core::Duration::Minutes(1)));
437
+
415
438
  // Only send the prefered rx frame size http2 setting if we are instructed
416
439
  // to auto size the buffers allocated at tcp level and we also can adjust
417
440
  // sending frame size.
@@ -420,6 +443,17 @@ static void read_channel_args(grpc_chttp2_transport* t,
420
443
  .GetBool(GRPC_ARG_EXPERIMENTAL_HTTP2_PREFERRED_CRYPTO_FRAME_SIZE)
421
444
  .value_or(false);
422
445
 
446
+ const auto max_requests_per_read =
447
+ channel_args.GetInt("grpc.http2.max_requests_per_read");
448
+ if (max_requests_per_read.has_value()) {
449
+ t->max_requests_per_read =
450
+ grpc_core::Clamp(*max_requests_per_read, 1, 10000);
451
+ } else if (grpc_core::IsChttp2BatchRequestsEnabled()) {
452
+ t->max_requests_per_read = 32;
453
+ } else {
454
+ t->max_requests_per_read = std::numeric_limits<size_t>::max();
455
+ }
456
+
423
457
  if (channel_args.GetBool(GRPC_ARG_ENABLE_CHANNELZ)
424
458
  .value_or(GRPC_ENABLE_CHANNELZ_DEFAULT)) {
425
459
  t->channelz_socket =
@@ -434,6 +468,19 @@ static void read_channel_args(grpc_chttp2_transport* t,
434
468
 
435
469
  t->ack_pings = channel_args.GetBool("grpc.http2.ack_pings").value_or(true);
436
470
 
471
+ t->allow_tarpit = channel_args.GetBool(GRPC_ARG_HTTP_ALLOW_TARPIT)
472
+ .value_or(grpc_core::IsTarpitEnabled());
473
+ t->min_tarpit_duration_ms =
474
+ channel_args
475
+ .GetDurationFromIntMillis(GRPC_ARG_HTTP_TARPIT_MIN_DURATION_MS)
476
+ .value_or(grpc_core::Duration::Milliseconds(100))
477
+ .millis();
478
+ t->max_tarpit_duration_ms =
479
+ channel_args
480
+ .GetDurationFromIntMillis(GRPC_ARG_HTTP_TARPIT_MAX_DURATION_MS)
481
+ .value_or(grpc_core::Duration::Seconds(1))
482
+ .millis();
483
+
437
484
  const int soft_limit =
438
485
  channel_args.GetInt(GRPC_ARG_MAX_METADATA_SIZE).value_or(-1);
439
486
  if (soft_limit < 0) {
@@ -499,8 +546,12 @@ static void read_channel_args(grpc_chttp2_transport* t,
499
546
  const int value = channel_args.GetInt(setting.channel_arg_name)
500
547
  .value_or(setting.default_value);
501
548
  if (value >= 0) {
502
- queue_setting_update(t, setting.setting_id,
503
- grpc_core::Clamp(value, setting.min, setting.max));
549
+ const int clamped_value =
550
+ grpc_core::Clamp(value, setting.min, setting.max);
551
+ queue_setting_update(t, setting.setting_id, clamped_value);
552
+ if (setting.setting_id == GRPC_CHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS) {
553
+ t->max_concurrent_streams_policy.SetTarget(clamped_value);
554
+ }
504
555
  } else if (setting.setting_id ==
505
556
  GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE) {
506
557
  // Set value to 1.25 * soft limit if this is larger than
@@ -533,6 +584,11 @@ static void read_channel_args(grpc_chttp2_transport* t,
533
584
  grpc_core::Clamp(INT_MAX, static_cast<int>(sp->min_value),
534
585
  static_cast<int>(sp->max_value)));
535
586
  }
587
+
588
+ t->ping_on_rst_stream_percent = grpc_core::Clamp(
589
+ channel_args.GetInt(GRPC_ARG_HTTP2_PING_ON_RST_STREAM_PERCENT)
590
+ .value_or(1),
591
+ 0, 100);
536
592
  }
537
593
 
538
594
  static void init_keepalive_pings_if_enabled_locked(
@@ -590,10 +646,10 @@ grpc_chttp2_transport::grpc_chttp2_transport(
590
646
  base.vtable = get_vtable();
591
647
 
592
648
  grpc_slice_buffer_init(&read_buffer);
593
- grpc_slice_buffer_init(&outbuf);
594
649
  if (is_client) {
595
- grpc_slice_buffer_add(&outbuf, grpc_slice_from_copied_string(
596
- GRPC_CHTTP2_CLIENT_CONNECT_STRING));
650
+ grpc_slice_buffer_add(
651
+ outbuf.c_slice_buffer(),
652
+ grpc_slice_from_copied_string(GRPC_CHTTP2_CLIENT_CONNECT_STRING));
597
653
  }
598
654
  grpc_slice_buffer_init(&qbuf);
599
655
  // copy in initial settings to all setting sets
@@ -618,6 +674,12 @@ grpc_chttp2_transport::grpc_chttp2_transport(
618
674
 
619
675
  read_channel_args(this, channel_args, is_client);
620
676
 
677
+ // Initially allow *UP TO* MAX_CONCURRENT_STREAMS incoming before we start
678
+ // blanket cancelling them.
679
+ num_incoming_streams_before_settings_ack =
680
+ settings[GRPC_LOCAL_SETTINGS]
681
+ [GRPC_CHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS];
682
+
621
683
  grpc_core::ExecCtx exec_ctx;
622
684
  combiner->Run(
623
685
  grpc_core::InitTransportClosure<init_keepalive_pings_if_enabled_locked>(
@@ -686,6 +748,18 @@ static void close_transport_locked(grpc_chttp2_transport* t,
686
748
  t->closed_with_error = error;
687
749
  connectivity_state_set(t, GRPC_CHANNEL_SHUTDOWN, absl::Status(),
688
750
  "close_transport");
751
+ if (t->keepalive_ping_timeout_handle !=
752
+ grpc_event_engine::experimental::EventEngine::TaskHandle::kInvalid) {
753
+ t->event_engine->Cancel(std::exchange(
754
+ t->keepalive_ping_timeout_handle,
755
+ grpc_event_engine::experimental::EventEngine::TaskHandle::kInvalid));
756
+ }
757
+ if (t->settings_ack_watchdog !=
758
+ grpc_event_engine::experimental::EventEngine::TaskHandle::kInvalid) {
759
+ t->event_engine->Cancel(std::exchange(
760
+ t->settings_ack_watchdog,
761
+ grpc_event_engine::experimental::EventEngine::TaskHandle::kInvalid));
762
+ }
689
763
  if (t->delayed_ping_timer_handle.has_value()) {
690
764
  if (t->event_engine->Cancel(*t->delayed_ping_timer_handle)) {
691
765
  t->delayed_ping_timer_handle.reset();
@@ -710,11 +784,6 @@ static void close_transport_locked(grpc_chttp2_transport* t,
710
784
  t->keepalive_ping_timer_handle.reset();
711
785
  }
712
786
  }
713
- if (t->keepalive_watchdog_timer_handle.has_value()) {
714
- if (t->event_engine->Cancel(*t->keepalive_watchdog_timer_handle)) {
715
- t->keepalive_watchdog_timer_handle.reset();
716
- }
717
- }
718
787
  break;
719
788
  case GRPC_CHTTP2_KEEPALIVE_STATE_DYING:
720
789
  case GRPC_CHTTP2_KEEPALIVE_STATE_DISABLED:
@@ -777,6 +846,7 @@ grpc_chttp2_stream::grpc_chttp2_stream(grpc_chttp2_transport* t,
777
846
  initial_metadata_buffer(arena),
778
847
  trailing_metadata_buffer(arena),
779
848
  flow_control(&t->flow_control) {
849
+ t->streams_allocated.fetch_add(1, std::memory_order_relaxed);
780
850
  if (server_data) {
781
851
  id = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(server_data));
782
852
  if (grpc_http_trace.enabled()) {
@@ -793,6 +863,7 @@ grpc_chttp2_stream::grpc_chttp2_stream(grpc_chttp2_transport* t,
793
863
  }
794
864
 
795
865
  grpc_chttp2_stream::~grpc_chttp2_stream() {
866
+ t->streams_allocated.fetch_sub(1, std::memory_order_relaxed);
796
867
  grpc_chttp2_list_remove_stalled_by_stream(t.get(), this);
797
868
  grpc_chttp2_list_remove_stalled_by_transport(t.get(), this);
798
869
 
@@ -1016,7 +1087,12 @@ static void write_action(grpc_chttp2_transport* t) {
1016
1087
  if (max_frame_size == 0) {
1017
1088
  max_frame_size = INT_MAX;
1018
1089
  }
1019
- grpc_endpoint_write(t->ep, &t->outbuf,
1090
+ if (GRPC_TRACE_FLAG_ENABLED(grpc_ping_trace)) {
1091
+ gpr_log(GPR_INFO, "%s[%p]: Write %" PRIdPTR " bytes",
1092
+ t->is_client ? "CLIENT" : "SERVER", t, t->outbuf.Length());
1093
+ }
1094
+ t->write_size_policy.BeginWrite(t->outbuf.Length());
1095
+ grpc_endpoint_write(t->ep, t->outbuf.c_slice_buffer(),
1020
1096
  grpc_core::InitTransportClosure<write_action_end>(
1021
1097
  t->Ref(), &t->write_action_end_locked),
1022
1098
  cl, max_frame_size);
@@ -1025,6 +1101,10 @@ static void write_action(grpc_chttp2_transport* t) {
1025
1101
  static void write_action_end(grpc_core::RefCountedPtr<grpc_chttp2_transport> t,
1026
1102
  grpc_error_handle error) {
1027
1103
  auto* tp = t.get();
1104
+ if (GRPC_TRACE_FLAG_ENABLED(grpc_ping_trace)) {
1105
+ gpr_log(GPR_INFO, "%s[%p]: Finish write",
1106
+ t->is_client ? "CLIENT" : "SERVER", t.get());
1107
+ }
1028
1108
  tp->combiner->Run(grpc_core::InitTransportClosure<write_action_end_locked>(
1029
1109
  std::move(t), &tp->write_action_end_locked),
1030
1110
  error);
@@ -1035,6 +1115,8 @@ static void write_action_end(grpc_core::RefCountedPtr<grpc_chttp2_transport> t,
1035
1115
  static void write_action_end_locked(
1036
1116
  grpc_core::RefCountedPtr<grpc_chttp2_transport> t,
1037
1117
  grpc_error_handle error) {
1118
+ t->write_size_policy.EndWrite(error.ok());
1119
+
1038
1120
  bool closed = false;
1039
1121
  if (!error.ok()) {
1040
1122
  close_transport_locked(t.get(), error);
@@ -1095,13 +1177,13 @@ static void queue_setting_update(grpc_chttp2_transport* t,
1095
1177
 
1096
1178
  // Cancel out streams that haven't yet started if we have received a GOAWAY
1097
1179
  static void cancel_unstarted_streams(grpc_chttp2_transport* t,
1098
- grpc_error_handle error) {
1180
+ grpc_error_handle error, bool tarpit) {
1099
1181
  grpc_chttp2_stream* s;
1100
1182
  while (grpc_chttp2_list_pop_waiting_for_concurrency(t, &s)) {
1101
1183
  s->trailing_metadata_buffer.Set(
1102
1184
  grpc_core::GrpcStreamNetworkState(),
1103
1185
  grpc_core::GrpcStreamNetworkState::kNotSentOnWire);
1104
- grpc_chttp2_cancel_stream(t, s, error);
1186
+ grpc_chttp2_cancel_stream(t, s, error, tarpit);
1105
1187
  }
1106
1188
  }
1107
1189
 
@@ -1134,7 +1216,7 @@ void grpc_chttp2_add_incoming_goaway(grpc_chttp2_transport* t,
1134
1216
  grpc_core::StatusToString(t->goaway_error).c_str());
1135
1217
  }
1136
1218
  if (t->is_client) {
1137
- cancel_unstarted_streams(t, t->goaway_error);
1219
+ cancel_unstarted_streams(t, t->goaway_error, false);
1138
1220
  // Cancel all unseen streams
1139
1221
  std::vector<grpc_chttp2_stream*> to_cancel;
1140
1222
  for (auto id_stream : t->stream_map) {
@@ -1146,7 +1228,7 @@ void grpc_chttp2_add_incoming_goaway(grpc_chttp2_transport* t,
1146
1228
  s->trailing_metadata_buffer.Set(
1147
1229
  grpc_core::GrpcStreamNetworkState(),
1148
1230
  grpc_core::GrpcStreamNetworkState::kNotSeenByServer);
1149
- grpc_chttp2_cancel_stream(s->t.get(), s, s->t->goaway_error);
1231
+ grpc_chttp2_cancel_stream(s->t.get(), s, s->t->goaway_error, false);
1150
1232
  }
1151
1233
  }
1152
1234
  absl::Status status = grpc_error_to_absl_status(t->goaway_error);
@@ -1185,7 +1267,7 @@ static void maybe_start_some_streams(grpc_chttp2_transport* t) {
1185
1267
  // maybe cancel out streams that haven't yet started if we have received a
1186
1268
  // GOAWAY
1187
1269
  if (!t->goaway_error.ok()) {
1188
- cancel_unstarted_streams(t, t->goaway_error);
1270
+ cancel_unstarted_streams(t, t->goaway_error, false);
1189
1271
  return;
1190
1272
  }
1191
1273
  // start streams where we have free grpc_chttp2_stream ids and free
@@ -1227,7 +1309,8 @@ static void maybe_start_some_streams(grpc_chttp2_transport* t) {
1227
1309
  t, s,
1228
1310
  grpc_error_set_int(GRPC_ERROR_CREATE("Stream IDs exhausted"),
1229
1311
  grpc_core::StatusIntProperty::kRpcStatus,
1230
- GRPC_STATUS_UNAVAILABLE));
1312
+ GRPC_STATUS_UNAVAILABLE),
1313
+ false);
1231
1314
  }
1232
1315
  }
1233
1316
  }
@@ -1372,7 +1455,8 @@ static void perform_stream_op_locked(void* stream_op,
1372
1455
  }
1373
1456
 
1374
1457
  if (op->cancel_stream) {
1375
- grpc_chttp2_cancel_stream(t, s, op_payload->cancel_stream.cancel_error);
1458
+ grpc_chttp2_cancel_stream(t, s, op_payload->cancel_stream.cancel_error,
1459
+ op_payload->cancel_stream.tarpit);
1376
1460
  }
1377
1461
 
1378
1462
  if (op->send_initial_metadata) {
@@ -1410,7 +1494,8 @@ static void perform_stream_op_locked(void* stream_op,
1410
1494
  GRPC_ERROR_CREATE_REFERENCING("Transport closed",
1411
1495
  &t->closed_with_error, 1),
1412
1496
  grpc_core::StatusIntProperty::kRpcStatus,
1413
- GRPC_STATUS_UNAVAILABLE));
1497
+ GRPC_STATUS_UNAVAILABLE),
1498
+ false);
1414
1499
  }
1415
1500
  } else {
1416
1501
  GPR_ASSERT(s->id != 0);
@@ -1618,14 +1703,38 @@ static void cancel_pings(grpc_chttp2_transport* t, grpc_error_handle error) {
1618
1703
  grpc_core::StatusToString(error).c_str()));
1619
1704
  // callback remaining pings: they're not allowed to call into the transport,
1620
1705
  // and maybe they hold resources that need to be freed
1621
- grpc_chttp2_ping_queue* pq = &t->ping_queue;
1622
- GPR_ASSERT(!error.ok());
1623
- for (size_t j = 0; j < GRPC_CHTTP2_PCL_COUNT; j++) {
1624
- grpc_closure_list_fail_all(&pq->lists[j], error);
1625
- grpc_core::ExecCtx::RunList(DEBUG_LOCATION, &pq->lists[j]);
1626
- }
1706
+ t->ping_callbacks.CancelAll(t->event_engine.get());
1627
1707
  }
1628
1708
 
1709
+ namespace {
1710
+ class PingClosureWrapper {
1711
+ public:
1712
+ explicit PingClosureWrapper(grpc_closure* closure) : closure_(closure) {}
1713
+ PingClosureWrapper(const PingClosureWrapper&) = delete;
1714
+ PingClosureWrapper& operator=(const PingClosureWrapper&) = delete;
1715
+ PingClosureWrapper(PingClosureWrapper&& other) noexcept
1716
+ : closure_(other.Take()) {}
1717
+ PingClosureWrapper& operator=(PingClosureWrapper&& other) noexcept {
1718
+ std::swap(closure_, other.closure_);
1719
+ return *this;
1720
+ }
1721
+ ~PingClosureWrapper() {
1722
+ if (closure_ != nullptr) {
1723
+ grpc_core::ExecCtx::Run(DEBUG_LOCATION, closure_, absl::CancelledError());
1724
+ }
1725
+ }
1726
+
1727
+ void operator()() {
1728
+ grpc_core::ExecCtx::Run(DEBUG_LOCATION, Take(), absl::OkStatus());
1729
+ }
1730
+
1731
+ private:
1732
+ grpc_closure* Take() { return std::exchange(closure_, nullptr); }
1733
+
1734
+ grpc_closure* closure_ = nullptr;
1735
+ };
1736
+ } // namespace
1737
+
1629
1738
  static void send_ping_locked(grpc_chttp2_transport* t,
1630
1739
  grpc_closure* on_initiate, grpc_closure* on_ack) {
1631
1740
  if (!t->closed_with_error.ok()) {
@@ -1633,11 +1742,8 @@ static void send_ping_locked(grpc_chttp2_transport* t,
1633
1742
  grpc_core::ExecCtx::Run(DEBUG_LOCATION, on_ack, t->closed_with_error);
1634
1743
  return;
1635
1744
  }
1636
- grpc_chttp2_ping_queue* pq = &t->ping_queue;
1637
- grpc_closure_list_append(&pq->lists[GRPC_CHTTP2_PCL_INITIATE], on_initiate,
1638
- absl::OkStatus());
1639
- grpc_closure_list_append(&pq->lists[GRPC_CHTTP2_PCL_NEXT], on_ack,
1640
- absl::OkStatus());
1745
+ t->ping_callbacks.OnPing(PingClosureWrapper(on_initiate),
1746
+ PingClosureWrapper(on_ack));
1641
1747
  }
1642
1748
 
1643
1749
  // Specialized form of send_ping_locked for keepalive ping. If there is already
@@ -1646,40 +1752,15 @@ static void send_ping_locked(grpc_chttp2_transport* t,
1646
1752
  static void send_keepalive_ping_locked(
1647
1753
  grpc_core::RefCountedPtr<grpc_chttp2_transport> t) {
1648
1754
  if (!t->closed_with_error.ok()) {
1649
- t->combiner->Run(
1650
- grpc_core::InitTransportClosure<start_keepalive_ping_locked>(
1651
- t->Ref(), &t->start_keepalive_ping_locked),
1652
- t->closed_with_error);
1653
1755
  t->combiner->Run(
1654
1756
  grpc_core::InitTransportClosure<finish_keepalive_ping_locked>(
1655
1757
  t->Ref(), &t->finish_keepalive_ping_locked),
1656
1758
  t->closed_with_error);
1657
1759
  return;
1658
1760
  }
1659
- grpc_chttp2_ping_queue* pq = &t->ping_queue;
1660
- if (!grpc_closure_list_empty(pq->lists[GRPC_CHTTP2_PCL_INFLIGHT])) {
1661
- // There is a ping in flight. Add yourself to the inflight closure list.
1662
- t->combiner->Run(
1663
- grpc_core::InitTransportClosure<start_keepalive_ping_locked>(
1664
- t->Ref(), &t->start_keepalive_ping_locked),
1665
- t->closed_with_error);
1666
- grpc_closure_list_append(
1667
- &pq->lists[GRPC_CHTTP2_PCL_INFLIGHT],
1668
- grpc_core::InitTransportClosure<finish_keepalive_ping>(
1669
- t->Ref(), &t->finish_keepalive_ping_locked),
1670
- absl::OkStatus());
1671
- return;
1672
- }
1673
- grpc_closure_list_append(
1674
- &pq->lists[GRPC_CHTTP2_PCL_INITIATE],
1675
- grpc_core::InitTransportClosure<start_keepalive_ping>(
1676
- t->Ref(), &t->start_keepalive_ping_locked),
1677
- absl::OkStatus());
1678
- grpc_closure_list_append(
1679
- &pq->lists[GRPC_CHTTP2_PCL_NEXT],
1680
- grpc_core::InitTransportClosure<finish_keepalive_ping>(
1681
- t->Ref(), &t->finish_keepalive_ping_locked),
1682
- absl::OkStatus());
1761
+ t->ping_callbacks.OnPingAck(
1762
+ PingClosureWrapper(grpc_core::InitTransportClosure<finish_keepalive_ping>(
1763
+ t->Ref(), &t->finish_keepalive_ping_locked)));
1683
1764
  }
1684
1765
 
1685
1766
  void grpc_chttp2_retry_initiate_ping(
@@ -1701,19 +1782,79 @@ static void retry_initiate_ping_locked(
1701
1782
  }
1702
1783
 
1703
1784
  void grpc_chttp2_ack_ping(grpc_chttp2_transport* t, uint64_t id) {
1704
- grpc_chttp2_ping_queue* pq = &t->ping_queue;
1705
- if (pq->inflight_id != id) {
1785
+ if (!t->ping_callbacks.AckPing(id, t->event_engine.get())) {
1706
1786
  gpr_log(GPR_DEBUG, "Unknown ping response from %s: %" PRIx64,
1707
1787
  std::string(t->peer_string.as_string_view()).c_str(), id);
1708
1788
  return;
1709
1789
  }
1710
- grpc_core::ExecCtx::RunList(DEBUG_LOCATION,
1711
- &pq->lists[GRPC_CHTTP2_PCL_INFLIGHT]);
1712
- if (!grpc_closure_list_empty(pq->lists[GRPC_CHTTP2_PCL_NEXT])) {
1790
+ if (t->ping_callbacks.ping_requested()) {
1713
1791
  grpc_chttp2_initiate_write(t, GRPC_CHTTP2_INITIATE_WRITE_CONTINUE_PINGS);
1714
1792
  }
1715
1793
  }
1716
1794
 
1795
+ void grpc_chttp2_keepalive_timeout(
1796
+ grpc_core::RefCountedPtr<grpc_chttp2_transport> t) {
1797
+ t->combiner->Run(
1798
+ grpc_core::NewClosure([t](grpc_error_handle) {
1799
+ gpr_log(GPR_INFO, "%s: Keepalive timeout. Closing transport.",
1800
+ std::string(t->peer_string.as_string_view()).c_str());
1801
+ send_goaway(
1802
+ t.get(),
1803
+ grpc_error_set_int(GRPC_ERROR_CREATE("keepalive_timeout"),
1804
+ grpc_core::StatusIntProperty::kHttp2Error,
1805
+ GRPC_HTTP2_ENHANCE_YOUR_CALM),
1806
+ /*immediate_disconnect_hint=*/true);
1807
+ close_transport_locked(
1808
+ t.get(),
1809
+ grpc_error_set_int(GRPC_ERROR_CREATE("keepalive timeout"),
1810
+ grpc_core::StatusIntProperty::kRpcStatus,
1811
+ GRPC_STATUS_UNAVAILABLE));
1812
+ }),
1813
+ absl::OkStatus());
1814
+ }
1815
+
1816
+ void grpc_chttp2_ping_timeout(
1817
+ grpc_core::RefCountedPtr<grpc_chttp2_transport> t) {
1818
+ t->combiner->Run(
1819
+ grpc_core::NewClosure([t](grpc_error_handle) {
1820
+ gpr_log(GPR_INFO, "%s: Ping timeout. Closing transport.",
1821
+ std::string(t->peer_string.as_string_view()).c_str());
1822
+ send_goaway(
1823
+ t.get(),
1824
+ grpc_error_set_int(GRPC_ERROR_CREATE("ping_timeout"),
1825
+ grpc_core::StatusIntProperty::kHttp2Error,
1826
+ GRPC_HTTP2_ENHANCE_YOUR_CALM),
1827
+ /*immediate_disconnect_hint=*/true);
1828
+ close_transport_locked(
1829
+ t.get(),
1830
+ grpc_error_set_int(GRPC_ERROR_CREATE("ping timeout"),
1831
+ grpc_core::StatusIntProperty::kRpcStatus,
1832
+ GRPC_STATUS_UNAVAILABLE));
1833
+ }),
1834
+ absl::OkStatus());
1835
+ }
1836
+
1837
+ void grpc_chttp2_settings_timeout(
1838
+ grpc_core::RefCountedPtr<grpc_chttp2_transport> t) {
1839
+ t->combiner->Run(
1840
+ grpc_core::NewClosure([t](grpc_error_handle) {
1841
+ gpr_log(GPR_INFO, "%s: Settings timeout. Closing transport.",
1842
+ std::string(t->peer_string.as_string_view()).c_str());
1843
+ send_goaway(
1844
+ t.get(),
1845
+ grpc_error_set_int(GRPC_ERROR_CREATE("settings_timeout"),
1846
+ grpc_core::StatusIntProperty::kHttp2Error,
1847
+ GRPC_HTTP2_SETTINGS_TIMEOUT),
1848
+ /*immediate_disconnect_hint=*/true);
1849
+ close_transport_locked(
1850
+ t.get(),
1851
+ grpc_error_set_int(GRPC_ERROR_CREATE("settings timeout"),
1852
+ grpc_core::StatusIntProperty::kRpcStatus,
1853
+ GRPC_STATUS_UNAVAILABLE));
1854
+ }),
1855
+ absl::OkStatus());
1856
+ }
1857
+
1717
1858
  namespace {
1718
1859
 
1719
1860
  // Fire and forget (deletes itself on completion). Does a graceful shutdown by
@@ -1733,20 +1874,13 @@ class GracefulGoaway : public grpc_core::RefCounted<GracefulGoaway> {
1733
1874
  explicit GracefulGoaway(grpc_chttp2_transport* t) : t_(t->Ref()) {
1734
1875
  t->sent_goaway_state = GRPC_CHTTP2_GRACEFUL_GOAWAY;
1735
1876
  grpc_chttp2_goaway_append((1u << 31) - 1, 0, grpc_empty_slice(), &t->qbuf);
1877
+ t->keepalive_timeout =
1878
+ std::min(t->keepalive_timeout, grpc_core::Duration::Seconds(20));
1879
+ t->ping_timeout =
1880
+ std::min(t->ping_timeout, grpc_core::Duration::Seconds(20));
1736
1881
  send_ping_locked(
1737
1882
  t, nullptr, GRPC_CLOSURE_INIT(&on_ping_ack_, OnPingAck, this, nullptr));
1738
1883
  grpc_chttp2_initiate_write(t, GRPC_CHTTP2_INITIATE_WRITE_GOAWAY_SENT);
1739
- timer_handle_ = t_->event_engine->RunAfter(
1740
- grpc_core::Duration::Seconds(20),
1741
- [self = Ref(DEBUG_LOCATION, "GoawayTimer")]() mutable {
1742
- grpc_core::ApplicationCallbackExecCtx callback_exec_ctx;
1743
- grpc_core::ExecCtx exec_ctx;
1744
- // The ref will be unreffed in the combiner.
1745
- auto* ptr = self.release();
1746
- ptr->t_->combiner->Run(
1747
- GRPC_CLOSURE_INIT(&ptr->on_timer_, OnTimerLocked, ptr, nullptr),
1748
- absl::OkStatus());
1749
- });
1750
1884
  }
1751
1885
 
1752
1886
  void MaybeSendFinalGoawayLocked() {
@@ -1787,26 +1921,12 @@ class GracefulGoaway : public grpc_core::RefCounted<GracefulGoaway> {
1787
1921
 
1788
1922
  static void OnPingAckLocked(void* arg, grpc_error_handle /* error */) {
1789
1923
  auto* self = static_cast<GracefulGoaway*>(arg);
1790
- if (self->timer_handle_ != TaskHandle::kInvalid) {
1791
- self->t_->event_engine->Cancel(
1792
- std::exchange(self->timer_handle_, TaskHandle::kInvalid));
1793
- }
1794
- self->MaybeSendFinalGoawayLocked();
1795
- self->Unref();
1796
- }
1797
-
1798
- static void OnTimerLocked(void* arg, grpc_error_handle /* error */) {
1799
- auto* self = static_cast<GracefulGoaway*>(arg);
1800
- // Clearing the handle since the timer has fired and the handle is invalid.
1801
- self->timer_handle_ = TaskHandle::kInvalid;
1802
1924
  self->MaybeSendFinalGoawayLocked();
1803
1925
  self->Unref();
1804
1926
  }
1805
1927
 
1806
1928
  const grpc_core::RefCountedPtr<grpc_chttp2_transport> t_;
1807
1929
  grpc_closure on_ping_ack_;
1808
- TaskHandle timer_handle_ = TaskHandle::kInvalid;
1809
- grpc_closure on_timer_;
1810
1930
  };
1811
1931
 
1812
1932
  } // namespace
@@ -2059,8 +2179,8 @@ void grpc_chttp2_maybe_complete_recv_trailing_metadata(grpc_chttp2_transport* t,
2059
2179
  }
2060
2180
  }
2061
2181
 
2062
- static void remove_stream(grpc_chttp2_transport* t, uint32_t id,
2063
- grpc_error_handle error) {
2182
+ static grpc_chttp2_transport::RemovedStreamHandle remove_stream(
2183
+ grpc_chttp2_transport* t, uint32_t id, grpc_error_handle error) {
2064
2184
  grpc_chttp2_stream* s = t->stream_map.extract(id).mapped();
2065
2185
  GPR_DEBUG_ASSERT(s);
2066
2186
  if (t->incoming_stream == s) {
@@ -2083,29 +2203,74 @@ static void remove_stream(grpc_chttp2_transport* t, uint32_t id,
2083
2203
  grpc_chttp2_list_remove_stalled_by_transport(t, s);
2084
2204
 
2085
2205
  maybe_start_some_streams(t);
2206
+
2207
+ if (t->is_client) return grpc_chttp2_transport::RemovedStreamHandle();
2208
+ return grpc_chttp2_transport::RemovedStreamHandle(t->Ref());
2086
2209
  }
2087
2210
 
2211
+ namespace grpc_core {
2212
+ namespace {
2213
+
2214
+ Duration TarpitDuration(grpc_chttp2_transport* t) {
2215
+ return Duration::Milliseconds(absl::LogUniform<int>(
2216
+ absl::BitGen(), t->min_tarpit_duration_ms, t->max_tarpit_duration_ms));
2217
+ }
2218
+
2219
+ template <typename F>
2220
+ void MaybeTarpit(grpc_chttp2_transport* t, bool tarpit, F fn) {
2221
+ if (!tarpit || !t->allow_tarpit || t->is_client) {
2222
+ fn(t);
2223
+ return;
2224
+ }
2225
+ const auto duration = TarpitDuration(t);
2226
+ t->event_engine->RunAfter(
2227
+ duration, [t = t->Ref(), fn = std::move(fn)]() mutable {
2228
+ ApplicationCallbackExecCtx app_exec_ctx;
2229
+ ExecCtx exec_ctx;
2230
+ t->combiner->Run(
2231
+ NewClosure([t, fn = std::move(fn)](grpc_error_handle) mutable {
2232
+ // TODO(ctiller): this can result in not sending RST_STREAMS if a
2233
+ // request gets tarpit behind a transport close.
2234
+ if (!t->closed_with_error.ok()) return;
2235
+ fn(t.get());
2236
+ }),
2237
+ absl::OkStatus());
2238
+ });
2239
+ }
2240
+
2241
+ } // namespace
2242
+ } // namespace grpc_core
2243
+
2088
2244
  void grpc_chttp2_cancel_stream(grpc_chttp2_transport* t, grpc_chttp2_stream* s,
2089
- grpc_error_handle due_to_error) {
2245
+ grpc_error_handle due_to_error, bool tarpit) {
2090
2246
  if (!t->is_client && !s->sent_trailing_metadata &&
2091
- grpc_error_has_clear_grpc_status(due_to_error)) {
2092
- close_from_api(t, s, due_to_error);
2247
+ grpc_error_has_clear_grpc_status(due_to_error) &&
2248
+ !(s->read_closed && s->write_closed)) {
2249
+ close_from_api(t, s, due_to_error, tarpit);
2093
2250
  return;
2094
2251
  }
2095
2252
 
2253
+ if (!due_to_error.ok() && !s->seen_error) {
2254
+ s->seen_error = true;
2255
+ }
2096
2256
  if (!s->read_closed || !s->write_closed) {
2097
2257
  if (s->id != 0) {
2098
2258
  grpc_http2_error_code http_error;
2099
2259
  grpc_error_get_status(due_to_error, s->deadline, nullptr, nullptr,
2100
2260
  &http_error, nullptr);
2101
- grpc_chttp2_add_rst_stream_to_next_write(
2102
- t, s->id, static_cast<uint32_t>(http_error), &s->stats.outgoing);
2103
- grpc_chttp2_initiate_write(t, GRPC_CHTTP2_INITIATE_WRITE_RST_STREAM);
2261
+ grpc_core::MaybeTarpit(
2262
+ t, tarpit,
2263
+ [id = s->id, http_error,
2264
+ remove_stream_handle = grpc_chttp2_mark_stream_closed(
2265
+ t, s, 1, 1, due_to_error)](grpc_chttp2_transport* t) {
2266
+ grpc_chttp2_add_rst_stream_to_next_write(
2267
+ t, id, static_cast<uint32_t>(http_error), nullptr);
2268
+ grpc_chttp2_initiate_write(t,
2269
+ GRPC_CHTTP2_INITIATE_WRITE_RST_STREAM);
2270
+ });
2271
+ return;
2104
2272
  }
2105
2273
  }
2106
- if (!due_to_error.ok() && !s->seen_error) {
2107
- s->seen_error = true;
2108
- }
2109
2274
  grpc_chttp2_mark_stream_closed(t, s, 1, 1, due_to_error);
2110
2275
  }
2111
2276
 
@@ -2198,9 +2363,10 @@ void grpc_chttp2_fail_pending_writes(grpc_chttp2_transport* t,
2198
2363
  flush_write_list(t, s, &s->on_flow_controlled_cbs, error);
2199
2364
  }
2200
2365
 
2201
- void grpc_chttp2_mark_stream_closed(grpc_chttp2_transport* t,
2202
- grpc_chttp2_stream* s, int close_reads,
2203
- int close_writes, grpc_error_handle error) {
2366
+ grpc_chttp2_transport::RemovedStreamHandle grpc_chttp2_mark_stream_closed(
2367
+ grpc_chttp2_transport* t, grpc_chttp2_stream* s, int close_reads,
2368
+ int close_writes, grpc_error_handle error) {
2369
+ grpc_chttp2_transport::RemovedStreamHandle rsh;
2204
2370
  if (grpc_http_trace.enabled()) {
2205
2371
  gpr_log(
2206
2372
  GPR_DEBUG, "MARK_STREAM_CLOSED: t=%p s=%p(id=%d) %s [%s]", t, s, s->id,
@@ -2216,7 +2382,7 @@ void grpc_chttp2_mark_stream_closed(grpc_chttp2_transport* t,
2216
2382
  grpc_chttp2_fake_status(t, s, overall_error);
2217
2383
  }
2218
2384
  grpc_chttp2_maybe_complete_recv_trailing_metadata(t, s);
2219
- return;
2385
+ return rsh;
2220
2386
  }
2221
2387
  bool closed_read = false;
2222
2388
  bool became_closed = false;
@@ -2234,7 +2400,7 @@ void grpc_chttp2_mark_stream_closed(grpc_chttp2_transport* t,
2234
2400
  became_closed = true;
2235
2401
  grpc_error_handle overall_error = removal_error(error, s, "Stream removed");
2236
2402
  if (s->id != 0) {
2237
- remove_stream(t, s->id, overall_error);
2403
+ rsh = remove_stream(t, s->id, overall_error);
2238
2404
  } else {
2239
2405
  // Purge streams waiting on concurrency still waiting for id assignment
2240
2406
  grpc_chttp2_list_remove_waiting_for_concurrency(t, s);
@@ -2258,17 +2424,11 @@ void grpc_chttp2_mark_stream_closed(grpc_chttp2_transport* t,
2258
2424
  grpc_chttp2_maybe_complete_recv_trailing_metadata(t, s);
2259
2425
  GRPC_CHTTP2_STREAM_UNREF(s, "chttp2");
2260
2426
  }
2427
+ return rsh;
2261
2428
  }
2262
2429
 
2263
2430
  static void close_from_api(grpc_chttp2_transport* t, grpc_chttp2_stream* s,
2264
- grpc_error_handle error) {
2265
- grpc_slice hdr;
2266
- grpc_slice status_hdr;
2267
- grpc_slice http_status_hdr;
2268
- grpc_slice content_type_hdr;
2269
- grpc_slice message_pfx;
2270
- uint8_t* p;
2271
- uint32_t len = 0;
2431
+ grpc_error_handle error, bool tarpit) {
2272
2432
  grpc_status_code grpc_status;
2273
2433
  std::string message;
2274
2434
  grpc_error_get_status(error, s->deadline, &grpc_status, &message, nullptr,
@@ -2276,147 +2436,167 @@ static void close_from_api(grpc_chttp2_transport* t, grpc_chttp2_stream* s,
2276
2436
 
2277
2437
  GPR_ASSERT(grpc_status >= 0 && (int)grpc_status < 100);
2278
2438
 
2279
- // Hand roll a header block.
2280
- // This is unnecessarily ugly - at some point we should find a more
2281
- // elegant solution.
2282
- // It's complicated by the fact that our send machinery would be dead by
2283
- // the time we got around to sending this, so instead we ignore HPACK
2284
- // compression and just write the uncompressed bytes onto the wire.
2285
- if (!s->sent_initial_metadata) {
2286
- http_status_hdr = GRPC_SLICE_MALLOC(13);
2287
- p = GRPC_SLICE_START_PTR(http_status_hdr);
2288
- *p++ = 0x00;
2289
- *p++ = 7;
2290
- *p++ = ':';
2291
- *p++ = 's';
2292
- *p++ = 't';
2293
- *p++ = 'a';
2294
- *p++ = 't';
2295
- *p++ = 'u';
2296
- *p++ = 's';
2297
- *p++ = 3;
2298
- *p++ = '2';
2299
- *p++ = '0';
2300
- *p++ = '0';
2301
- GPR_ASSERT(p == GRPC_SLICE_END_PTR(http_status_hdr));
2302
- len += static_cast<uint32_t> GRPC_SLICE_LENGTH(http_status_hdr);
2303
-
2304
- content_type_hdr = GRPC_SLICE_MALLOC(31);
2305
- p = GRPC_SLICE_START_PTR(content_type_hdr);
2306
- *p++ = 0x00;
2307
- *p++ = 12;
2308
- *p++ = 'c';
2309
- *p++ = 'o';
2310
- *p++ = 'n';
2311
- *p++ = 't';
2312
- *p++ = 'e';
2313
- *p++ = 'n';
2314
- *p++ = 't';
2315
- *p++ = '-';
2316
- *p++ = 't';
2317
- *p++ = 'y';
2318
- *p++ = 'p';
2319
- *p++ = 'e';
2320
- *p++ = 16;
2321
- *p++ = 'a';
2322
- *p++ = 'p';
2323
- *p++ = 'p';
2324
- *p++ = 'l';
2325
- *p++ = 'i';
2326
- *p++ = 'c';
2327
- *p++ = 'a';
2328
- *p++ = 't';
2329
- *p++ = 'i';
2330
- *p++ = 'o';
2331
- *p++ = 'n';
2332
- *p++ = '/';
2333
- *p++ = 'g';
2334
- *p++ = 'r';
2335
- *p++ = 'p';
2336
- *p++ = 'c';
2337
- GPR_ASSERT(p == GRPC_SLICE_END_PTR(content_type_hdr));
2338
- len += static_cast<uint32_t> GRPC_SLICE_LENGTH(content_type_hdr);
2339
- }
2340
-
2341
- status_hdr = GRPC_SLICE_MALLOC(15 + (grpc_status >= 10));
2342
- p = GRPC_SLICE_START_PTR(status_hdr);
2343
- *p++ = 0x00; // literal header, not indexed
2344
- *p++ = 11; // len(grpc-status)
2345
- *p++ = 'g';
2346
- *p++ = 'r';
2347
- *p++ = 'p';
2348
- *p++ = 'c';
2349
- *p++ = '-';
2350
- *p++ = 's';
2351
- *p++ = 't';
2352
- *p++ = 'a';
2353
- *p++ = 't';
2354
- *p++ = 'u';
2355
- *p++ = 's';
2356
- if (grpc_status < 10) {
2357
- *p++ = 1;
2358
- *p++ = static_cast<uint8_t>('0' + grpc_status);
2359
- } else {
2360
- *p++ = 2;
2361
- *p++ = static_cast<uint8_t>('0' + (grpc_status / 10));
2362
- *p++ = static_cast<uint8_t>('0' + (grpc_status % 10));
2363
- }
2364
- GPR_ASSERT(p == GRPC_SLICE_END_PTR(status_hdr));
2365
- len += static_cast<uint32_t> GRPC_SLICE_LENGTH(status_hdr);
2366
-
2367
- size_t msg_len = message.length();
2368
- GPR_ASSERT(msg_len <= UINT32_MAX);
2369
- grpc_core::VarintWriter<1> msg_len_writer(static_cast<uint32_t>(msg_len));
2370
- message_pfx = GRPC_SLICE_MALLOC(14 + msg_len_writer.length());
2371
- p = GRPC_SLICE_START_PTR(message_pfx);
2372
- *p++ = 0x00; // literal header, not indexed
2373
- *p++ = 12; // len(grpc-message)
2374
- *p++ = 'g';
2375
- *p++ = 'r';
2376
- *p++ = 'p';
2377
- *p++ = 'c';
2378
- *p++ = '-';
2379
- *p++ = 'm';
2380
- *p++ = 'e';
2381
- *p++ = 's';
2382
- *p++ = 's';
2383
- *p++ = 'a';
2384
- *p++ = 'g';
2385
- *p++ = 'e';
2386
- msg_len_writer.Write(0, p);
2387
- p += msg_len_writer.length();
2388
- GPR_ASSERT(p == GRPC_SLICE_END_PTR(message_pfx));
2389
- len += static_cast<uint32_t> GRPC_SLICE_LENGTH(message_pfx);
2390
- len += static_cast<uint32_t>(msg_len);
2391
-
2392
- hdr = GRPC_SLICE_MALLOC(9);
2393
- p = GRPC_SLICE_START_PTR(hdr);
2394
- *p++ = static_cast<uint8_t>(len >> 16);
2395
- *p++ = static_cast<uint8_t>(len >> 8);
2396
- *p++ = static_cast<uint8_t>(len);
2397
- *p++ = GRPC_CHTTP2_FRAME_HEADER;
2398
- *p++ = GRPC_CHTTP2_DATA_FLAG_END_STREAM | GRPC_CHTTP2_DATA_FLAG_END_HEADERS;
2399
- *p++ = static_cast<uint8_t>(s->id >> 24);
2400
- *p++ = static_cast<uint8_t>(s->id >> 16);
2401
- *p++ = static_cast<uint8_t>(s->id >> 8);
2402
- *p++ = static_cast<uint8_t>(s->id);
2403
- GPR_ASSERT(p == GRPC_SLICE_END_PTR(hdr));
2404
-
2405
- grpc_slice_buffer_add(&t->qbuf, hdr);
2406
- if (!s->sent_initial_metadata) {
2407
- grpc_slice_buffer_add(&t->qbuf, http_status_hdr);
2408
- grpc_slice_buffer_add(&t->qbuf, content_type_hdr);
2409
- }
2410
- grpc_slice_buffer_add(&t->qbuf, status_hdr);
2411
- grpc_slice_buffer_add(&t->qbuf, message_pfx);
2412
- grpc_slice_buffer_add(&t->qbuf,
2413
- grpc_slice_from_cpp_string(std::move(message)));
2414
- grpc_chttp2_reset_ping_clock(t);
2415
- grpc_chttp2_add_rst_stream_to_next_write(t, s->id, GRPC_HTTP2_NO_ERROR,
2416
- &s->stats.outgoing);
2417
-
2418
- grpc_chttp2_mark_stream_closed(t, s, 1, 1, error);
2419
- grpc_chttp2_initiate_write(t, GRPC_CHTTP2_INITIATE_WRITE_CLOSE_FROM_API);
2439
+ auto remove_stream_handle = grpc_chttp2_mark_stream_closed(t, s, 1, 1, error);
2440
+ grpc_core::MaybeTarpit(
2441
+ t, tarpit,
2442
+ [error = std::move(error),
2443
+ sent_initial_metadata = s->sent_initial_metadata, id = s->id,
2444
+ grpc_status, message = std::move(message),
2445
+ remove_stream_handle =
2446
+ std::move(remove_stream_handle)](grpc_chttp2_transport* t) mutable {
2447
+ grpc_slice hdr;
2448
+ grpc_slice status_hdr;
2449
+ grpc_slice http_status_hdr;
2450
+ grpc_slice content_type_hdr;
2451
+ grpc_slice message_pfx;
2452
+ uint8_t* p;
2453
+ uint32_t len = 0;
2454
+
2455
+ // Hand roll a header block.
2456
+ // This is unnecessarily ugly - at some point we should find a more
2457
+ // elegant solution.
2458
+ // It's complicated by the fact that our send machinery would be dead
2459
+ // by the time we got around to sending this, so instead we ignore
2460
+ // HPACK compression and just write the uncompressed bytes onto the
2461
+ // wire.
2462
+ if (!sent_initial_metadata) {
2463
+ http_status_hdr = GRPC_SLICE_MALLOC(13);
2464
+ p = GRPC_SLICE_START_PTR(http_status_hdr);
2465
+ *p++ = 0x00;
2466
+ *p++ = 7;
2467
+ *p++ = ':';
2468
+ *p++ = 's';
2469
+ *p++ = 't';
2470
+ *p++ = 'a';
2471
+ *p++ = 't';
2472
+ *p++ = 'u';
2473
+ *p++ = 's';
2474
+ *p++ = 3;
2475
+ *p++ = '2';
2476
+ *p++ = '0';
2477
+ *p++ = '0';
2478
+ GPR_ASSERT(p == GRPC_SLICE_END_PTR(http_status_hdr));
2479
+ len += static_cast<uint32_t> GRPC_SLICE_LENGTH(http_status_hdr);
2480
+
2481
+ content_type_hdr = GRPC_SLICE_MALLOC(31);
2482
+ p = GRPC_SLICE_START_PTR(content_type_hdr);
2483
+ *p++ = 0x00;
2484
+ *p++ = 12;
2485
+ *p++ = 'c';
2486
+ *p++ = 'o';
2487
+ *p++ = 'n';
2488
+ *p++ = 't';
2489
+ *p++ = 'e';
2490
+ *p++ = 'n';
2491
+ *p++ = 't';
2492
+ *p++ = '-';
2493
+ *p++ = 't';
2494
+ *p++ = 'y';
2495
+ *p++ = 'p';
2496
+ *p++ = 'e';
2497
+ *p++ = 16;
2498
+ *p++ = 'a';
2499
+ *p++ = 'p';
2500
+ *p++ = 'p';
2501
+ *p++ = 'l';
2502
+ *p++ = 'i';
2503
+ *p++ = 'c';
2504
+ *p++ = 'a';
2505
+ *p++ = 't';
2506
+ *p++ = 'i';
2507
+ *p++ = 'o';
2508
+ *p++ = 'n';
2509
+ *p++ = '/';
2510
+ *p++ = 'g';
2511
+ *p++ = 'r';
2512
+ *p++ = 'p';
2513
+ *p++ = 'c';
2514
+ GPR_ASSERT(p == GRPC_SLICE_END_PTR(content_type_hdr));
2515
+ len += static_cast<uint32_t> GRPC_SLICE_LENGTH(content_type_hdr);
2516
+ }
2517
+
2518
+ status_hdr = GRPC_SLICE_MALLOC(15 + (grpc_status >= 10));
2519
+ p = GRPC_SLICE_START_PTR(status_hdr);
2520
+ *p++ = 0x00; // literal header, not indexed
2521
+ *p++ = 11; // len(grpc-status)
2522
+ *p++ = 'g';
2523
+ *p++ = 'r';
2524
+ *p++ = 'p';
2525
+ *p++ = 'c';
2526
+ *p++ = '-';
2527
+ *p++ = 's';
2528
+ *p++ = 't';
2529
+ *p++ = 'a';
2530
+ *p++ = 't';
2531
+ *p++ = 'u';
2532
+ *p++ = 's';
2533
+ if (grpc_status < 10) {
2534
+ *p++ = 1;
2535
+ *p++ = static_cast<uint8_t>('0' + grpc_status);
2536
+ } else {
2537
+ *p++ = 2;
2538
+ *p++ = static_cast<uint8_t>('0' + (grpc_status / 10));
2539
+ *p++ = static_cast<uint8_t>('0' + (grpc_status % 10));
2540
+ }
2541
+ GPR_ASSERT(p == GRPC_SLICE_END_PTR(status_hdr));
2542
+ len += static_cast<uint32_t> GRPC_SLICE_LENGTH(status_hdr);
2543
+
2544
+ size_t msg_len = message.length();
2545
+ GPR_ASSERT(msg_len <= UINT32_MAX);
2546
+ grpc_core::VarintWriter<1> msg_len_writer(
2547
+ static_cast<uint32_t>(msg_len));
2548
+ message_pfx = GRPC_SLICE_MALLOC(14 + msg_len_writer.length());
2549
+ p = GRPC_SLICE_START_PTR(message_pfx);
2550
+ *p++ = 0x00; // literal header, not indexed
2551
+ *p++ = 12; // len(grpc-message)
2552
+ *p++ = 'g';
2553
+ *p++ = 'r';
2554
+ *p++ = 'p';
2555
+ *p++ = 'c';
2556
+ *p++ = '-';
2557
+ *p++ = 'm';
2558
+ *p++ = 'e';
2559
+ *p++ = 's';
2560
+ *p++ = 's';
2561
+ *p++ = 'a';
2562
+ *p++ = 'g';
2563
+ *p++ = 'e';
2564
+ msg_len_writer.Write(0, p);
2565
+ p += msg_len_writer.length();
2566
+ GPR_ASSERT(p == GRPC_SLICE_END_PTR(message_pfx));
2567
+ len += static_cast<uint32_t> GRPC_SLICE_LENGTH(message_pfx);
2568
+ len += static_cast<uint32_t>(msg_len);
2569
+
2570
+ hdr = GRPC_SLICE_MALLOC(9);
2571
+ p = GRPC_SLICE_START_PTR(hdr);
2572
+ *p++ = static_cast<uint8_t>(len >> 16);
2573
+ *p++ = static_cast<uint8_t>(len >> 8);
2574
+ *p++ = static_cast<uint8_t>(len);
2575
+ *p++ = GRPC_CHTTP2_FRAME_HEADER;
2576
+ *p++ = GRPC_CHTTP2_DATA_FLAG_END_STREAM |
2577
+ GRPC_CHTTP2_DATA_FLAG_END_HEADERS;
2578
+ *p++ = static_cast<uint8_t>(id >> 24);
2579
+ *p++ = static_cast<uint8_t>(id >> 16);
2580
+ *p++ = static_cast<uint8_t>(id >> 8);
2581
+ *p++ = static_cast<uint8_t>(id);
2582
+ GPR_ASSERT(p == GRPC_SLICE_END_PTR(hdr));
2583
+
2584
+ grpc_slice_buffer_add(&t->qbuf, hdr);
2585
+ if (!sent_initial_metadata) {
2586
+ grpc_slice_buffer_add(&t->qbuf, http_status_hdr);
2587
+ grpc_slice_buffer_add(&t->qbuf, content_type_hdr);
2588
+ }
2589
+ grpc_slice_buffer_add(&t->qbuf, status_hdr);
2590
+ grpc_slice_buffer_add(&t->qbuf, message_pfx);
2591
+ grpc_slice_buffer_add(&t->qbuf,
2592
+ grpc_slice_from_cpp_string(std::move(message)));
2593
+ grpc_chttp2_reset_ping_clock(t);
2594
+ grpc_chttp2_add_rst_stream_to_next_write(t, id, GRPC_HTTP2_NO_ERROR,
2595
+ nullptr);
2596
+
2597
+ grpc_chttp2_initiate_write(t,
2598
+ GRPC_CHTTP2_INITIATE_WRITE_CLOSE_FROM_API);
2599
+ });
2420
2600
  }
2421
2601
 
2422
2602
  static void end_all_the_calls(grpc_chttp2_transport* t,
@@ -2429,13 +2609,13 @@ static void end_all_the_calls(grpc_chttp2_transport* t,
2429
2609
  error = grpc_error_set_int(error, grpc_core::StatusIntProperty::kRpcStatus,
2430
2610
  GRPC_STATUS_UNAVAILABLE);
2431
2611
  }
2432
- cancel_unstarted_streams(t, error);
2612
+ cancel_unstarted_streams(t, error, false);
2433
2613
  std::vector<grpc_chttp2_stream*> to_cancel;
2434
2614
  for (auto id_stream : t->stream_map) {
2435
2615
  to_cancel.push_back(id_stream.second);
2436
2616
  }
2437
2617
  for (auto s : to_cancel) {
2438
- grpc_chttp2_cancel_stream(t, s, error);
2618
+ grpc_chttp2_cancel_stream(t, s, error, false);
2439
2619
  }
2440
2620
  }
2441
2621
 
@@ -2528,21 +2708,34 @@ static void read_action(grpc_core::RefCountedPtr<grpc_chttp2_transport> t,
2528
2708
  error);
2529
2709
  }
2530
2710
 
2531
- static void read_action_locked(
2711
+ static void read_action_parse_loop_locked(
2532
2712
  grpc_core::RefCountedPtr<grpc_chttp2_transport> t,
2533
2713
  grpc_error_handle error) {
2534
- grpc_error_handle err = error;
2535
- if (!err.ok()) {
2536
- err = grpc_error_set_int(
2537
- GRPC_ERROR_CREATE_REFERENCING("Endpoint read failed", &err, 1),
2538
- grpc_core::StatusIntProperty::kOccurredDuringWrite, t->write_state);
2539
- }
2540
- std::swap(err, error);
2541
2714
  if (t->closed_with_error.ok()) {
2542
- size_t i = 0;
2543
2715
  grpc_error_handle errors[3] = {error, absl::OkStatus(), absl::OkStatus()};
2544
- for (; i < t->read_buffer.count && errors[1] == absl::OkStatus(); i++) {
2545
- errors[1] = grpc_chttp2_perform_read(t.get(), t->read_buffer.slices[i]);
2716
+ size_t requests_started = 0;
2717
+ for (size_t i = 0;
2718
+ i < t->read_buffer.count && errors[1] == absl::OkStatus(); i++) {
2719
+ auto r = grpc_chttp2_perform_read(t.get(), t->read_buffer.slices[i],
2720
+ requests_started);
2721
+ if (auto* partial_read_size = absl::get_if<size_t>(&r)) {
2722
+ for (size_t j = 0; j < i; j++) {
2723
+ grpc_core::CSliceUnref(grpc_slice_buffer_take_first(&t->read_buffer));
2724
+ }
2725
+ grpc_slice_buffer_sub_first(
2726
+ &t->read_buffer, *partial_read_size,
2727
+ GRPC_SLICE_LENGTH(t->read_buffer.slices[0]));
2728
+ t->combiner->ForceOffload();
2729
+ auto* tp = t.get();
2730
+ tp->combiner->Run(
2731
+ grpc_core::InitTransportClosure<read_action_parse_loop_locked>(
2732
+ std::move(t), &tp->read_action_locked),
2733
+ std::move(errors[0]));
2734
+ // Early return: we queued to retry later.
2735
+ return;
2736
+ } else {
2737
+ errors[1] = std::move(absl::get<absl::Status>(r));
2738
+ }
2546
2739
  }
2547
2740
  if (errors[1] != absl::OkStatus()) {
2548
2741
  errors[2] = try_http_parsing(t.get());
@@ -2601,6 +2794,33 @@ static void read_action_locked(
2601
2794
  }
2602
2795
  }
2603
2796
 
2797
+ static void read_action_locked(
2798
+ grpc_core::RefCountedPtr<grpc_chttp2_transport> t,
2799
+ grpc_error_handle error) {
2800
+ // got an incoming read, cancel any pending keepalive timers
2801
+ t->keepalive_incoming_data_wanted = false;
2802
+ if (t->keepalive_ping_timeout_handle !=
2803
+ grpc_event_engine::experimental::EventEngine::TaskHandle::kInvalid) {
2804
+ if (GRPC_TRACE_FLAG_ENABLED(grpc_ping_trace) ||
2805
+ GRPC_TRACE_FLAG_ENABLED(grpc_keepalive_trace)) {
2806
+ gpr_log(GPR_INFO,
2807
+ "%s[%p]: Clear keepalive timer because data was received",
2808
+ t->is_client ? "CLIENT" : "SERVER", t.get());
2809
+ }
2810
+ t->event_engine->Cancel(std::exchange(
2811
+ t->keepalive_ping_timeout_handle,
2812
+ grpc_event_engine::experimental::EventEngine::TaskHandle::kInvalid));
2813
+ }
2814
+ grpc_error_handle err = error;
2815
+ if (!err.ok()) {
2816
+ err = grpc_error_set_int(
2817
+ GRPC_ERROR_CREATE_REFERENCING("Endpoint read failed", &err, 1),
2818
+ grpc_core::StatusIntProperty::kOccurredDuringWrite, t->write_state);
2819
+ }
2820
+ std::swap(err, error);
2821
+ read_action_parse_loop_locked(std::move(t), std::move(err));
2822
+ }
2823
+
2604
2824
  static void continue_read_action_locked(
2605
2825
  grpc_core::RefCountedPtr<grpc_chttp2_transport> t) {
2606
2826
  const bool urgent = !t->goaway_error.ok();
@@ -2793,39 +3013,6 @@ static void init_keepalive_ping_locked(
2793
3013
  }
2794
3014
  }
2795
3015
 
2796
- static void start_keepalive_ping(
2797
- grpc_core::RefCountedPtr<grpc_chttp2_transport> t,
2798
- grpc_error_handle error) {
2799
- auto* tp = t.get();
2800
- tp->combiner->Run(
2801
- grpc_core::InitTransportClosure<start_keepalive_ping_locked>(
2802
- std::move(t), &tp->start_keepalive_ping_locked),
2803
- error);
2804
- }
2805
-
2806
- static void start_keepalive_ping_locked(
2807
- grpc_core::RefCountedPtr<grpc_chttp2_transport> t,
2808
- grpc_error_handle error) {
2809
- if (!error.ok()) {
2810
- return;
2811
- }
2812
- if (t->channelz_socket != nullptr) {
2813
- t->channelz_socket->RecordKeepaliveSent();
2814
- }
2815
- if (GRPC_TRACE_FLAG_ENABLED(grpc_http_trace) ||
2816
- GRPC_TRACE_FLAG_ENABLED(grpc_keepalive_trace)) {
2817
- gpr_log(GPR_INFO, "%s: Start keepalive ping",
2818
- std::string(t->peer_string.as_string_view()).c_str());
2819
- }
2820
- t->keepalive_watchdog_timer_handle =
2821
- t->event_engine->RunAfter(t->keepalive_timeout, [t]() mutable {
2822
- grpc_core::ApplicationCallbackExecCtx callback_exec_ctx;
2823
- grpc_core::ExecCtx exec_ctx;
2824
- keepalive_watchdog_fired(std::move(t));
2825
- });
2826
- t->keepalive_ping_started = true;
2827
- }
2828
-
2829
3016
  static void finish_keepalive_ping(
2830
3017
  grpc_core::RefCountedPtr<grpc_chttp2_transport> t,
2831
3018
  grpc_error_handle error) {
@@ -2846,19 +3033,7 @@ static void finish_keepalive_ping_locked(
2846
3033
  gpr_log(GPR_INFO, "%s: Finish keepalive ping",
2847
3034
  std::string(t->peer_string.as_string_view()).c_str());
2848
3035
  }
2849
- if (!t->keepalive_ping_started) {
2850
- // start_keepalive_ping_locked has not run yet. Reschedule
2851
- // finish_keepalive_ping_locked for it to be run later.
2852
- finish_keepalive_ping(std::move(t), std::move(error));
2853
- return;
2854
- }
2855
- t->keepalive_ping_started = false;
2856
3036
  t->keepalive_state = GRPC_CHTTP2_KEEPALIVE_STATE_WAITING;
2857
- if (t->keepalive_watchdog_timer_handle.has_value()) {
2858
- if (t->event_engine->Cancel(*t->keepalive_watchdog_timer_handle)) {
2859
- t->keepalive_watchdog_timer_handle.reset();
2860
- }
2861
- }
2862
3037
  GPR_ASSERT(!t->keepalive_ping_timer_handle.has_value());
2863
3038
  t->keepalive_ping_timer_handle =
2864
3039
  t->event_engine->RunAfter(t->keepalive_time, [t] {
@@ -2870,39 +3045,6 @@ static void finish_keepalive_ping_locked(
2870
3045
  }
2871
3046
  }
2872
3047
 
2873
- static void keepalive_watchdog_fired(
2874
- grpc_core::RefCountedPtr<grpc_chttp2_transport> t) {
2875
- auto* tp = t.get();
2876
- tp->combiner->Run(
2877
- grpc_core::InitTransportClosure<keepalive_watchdog_fired_locked>(
2878
- std::move(t), &tp->keepalive_watchdog_fired_locked),
2879
- absl::OkStatus());
2880
- }
2881
-
2882
- static void keepalive_watchdog_fired_locked(
2883
- grpc_core::RefCountedPtr<grpc_chttp2_transport> t,
2884
- GRPC_UNUSED grpc_error_handle error) {
2885
- GPR_DEBUG_ASSERT(error.ok());
2886
- GPR_ASSERT(t->keepalive_watchdog_timer_handle.has_value());
2887
- t->keepalive_watchdog_timer_handle.reset();
2888
- if (t->keepalive_state == GRPC_CHTTP2_KEEPALIVE_STATE_PINGING) {
2889
- gpr_log(GPR_INFO, "%s: Keepalive watchdog fired. Closing transport.",
2890
- std::string(t->peer_string.as_string_view()).c_str());
2891
- t->keepalive_state = GRPC_CHTTP2_KEEPALIVE_STATE_DYING;
2892
- close_transport_locked(
2893
- t.get(),
2894
- grpc_error_set_int(GRPC_ERROR_CREATE("keepalive watchdog timeout"),
2895
- grpc_core::StatusIntProperty::kRpcStatus,
2896
- GRPC_STATUS_UNAVAILABLE));
2897
- } else {
2898
- // If keepalive_state is not PINGING, we consider it as an error. Maybe the
2899
- // cancellation failed in finish_keepalive_ping_locked. Users have seen
2900
- // other states: https://github.com/grpc/grpc/issues/32085.
2901
- gpr_log(GPR_ERROR, "keepalive_ping_end state error: %d (expect: %d)",
2902
- t->keepalive_state, GRPC_CHTTP2_KEEPALIVE_STATE_PINGING);
2903
- }
2904
- }
2905
-
2906
3048
  static void maybe_reset_keepalive_ping_timer_locked(grpc_chttp2_transport* t) {
2907
3049
  if (t->keepalive_ping_timer_handle.has_value()) {
2908
3050
  if (t->event_engine->Cancel(*t->keepalive_ping_timer_handle)) {
@@ -3038,7 +3180,8 @@ static void destructive_reclaimer_locked(
3038
3180
  t.get(), s,
3039
3181
  grpc_error_set_int(GRPC_ERROR_CREATE("Buffers full"),
3040
3182
  grpc_core::StatusIntProperty::kHttp2Error,
3041
- GRPC_HTTP2_ENHANCE_YOUR_CALM));
3183
+ GRPC_HTTP2_ENHANCE_YOUR_CALM),
3184
+ false);
3042
3185
  if (!t->stream_map.empty()) {
3043
3186
  // Since we cancel one stream per destructive reclamation, if
3044
3187
  // there are more streams left, we can immediately post a new