grpc 1.59.0 → 1.59.2

Sign up to get free protection for your applications and to get access to all the features.
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