1
#include "source/common/http/http2/codec_impl.h"
2

            
3
#include <algorithm>
4
#include <cstdint>
5
#include <memory>
6
#include <ostream>
7
#include <vector>
8

            
9
#include "envoy/event/dispatcher.h"
10
#include "envoy/http/codes.h"
11
#include "envoy/http/header_map.h"
12
#include "envoy/network/connection.h"
13

            
14
#include "source/common/common/assert.h"
15
#include "source/common/common/cleanup.h"
16
#include "source/common/common/dump_state_utils.h"
17
#include "source/common/common/enum_to_int.h"
18
#include "source/common/common/scope_tracker.h"
19
#include "source/common/common/utility.h"
20
#include "source/common/http/codes.h"
21
#include "source/common/http/header_utility.h"
22
#include "source/common/http/headers.h"
23
#include "source/common/http/http2/codec_stats.h"
24
#include "source/common/http/utility.h"
25
#include "source/common/runtime/runtime_features.h"
26

            
27
#include "absl/cleanup/cleanup.h"
28
#include "absl/container/flat_hash_map.h"
29
#include "quiche/common/quiche_endian.h"
30
#include "quiche/http2/adapter/nghttp2_adapter.h"
31
#include "quiche/http2/adapter/oghttp2_adapter.h"
32

            
33
namespace Envoy {
34
namespace Http {
35
namespace Http2 {
36

            
37
namespace {
38

            
39
// Optimization: Map of well-known header names to Envoy's static LowerCaseString objects.
40
// This allows us to avoid copying header names for common HTTP/2 headers.
41
// The string_views point to compile-time string literals which live forever.
42
class StaticHeaderNameLookup {
43
public:
44
340
  StaticHeaderNameLookup() {
45
340
    const auto& headers = Headers::get();
46
340
    const auto& custom_headers = CustomHeaders::get();
47

            
48
    // HTTP/2 pseudo-headers (most common).
49
340
    addMapping(":authority", headers.Host);
50
340
    addMapping(":method", headers.Method);
51
340
    addMapping(":path", headers.Path);
52
340
    addMapping(":scheme", headers.Scheme);
53
340
    addMapping(":status", headers.Status);
54
340
    addMapping(":protocol", headers.Protocol);
55

            
56
    // Common request headers.
57
340
    addMapping("accept", custom_headers.Accept);
58
340
    addMapping("accept-encoding", custom_headers.AcceptEncoding);
59
340
    addMapping("authorization", custom_headers.Authorization);
60
340
    addMapping("cache-control", custom_headers.CacheControl);
61
340
    addMapping("content-encoding", custom_headers.ContentEncoding);
62
340
    addMapping("content-length", headers.ContentLength);
63
340
    addMapping("content-type", headers.ContentType);
64
340
    addMapping("cookie", headers.Cookie);
65
340
    addMapping("date", headers.Date);
66
340
    addMapping("expect", headers.Expect);
67
340
    addMapping("grpc-timeout", headers.GrpcTimeout);
68
340
    addMapping("host", headers.HostLegacy);
69
340
    addMapping("user-agent", headers.UserAgent);
70

            
71
    // Common response headers.
72
340
    addMapping("location", headers.Location);
73
340
    addMapping("server", headers.Server);
74
340
    addMapping("set-cookie", headers.SetCookie);
75
340
    addMapping("grpc-status", headers.GrpcStatus);
76
340
    addMapping("grpc-message", headers.GrpcMessage);
77

            
78
    // Common request/response headers.
79
340
    addMapping("connection", headers.Connection);
80
340
    addMapping("keep-alive", headers.KeepAlive);
81
340
    addMapping("proxy-connection", headers.ProxyConnection);
82
340
    addMapping("te", headers.TE);
83
340
    addMapping("transfer-encoding", headers.TransferEncoding);
84
340
    addMapping("upgrade", headers.Upgrade);
85
340
    addMapping("via", headers.Via);
86
340
    addMapping("x-request-id", headers.RequestId);
87

            
88
    // X-Forwarded headers.
89
340
    addMapping("x-forwarded-for", headers.ForwardedFor);
90
340
    addMapping("x-forwarded-host", headers.ForwardedHost);
91
340
    addMapping("x-forwarded-proto", headers.ForwardedProto);
92
340
    addMapping("x-forwarded-port", headers.ForwardedPort);
93
340
  }
94

            
95
763448
  const LowerCaseString* lookup(absl::string_view name) const {
96
763448
    auto it = map_.find(name);
97
763448
    return it != map_.end() ? it->second : nullptr;
98
763448
  }
99

            
100
private:
101
12240
  void addMapping(absl::string_view name, const LowerCaseString& header) {
102
12240
    map_.emplace(name, &header);
103
12240
  }
104

            
105
  absl::flat_hash_map<absl::string_view, const LowerCaseString*> map_;
106
};
107

            
108
763448
const StaticHeaderNameLookup& getStaticHeaderNameLookup() {
109
763448
  CONSTRUCT_ON_FIRST_USE(StaticHeaderNameLookup);
110
763448
}
111

            
112
} // namespace
113

            
114
// for nghttp2 compatibility.
115
const int ERR_CALLBACK_FAILURE = -902;
116
const int INITIAL_CONNECTION_WINDOW_SIZE = ((1 << 16) - 1);
117
const int ERR_TEMPORAL_CALLBACK_FAILURE = -521;
118
const int ERR_REFUSED_STREAM = -533;
119
const int ERR_HTTP_HEADER = -531;
120
const int ERR_HTTP_MESSAGING = -532;
121
const int ERR_PROTO = -505;
122
const int ERR_STREAM_CLOSED = -510;
123
const int ERR_FLOW_CONTROL = -524;
124

            
125
// Changes or additions to details should be reflected in
126
// docs/root/configuration/http/http_conn_man/response_code_details.rst
127
class Http2ResponseCodeDetailValues {
128
public:
129
  // Invalid HTTP header field was received and stream is going to be
130
  // closed.
131
  const absl::string_view ng_http2_err_http_header_ = "http2.invalid.header.field";
132
  // Violation in HTTP messaging rule.
133
  const absl::string_view ng_http2_err_http_messaging_ = "http2.violation.of.messaging.rule";
134
  // none of the above
135
  const absl::string_view ng_http2_err_unknown_ = "http2.unknown.nghttp2.error";
136
  // oghttp2 does not provide details yet.
137
  const absl::string_view oghttp2_err_unknown_ = "http2.unknown.oghttp2.error";
138
  // The number of headers (or trailers) exceeded the configured limits
139
  const absl::string_view too_many_headers = "http2.too_many_headers";
140
  // Envoy detected an HTTP/2 frame flood from the server.
141
  const absl::string_view outbound_frame_flood = "http2.outbound_frames_flood";
142
  // Envoy detected an inbound HTTP/2 frame flood.
143
  const absl::string_view inbound_empty_frame_flood = "http2.inbound_empty_frames_flood";
144
  // Envoy was configured to drop requests with header keys beginning with underscores.
145
  const absl::string_view invalid_underscore = "http2.unexpected_underscore";
146
  // The peer refused the stream.
147
  const absl::string_view remote_refused = "http2.remote_refuse";
148
  // The peer reset the stream.
149
  const absl::string_view remote_reset = "http2.remote_reset";
150

            
151
#ifdef ENVOY_NGHTTP2
152
1045
  const absl::string_view errorDetails(int error_code) const {
153
1045
    switch (error_code) {
154
871
    case NGHTTP2_ERR_HTTP_HEADER:
155
871
      return ng_http2_err_http_header_;
156
173
    case NGHTTP2_ERR_HTTP_MESSAGING:
157
173
      return ng_http2_err_http_messaging_;
158
1
    default:
159
1
      return ng_http2_err_unknown_;
160
1045
    }
161
1045
  }
162
};
163
832
const char* codecStrError(int error_code) { return nghttp2_strerror(error_code); }
164
#else
165
  const absl::string_view errorDetails(int) const { return oghttp2_err_unknown_; }
166
};
167
const char* codecStrError(int) { return "unknown_error"; }
168
#endif
169

            
170
/**
171
 * Convert StreamResetReason to HTTP/2 error code.
172
 * @param reason the StreamResetReason to convert
173
 * @param response_end_stream_sent whether END_STREAM has been sent for a server stream.
174
 * True means the response has been fully sent.
175
 */
176
12698
int reasonToReset(StreamResetReason reason, bool response_end_stream_sent) {
177
12698
  switch (reason) {
178
4
  case StreamResetReason::LocalRefusedStreamReset:
179
4
    return OGHTTP2_REFUSED_STREAM;
180
29
  case StreamResetReason::ConnectError:
181
29
    return OGHTTP2_CONNECT_ERROR;
182
106
  case StreamResetReason::ProtocolError:
183
106
    if (!Runtime::runtimeFeatureEnabled("envoy.reloadable_features.reset_with_error")) {
184
2
      return OGHTTP2_NO_ERROR;
185
2
    }
186
104
    return OGHTTP2_PROTOCOL_ERROR;
187
12559
  default:
188
12559
    if (!Runtime::runtimeFeatureEnabled("envoy.reloadable_features.reset_with_error")) {
189
6
      return OGHTTP2_NO_ERROR;
190
6
    }
191
    // If the response has been fully sent then we reset with OGHTTP2_NO_ERROR to tell
192
    // there is no transport level error.
193
12553
    return response_end_stream_sent ? OGHTTP2_NO_ERROR : OGHTTP2_INTERNAL_ERROR;
194
12698
  }
195
12698
}
196

            
197
12346
StreamResetReason errorCodeToResetReason(int error_code) {
198
12346
  switch (error_code) {
199
22
  case OGHTTP2_REFUSED_STREAM:
200
22
    return StreamResetReason::RemoteRefusedStreamReset;
201
25
  case OGHTTP2_CONNECT_ERROR:
202
25
    return StreamResetReason::ConnectError;
203
135
  case OGHTTP2_PROTOCOL_ERROR:
204
135
    return StreamResetReason::ProtocolError;
205
12164
  default:
206
12164
    return StreamResetReason::RemoteReset;
207
12346
  }
208
12346
}
209

            
210
using Http2ResponseCodeDetails = ConstSingleton<Http2ResponseCodeDetailValues>;
211
using OnHeaderResult = http2::adapter::Http2VisitorInterface::OnHeaderResult;
212

            
213
enum Settings {
214
  // SETTINGS_HEADER_TABLE_SIZE = 0x01,
215
  // SETTINGS_ENABLE_PUSH = 0x02,
216
  SETTINGS_MAX_CONCURRENT_STREAMS = 0x03,
217
  // SETTINGS_INITIAL_WINDOW_SIZE = 0x04,
218
  // SETTINGS_MAX_FRAME_SIZE = 0x05,
219
  // SETTINGS_MAX_HEADER_LIST_SIZE = 0x06,
220
  // SETTINGS_ENABLE_CONNECT_PROTOCOL = 0x08,
221
  // SETTINGS_NO_RFC7540_PRIORITIES = 0x09
222
};
223

            
224
enum Flags {
225
  // FLAG_NONE = 0,
226
  FLAG_END_STREAM = 0x01,
227
  // FLAG_END_HEADERS = 0x04,
228
  FLAG_ACK = 0x01,
229
  // FLAG_PADDED = 0x08,
230
  // FLAG_PRIORITY = 0x20
231
};
232

            
233
ReceivedSettingsImpl::ReceivedSettingsImpl(
234
28006
    absl::Span<const http2::adapter::Http2Setting> settings) {
235
89084
  for (const auto& [id, value] : settings) {
236
88778
    if (id == SETTINGS_MAX_CONCURRENT_STREAMS) {
237
27437
      concurrent_stream_limit_ = value;
238
27437
      break;
239
27437
    }
240
88778
  }
241
28006
}
242

            
243
bool Utility::reconstituteCrumbledCookies(const HeaderString& key, const HeaderString& value,
244
763429
                                          HeaderString& cookies) {
245
763431
  if (key != Headers::get().Cookie.get().c_str()) {
246
755108
    return false;
247
755108
  }
248

            
249
8323
  if (!cookies.empty()) {
250
8064
    cookies.append("; ", 2);
251
8064
  }
252

            
253
8323
  const absl::string_view value_view = value.getStringView();
254
8323
  cookies.append(value_view.data(), value_view.size());
255
8323
  return true;
256
763429
}
257

            
258
std::unique_ptr<http2::adapter::Http2Adapter>
259
ProdNghttp2SessionFactory::create(ConnectionImpl* connection,
260
3501
                                  const http2::adapter::OgHttp2Adapter::Options& options) {
261
3501
  auto visitor = std::make_unique<ConnectionImpl::Http2Visitor>(connection);
262
3501
  std::unique_ptr<http2::adapter::Http2Adapter> adapter =
263
3501
      http2::adapter::OgHttp2Adapter::Create(*visitor, options);
264
3501
  connection->setVisitor(std::move(visitor));
265
3501
  return adapter;
266
3501
}
267

            
268
#ifdef ENVOY_NGHTTP2
269
std::unique_ptr<http2::adapter::Http2Adapter>
270
11316
ProdNghttp2SessionFactory::create(ConnectionImpl* connection, const nghttp2_option* options) {
271
11316
  auto visitor = std::make_unique<ConnectionImpl::Http2Visitor>(connection);
272
11316
  auto adapter = http2::adapter::NgHttp2Adapter::CreateClientAdapter(*visitor, options);
273
38340
  auto stream_close_listener = [p = adapter.get()](http2::adapter::Http2StreamId stream_id) {
274
36626
    p->RemoveStream(stream_id);
275
36626
  };
276
11316
  visitor->setStreamCloseListener(std::move(stream_close_listener));
277
11316
  connection->setVisitor(std::move(visitor));
278
11316
  return adapter;
279
11316
}
280
#endif
281

            
282
void ProdNghttp2SessionFactory::init(ConnectionImpl* connection,
283
14817
                                     const envoy::config::core::v3::Http2ProtocolOptions& options) {
284
14817
  connection->sendSettings(options, true);
285
14817
}
286

            
287
/**
288
 * Helper to remove const during a cast. nghttp2 takes non-const pointers for headers even though
289
 * it copies them.
290
 */
291
template <typename T> static T* removeConst(const void* object) {
292
  return const_cast<T*>(reinterpret_cast<const T*>(object));
293
}
294

            
295
ConnectionImpl::StreamImpl::StreamImpl(ConnectionImpl& parent, uint32_t buffer_limit)
296
148837
    : MultiplexedStreamImplBase(parent.connection_.dispatcher()), parent_(parent),
297
148837
      pending_recv_data_(parent_.connection_.dispatcher().getWatermarkFactory().createBuffer(
298
148837
          [this]() -> void { this->pendingRecvBufferLowWatermark(); },
299
148837
          [this]() -> void { this->pendingRecvBufferHighWatermark(); },
300
148837
          []() -> void { /* TODO(adisuissa): Handle overflow watermark */ })),
301
148837
      pending_send_data_(parent_.connection_.dispatcher().getWatermarkFactory().createBuffer(
302
148837
          [this]() -> void { this->pendingSendBufferLowWatermark(); },
303
148837
          [this]() -> void { this->pendingSendBufferHighWatermark(); },
304
148837
          []() -> void { /* TODO(adisuissa): Handle overflow watermark */ })),
305
148837
      local_end_stream_sent_(false), remote_end_stream_(false), remote_rst_(false),
306
148837
      data_deferred_(false), received_noninformational_headers_(false),
307
148837
      pending_receive_buffer_high_watermark_called_(false),
308
148837
      pending_send_buffer_high_watermark_called_(false), reset_due_to_messaging_error_(false),
309
148837
      extend_stream_lifetime_flag_(false) {
310
148837
  parent_.stats_.streams_active_.inc();
311
148838
  if (buffer_limit > 0) {
312
148838
    setWriteBufferWatermarks(buffer_limit);
313
148838
  }
314
148837
  stream_manager_.defer_processing_segment_size_ = parent.connection_.bufferLimit();
315
148837
}
316

            
317
148834
void ConnectionImpl::StreamImpl::destroy() {
318
  // Cancel any pending buffered data callback for the stream.
319
148834
  process_buffered_data_callback_.reset();
320

            
321
148834
  MultiplexedStreamImplBase::destroy();
322
148834
  parent_.stats_.streams_active_.dec();
323
148834
  parent_.stats_.pending_send_bytes_.sub(pending_send_data_->length());
324
148834
}
325

            
326
95882
void ConnectionImpl::ServerStreamImpl::destroy() {
327
  // Only the downstream stream should clear the downstream of the
328
  // memory account.
329
  // This occurs in destroy as we want to ensure the Stream does not get
330
  // reset called on it from the account.
331
  //
332
  // There are cases where a corresponding upstream stream dtor might
333
  // be called, but the downstream stream isn't going to terminate soon
334
  // such as StreamDecoderFilterCallbacks::recreateStream().
335
95882
  if (buffer_memory_account_) {
336
84
    buffer_memory_account_->clearDownstream();
337
84
  }
338

            
339
95882
  StreamImpl::destroy();
340
95882
}
341

            
342
1258139
http2::adapter::HeaderRep getRep(const HeaderString& str) {
343
1258139
  if (str.isReference()) {
344
504730
    return str.getStringView();
345
810624
  } else {
346
753409
    return std::string(str.getStringView());
347
753409
  }
348
1258139
}
349

            
350
std::vector<http2::adapter::Header>
351
118786
ConnectionImpl::StreamImpl::buildHeaders(const HeaderMap& headers) {
352
118786
  std::vector<http2::adapter::Header> out;
353
118786
  out.reserve(headers.size());
354
629070
  headers.iterate([&out](const HeaderEntry& header) -> HeaderMap::Iterate {
355
629070
    out.push_back({getRep(header.key()), getRep(header.value())});
356
629070
    return HeaderMap::Iterate::Continue;
357
629070
  });
358
118786
  return out;
359
118786
}
360

            
361
154
void ConnectionImpl::ServerStreamImpl::encode1xxHeaders(const ResponseHeaderMap& headers) {
362
154
  ASSERT(HeaderUtility::isSpecial1xx(headers));
363
154
  encodeHeaders(headers, false);
364
154
}
365

            
366
117144
void ConnectionImpl::StreamImpl::encodeHeadersBase(const HeaderMap& headers, bool end_stream) {
367
117144
  local_end_stream_ = end_stream;
368

            
369
117144
  bytes_meter_->addDecompressedHeaderBytesSent(headers.byteSize());
370

            
371
117144
  submitHeaders(headers, end_stream);
372
117144
  if (parent_.sendPendingFramesAndHandleError()) {
373
    // Intended to check through coverage that this error case is tested
374
8
    return;
375
8
  }
376
117144
}
377

            
378
Status ConnectionImpl::ClientStreamImpl::encodeHeaders(const RequestHeaderMap& headers,
379
52334
                                                       bool end_stream) {
380
52334
  parent_.updateActiveStreamsOnEncode(*this);
381
52334
#ifndef ENVOY_ENABLE_UHV
382
  // Headers are now validated by UHV before encoding by the codec. Two checks below are not needed
383
  // when UHV is enabled.
384
  //
385
  // Required headers must be present. This can only happen by some erroneous processing after the
386
  // downstream codecs decode.
387
52334
  RETURN_IF_ERROR(HeaderUtility::checkRequiredRequestHeaders(headers));
388
  // Verify that a filter hasn't added an invalid header key or value.
389
52319
  RETURN_IF_ERROR(HeaderUtility::checkValidRequestHeaders(headers));
390
  // Extended CONNECT to H/1 upgrade transformation has moved to UHV
391
  // This must exist outside of the scope of isUpgrade as the underlying memory is
392
  // needed until encodeHeadersBase has been called.
393
52316
  Http::RequestHeaderMapPtr modified_headers;
394
52316
  if (Http::Utility::isUpgrade(headers)) {
395
188
    modified_headers = createHeaderMap<RequestHeaderMapImpl>(headers);
396
188
    upgrade_type_ = std::string(headers.getUpgradeValue());
397
188
    Http::Utility::transformUpgradeRequestFromH1toH2(*modified_headers);
398
188
    encodeHeadersBase(*modified_headers, end_stream);
399
52295
  } else if (headers.Method() && headers.Method()->value() == "CONNECT") {
400
600
    modified_headers = createHeaderMap<RequestHeaderMapImpl>(headers);
401
600
    modified_headers->removeScheme();
402
600
    modified_headers->removePath();
403
600
    modified_headers->removeProtocol();
404
600
    encodeHeadersBase(*modified_headers, end_stream);
405
52081
  } else {
406
51528
    encodeHeadersBase(headers, end_stream);
407
51528
  }
408
#else
409
  encodeHeadersBase(headers, end_stream);
410
#endif
411
52316
  return okStatus();
412
52319
}
413

            
414
void ConnectionImpl::ServerStreamImpl::encodeHeaders(const ResponseHeaderMap& headers,
415
64829
                                                     bool end_stream) {
416
64829
  parent_.updateActiveStreamsOnEncode(*this);
417
  // The contract is that client codecs must ensure that :status is present.
418
64829
  ASSERT(headers.Status() != nullptr);
419

            
420
64829
#ifndef ENVOY_ENABLE_UHV
421
  // Extended CONNECT to H/1 upgrade transformation has moved to UHV
422
  // This must exist outside of the scope of isUpgrade as the underlying memory is
423
  // needed until encodeHeadersBase has been called.
424
64829
  Http::ResponseHeaderMapPtr modified_headers;
425
64829
  if (Http::Utility::isUpgrade(headers)) {
426
54
    modified_headers = createHeaderMap<ResponseHeaderMapImpl>(headers);
427
54
    Http::Utility::transformUpgradeResponseFromH1toH2(*modified_headers);
428
54
    encodeHeadersBase(*modified_headers, end_stream);
429
64793
  } else {
430
64775
    encodeHeadersBase(headers, end_stream);
431
64775
  }
432
#else
433
  encodeHeadersBase(headers, end_stream);
434
#endif
435
64829
}
436

            
437
6622
void ConnectionImpl::StreamImpl::encodeTrailersBase(const HeaderMap& trailers) {
438
6622
  parent_.updateActiveStreamsOnEncode(*this);
439
6622
  ASSERT(!local_end_stream_);
440
6622
  local_end_stream_ = true;
441

            
442
6622
  bytes_meter_->addDecompressedHeaderBytesSent(trailers.byteSize());
443

            
444
6622
  if (pending_send_data_->length() > 0) {
445
    // In this case we want trailers to come after we release all pending body data that is
446
    // waiting on window updates. We need to save the trailers so that we can emit them later.
447
    // However, for empty trailers, we don't need to to save the trailers.
448
24
    ASSERT(!pending_trailers_to_encode_);
449
24
    const bool skip_encoding_empty_trailers = trailers.empty();
450
24
    if (!skip_encoding_empty_trailers) {
451
22
      pending_trailers_to_encode_ = cloneTrailers(trailers);
452
22
      onLocalEndStream();
453
22
    }
454
6598
  } else {
455
6598
    submitTrailers(trailers);
456
6598
    if (parent_.sendPendingFramesAndHandleError()) {
457
      // Intended to check through coverage that this error case is tested
458
4
      return;
459
4
    }
460
6598
  }
461
6622
}
462

            
463
2874
void ConnectionImpl::StreamImpl::encodeMetadata(const MetadataMapVector& metadata_map_vector) {
464
2874
  parent_.updateActiveStreamsOnEncode(*this);
465
2874
  ASSERT(parent_.allow_metadata_);
466
2874
  NewMetadataEncoder& metadata_encoder = getMetadataEncoder();
467
2874
  auto sources_vec = metadata_encoder.createSources(metadata_map_vector);
468
2964
  for (auto& source : sources_vec) {
469
2964
    parent_.adapter_->SubmitMetadata(stream_id_, 16 * 1024, std::move(source));
470
2964
  }
471

            
472
2874
  if (parent_.sendPendingFramesAndHandleError()) {
473
    // Intended to check through coverage that this error case is tested
474
4
    return;
475
4
  }
476
2874
}
477

            
478
455829
void ConnectionImpl::StreamImpl::processBufferedData() {
479
455829
  ENVOY_CONN_LOG(debug, "Stream {} processing buffered data.", parent_.connection_, stream_id_);
480

            
481
  // Restore crash dump context when processing buffered data.
482
455829
  Event::Dispatcher& dispatcher = parent_.connection_.dispatcher();
483
  // This method is only called from a callback placed directly on the
484
  // dispatcher, as such the dispatcher shouldn't have any tracked objects.
485
455829
  ASSERT(dispatcher.trackedObjectStackIsEmpty());
486
455829
  Envoy::ScopeTrackedObjectStack stack;
487
455829
  stack.add(parent_.connection_);
488

            
489
455829
  absl::Cleanup clear_current_stream_id = [this]() { parent_.current_stream_id_.reset(); };
490
  // TODO(kbaichoo): When we add support to *ConnectionImpl::getStream* for
491
  // deferred closed streams we can use their stream id here.
492
455829
  if (!stream_manager_.buffered_on_stream_close_) {
493
449255
    ASSERT(!parent_.current_stream_id_.has_value());
494
449255
    parent_.current_stream_id_ = stream_id_;
495
449255
  }
496

            
497
455829
  stack.add(parent_);
498
455829
  ScopeTrackerScopeState scope{&stack, dispatcher};
499

            
500
455829
  if (stream_manager_.body_buffered_ && continueProcessingBufferedData()) {
501
334053
    decodeData();
502
334053
  }
503

            
504
455829
  if (stream_manager_.trailers_buffered_ && !stream_manager_.body_buffered_ &&
505
455829
      continueProcessingBufferedData()) {
506
17
    decodeTrailers();
507
17
    ASSERT(!stream_manager_.trailers_buffered_);
508
17
  }
509

            
510
  // Reset cases are handled by resetStream and directly invoke onStreamClose,
511
  // which consumes the buffered_on_stream_close_ so we don't invoke
512
  // onStreamClose twice.
513
455829
  if (stream_manager_.buffered_on_stream_close_ && !stream_manager_.hasBufferedBodyOrTrailers()) {
514
101
    ASSERT(!reset_reason_.has_value());
515
101
    ENVOY_CONN_LOG(debug, "invoking onStreamClose for stream: {} via processBufferedData",
516
101
                   parent_.connection_, stream_id_);
517
    // We only buffer the onStreamClose if we had no errors.
518
101
    Status status = parent_.onStreamClose(this, 0);
519
101
    ASSERT(status.ok());
520
101
  }
521
455829
}
522

            
523
175067
void ConnectionImpl::StreamImpl::grantPeerAdditionalStreamWindow() {
524
175067
  parent_.adapter_->MarkDataConsumedForStream(stream_id_, unconsumed_bytes_);
525
175067
  unconsumed_bytes_ = 0;
526
175067
  if (parent_.sendPendingFramesAndHandleError()) {
527
    // Intended to check through coverage that this error case is tested
528
2
    return;
529
2
  }
530
175067
}
531

            
532
427145
void ConnectionImpl::StreamImpl::readDisable(bool disable) {
533
427145
  ENVOY_CONN_LOG(debug, "Stream {} {}, unconsumed_bytes {} read_disable_count {}",
534
427145
                 parent_.connection_, stream_id_, (disable ? "disabled" : "enabled"),
535
427145
                 unconsumed_bytes_, read_disable_count_);
536
427145
  if (disable) {
537
213583
    ++read_disable_count_;
538
213583
  } else {
539
213562
    ASSERT(read_disable_count_ > 0);
540
213562
    --read_disable_count_;
541
213562
    if (!buffersOverrun()) {
542
213512
      scheduleProcessingOfBufferedData(false);
543
213512
      if (shouldAllowPeerAdditionalStreamWindow()) {
544
174528
        grantPeerAdditionalStreamWindow();
545
174528
      }
546
213512
    }
547
213562
  }
548
427145
}
549

            
550
474979
void ConnectionImpl::StreamImpl::scheduleProcessingOfBufferedData(bool schedule_next_iteration) {
551
474979
  if (stream_manager_.hasBufferedBodyOrTrailers()) {
552
469390
    if (!process_buffered_data_callback_) {
553
563
      process_buffered_data_callback_ = parent_.connection_.dispatcher().createSchedulableCallback(
554
455831
          [this]() { processBufferedData(); });
555
563
    }
556

            
557
    // We schedule processing to occur in another callback to avoid
558
    // reentrant and deep call stacks.
559
469390
    if (schedule_next_iteration) {
560
261467
      process_buffered_data_callback_->scheduleCallbackNextIteration();
561
403821
    } else {
562
207923
      process_buffered_data_callback_->scheduleCallbackCurrentIteration();
563
207923
    }
564
469390
  }
565
474979
}
566

            
567
543
void ConnectionImpl::StreamImpl::pendingRecvBufferHighWatermark() {
568
  // Due to deferred processing of backed up streams, this is a NOP: read
569
  // disabling here can become dangerous as it can prevent us from processing
570
  // buffered data.
571
543
}
572

            
573
539
void ConnectionImpl::StreamImpl::pendingRecvBufferLowWatermark() {
574
  // Due to deferred processing of backed up streams, we don't read disable on
575
  // high watermark, so we shouldn't read disable here.
576
539
  if (shouldAllowPeerAdditionalStreamWindow()) {
577
    // We should grant additional stream window here, in case the
578
    // `pending_recv_buffer_` was blocking flow control updates
579
    // from going to the peer.
580
539
    grantPeerAdditionalStreamWindow();
581
539
  }
582
539
}
583

            
584
684961
void ConnectionImpl::StreamImpl::decodeData() {
585
684961
  if (buffersOverrun()) {
586
30047
    ENVOY_CONN_LOG(trace, "Stream {} buffering decodeData() call.", parent_.connection_,
587
30047
                   stream_id_);
588
30047
    stream_manager_.body_buffered_ = true;
589
30047
    return;
590
30047
  }
591

            
592
  // Some buffered body will be consumed. If there remains buffered body after
593
  // this call, set this to true.
594
654914
  stream_manager_.body_buffered_ = false;
595

            
596
654914
  bool already_drained_data = false;
597
  // It's possible that we are waiting to send a deferred reset, so only raise data if local
598
  // is not complete.
599
654914
  if (!deferred_reset_) {
600
    // We should decode data in chunks only if we have defer processing enabled
601
    // with a non-zero defer_processing_segment_size, and the buffer holds more
602
    // data than the defer_processing_segment_size. Otherwise, push the
603
    // entire buffer through.
604
654644
    const bool decode_data_in_chunk =
605
654644
        stream_manager_.decodeAsChunks() &&
606
654644
        pending_recv_data_->length() > stream_manager_.defer_processing_segment_size_;
607

            
608
654644
    StreamDecoder* stream_decoder = decoder();
609
654644
    if (decode_data_in_chunk) {
610
351036
      Buffer::OwnedImpl chunk_buffer;
611
      // TODO(kbaichoo): Consider implementing an approximate move for chunking.
612
351036
      chunk_buffer.move(*pending_recv_data_, stream_manager_.defer_processing_segment_size_);
613

            
614
      // With the current implementation this should always be true,
615
      // though this can change with approximation.
616
351036
      stream_manager_.body_buffered_ = true;
617
351036
      ASSERT(pending_recv_data_->length() > 0);
618

            
619
351036
      if (stream_decoder) {
620
351036
        stream_decoder->decodeData(chunk_buffer, sendEndStream());
621
351036
      }
622
351036
      already_drained_data = true;
623

            
624
351036
      if (!buffersOverrun()) {
625
261467
        scheduleProcessingOfBufferedData(true);
626
261467
      }
627
503058
    } else {
628
      // Send the entire buffer through.
629
303744
      if (stream_decoder) {
630
303743
        stream_decoder->decodeData(*pending_recv_data_, sendEndStream());
631
303743
      }
632
303608
    }
633
654644
  }
634

            
635
654914
  if (!already_drained_data) {
636
303956
    pending_recv_data_->drain(pending_recv_data_->length());
637
303956
  }
638
654914
}
639

            
640
41608
void ConnectionImpl::ClientStreamImpl::decodeHeaders() {
641
41608
  auto& headers = absl::get<ResponseHeaderMapPtr>(headers_or_trailers_);
642
41608
#ifndef ENVOY_ENABLE_UHV
643
41608
  const uint64_t status = Http::Utility::getResponseStatus(*headers);
644

            
645
  // Extended CONNECT to H/1 upgrade transformation has moved to UHV
646
41608
  if (!upgrade_type_.empty() && headers->Status()) {
647
170
    Http::Utility::transformUpgradeResponseFromH2toH1(*headers, upgrade_type_);
648
170
  }
649
#else
650
  // In UHV mode the :status header at this point can be malformed, as it is validated
651
  // later on in the response_decoder_.decodeHeaders() call.
652
  // Account for this here.
653
  absl::optional<uint64_t> status_opt = Http::Utility::getResponseStatusOrNullopt(*headers);
654
  if (!status_opt.has_value()) {
655
    // In case the status is invalid or missing, the response_decoder_.decodeHeaders() will fail the
656
    // request
657
    response_decoder_.decodeHeaders(std::move(headers), sendEndStream());
658
    return;
659
  }
660
  const uint64_t status = status_opt.value();
661
#endif
662
  // Non-informational headers are non-1xx OR 101-SwitchingProtocols, since 101 implies that further
663
  // proxying is on an upgrade path.
664
  // TODO(#29071) determine how to handle 101, since it is not supported by HTTP/2
665
41608
  received_noninformational_headers_ =
666
41608
      !CodeUtility::is1xx(status) || status == enumToInt(Http::Code::SwitchingProtocols);
667

            
668
41608
  if (HeaderUtility::isSpecial1xx(*headers)) {
669
160
    response_decoder_.decode1xxHeaders(std::move(headers));
670
41448
  } else {
671
41448
    response_decoder_.decodeHeaders(std::move(headers), sendEndStream());
672
41448
  }
673
41608
}
674

            
675
1566
bool ConnectionImpl::StreamImpl::maybeDeferDecodeTrailers() {
676
1566
  ASSERT(!deferred_reset_.has_value());
677
  // Buffer trailers if we're deferring processing and not flushing all data
678
  // through and either
679
  // 1) Buffers are overrun
680
  // 2) There's buffered body which should get processed before these trailers
681
  //    to avoid losing data.
682
1566
  if (buffersOverrun() || stream_manager_.body_buffered_) {
683
18
    stream_manager_.trailers_buffered_ = true;
684
18
    ENVOY_CONN_LOG(trace, "Stream {} buffering decodeTrailers() call.", parent_.connection_,
685
18
                   stream_id_);
686
18
    return true;
687
18
  }
688

            
689
1548
  return false;
690
1566
}
691

            
692
876
void ConnectionImpl::ClientStreamImpl::decodeTrailers() {
693
876
  if (maybeDeferDecodeTrailers()) {
694
12
    return;
695
12
  }
696

            
697
  // Consume any buffered trailers.
698
864
  stream_manager_.trailers_buffered_ = false;
699

            
700
864
  response_decoder_.decodeTrailers(
701
864
      std::move(absl::get<ResponseTrailerMapPtr>(headers_or_trailers_)));
702
864
}
703

            
704
95020
void ConnectionImpl::ServerStreamImpl::decodeHeaders() {
705
95020
  auto& headers = absl::get<RequestHeaderMapSharedPtr>(headers_or_trailers_);
706
95020
#ifndef ENVOY_ENABLE_UHV
707
  // Extended CONNECT to H/1 upgrade transformation has moved to UHV
708
95020
  if (Http::Utility::isH2UpgradeRequest(*headers)) {
709
186
    Http::Utility::transformUpgradeRequestFromH2toH1(*headers);
710
186
  }
711
95020
#endif
712
95020
  RequestDecoder* request_decoder = request_decoder_handle_->get().ptr();
713
95020
  ENVOY_BUG(request_decoder != nullptr, "Missing request_decoder_");
714
95020
  if (request_decoder) {
715
95020
    request_decoder->decodeHeaders(std::move(headers), sendEndStream());
716
95020
  }
717
95020
}
718

            
719
690
void ConnectionImpl::ServerStreamImpl::decodeTrailers() {
720
690
  if (maybeDeferDecodeTrailers()) {
721
6
    return;
722
6
  }
723

            
724
  // Consume any buffered trailers.
725
684
  stream_manager_.trailers_buffered_ = false;
726

            
727
684
  RequestDecoder* request_decoder = request_decoder_handle_->get().ptr();
728
684
  ENVOY_BUG(request_decoder != nullptr, "Missing request_decoder_");
729
684
  if (request_decoder) {
730
684
    request_decoder->decodeTrailers(
731
684
        std::move(absl::get<RequestTrailerMapPtr>(headers_or_trailers_)));
732
684
  }
733
684
}
734

            
735
69
void ConnectionImpl::StreamImpl::pendingSendBufferHighWatermark() {
736
69
  ENVOY_CONN_LOG(debug, "send buffer over limit ", parent_.connection_);
737
69
  ASSERT(!pending_send_buffer_high_watermark_called_);
738
69
  pending_send_buffer_high_watermark_called_ = true;
739
69
  runHighWatermarkCallbacks();
740
69
}
741

            
742
57
void ConnectionImpl::StreamImpl::pendingSendBufferLowWatermark() {
743
57
  ENVOY_CONN_LOG(debug, "send buffer under limit ", parent_.connection_);
744
57
  ASSERT(pending_send_buffer_high_watermark_called_);
745
57
  pending_send_buffer_high_watermark_called_ = false;
746
57
  runLowWatermarkCallbacks();
747
57
}
748

            
749
763425
void ConnectionImpl::StreamImpl::saveHeader(HeaderString&& name, HeaderString&& value) {
750
763427
  if (!Utility::reconstituteCrumbledCookies(name, value, cookies_)) {
751
755106
    headers().addViaMove(std::move(name), std::move(value));
752
755106
  }
753
763425
}
754

            
755
6618
void ConnectionImpl::StreamImpl::submitTrailers(const HeaderMap& trailers) {
756
6618
  ASSERT(local_end_stream_);
757
6618
  const bool skip_encoding_empty_trailers = trailers.empty();
758
6618
  if (skip_encoding_empty_trailers) {
759
4976
    ENVOY_CONN_LOG(debug, "skipping submitting trailers", parent_.connection_);
760

            
761
    // Instead of submitting empty trailers, we send empty data instead.
762
4976
    Buffer::OwnedImpl empty_buffer;
763
4976
    encodeDataHelper(empty_buffer, /*end_stream=*/true, skip_encoding_empty_trailers);
764
4976
    return;
765
4976
  }
766

            
767
1642
  std::vector<http2::adapter::Header> final_headers = buildHeaders(trailers);
768
1642
  parent_.adapter_->SubmitTrailer(stream_id_, final_headers);
769
1642
}
770

            
771
52315
void ConnectionImpl::ClientStreamImpl::submitHeaders(const HeaderMap& headers, bool end_stream) {
772
52315
  ASSERT(stream_id_ == -1);
773
52315
  stream_id_ = parent_.adapter_->SubmitRequest(buildHeaders(headers), end_stream, base());
774
52315
  ASSERT(stream_id_ > 0);
775
52315
}
776

            
777
42727
Status ConnectionImpl::ClientStreamImpl::onBeginHeaders() {
778
42727
  if (headers_state_ == HeadersState::Headers) {
779
1016
    allocTrailers();
780
1016
  }
781

            
782
42727
  return okStatus();
783
42727
}
784

            
785
42473
void ConnectionImpl::ClientStreamImpl::advanceHeadersState() {
786
42473
  RELEASE_ASSERT(
787
42473
      headers_state_ == HeadersState::Response || headers_state_ == HeadersState::Headers, "");
788
42473
  headers_state_ = HeadersState::Headers;
789
42473
}
790

            
791
64829
void ConnectionImpl::ServerStreamImpl::submitHeaders(const HeaderMap& headers, bool end_stream) {
792
64829
  ASSERT(stream_id_ != -1);
793
64829
  parent_.adapter_->SubmitResponse(stream_id_, buildHeaders(headers), end_stream);
794
64829
}
795

            
796
96596
Status ConnectionImpl::ServerStreamImpl::onBeginHeaders() {
797
96596
  if (headers_state_ != HeadersState::Request) {
798
713
    parent_.stats_.trailers_.inc();
799
713
    ASSERT(headers_state_ == HeadersState::Headers);
800

            
801
713
    allocTrailers();
802
713
  }
803

            
804
96596
  return okStatus();
805
96596
}
806

            
807
95708
void ConnectionImpl::ServerStreamImpl::advanceHeadersState() {
808
95708
  RELEASE_ASSERT(headers_state_ == HeadersState::Request || headers_state_ == HeadersState::Headers,
809
95708
                 "");
810
95708
  headers_state_ = HeadersState::Headers;
811
95708
}
812

            
813
13
void ConnectionImpl::StreamImpl::onPendingFlushTimer() {
814
13
  ENVOY_CONN_LOG(debug, "pending stream flush timeout", parent_.connection_);
815
13
  MultiplexedStreamImplBase::onPendingFlushTimer();
816
13
  parent_.stats_.tx_flush_timeout_.inc();
817
13
  ASSERT(local_end_stream_ && !local_end_stream_sent_);
818
  // This will emit a reset frame for this stream and close the stream locally.
819
  // Only the stream adapter's reset callback should run as other higher layers
820
  // think the stream is already finished.
821
13
  resetStreamWorker(StreamResetReason::LocalReset);
822
13
  if (parent_.sendPendingFramesAndHandleError()) {
823
    // Intended to check through coverage that this error case is tested
824
4
    return;
825
4
  }
826
13
}
827

            
828
416671
void ConnectionImpl::StreamImpl::encodeData(Buffer::Instance& data, bool end_stream) {
829
416671
  parent_.updateActiveStreamsOnEncode(*this);
830
416671
  ASSERT(!local_end_stream_);
831
416671
  encodeDataHelper(data, end_stream,
832
                   /*skip_encoding_empty_trailers=*/
833
416671
                   false);
834
416671
}
835

            
836
void ConnectionImpl::StreamImpl::encodeDataHelper(Buffer::Instance& data, bool end_stream,
837
421647
                                                  bool skip_encoding_empty_trailers) {
838
421647
  if (skip_encoding_empty_trailers) {
839
4976
    ASSERT(data.length() == 0 && end_stream);
840
4976
  }
841

            
842
421647
  local_end_stream_ = end_stream;
843
421647
  parent_.stats_.pending_send_bytes_.add(data.length());
844
421647
  pending_send_data_->move(data);
845
421647
  if (data_deferred_) {
846
419577
    bool success = parent_.adapter_->ResumeStream(stream_id_);
847
419577
    ASSERT(success);
848

            
849
419577
    data_deferred_ = false;
850
419577
  }
851

            
852
421647
  if (parent_.sendPendingFramesAndHandleError()) {
853
    // Intended to check through coverage that this error case is tested
854
110
    return;
855
110
  }
856
421537
  if (local_end_stream_) {
857
18997
    onLocalEndStream();
858
18997
  }
859
421537
}
860

            
861
1157
void ConnectionImpl::ServerStreamImpl::resetStream(StreamResetReason reason) {
862
  // Clear the downstream on the account since we're resetting the downstream.
863
1157
  if (buffer_memory_account_) {
864
12
    buffer_memory_account_->clearDownstream();
865
12
  }
866

            
867
1157
  StreamImpl::resetStream(reason);
868
1157
}
869

            
870
13519
void ConnectionImpl::StreamImpl::resetStream(StreamResetReason reason) {
871
13519
  reset_reason_ = reason;
872

            
873
  // Higher layers expect calling resetStream() to immediately raise reset callbacks.
874
13519
  runResetCallbacks(reason, absl::string_view());
875

            
876
  // If we've bufferedOnStreamClose for this stream, we shouldn't propagate this
877
  // reset as nghttp2 will have forgotten about the stream.
878
13519
  if (stream_manager_.buffered_on_stream_close_) {
879
8
    ENVOY_CONN_LOG(
880
8
        trace, "Stopped propagating reset to codec as we've buffered onStreamClose for stream {}",
881
8
        parent_.connection_, stream_id_);
882
    // The stream didn't originally have an NGHTTP2 error, since we buffered
883
    // its stream close.
884
8
    if (Status status = parent_.onStreamClose(this, 0); !status.ok()) {
885
      ENVOY_CONN_LOG(debug, "error invoking onStreamClose: {}", parent_.connection_,
886
                     status.message()); // LCOV_EXCL_LINE
887
    }
888
8
    return;
889
8
  }
890

            
891
  // If we submit a reset, the codec may cancel outbound frames that have not yet been sent.
892
  // We want these frames to go out so we defer the reset until we send all of the frames that
893
  // end the local stream. However, if we're resetting the stream due to
894
  // overload, we should reset the stream as soon as possible to free used
895
  // resources.
896
13511
  if (useDeferredReset() && local_end_stream_ && !local_end_stream_sent_ &&
897
13511
      reason != StreamResetReason::OverloadManager) {
898
307
    ASSERT(parent_.getStreamUnchecked(stream_id_) != nullptr);
899
307
    parent_.pending_deferred_reset_streams_.emplace(stream_id_, this);
900
307
    deferred_reset_ = reason;
901
307
    ENVOY_CONN_LOG(trace, "deferred reset stream", parent_.connection_);
902
13342
  } else {
903
13204
    resetStreamWorker(reason);
904
13204
  }
905

            
906
  // We must still call sendPendingFrames() in both the deferred and not deferred path. This forces
907
  // the cleanup logic to run which will reset the stream in all cases if all data frames could not
908
  // be sent.
909
13511
  if (parent_.sendPendingFramesAndHandleError()) {
910
    // Intended to check through coverage that this error case is tested
911
58
    return;
912
58
  }
913
13511
}
914

            
915
13315
void ConnectionImpl::StreamImpl::resetStreamWorker(StreamResetReason reason) {
916
13315
  if (stream_id_ == -1) {
917
    // Handle the case where client streams are reset before headers are created.
918
    // For example, if we send local reply after the stream is created but before
919
    // headers are sent, we will end up here.
920
617
    ENVOY_CONN_LOG(trace, "Stream {} reset before headers sent.", parent_.connection_, stream_id_);
921
617
    Status status = parent_.onStreamClose(this, 0);
922
617
    ASSERT(status.ok());
923
617
    return;
924
617
  }
925
12698
  if (codec_callbacks_) {
926
    // TODO(wbpcode): this ensure that onCodecLowLevelReset is only called once. But
927
    // we should replace this with a better design later.
928
    // See https://github.com/envoyproxy/envoy/issues/42264 for why we need this.
929
27
    if (!codec_low_level_reset_is_called_) {
930
27
      codec_low_level_reset_is_called_ = true;
931
27
      codec_callbacks_->onCodecLowLevelReset();
932
27
    }
933
27
  }
934

            
935
12698
  const bool response_end_stream_sent =
936
12698
      parent_.adapter_->IsServerSession() ? local_end_stream_sent_ : false;
937
12698
  parent_.adapter_->SubmitRst(stream_id_, static_cast<http2::adapter::Http2ErrorCode>(
938
12698
                                              reasonToReset(reason, response_end_stream_sent)));
939
12698
}
940

            
941
2873
NewMetadataEncoder& ConnectionImpl::StreamImpl::getMetadataEncoder() {
942
2873
  if (metadata_encoder_ == nullptr) {
943
254
    metadata_encoder_ = std::make_unique<NewMetadataEncoder>();
944
254
  }
945
2873
  return *metadata_encoder_;
946
2873
}
947

            
948
5507
MetadataDecoder& ConnectionImpl::StreamImpl::getMetadataDecoder() {
949
5507
  if (metadata_decoder_ == nullptr) {
950
2355
    auto cb = [this](MetadataMapPtr&& metadata_map_ptr) {
951
2354
      this->onMetadataDecoded(std::move(metadata_map_ptr));
952
2354
    };
953
287
    metadata_decoder_ = std::make_unique<MetadataDecoder>(cb, parent_.max_metadata_size_);
954
287
  }
955
5507
  return *metadata_decoder_;
956
5507
}
957

            
958
2354
void ConnectionImpl::StreamImpl::onMetadataDecoded(MetadataMapPtr&& metadata_map_ptr) {
959
  // Empty metadata maps should not be decoded.
960
2354
  if (metadata_map_ptr->empty()) {
961
5
    ENVOY_CONN_LOG(debug, "decode metadata called with empty map, skipping", parent_.connection_);
962
5
    parent_.stats_.metadata_empty_frames_.inc();
963
2349
  } else {
964
2349
    StreamDecoder* stream_decoder = decoder();
965
2349
    if (stream_decoder) {
966
2349
      stream_decoder->decodeMetadata(std::move(metadata_map_ptr));
967
2349
    }
968
2349
  }
969
2354
}
970

            
971
94175
void ConnectionImpl::StreamImpl::setAccount(Buffer::BufferMemoryAccountSharedPtr account) {
972
94175
  buffer_memory_account_ = account;
973
94175
  pending_recv_data_->bindAccount(buffer_memory_account_);
974
94175
  pending_send_data_->bindAccount(buffer_memory_account_);
975
94175
}
976

            
977
ConnectionImpl::ConnectionImpl(Network::Connection& connection, CodecStats& stats,
978
                               Random::RandomGenerator& random_generator,
979
                               const envoy::config::core::v3::Http2ProtocolOptions& http2_options,
980
                               const uint32_t max_headers_kb, const uint32_t max_headers_count)
981
30424
    : stats_(stats), connection_(connection), max_headers_kb_(max_headers_kb),
982
30424
      max_headers_count_(max_headers_count),
983
30424
      per_stream_buffer_limit_(http2_options.initial_stream_window_size().value()),
984
      stream_error_on_invalid_http_messaging_(
985
30424
          http2_options.override_stream_error_on_invalid_http_message().value()),
986
30424
      protocol_constraints_(stats, http2_options), dispatching_(false), raised_goaway_(false),
987
30424
      random_(random_generator),
988
30424
      last_received_data_time_(connection_.dispatcher().timeSource().monotonicTime()) {
989
30424
  if (http2_options.has_use_oghttp2_codec()) {
990
2
    use_oghttp2_library_ = http2_options.use_oghttp2_codec().value();
991
30422
  } else {
992
30422
    use_oghttp2_library_ =
993
30422
        Runtime::runtimeFeatureEnabled("envoy.reloadable_features.http2_use_oghttp2");
994
30422
  }
995
30424
  if (http2_options.has_connection_keepalive()) {
996
14
    keepalive_interval_ = std::chrono::milliseconds(
997
14
        PROTOBUF_GET_MS_OR_DEFAULT(http2_options.connection_keepalive(), interval, 0));
998
14
    keepalive_timeout_ = std::chrono::milliseconds(
999
14
        PROTOBUF_GET_MS_REQUIRED(http2_options.connection_keepalive(), timeout));
14
    keepalive_interval_jitter_percent_ = PROTOBUF_GET_WRAPPED_OR_DEFAULT(
14
        http2_options.connection_keepalive(), interval_jitter, 15.0);
14
    if (keepalive_interval_.count() > 0) {
512
      keepalive_send_timer_ = connection.dispatcher().createTimer([this]() { sendKeepalive(); });
12
    }
14
    keepalive_timeout_timer_ =
14
        connection.dispatcher().createTimer([this]() { onKeepaliveResponseTimeout(); });
    // This call schedules the initial interval, with jitter.
14
    onKeepaliveResponse();
14
  }
30424
}
30424
ConnectionImpl::~ConnectionImpl() {
80524
  for (const auto& stream : active_streams_) {
56960
    stream->destroy();
56960
  }
30424
}
512
void ConnectionImpl::sendKeepalive() {
512
  ASSERT(keepalive_timeout_timer_);
512
  if (keepalive_timeout_timer_->enabled()) {
    ENVOY_CONN_LOG(trace, "Skipping PING: already awaiting PING ACK", connection_);
    return;
  }
  // Include the current time as the payload to help with debugging.
512
  SystemTime now = connection_.dispatcher().timeSource().systemTime();
512
  uint64_t ms_since_epoch =
512
      std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count();
512
  ENVOY_CONN_LOG(trace, "Sending keepalive PING {}", connection_, ms_since_epoch);
512
  adapter_->SubmitPing(ms_since_epoch);
512
  if (sendPendingFramesAndHandleError()) {
    // Intended to check through coverage that this error case is tested
4
    return;
4
  }
508
  keepalive_timeout_timer_->enableTimer(keepalive_timeout_);
508
}
2522
void ConnectionImpl::onKeepaliveResponse() {
  // Check the timers for nullptr in case the peer sent an unsolicited PING ACK.
2522
  if (keepalive_timeout_timer_ != nullptr) {
518
    keepalive_timeout_timer_->disableTimer();
518
  }
2522
  if (keepalive_send_timer_ != nullptr && keepalive_interval_.count()) {
516
    uint64_t interval_ms = keepalive_interval_.count();
516
    const uint64_t jitter_percent_mod = keepalive_interval_jitter_percent_ * interval_ms / 100;
516
    if (jitter_percent_mod > 0) {
504
      interval_ms += random_.random() % jitter_percent_mod;
504
    }
516
    keepalive_send_timer_->enableTimer(std::chrono::milliseconds(interval_ms));
516
  }
2522
}
4
void ConnectionImpl::onKeepaliveResponseTimeout() {
4
  ENVOY_CONN_LOG_EVENT(debug, "h2_ping_timeout", "Closing connection due to keepalive timeout",
4
                       connection_);
4
  stats_.keepalive_timeout_.inc();
4
  connection_.close(Network::ConnectionCloseType::NoFlush,
4
                    StreamInfo::LocalCloseReasons::get().Http2PingTimeout);
4
}
4
bool ConnectionImpl::slowContainsStreamId(int32_t stream_id) const {
4
  for (const auto& stream : active_streams_) {
4
    if (stream->stream_id_ == stream_id) {
2
      return true;
2
    }
4
  }
2
  return false;
4
}
294656
Http::Status ConnectionImpl::dispatch(Buffer::Instance& data) {
294656
  ScopeTrackerScopeState scope(this, connection_.dispatcher());
294656
  ENVOY_CONN_LOG(trace, "dispatching {} bytes", connection_, data.length());
  // Make sure that dispatching_ is set to false after dispatching, even when
  // ConnectionImpl::dispatch returns early or throws an exception (consider removing if there is a
  // single return after exception removal (#10878)).
294663
  Cleanup cleanup([this]() {
294660
    dispatching_ = false;
294660
    current_slice_ = nullptr;
294660
    current_stream_id_.reset();
294660
  });
294656
  last_received_data_time_ = connection_.dispatcher().timeSource().monotonicTime();
392747
  for (const Buffer::RawSlice& slice : data.getRawSlices()) {
392691
    current_slice_ = &slice;
392691
    dispatching_ = true;
392691
    ssize_t rc;
392691
    rc = adapter_->ProcessBytes(absl::string_view(static_cast<char*>(slice.mem_), slice.len_));
392691
    if (!codec_callback_status_.ok()) {
476
      return codec_callback_status_;
476
    }
392215
#ifdef ENVOY_NGHTTP2
    // This error is returned when nghttp2 library detected a frame flood by one of its
    // internal mechanisms. Most flood protection is done by Envoy's codec and this error
    // should never be returned. However it is handled here in case nghttp2 has some flood
    // protections that Envoy's codec does not have.
392215
    static const int ERR_FLOODED = -904;
392215
    if (rc == ERR_FLOODED) {
      return bufferFloodError(
          "Flooding was detected in this HTTP/2 session, and it must be closed"); // LCOV_EXCL_LINE
    }
392215
#endif
392215
    if (rc != static_cast<ssize_t>(slice.len_)) {
762
      return codecProtocolError(codecStrError(rc));
762
    }
391453
    current_slice_ = nullptr;
391453
    dispatching_ = false;
391453
    current_stream_id_.reset();
391453
  }
293418
  ENVOY_CONN_LOG(trace, "dispatched {} bytes", connection_, data.length());
293418
  data.drain(data.length());
  // Decoding incoming frames can generate outbound frames so flush pending.
293418
  return sendPendingFrames();
294656
}
6
const ConnectionImpl::StreamImpl* ConnectionImpl::getStream(int32_t stream_id) const {
  // Delegate to the non-const version.
6
  return const_cast<ConnectionImpl*>(this)->getStream(stream_id);
6
}
2088693
ConnectionImpl::StreamImpl* ConnectionImpl::getStream(int32_t stream_id) {
2088693
  StreamImpl* stream = getStreamUnchecked(stream_id);
2088693
  SLOW_ASSERT(stream != nullptr || !slowContainsStreamId(stream_id));
2088693
  return stream;
2088693
}
2
const ConnectionImpl::StreamImpl* ConnectionImpl::getStreamUnchecked(int32_t stream_id) const {
  // Delegate to the non-const version.
2
  return const_cast<ConnectionImpl*>(this)->getStreamUnchecked(stream_id);
2
}
4820916
ConnectionImpl::StreamImpl* ConnectionImpl::getStreamUnchecked(int32_t stream_id) {
4820916
  return static_cast<StreamImpl*>(adapter_->GetStreamUserData(stream_id));
4820916
}
457285
int ConnectionImpl::onData(int32_t stream_id, const uint8_t* data, size_t len) {
457285
  ASSERT(connection_.state() == Network::Connection::State::Open);
457285
  StreamImpl* stream = getStream(stream_id);
  // If this results in buffering too much data, the watermark buffer will call
  // pendingRecvBufferHighWatermark, resulting in ++read_disable_count_
457285
  stream->pending_recv_data_->add(data, len);
  // Update the window to the peer unless some consumer of this stream's data has hit a flow control
  // limit and disabled reads on this stream
457288
  if (stream->shouldAllowPeerAdditionalStreamWindow()) {
395230
    adapter_->MarkDataConsumedForStream(stream_id, len);
335229
  } else {
62058
    stream->unconsumed_bytes_ += len;
62058
  }
457285
  return 0;
457285
}
114
void ConnectionImpl::goAway() {
114
  adapter_->SubmitGoAway(adapter_->GetHighestReceivedStreamId(),
114
                         http2::adapter::Http2ErrorCode::HTTP2_NO_ERROR, "");
114
  stats_.goaway_sent_.inc();
114
  if (sendPendingFramesAndHandleError()) {
    // Intended to check through coverage that this error case is tested
4
    return;
4
  }
114
}
119
void ConnectionImpl::shutdownNotice() {
119
  adapter_->SubmitShutdownNotice();
119
  if (sendPendingFramesAndHandleError()) {
    // Intended to check through coverage that this error case is tested
8
    return;
8
  }
119
}
3
Status ConnectionImpl::protocolErrorForTest() {
3
  adapter_->SubmitGoAway(adapter_->GetHighestReceivedStreamId(),
3
                         http2::adapter::Http2ErrorCode::PROTOCOL_ERROR, "");
3
  return sendPendingFrames();
3
}
Status ConnectionImpl::onBeforeFrameReceived(int32_t stream_id, size_t length, uint8_t type,
619945
                                             uint8_t flags) {
619945
  ENVOY_CONN_LOG(trace, "about to recv frame type={}, flags={}, stream_id={}", connection_,
619945
                 static_cast<uint64_t>(type), static_cast<uint64_t>(flags), stream_id);
619945
  ASSERT(connection_.state() == Network::Connection::State::Open);
619945
  current_stream_id_ = stream_id;
619945
  if (type == OGHTTP2_PING_FRAME_TYPE && (flags & FLAG_ACK)) {
2508
    return okStatus();
2508
  }
  // In slow networks, HOL blocking can prevent the ping response from coming in a reasonable
  // amount of time. To avoid HOL blocking influence, if we receive *any* frame extend the
  // timeout for another timeout period. This will still timeout the connection if there is no
  // activity, but if there is frame activity we assume the connection is still healthy and the
  // PING ACK may be delayed behind other frames.
617437
  if (keepalive_timeout_timer_ != nullptr && keepalive_timeout_timer_->enabled()) {
2
    keepalive_timeout_timer_->enableTimer(keepalive_timeout_);
2
  }
  // Track all the frames without padding here, since this is the only callback we receive
  // for some of them (e.g. CONTINUATION frame, frames sent on closed streams, etc.).
  // DATA frame is tracked in onFrameReceived().
617437
  auto status = okStatus();
617437
  if (type != OGHTTP2_DATA_FRAME_TYPE) {
266360
    status = trackInboundFrames(stream_id, length, type, flags, 0);
266360
  }
617437
  return status;
619945
}
ABSL_MUST_USE_RESULT
155
enum GoAwayErrorCode ngHttp2ErrorCodeToErrorCode(uint32_t code) noexcept {
155
  switch (code) {
146
  case OGHTTP2_NO_ERROR:
146
    return GoAwayErrorCode::NoError;
9
  default:
9
    return GoAwayErrorCode::Other;
155
  }
155
}
12345
Status ConnectionImpl::onPing(uint64_t opaque_data, bool is_ack) {
12345
  ENVOY_CONN_LOG(trace, "recv frame type=PING", connection_);
12345
  ASSERT(connection_.state() == Network::Connection::State::Open);
12345
  if (is_ack) {
2508
    ENVOY_CONN_LOG(trace, "recv PING ACK {}", connection_, opaque_data);
2508
    onKeepaliveResponse();
2508
  }
12345
  return okStatus();
12345
}
Status ConnectionImpl::onBeginData(int32_t stream_id, size_t length, uint8_t flags,
350988
                                   size_t padding) {
350988
  ENVOY_CONN_LOG(trace, "recv frame type=DATA stream_id={}", connection_, stream_id);
350988
  RETURN_IF_ERROR(trackInboundFrames(stream_id, length, OGHTTP2_DATA_FRAME_TYPE, flags, padding));
350982
  StreamImpl* stream = getStreamUnchecked(stream_id);
350982
  if (!stream) {
    return okStatus();
  }
  // Track bytes received.
350982
  stream->bytes_meter_->addWireBytesReceived(length + H2_FRAME_HEADER_SIZE);
350982
  stream->remote_end_stream_ = flags & FLAG_END_STREAM;
350982
  stream->decodeData();
350982
  return okStatus();
350982
}
192
Status ConnectionImpl::onGoAway(uint32_t error_code) {
192
  ENVOY_CONN_LOG(trace, "recv frame type=GOAWAY", connection_);
  // Only raise GOAWAY once, since we don't currently expose stream information. Shutdown
  // notifications are the same as a normal GOAWAY.
  // TODO: handle multiple GOAWAY frames.
192
  if (!raised_goaway_) {
155
    raised_goaway_ = true;
155
    callbacks().onGoAway(ngHttp2ErrorCodeToErrorCode(error_code));
155
  }
192
  return okStatus();
192
}
138181
Status ConnectionImpl::onHeaders(int32_t stream_id, size_t length, uint8_t flags) {
138181
  StreamImpl* stream = getStreamUnchecked(stream_id);
138181
  if (!stream) {
    return okStatus();
  }
  // Track bytes received.
138181
  stream->bytes_meter_->addWireBytesReceived(length + H2_FRAME_HEADER_SIZE);
138181
  stream->bytes_meter_->addHeaderBytesReceived(length + H2_FRAME_HEADER_SIZE);
138181
  stream->remote_end_stream_ = flags & FLAG_END_STREAM;
138181
  if (!stream->cookies_.empty()) {
252
    HeaderString key(Headers::get().Cookie);
252
    stream->headers().addViaMove(std::move(key), std::move(stream->cookies_));
252
  }
138181
  StreamImpl::HeadersState headers_state = stream->headersState();
138181
  switch (headers_state) {
41463
  case StreamImpl::HeadersState::Response:
136483
  case StreamImpl::HeadersState::Request: {
136483
    stream->decodeHeaders();
136483
    break;
41463
  }
1698
  case StreamImpl::HeadersState::Headers: {
    // It's possible that we are waiting to send a deferred reset, so only raise headers/trailers
    // if local is not complete.
1698
    if (!stream->deferred_reset_) {
1694
      if (adapter_->IsServerSession() || stream->received_noninformational_headers_) {
1549
        ASSERT(stream->remote_end_stream_);
1549
        stream->decodeTrailers();
1581
      } else {
        // We're a client session and still waiting for non-informational headers.
145
        stream->decodeHeaders();
145
      }
1694
    }
1698
    break;
41463
  }
  default:
    // We do not currently support push.
    ENVOY_BUG(false, "push not supported"); // LCOV_EXCL_LINE
138181
  }
138181
  stream->advanceHeadersState();
138181
  return okStatus();
138181
}
5178
Status ConnectionImpl::onRstStream(int32_t stream_id, uint32_t error_code) {
5178
  ENVOY_CONN_LOG(trace, "recv frame type=RST_STREAM stream_id={}", connection_, stream_id);
5178
  StreamImpl* stream = getStreamUnchecked(stream_id);
5178
  if (!stream) {
52
    return okStatus();
52
  }
5126
  ENVOY_CONN_LOG(trace, "remote reset: {} {}", connection_, stream_id, error_code);
  // Track bytes received.
5126
  stream->bytes_meter_->addWireBytesReceived(/*frame_length=*/4 + H2_FRAME_HEADER_SIZE);
5126
  stream->remote_rst_ = true;
5126
  stats_.rx_reset_.inc();
5126
  return okStatus();
5178
}
int ConnectionImpl::onFrameSend(int32_t stream_id, size_t length, uint8_t type, uint8_t flags,
767915
                                uint32_t error_code) {
  // The codec library does not cleanly give us a way to determine whether we received invalid
  // data from our peer. Sometimes it raises the invalid frame callback, and sometimes it does not.
  // In all cases however it will attempt to send a GOAWAY frame with an error status. If we see
  // an outgoing frame of this type, we will return an error code so that we can abort execution.
767915
  ENVOY_CONN_LOG(trace, "sent frame type={}, stream_id={}, length={}", connection_,
767915
                 static_cast<uint64_t>(type), stream_id, length);
767915
  StreamImpl* stream = getStreamUnchecked(stream_id);
767915
  if (stream != nullptr) {
663126
    if (type != METADATA_FRAME_TYPE) {
660050
      stream->bytes_meter_->addWireBytesSent(length + H2_FRAME_HEADER_SIZE);
660050
    }
663126
    if (type == OGHTTP2_HEADERS_FRAME_TYPE || type == OGHTTP2_CONTINUATION_FRAME_TYPE) {
118777
      stream->bytes_meter_->addHeaderBytesSent(length + H2_FRAME_HEADER_SIZE);
118777
    }
663126
  }
767915
  switch (type) {
284
  case OGHTTP2_GOAWAY_FRAME_TYPE: {
284
    ENVOY_CONN_LOG(debug, "sent goaway code={}", connection_, error_code);
284
    if (error_code != OGHTTP2_NO_ERROR) {
      // TODO(mattklein123): Returning this error code abandons standard nghttp2 frame accounting.
      // As such, it is not reliable to call sendPendingFrames() again after this and we assume
      // that the connection is going to get torn down immediately. One byproduct of this is that
      // we need to cancel all pending flush stream timeouts since they can race with connection
      // teardown. As part of the work to remove exceptions we should aim to clean up all of this
      // error handling logic and only handle this type of case at the end of dispatch.
68
      for (auto& stream : active_streams_) {
58
        stream->disarmStreamFlushTimer();
58
      }
68
      return ERR_CALLBACK_FAILURE;
68
    }
216
    break;
284
  }
7411
  case OGHTTP2_RST_STREAM_FRAME_TYPE: {
7348
    ENVOY_CONN_LOG(debug, "sent reset code={}", connection_, error_code);
7348
    stats_.tx_reset_.inc();
7348
    if (stream != nullptr && !stream->local_end_stream_sent_) {
      // The RST_STREAM may preempt further DATA frames, and serves as the
      // notification of the end of the stream.
6042
      stream->onResetEncoded(error_code);
6042
      stream->local_end_stream_sent_ = true;
6042
    }
7348
    break;
284
  }
118777
  case OGHTTP2_HEADERS_FRAME_TYPE:
647284
  case OGHTTP2_DATA_FRAME_TYPE: {
    // This should be the case since we're sending these frames. It's possible
    // that codec fuzzers would incorrectly send frames for non-existent streams
    // which is why this is not an assert.
647284
    if (stream != nullptr) {
647284
      const bool end_stream_sent = flags & FLAG_END_STREAM;
647284
      stream->local_end_stream_sent_ = end_stream_sent;
647284
      if (end_stream_sent) {
82831
        stream->onEndStreamEncoded();
82831
      }
647284
    }
647284
    break;
118777
  }
767915
  }
767848
  return 0;
767915
}
668
int ConnectionImpl::onError(absl::string_view error) {
668
  ENVOY_CONN_LOG(debug, "invalid http2: {}", connection_, error);
668
  return 0;
668
}
1064
int ConnectionImpl::onInvalidFrame(int32_t stream_id, int error_code) {
1064
  ENVOY_CONN_LOG(debug, "invalid frame: {} on stream {}", connection_, codecStrError(error_code),
1064
                 stream_id);
  // Set details of error_code in the stream whenever we have one.
1064
  StreamImpl* stream = getStreamUnchecked(stream_id);
1064
  if (stream != nullptr) {
1045
    stream->setDetails(Http2ResponseCodeDetails::get().errorDetails(error_code));
1045
  }
1064
  switch (error_code) {
4
  case ERR_REFUSED_STREAM:
4
    stats_.stream_refused_errors_.inc();
4
    return 0;
871
  case ERR_HTTP_HEADER:
1044
  case ERR_HTTP_MESSAGING:
1044
    stats_.rx_messaging_error_.inc();
1044
    if (stream_error_on_invalid_http_messaging_) {
      // The stream is about to be closed due to an invalid header or messaging. Don't kill the
      // entire connection if one stream has bad headers or messaging.
308
      if (stream != nullptr) {
        // See comment below in onStreamClose() for why we do this.
308
        stream->reset_due_to_messaging_error_ = true;
308
      }
308
      return 0;
308
    }
736
    break;
736
  case ERR_FLOW_CONTROL:
16
  case ERR_PROTO:
16
  case ERR_STREAM_CLOSED:
    // Known error conditions that should trigger connection close.
16
    break;
  default:
    // Unknown error conditions. Trigger ENVOY_BUG and connection close.
    ENVOY_BUG(false, absl::StrCat("Unexpected error_code: ", error_code)); // LCOV_EXCL_LINE
    break;
1064
  }
  // Cause dispatch to return with an error code.
752
  return ERR_CALLBACK_FAILURE;
1064
}
int ConnectionImpl::onBeforeFrameSend(int32_t /*stream_id*/, size_t /*length*/, uint8_t type,
239409
                                      uint8_t flags) {
239409
  ENVOY_CONN_LOG(trace, "about to send frame type={}, flags={}", connection_,
239409
                 static_cast<uint64_t>(type), static_cast<uint64_t>(flags));
239409
  ASSERT(!is_outbound_flood_monitored_control_frame_);
  // Flag flood monitored outbound control frames.
239409
  is_outbound_flood_monitored_control_frame_ =
239409
      ((type == OGHTTP2_PING_FRAME_TYPE || type == OGHTTP2_SETTINGS_FRAME_TYPE) &&
239409
       flags & FLAG_ACK) ||
239409
      type == OGHTTP2_RST_STREAM_FRAME_TYPE;
239409
  return 0;
239409
}
void ConnectionImpl::addOutboundFrameFragment(Buffer::OwnedImpl& output, const uint8_t* data,
786377
                                              size_t length) {
  // Reset the outbound frame type (set in the onBeforeFrameSend callback) since the
  // onBeforeFrameSend callback is not called for DATA frames.
786377
  bool is_outbound_flood_monitored_control_frame = false;
786377
  std::swap(is_outbound_flood_monitored_control_frame, is_outbound_flood_monitored_control_frame_);
786377
  auto releasor =
786377
      protocol_constraints_.incrementOutboundFrameCount(is_outbound_flood_monitored_control_frame);
786377
  output.add(data, length);
786377
  output.addDrainTracker(releasor);
786377
}
Status ConnectionImpl::trackInboundFrames(int32_t stream_id, size_t length, uint8_t type,
617346
                                          uint8_t flags, uint32_t padding_length) {
617346
  Status result;
617346
  ENVOY_CONN_LOG(trace, "track inbound frame type={} flags={} length={} padding_length={}",
617346
                 connection_, static_cast<uint64_t>(type), static_cast<uint64_t>(flags),
617346
                 static_cast<uint64_t>(length), padding_length);
617346
  const bool end_stream = (type == OGHTTP2_DATA_FRAME_TYPE || type == OGHTTP2_HEADERS_FRAME_TYPE) &&
617346
                          (flags & FLAG_END_STREAM);
617346
  const bool is_empty = (length - padding_length) == 0;
617346
  result = protocol_constraints_.trackInboundFrame(type, end_stream, is_empty);
617346
  if (!result.ok()) {
32
    ENVOY_CONN_LOG(trace, "error reading frame: {} received in this HTTP/2 session.", connection_,
32
                   result.message());
32
    if (isInboundFramesWithEmptyPayloadError(result)) {
14
      ConnectionImpl::StreamImpl* stream = getStreamUnchecked(stream_id);
14
      if (stream) {
12
        stream->setDetails(Http2ResponseCodeDetails::get().inbound_empty_frame_flood);
12
      }
      // Above if is defensive, because the stream has just been created and therefore always
      // exists.
14
    }
32
  }
617346
  return result;
617346
}
257872
ssize_t ConnectionImpl::onSend(const uint8_t* data, size_t length) {
257872
  ENVOY_CONN_LOG(trace, "send data: bytes={}", connection_, length);
257872
  Buffer::OwnedImpl buffer;
257872
  addOutboundFrameFragment(buffer, data, length);
  // While the buffer is transient the fragment it contains will be moved into the
  // write_buffer_ of the underlying connection_ by the write method below.
  // This creates lifetime dependency between the write_buffer_ of the underlying connection
  // and the codec object. Specifically the write_buffer_ MUST be either fully drained or
  // deleted before the codec object is deleted. This is presently guaranteed by the
  // destruction order of the Network::ConnectionImpl object where write_buffer_ is
  // destroyed before the filter_manager_ which owns the codec through Http::ConnectionManagerImpl.
257872
  connection_.write(buffer, false);
257872
  return length;
257872
}
92004
Status ConnectionImpl::onStreamClose(StreamImpl* stream, uint32_t error_code) {
92004
  if (stream) {
91989
    const int32_t stream_id = stream->stream_id_;
    // Consume buffered on stream_close.
91989
    if (stream->stream_manager_.buffered_on_stream_close_) {
109
      stream->stream_manager_.buffered_on_stream_close_ = false;
109
      stats_.deferred_stream_close_.dec();
109
    }
91989
    ENVOY_CONN_LOG(debug, "stream {} closed: {}", connection_, stream_id, error_code);
    // Even if we have received both the remote_end_stream and the
    // local_end_stream (e.g. we have all the data for the response), if we've
    // received a remote reset we should reset the stream.
    // We only do so currently for server side streams by checking for
    // extend_stream_lifetime_flag_ as its observers all unregisters stream
    // callbacks.
91989
    bool should_reset_stream = !stream->remote_end_stream_ || !stream->local_end_stream_;
91989
    if (stream->extend_stream_lifetime_flag_) {
30678
      should_reset_stream = should_reset_stream || stream->remote_rst_;
30678
    }
91989
    if (should_reset_stream) {
12670
      StreamResetReason reason;
12670
      if (stream->reset_due_to_messaging_error_) {
        // Unfortunately, the nghttp2 API makes it incredibly difficult to clearly understand
        // the flow of resets. I.e., did the reset originate locally? Was it remote? Here,
        // we attempt to track cases in which we sent a reset locally due to an invalid frame
        // received from the remote. We only do that in two cases currently (HTTP messaging layer
        // errors from https://tools.ietf.org/html/rfc7540#section-8 which nghttp2 is very strict
        // about). In other cases we treat invalid frames as a protocol error and just kill
        // the connection.
        // Get ClientConnectionImpl or ServerConnectionImpl specific stream reset reason,
        // depending whether the connection is upstream or downstream.
308
        reason = getMessagingErrorResetReason();
12564
      } else {
12362
        if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.reset_with_error")) {
12346
          reason = errorCodeToResetReason(error_code);
12346
          if (error_code == OGHTTP2_REFUSED_STREAM) {
22
            stream->setDetails(Http2ResponseCodeDetails::get().remote_refused);
12324
          } else {
12324
            stream->setDetails(Http2ResponseCodeDetails::get().remote_reset);
12324
          }
12346
        } else {
16
          if (error_code == OGHTTP2_REFUSED_STREAM) {
            reason = StreamResetReason::RemoteRefusedStreamReset;
            stream->setDetails(Http2ResponseCodeDetails::get().remote_refused);
16
          } else {
16
            if (error_code == OGHTTP2_CONNECT_ERROR) {
              reason = StreamResetReason::ConnectError;
16
            } else {
16
              reason = StreamResetReason::RemoteReset;
16
            }
16
            stream->setDetails(Http2ResponseCodeDetails::get().remote_reset);
16
          }
16
        }
12362
      }
12670
      stream->runResetCallbacks(reason, absl::string_view());
85536
    } else if (!stream->reset_reason_.has_value() &&
79319
               stream->stream_manager_.hasBufferedBodyOrTrailers()) {
109
      ENVOY_CONN_LOG(debug, "buffered onStreamClose for stream: {}", connection_, stream_id);
      // Buffer the call, rely on the stream->process_buffered_data_callback_
      // to end up invoking.
109
      stream->stream_manager_.buffered_on_stream_close_ = true;
109
      stats_.deferred_stream_close_.inc();
109
      return okStatus();
109
    }
91880
    stream->destroy();
91880
    current_stream_id_.reset();
    // TODO(antoniovicente) Test coverage for onCloseStream before deferred reset handling happens.
91880
    pending_deferred_reset_streams_.erase(stream->stream_id_);
91880
    connection_.dispatcher().deferredDelete(stream->removeFromList(active_streams_));
    // Any unconsumed data must be consumed before the stream is deleted.
    // nghttp2 does not appear to track this internally, and any stream deleted
    // with outstanding window will contribute to a slow connection-window leak.
91880
    ENVOY_CONN_LOG(debug, "Recouping {} bytes of flow control window for stream {}.", connection_,
91880
                   stream->unconsumed_bytes_, stream_id);
91880
    adapter_->MarkDataConsumedForStream(stream_id, stream->unconsumed_bytes_);
91880
    stream->unconsumed_bytes_ = 0;
91880
    adapter_->SetStreamUserData(stream->stream_id_, nullptr);
91880
  }
91895
  return okStatus();
92004
}
91278
Status ConnectionImpl::onStreamClose(int32_t stream_id, uint32_t error_code) {
91278
  return onStreamClose(getStreamUnchecked(stream_id), error_code);
91278
}
3158
int ConnectionImpl::onMetadataReceived(int32_t stream_id, const uint8_t* data, size_t len) {
3158
  ENVOY_CONN_LOG(trace, "recv {} bytes METADATA", connection_, len);
3158
  StreamImpl* stream = getStreamUnchecked(stream_id);
3158
  if (!stream || stream->remote_end_stream_) {
7
    if (!stream) {
3
      ENVOY_CONN_LOG(debug, "no stream for stream_id {} while receiving METADATA", connection_,
3
                     stream_id);
3
    }
7
    return 0;
7
  }
3151
  bool success = stream->getMetadataDecoder().receiveMetadata(data, len);
3151
  return success ? 0 : ERR_CALLBACK_FAILURE;
3158
}
2363
int ConnectionImpl::onMetadataFrameComplete(int32_t stream_id, bool end_metadata) {
2363
  ENVOY_CONN_LOG(trace, "recv METADATA frame on stream {}, end_metadata: {}", connection_,
2363
                 stream_id, end_metadata);
2363
  StreamImpl* stream = getStreamUnchecked(stream_id);
2363
  if (!stream || stream->remote_end_stream_) {
7
    if (!stream) {
3
      ENVOY_CONN_LOG(debug, "no stream for stream_id {} while completing METADATA", connection_,
3
                     stream_id);
3
    }
7
    return 0;
7
  }
2356
  bool result = stream->getMetadataDecoder().onMetadataFrameComplete(end_metadata);
2356
  return result ? 0 : ERR_CALLBACK_FAILURE;
2363
}
763447
int ConnectionImpl::saveHeader(int32_t stream_id, HeaderString&& name, HeaderString&& value) {
763447
  StreamImpl* stream = getStreamUnchecked(stream_id);
763447
  if (!stream) {
    // We have seen 1 or 2 crashes where we get a headers callback but there is no associated
    // stream data. I honestly am not sure how this can happen. However, from reading the nghttp2
    // code it looks possible that inflate_header_block() can safely inflate headers for an already
    // closed stream, but will still call the headers callback. Since that seems possible, we should
    // ignore this case here.
    // TODO(mattklein123): Figure out a test case that can hit this.
    stats_.headers_cb_no_stream_.inc();
    return 0;
  }
763447
  stream->bytes_meter_->addDecompressedHeaderBytesReceived(name.size() + value.size());
  // TODO(10646): Switch to use HeaderUtility::checkHeaderNameForUnderscores().
763447
  auto should_return = checkHeaderNameForUnderscores(name.getStringView());
763447
  if (should_return) {
22
    stream->setDetails(Http2ResponseCodeDetails::get().invalid_underscore);
22
    name.clear();
22
    value.clear();
22
    return should_return.value();
22
  }
763425
  stream->saveHeader(std::move(name), std::move(value));
763425
  if (stream->headers().byteSize() > max_headers_kb_ * 1024 ||
763425
      stream->headers().size() > max_headers_count_) {
25
    stream->setDetails(Http2ResponseCodeDetails::get().too_many_headers);
25
    stats_.header_overflow_.inc();
    // This will cause the library to reset/close the stream.
25
    return ERR_TEMPORAL_CALLBACK_FAILURE;
763400
  } else {
763400
    return 0;
763400
  }
763425
}
1031094
Status ConnectionImpl::sendPendingFrames() {
1031094
  if (dispatching_ || connection_.state() == Network::Connection::State::Closed) {
14548
    return okStatus();
14548
  }
1016546
  const int rc = adapter_->Send();
1016546
  if (rc != 0) {
69
    ASSERT(rc == ERR_CALLBACK_FAILURE);
69
    return codecProtocolError(codecStrError(rc));
69
  }
  // See ConnectionImpl::StreamImpl::resetStream() for why we do this. This is an uncommon event,
  // so iterating through every stream to find the ones that have a deferred reset is not a big
  // deal. Furthermore, queueing a reset frame does not actually invoke the close stream callback.
  // This is only done when the reset frame is sent. Thus, it's safe to work directly with the
  // stream map.
  // NOTE: The way we handle deferred reset is essentially best effort. If we intend to do a
  //       deferred reset, we try to finish the stream, including writing any pending data frames.
  //       If we cannot do this (potentially due to not enough window), we just reset the stream.
  //       In general this behavior occurs only when we are trying to send immediate error messages
  //       to short circuit requests. In the best effort case, we complete the stream before
  //       resetting. In other cases, we just do the reset now which will blow away pending data
  //       frames and release any memory associated with the stream.
1016477
  if (!pending_deferred_reset_streams_.empty()) {
196
    while (!pending_deferred_reset_streams_.empty()) {
98
      auto it = pending_deferred_reset_streams_.begin();
98
      auto* stream = it->second;
      // Sanity check: the stream's id matches the map key.
98
      ASSERT(it->first == stream->stream_id_);
98
      pending_deferred_reset_streams_.erase(it);
98
      ASSERT(stream->deferred_reset_);
98
      stream->resetStreamWorker(stream->deferred_reset_.value());
98
    }
98
    RETURN_IF_ERROR(sendPendingFrames());
98
  }
  // After all pending frames have been written into the outbound buffer check if any of
  // protocol constraints had been violated.
1016477
  Status status = protocol_constraints_.checkOutboundFrameLimits();
1016477
  if (!status.ok()) {
234
    ENVOY_CONN_LOG(debug, "error sending frames: Too many frames in the outbound queue.",
234
                   connection_);
234
  }
1016477
  return status;
1016477
}
737600
bool ConnectionImpl::sendPendingFramesAndHandleError() {
737600
  if (!sendPendingFrames().ok()) {
206
    scheduleProtocolConstraintViolationCallback();
206
    return true;
206
  }
737394
  return false;
737600
}
void ConnectionImpl::sendSettingsHelper(
30424
    const envoy::config::core::v3::Http2ProtocolOptions& http2_options, bool disable_push) {
30424
  absl::InlinedVector<http2::adapter::Http2Setting, 10> settings;
30424
  auto insertParameter = [&settings](const http2::adapter::Http2Setting& entry) mutable -> bool {
    // Consider using a set as an intermediate data structure, rather than this ad-hoc
    // deduplication.
8
    const auto it = std::find_if(
8
        settings.cbegin(), settings.cend(),
10
        [&entry](const http2::adapter::Http2Setting& existing) { return entry.id == existing.id; });
8
    if (it != settings.end()) {
      return false;
    }
8
    settings.push_back(entry);
8
    return true;
8
  };
  // Universally disable receiving push promise frames as we don't currently
  // support them.
  // NOTE: This is a special case with respect to custom parameter overrides in
  // that server push is not supported and therefore not end user configurable.
30424
  if (disable_push) {
14817
    settings.push_back({static_cast<int32_t>(http2::adapter::ENABLE_PUSH), disable_push ? 0U : 1U});
14817
  }
30424
  for (const auto& it : http2_options.custom_settings_parameters()) {
8
    ASSERT(it.identifier().value() <= std::numeric_limits<uint16_t>::max());
8
    const bool result =
8
        insertParameter({static_cast<http2::adapter::Http2SettingsId>(it.identifier().value()),
8
                         it.value().value()});
8
    ASSERT(result);
8
    ENVOY_CONN_LOG(debug, "adding custom settings parameter with id {:#x} to {}", connection_,
8
                   it.identifier().value(), it.value().value());
8
  }
  // Insert named parameters.
30424
  settings.insert(
30424
      settings.end(),
30424
      {{http2::adapter::HEADER_TABLE_SIZE, http2_options.hpack_table_size().value()},
30424
       {http2::adapter::ENABLE_CONNECT_PROTOCOL, http2_options.allow_connect()},
30424
       {http2::adapter::MAX_CONCURRENT_STREAMS, http2_options.max_concurrent_streams().value()},
30424
       {http2::adapter::INITIAL_WINDOW_SIZE, http2_options.initial_stream_window_size().value()}});
30424
  adapter_->SubmitSettings(settings);
30424
}
void ConnectionImpl::sendSettings(
30424
    const envoy::config::core::v3::Http2ProtocolOptions& http2_options, bool disable_push) {
30424
  sendSettingsHelper(http2_options, disable_push);
30424
  const uint32_t initial_connection_window_size =
30424
      http2_options.initial_connection_window_size().value();
  // Increase connection window size up to our default size.
30424
  if (initial_connection_window_size != INITIAL_CONNECTION_WINDOW_SIZE) {
29845
    ENVOY_CONN_LOG(debug, "updating connection-level initial window size to {}", connection_,
29845
                   initial_connection_window_size);
29845
    adapter_->SubmitWindowUpdate(0,
29845
                                 initial_connection_window_size - INITIAL_CONNECTION_WINDOW_SIZE);
29845
  }
30424
}
1352248
int ConnectionImpl::setAndCheckCodecCallbackStatus(Status&& status) {
  // Keep the error status that caused the original failure. Subsequent
  // error statuses are silently discarded.
1352248
  codec_callback_status_.Update(std::move(status));
1352251
  if (codec_callback_status_.ok() && connection_.state() != Network::Connection::State::Open) {
445
    if (!active_streams_.empty() || !raised_goaway_) {
418
      codec_callback_status_ = codecProtocolError("Connection was closed while dispatching frames");
431
    } else {
27
      codec_callback_status_ = goAwayGracefulCloseError();
27
    }
445
  }
1193946
  return codec_callback_status_.ok() ? 0 : ERR_CALLBACK_FAILURE;
1352248
}
206
void ConnectionImpl::scheduleProtocolConstraintViolationCallback() {
206
  if (!protocol_constraint_violation_callback_) {
52
    protocol_constraint_violation_callback_ = connection_.dispatcher().createSchedulableCallback(
52
        [this]() { onProtocolConstraintViolation(); });
52
    protocol_constraint_violation_callback_->scheduleCallbackCurrentIteration();
52
  }
206
}
52
void ConnectionImpl::onProtocolConstraintViolation() {
  // Flooded outbound queue implies that peer is not reading and it does not
  // make sense to try to flush pending bytes.
52
  connection_.close(Envoy::Network::ConnectionCloseType::NoFlush,
52
                    StreamInfo::LocalCloseReasons::get().Http2ConnectionProtocolViolation);
52
}
76519
void ConnectionImpl::onUnderlyingConnectionBelowWriteBufferLowWatermark() {
  // Notify the streams based on least recently encoding to the connection.
258514
  for (auto it = active_streams_.rbegin(); it != active_streams_.rend(); ++it) {
181995
    (*it)->runLowWatermarkCallbacks();
181995
  }
76519
}
30424
ConnectionImpl::Http2Visitor::Http2Visitor(ConnectionImpl* connection) : connection_(connection) {}
257872
int64_t ConnectionImpl::Http2Visitor::OnReadyToSend(absl::string_view serialized) {
257872
  return connection_->onSend(reinterpret_cast<const uint8_t*>(serialized.data()),
257872
                             serialized.size());
257872
}
ConnectionImpl::Http2Visitor::DataFrameHeaderInfo
ConnectionImpl::Http2Visitor::OnReadyToSendDataForStream(Http2StreamId stream_id,
963535
                                                         size_t max_length) {
963535
  StreamImpl* stream = connection_->getStream(stream_id);
963535
  if (stream == nullptr) {
    return {/*payload_length=*/-1, /*end_data=*/false, /*end_stream=*/false};
  }
963535
  if (stream->pending_send_data_->length() == 0 && !stream->local_end_stream_) {
435026
    stream->data_deferred_ = true;
435026
    return {/*payload_length=*/0, /*end_data=*/false, /*end_stream=*/false};
435026
  }
528509
  const size_t length = std::min<size_t>(max_length, stream->pending_send_data_->length());
528509
  bool end_data = false;
528509
  bool end_stream = false;
528509
  if (stream->local_end_stream_ && length == stream->pending_send_data_->length()) {
18654
    end_data = true;
18654
    if (stream->pending_trailers_to_encode_) {
20
      stream->submitTrailers(*stream->pending_trailers_to_encode_);
20
      stream->pending_trailers_to_encode_.reset();
18634
    } else {
18634
      end_stream = true;
18634
    }
18654
  }
528509
  return {static_cast<int64_t>(length), end_data, end_stream};
963535
}
bool ConnectionImpl::Http2Visitor::SendDataFrame(Http2StreamId stream_id,
                                                 absl::string_view frame_header,
528507
                                                 size_t payload_length) {
528507
  connection_->protocol_constraints_.incrementOutboundDataFrameCount();
528507
  StreamImpl* stream = connection_->getStream(stream_id);
528507
  if (stream == nullptr) {
    ENVOY_CONN_LOG(error, "error sending data frame: stream {} not found", connection_->connection_,
                   stream_id);
    return false;
  }
528507
  Buffer::OwnedImpl output;
528507
  connection_->addOutboundFrameFragment(
528507
      output, reinterpret_cast<const uint8_t*>(frame_header.data()), frame_header.size());
528507
  if (!connection_->protocol_constraints_.checkOutboundFrameLimits().ok()) {
112
    ENVOY_CONN_LOG(debug, "error sending data frame: Too many frames in the outbound queue",
112
                   connection_->connection_);
112
    stream->setDetails(Http2ResponseCodeDetails::get().outbound_frame_flood);
112
  }
528507
  connection_->stats_.pending_send_bytes_.sub(payload_length);
528507
  output.move(*stream->pending_send_data_, payload_length);
528507
  connection_->connection_.write(output, false);
528507
  return true;
528507
}
bool ConnectionImpl::Http2Visitor::OnFrameHeader(Http2StreamId stream_id, size_t length,
619944
                                                 uint8_t type, uint8_t flags) {
619944
  ENVOY_CONN_LOG(trace, "Http2Visitor::OnFrameHeader({}, {}, {}, {})", connection_->connection_,
619944
                 stream_id, length, int(type), int(flags));
619944
  if (type == OGHTTP2_CONTINUATION_FRAME_TYPE) {
8596
    if (current_frame_.stream_id != stream_id) {
      return false;
    }
8596
    current_frame_.length += length;
8596
    current_frame_.flags |= flags;
611923
  } else {
611348
    current_frame_ = {stream_id, length, type, flags};
611348
    padding_length_ = 0;
611348
    remaining_data_payload_ = 0;
611348
  }
619944
  Status status = connection_->onBeforeFrameReceived(stream_id, length, type, flags);
619944
  return 0 == connection_->setAndCheckCodecCallbackStatus(std::move(status));
619944
}
139323
bool ConnectionImpl::Http2Visitor::OnBeginHeadersForStream(Http2StreamId stream_id) {
139323
  Status status = connection_->onBeginHeaders(stream_id);
139323
  return 0 == connection_->setAndCheckCodecCallbackStatus(std::move(status));
139323
}
OnHeaderResult ConnectionImpl::Http2Visitor::OnHeaderForStream(Http2StreamId stream_id,
                                                               absl::string_view name_view,
763454
                                                               absl::string_view value_view) {
  // We use reference counting to avoid copying well-known header names.
  // For common HTTP/2 headers (e.g., :method, :path, :status), we reference Envoy's
  // static LowerCaseString objects instead of allocating and copying the name string.
  // This significantly reduces memory allocations and copy operations for typical requests.
763454
  HeaderString name;
763454
  const LowerCaseString* static_name = getStaticHeaderNameLookup().lookup(name_view);
763454
  if (static_name != nullptr) {
    // Header name matches a well-known header. Use setReference to avoid copying.
552276
    name.setReference(static_name->get());
662787
  } else {
    // Unknown header name. Copy the data.
211178
    name.setCopy(name_view.data(), name_view.size());
211178
  }
  // Always copy the value, as header values are highly variable and the data from
  // the HTTP/2 adapter is only valid during this callback.
763454
  HeaderString value;
763454
  value.setCopy(value_view.data(), value_view.size());
763454
  const int result = connection_->onHeader(stream_id, std::move(name), std::move(value));
763454
  switch (result) {
763404
  case 0:
763404
    return OnHeaderResult::HEADER_OK;
35
  case ERR_TEMPORAL_CALLBACK_FAILURE:
35
    return OnHeaderResult::HEADER_RST_STREAM;
  default:
    return OnHeaderResult::HEADER_CONNECTION_ERROR;
763454
  }
763454
}
138171
bool ConnectionImpl::Http2Visitor::OnEndHeadersForStream(Http2StreamId stream_id) {
138171
  ENVOY_CONN_LOG(trace, "Http2Visitor::OnEndHeadersForStream({})", connection_->connection_,
138171
                 stream_id);
138171
  Status status = connection_->onHeaders(stream_id, current_frame_.length, current_frame_.flags);
138171
  return 0 == connection_->setAndCheckCodecCallbackStatus(std::move(status));
138171
}
bool ConnectionImpl::Http2Visitor::OnBeginDataForStream(Http2StreamId stream_id,
351012
                                                        size_t payload_length) {
351012
  ENVOY_CONN_LOG(trace, "Http2Visitor::OnBeginDataForStream({}, {})", connection_->connection_,
351012
                 stream_id, payload_length);
351012
  remaining_data_payload_ = payload_length;
351012
  padding_length_ = 0;
351012
  if (remaining_data_payload_ == 0 && (current_frame_.flags & FLAG_END_STREAM) == 0) {
16
    ENVOY_CONN_LOG(trace, "Http2Visitor dispatching DATA for stream {}", connection_->connection_,
16
                   stream_id);
16
    Status status = connection_->onBeginData(stream_id, current_frame_.length, current_frame_.flags,
16
                                             padding_length_);
16
    return 0 == connection_->setAndCheckCodecCallbackStatus(std::move(status));
16
  }
350996
  ENVOY_CONN_LOG(debug, "Http2Visitor: remaining data payload: {}, stream_id: {}, end_stream: {}",
350996
                 connection_->connection_, remaining_data_payload_, stream_id,
350996
                 bool(current_frame_.flags & FLAG_END_STREAM));
350996
  return true;
351012
}
bool ConnectionImpl::Http2Visitor::OnDataPaddingLength(Http2StreamId stream_id,
2
                                                       size_t padding_length) {
2
  padding_length_ = padding_length;
2
  remaining_data_payload_ -= padding_length;
2
  if (remaining_data_payload_ == 0 && (current_frame_.flags & FLAG_END_STREAM) == 0) {
1
    ENVOY_CONN_LOG(trace, "Http2Visitor dispatching DATA for stream {}", connection_->connection_,
1
                   stream_id);
1
    Status status = connection_->onBeginData(stream_id, current_frame_.length, current_frame_.flags,
1
                                             padding_length_);
1
    return 0 == connection_->setAndCheckCodecCallbackStatus(std::move(status));
1
  }
1
  ENVOY_CONN_LOG(trace, "Http2Visitor: remaining data payload: {}, stream_id: {}, end_stream: {}",
1
                 connection_->connection_, remaining_data_payload_, stream_id,
1
                 bool(current_frame_.flags & FLAG_END_STREAM));
1
  return true;
2
}
bool ConnectionImpl::Http2Visitor::OnDataForStream(Http2StreamId stream_id,
457281
                                                   absl::string_view data) {
457281
  const int result =
457281
      connection_->onData(stream_id, reinterpret_cast<const uint8_t*>(data.data()), data.size());
457281
  remaining_data_payload_ -= data.size();
457295
  if (result == 0 && remaining_data_payload_ == 0 &&
457281
      (current_frame_.flags & FLAG_END_STREAM) == 0) {
331993
    ENVOY_CONN_LOG(trace, "Http2Visitor dispatching DATA for stream {}", connection_->connection_,
331993
                   stream_id);
331993
    Status status = connection_->onBeginData(stream_id, current_frame_.length, current_frame_.flags,
331993
                                             padding_length_);
331993
    return 0 == connection_->setAndCheckCodecCallbackStatus(std::move(status));
331993
  }
125288
  ENVOY_CONN_LOG(trace, "Http2Visitor: remaining data payload: {}, stream_id: {}, end_stream: {}",
125288
                 connection_->connection_, remaining_data_payload_, stream_id,
125288
                 bool(current_frame_.flags & FLAG_END_STREAM));
125288
  return result == 0;
457281
}
122698
bool ConnectionImpl::Http2Visitor::OnEndStream(Http2StreamId stream_id) {
122698
  ENVOY_CONN_LOG(trace, "Http2Visitor::OnEndStream({})", connection_->connection_, stream_id);
122698
  if (current_frame_.type == OGHTTP2_DATA_FRAME_TYPE) {
    // `onBeginData` is invoked here to ensure that the connection has successfully validated and
    // processed the entire DATA frame.
18981
    ENVOY_CONN_LOG(trace, "Http2Visitor dispatching DATA for stream {}", connection_->connection_,
18981
                   stream_id);
18981
    Status status = connection_->onBeginData(stream_id, current_frame_.length, current_frame_.flags,
18981
                                             padding_length_);
18981
    return 0 == connection_->setAndCheckCodecCallbackStatus(std::move(status));
18981
  }
103717
  return true;
122698
}
5178
void ConnectionImpl::Http2Visitor::OnRstStream(Http2StreamId stream_id, Http2ErrorCode error_code) {
5178
  (void)connection_->onRstStream(stream_id, static_cast<uint32_t>(error_code));
5178
}
bool ConnectionImpl::Http2Visitor::OnCloseStream(Http2StreamId stream_id,
91278
                                                 Http2ErrorCode error_code) {
91278
  Status status = connection_->onStreamClose(stream_id, static_cast<uint32_t>(error_code));
91278
  ASSERT(status.ok());
91278
  if (stream_close_listener_) {
73708
    ENVOY_CONN_LOG(trace, "Http2Visitor invoking stream close listener for stream {}",
73708
                   connection_->connection_, stream_id);
73708
    stream_close_listener_(stream_id);
73708
  }
91278
  return 0 == connection_->setAndCheckCodecCallbackStatus(std::move(status));
91278
}
12345
void ConnectionImpl::Http2Visitor::OnPing(Http2PingId ping_id, bool is_ack) {
12345
  const uint64_t network_order_opaque_data = quiche::QuicheEndian::HostToNet64(ping_id);
12345
  Status status = connection_->onPing(network_order_opaque_data, is_ack);
12345
  connection_->setAndCheckCodecCallbackStatus(std::move(status));
12345
}
bool ConnectionImpl::Http2Visitor::OnGoAway(Http2StreamId /*last_accepted_stream_id*/,
                                            Http2ErrorCode error_code,
192
                                            absl::string_view /*opaque_data*/) {
192
  Status status = connection_->onGoAway(static_cast<uint32_t>(error_code));
192
  return 0 == connection_->setAndCheckCodecCallbackStatus(std::move(status));
192
}
int ConnectionImpl::Http2Visitor::OnBeforeFrameSent(uint8_t frame_type, Http2StreamId stream_id,
239409
                                                    size_t length, uint8_t flags) {
239409
  return connection_->onBeforeFrameSend(stream_id, length, frame_type, flags);
239409
}
int ConnectionImpl::Http2Visitor::OnFrameSent(uint8_t frame_type, Http2StreamId stream_id,
767915
                                              size_t length, uint8_t flags, uint32_t error_code) {
767915
  return connection_->onFrameSend(stream_id, length, frame_type, flags, error_code);
767915
}
bool ConnectionImpl::Http2Visitor::OnInvalidFrame(Http2StreamId stream_id,
1064
                                                  InvalidFrameError error) {
1064
  return 0 == connection_->onInvalidFrame(stream_id, http2::adapter::ToNgHttp2ErrorCode(error));
1064
}
bool ConnectionImpl::Http2Visitor::OnMetadataForStream(Http2StreamId stream_id,
3158
                                                       absl::string_view metadata) {
3158
  return 0 == connection_->onMetadataReceived(
3158
                  stream_id, reinterpret_cast<const uint8_t*>(metadata.data()), metadata.size());
3158
}
2363
bool ConnectionImpl::Http2Visitor::OnMetadataEndForStream(Http2StreamId stream_id) {
2363
  return 0 == connection_->onMetadataFrameComplete(stream_id, true);
2363
}
668
void ConnectionImpl::Http2Visitor::OnErrorDebug(absl::string_view message) {
668
  connection_->onError(message);
668
}
ConnectionImpl::Http2Options::Http2Options(
30424
    const envoy::config::core::v3::Http2ProtocolOptions& http2_options, uint32_t max_headers_kb) {
30424
  og_options_.perspective = http2::adapter::Perspective::kServer;
30424
  og_options_.max_hpack_encoding_table_capacity = http2_options.hpack_table_size().value();
30424
  og_options_.max_header_list_bytes = max_headers_kb * 1024;
30424
  og_options_.max_header_field_size = max_headers_kb * 1024;
30424
  og_options_.allow_extended_connect = http2_options.allow_connect();
30424
  og_options_.allow_different_host_and_authority = true;
30424
  if (!PROTOBUF_GET_WRAPPED_OR_DEFAULT(http2_options, enable_huffman_encoding, true)) {
2
    if (http2_options.has_hpack_table_size() && http2_options.hpack_table_size().value() == 0) {
      og_options_.compression_option = http2::adapter::OgHttp2Session::Options::DISABLE_COMPRESSION;
2
    } else {
2
      og_options_.compression_option = http2::adapter::OgHttp2Session::Options::DISABLE_HUFFMAN;
2
    }
2
  }
#ifdef ENVOY_ENABLE_UHV
  // UHV - disable header validations in oghttp2
  og_options_.validate_http_headers = false;
#endif
30424
#ifdef ENVOY_NGHTTP2
30424
  nghttp2_option_new(&options_);
  // Currently we do not do anything with stream priority. Setting the following option prevents
  // nghttp2 from keeping around closed streams for use during stream priority dependency graph
  // calculations. This saves a tremendous amount of memory in cases where there are a large
  // number of kept alive HTTP/2 connections.
30424
  nghttp2_option_set_no_closed_streams(options_, 1);
30424
  nghttp2_option_set_no_auto_window_update(options_, 1);
  // RFC9113 invalidates trailing whitespace in header values but this is a new validation which
  // can break existing deployments.
  // Disable this validation for now.
30424
  nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation(options_, 1);
  // The max send header block length is configured to an arbitrarily high number so as to never
  // trigger the check within nghttp2, as we check request headers length in
  // codec_impl::saveHeader.
30424
  nghttp2_option_set_max_send_header_block_length(options_, 0x2000000);
30424
  if (http2_options.hpack_table_size().value() != NGHTTP2_DEFAULT_HEADER_TABLE_SIZE) {
1024
    nghttp2_option_set_max_deflate_dynamic_table_size(options_,
1024
                                                      http2_options.hpack_table_size().value());
1024
  }
30424
  if (!PROTOBUF_GET_WRAPPED_OR_DEFAULT(http2_options, enable_huffman_encoding, true)) {
2
    nghttp2_option_set_disable_huffman_encoding(options_, 1);
2
  }
30424
  if (http2_options.has_max_header_field_size_kb()) {
5
    nghttp2_option_set_max_hd_nv_size(options_,
5
                                      http2_options.max_header_field_size_kb().value() * 1024);
5
  }
30424
  if (http2_options.allow_metadata()) {
14268
    nghttp2_option_set_user_recv_extension_type(options_, METADATA_FRAME_TYPE);
17175
  } else {
16156
    ENVOY_LOG(trace, "Codec does not have Metadata frame support.");
16156
  }
  // nghttp2 v1.39.2 lowered the internal flood protection limit from 10K to 1K of ACK frames.
  // This new limit may cause the internal nghttp2 mitigation to trigger more often (as it
  // requires just 9K of incoming bytes for smallest 9 byte SETTINGS frame), bypassing the same
  // mitigation and its associated behavior in the envoy HTTP/2 codec. Since envoy does not rely
  // on this mitigation, set back to the old 10K number to avoid any changes in the HTTP/2 codec
  // behavior.
30424
  nghttp2_option_set_max_outbound_ack(options_, 10000);
  // nghttp2 REQUIRES setting max number of CONTINUATION frames.
  // 512 is chosen to accommodate Envoy's 8Mb max limit of max_request_headers_kb
  // in both headers and trailers
30424
  nghttp2_option_set_max_continuations(options_, 512);
30424
#endif
30424
}
30424
ConnectionImpl::Http2Options::~Http2Options() {
30424
#ifdef ENVOY_NGHTTP2
30424
  nghttp2_option_del(options_);
30424
#endif
30424
}
ConnectionImpl::ClientHttp2Options::ClientHttp2Options(
    const envoy::config::core::v3::Http2ProtocolOptions& http2_options, uint32_t max_headers_kb)
14817
    : Http2Options(http2_options, max_headers_kb) {
14817
  og_options_.perspective = http2::adapter::Perspective::kClient;
14817
  og_options_.remote_max_concurrent_streams =
14817
      ::Envoy::Http2::Utility::OptionsLimits::MAX_MAX_CONCURRENT_STREAMS;
14817
#ifdef ENVOY_NGHTTP2
  // Temporarily disable initial max streams limit/protection, since we might want to create
  // more than 100 streams before receiving the HTTP/2 SETTINGS frame from the server.
  //
  // TODO(PiotrSikora): remove this once multiple upstream connections or queuing are implemented.
14817
  nghttp2_option_set_peer_max_concurrent_streams(
14817
      options_, ::Envoy::Http2::Utility::OptionsLimits::MAX_MAX_CONCURRENT_STREAMS);
  // nghttp2 REQUIRES setting max number of CONTINUATION frames.
  // 1024 is chosen to accommodate Envoy's 8Mb max limit of max_request_headers_kb
  // in both headers and trailers
14817
  nghttp2_option_set_max_continuations(options_, 1024);
14817
#endif
14817
}
OptRef<const StreamInfo::StreamInfo> ConnectionImpl::trackedStream() const {
  return connection_.trackedStream();
}
12
void ConnectionImpl::dumpState(std::ostream& os, int indent_level) const {
12
  const char* spaces = spacesForLevel(indent_level);
12
  os << spaces << "Http2::ConnectionImpl " << this << DUMP_MEMBER(max_headers_kb_)
12
     << DUMP_MEMBER(max_headers_count_) << DUMP_MEMBER(per_stream_buffer_limit_)
12
     << DUMP_MEMBER(allow_metadata_) << DUMP_MEMBER(stream_error_on_invalid_http_messaging_)
12
     << DUMP_MEMBER(is_outbound_flood_monitored_control_frame_) << DUMP_MEMBER(dispatching_)
12
     << DUMP_MEMBER(raised_goaway_) << DUMP_MEMBER(pending_deferred_reset_streams_.size()) << '\n';
  // Dump the protocol constraints
12
  DUMP_DETAILS(&protocol_constraints_);
  // Dump either a targeted stream or several of the active streams.
12
  dumpStreams(os, indent_level);
  // Dump the active slice
12
  if (current_slice_ == nullptr) {
    // No current slice, use macro for consistent formatting.
8
    os << spaces << "current_slice_: null\n";
8
  } else {
4
    auto slice_view =
4
        absl::string_view(static_cast<const char*>(current_slice_->mem_), current_slice_->len_);
4
    os << spaces << "current slice length: " << slice_view.length() << " contents: \"";
4
    StringUtil::escapeToOstream(os, slice_view);
4
    os << "\"\n";
4
  }
12
}
12
void ConnectionImpl::dumpStreams(std::ostream& os, int indent_level) const {
12
  const char* spaces = spacesForLevel(indent_level);
  // Try to dump details for the current stream.
  // If none, dump a subset of our active streams.
12
  os << spaces << "Number of active streams: " << active_streams_.size()
12
     << DUMP_OPTIONAL_MEMBER(current_stream_id_);
12
  if (current_stream_id_.has_value()) {
6
    os << " Dumping current stream:\n";
6
    const ConnectionImpl::StreamImpl* stream = getStream(current_stream_id_.value());
6
    DUMP_DETAILS(stream);
8
  } else {
6
    os << " Dumping " << std::min<size_t>(25, active_streams_.size()) << " Active Streams:\n";
6
    size_t count = 0;
6
    for (auto& stream : active_streams_) {
4
      DUMP_DETAILS(stream);
4
      if (++count >= 25) {
        break;
      }
4
    }
6
  }
12
}
4
void ClientConnectionImpl::dumpStreams(std::ostream& os, int indent_level) const {
4
  ConnectionImpl::dumpStreams(os, indent_level);
4
  if (!current_stream_id_.has_value()) {
2
    return;
2
  }
  // Try to dump the downstream request information, corresponding to the
  // stream we were processing.
2
  const char* spaces = spacesForLevel(indent_level);
2
  os << spaces << "Dumping corresponding downstream request for upstream stream "
2
     << current_stream_id_.value() << ":\n";
2
  const ClientStreamImpl* client_stream =
2
      static_cast<const ClientStreamImpl*>(getStreamUnchecked(current_stream_id_.value()));
2
  if (client_stream) {
2
    client_stream->response_decoder_.dumpState(os, indent_level + 1);
2
  } else {
    os << spaces
       << " Failed to get the upstream stream with stream id: " << current_stream_id_.value()
       << " Unable to dump downstream request.\n";
  }
2
}
10
void ConnectionImpl::StreamImpl::dumpState(std::ostream& os, int indent_level) const {
10
  const char* spaces = spacesForLevel(indent_level);
10
  os << spaces << "ConnectionImpl::StreamImpl " << this << DUMP_MEMBER(stream_id_)
10
     << DUMP_MEMBER(unconsumed_bytes_) << DUMP_MEMBER(read_disable_count_)
10
     << DUMP_MEMBER(local_end_stream_) << DUMP_MEMBER(local_end_stream_sent_)
10
     << DUMP_MEMBER(remote_end_stream_) << DUMP_MEMBER(data_deferred_)
10
     << DUMP_MEMBER(received_noninformational_headers_)
10
     << DUMP_MEMBER(pending_receive_buffer_high_watermark_called_)
10
     << DUMP_MEMBER(pending_send_buffer_high_watermark_called_)
10
     << DUMP_MEMBER(reset_due_to_messaging_error_)
10
     << DUMP_MEMBER_AS(cookies_, cookies_.getStringView());
10
  DUMP_DETAILS(pending_trailers_to_encode_);
10
}
4
void ConnectionImpl::ClientStreamImpl::dumpState(std::ostream& os, int indent_level) const {
4
  const char* spaces = spacesForLevel(indent_level);
4
  StreamImpl::dumpState(os, indent_level);
  // Dump header map
4
  if (absl::holds_alternative<ResponseHeaderMapPtr>(headers_or_trailers_)) {
4
    DUMP_DETAILS(absl::get<ResponseHeaderMapPtr>(headers_or_trailers_));
4
  } else {
    DUMP_DETAILS(absl::get<ResponseTrailerMapPtr>(headers_or_trailers_));
  }
4
}
6
void ConnectionImpl::ServerStreamImpl::dumpState(std::ostream& os, int indent_level) const {
6
  const char* spaces = spacesForLevel(indent_level);
6
  StreamImpl::dumpState(os, indent_level);
  // Dump header map
6
  if (absl::holds_alternative<RequestHeaderMapSharedPtr>(headers_or_trailers_)) {
6
    DUMP_DETAILS(absl::get<RequestHeaderMapSharedPtr>(headers_or_trailers_));
6
  } else {
    DUMP_DETAILS(absl::get<RequestTrailerMapPtr>(headers_or_trailers_));
  }
6
}
ClientConnectionImpl::ClientConnectionImpl(
    Network::Connection& connection, Http::ConnectionCallbacks& callbacks, CodecStats& stats,
    Random::RandomGenerator& random_generator,
    const envoy::config::core::v3::Http2ProtocolOptions& http2_options,
    const uint32_t max_response_headers_kb, const uint32_t max_response_headers_count,
    Http2SessionFactory& http2_session_factory)
14817
    : ConnectionImpl(connection, stats, random_generator, http2_options, max_response_headers_kb,
14817
                     max_response_headers_count),
14817
      callbacks_(callbacks) {
14817
  ClientHttp2Options client_http2_options(http2_options, max_response_headers_kb);
14817
  if (!use_oghttp2_library_) {
11316
#ifdef ENVOY_NGHTTP2
11316
    adapter_ = http2_session_factory.create(base(), client_http2_options.options());
11316
#endif
11316
  }
14817
  if (!adapter_) {
3501
    adapter_ = http2_session_factory.create(base(), client_http2_options.ogOptions());
3501
  }
14817
  http2_session_factory.init(base(), http2_options);
14817
  allow_metadata_ = http2_options.allow_metadata();
14817
  max_metadata_size_ =
14817
      PROTOBUF_GET_WRAPPED_OR_DEFAULT(http2_options, max_metadata_size, 1024 * 1024);
14817
  idle_session_requires_ping_interval_ = std::chrono::milliseconds(PROTOBUF_GET_MS_OR_DEFAULT(
14817
      http2_options.connection_keepalive(), connection_idle_interval, 0));
14817
}
52957
RequestEncoder& ClientConnectionImpl::newStream(ResponseDecoder& decoder) {
  // If the connection has been idle long enough to trigger a ping, send one
  // ahead of creating the stream.
52957
  if (idle_session_requires_ping_interval_.count() != 0 &&
52957
      (connection_.dispatcher().timeSource().monotonicTime() - lastReceivedDataTime() >
4
       idle_session_requires_ping_interval_)) {
    sendKeepalive();
  }
52957
  ClientStreamImplPtr stream(new ClientStreamImpl(*this, per_stream_buffer_limit_, decoder));
  // If the connection is currently above the high watermark, make sure to inform the new stream.
  // The connection can not pass this on automatically as it has no awareness that a new stream is
  // created.
52957
  if (connection_.aboveHighWatermark()) {
41
    stream->runHighWatermarkCallbacks();
41
  }
52957
  ClientStreamImpl& stream_ref = *stream;
52957
  LinkedList::moveIntoList(std::move(stream), active_streams_);
52957
  protocol_constraints_.incrementOpenedStreamCount();
52957
  return stream_ref;
52957
}
42729
Status ClientConnectionImpl::onBeginHeaders(int32_t stream_id) {
42729
  StreamImpl* stream = getStream(stream_id);
42729
  if (stream != nullptr) {
42727
    return stream->onBeginHeaders();
42727
  }
2
  return codecClientError(absl::StrFormat("stream %d is already gone", stream_id));
42729
}
154571
int ClientConnectionImpl::onHeader(int32_t stream_id, HeaderString&& name, HeaderString&& value) {
154571
  ASSERT(connection_.state() == Network::Connection::State::Open);
154571
  return saveHeader(stream_id, std::move(name), std::move(value));
154571
}
23
StreamResetReason ClientConnectionImpl::getMessagingErrorResetReason() const {
23
  connection_.streamInfo().setResponseFlag(StreamInfo::CoreResponseFlag::UpstreamProtocolError);
23
  return StreamResetReason::ProtocolError;
23
}
ServerConnectionImpl::ServerConnectionImpl(
    Network::Connection& connection, Http::ServerConnectionCallbacks& callbacks, CodecStats& stats,
    Random::RandomGenerator& random_generator,
    const envoy::config::core::v3::Http2ProtocolOptions& http2_options,
    const uint32_t max_request_headers_kb, const uint32_t max_request_headers_count,
    envoy::config::core::v3::HttpProtocolOptions::HeadersWithUnderscoresAction
        headers_with_underscores_action,
    Server::OverloadManager& overload_manager)
15607
    : ConnectionImpl(connection, stats, random_generator, http2_options, max_request_headers_kb,
15607
                     max_request_headers_count),
15607
      callbacks_(callbacks), headers_with_underscores_action_(headers_with_underscores_action),
15607
      should_send_go_away_on_dispatch_(overload_manager.getLoadShedPoint(
15607
          Server::LoadShedPointName::get().H2ServerGoAwayOnDispatch)),
15607
      should_send_go_away_and_close_on_dispatch_(overload_manager.getLoadShedPoint(
15607
          Server::LoadShedPointName::get().H2ServerGoAwayAndCloseOnDispatch)) {
15607
  ENVOY_LOG_ONCE_IF(trace, should_send_go_away_on_dispatch_ == nullptr,
15607
                    "LoadShedPoint envoy.load_shed_points.http2_server_go_away_on_dispatch is not "
15607
                    "found. Is it configured?");
15607
  ENVOY_LOG_ONCE_IF(
15607
      trace, should_send_go_away_and_close_on_dispatch_ == nullptr,
15607
      "LoadShedPoint envoy.load_shed_points.http2_server_go_away_and_close_on_dispatch is not "
15607
      "found. Is it configured?");
15607
  Http2Options h2_options(http2_options, max_request_headers_kb);
15607
  auto direct_visitor = std::make_unique<Http2Visitor>(this);
15607
#ifdef ENVOY_NGHTTP2
15607
  if (use_oghttp2_library_) {
3497
#endif
3497
    visitor_ = std::move(direct_visitor);
3497
    adapter_ = http2::adapter::OgHttp2Adapter::Create(*visitor_, h2_options.ogOptions());
3497
#ifdef ENVOY_NGHTTP2
13546
  } else {
12110
    auto adapter =
12110
        http2::adapter::NgHttp2Adapter::CreateServerAdapter(*direct_visitor, h2_options.options());
39108
    auto stream_close_listener = [p = adapter.get()](http2::adapter::Http2StreamId stream_id) {
37082
      p->RemoveStream(stream_id);
37082
    };
12110
    direct_visitor->setStreamCloseListener(std::move(stream_close_listener));
12110
    visitor_ = std::move(direct_visitor);
12110
    adapter_ = std::move(adapter);
12110
  }
15607
#endif
15607
  sendSettings(http2_options, false);
15607
  allow_metadata_ = http2_options.allow_metadata();
15607
  max_metadata_size_ =
15607
      PROTOBUF_GET_WRAPPED_OR_DEFAULT(http2_options, max_metadata_size, 1024 * 1024);
15607
}
96596
Status ServerConnectionImpl::onBeginHeaders(int32_t stream_id) {
96596
  ASSERT(connection_.state() == Network::Connection::State::Open);
96596
  StreamImpl* stream_ptr = getStream(stream_id);
96596
  if (stream_ptr != nullptr) {
713
    return stream_ptr->onBeginHeaders();
713
  }
95883
  ServerStreamImplPtr stream(new ServerStreamImpl(*this, per_stream_buffer_limit_));
95883
  if (connection_.aboveHighWatermark()) {
7
    stream->runHighWatermarkCallbacks();
7
  }
95883
  stream->setRequestDecoder(callbacks_.newStream(*stream));
95883
  stream->stream_id_ = stream_id;
95883
  LinkedList::moveIntoList(std::move(stream), active_streams_);
95883
  adapter_->SetStreamUserData(stream_id, active_streams_.front().get());
95883
  protocol_constraints_.incrementOpenedStreamCount();
95883
  return active_streams_.front()->onBeginHeaders();
96596
}
608875
int ServerConnectionImpl::onHeader(int32_t stream_id, HeaderString&& name, HeaderString&& value) {
608880
  if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.http2_discard_host_header")) {
608856
    StreamImpl* stream = getStreamUnchecked(stream_id);
608856
    if (stream && name == static_cast<absl::string_view>(Http::Headers::get().HostLegacy)) {
      // Check if there is already the :authority header
6
      const auto result = stream->headers().get(Http::Headers::get().Host);
6
      if (!result.empty()) {
        // Discard the host header value
4
        return 0;
4
      }
      // Otherwise use host value as :authority
6
    }
608856
  }
608871
  return saveHeader(stream_id, std::move(name), std::move(value));
608875
}
221215
Http::Status ServerConnectionImpl::dispatch(Buffer::Instance& data) {
  // Make sure downstream outbound queue was not flooded by the upstream frames.
221215
  RETURN_IF_ERROR(protocol_constraints_.checkOutboundFrameLimits());
221213
  if (should_send_go_away_and_close_on_dispatch_ != nullptr &&
221213
      should_send_go_away_and_close_on_dispatch_->shouldShedLoad()) {
4
    ConnectionImpl::goAway();
4
    sent_go_away_on_dispatch_ = true;
4
    return envoyOverloadError(
4
        "Load shed point http2_server_go_away_and_close_on_dispatch triggered");
4
  }
221209
  if (should_send_go_away_on_dispatch_ != nullptr && !sent_go_away_on_dispatch_ &&
221209
      should_send_go_away_on_dispatch_->shouldShedLoad()) {
4
    ConnectionImpl::goAway();
4
    sent_go_away_on_dispatch_ = true;
4
  }
221209
  return ConnectionImpl::dispatch(data);
221213
}
absl::optional<int> ServerConnectionImpl::checkHeaderNameForUnderscores(
608873
    [[maybe_unused]] absl::string_view header_name) {
608873
#ifndef ENVOY_ENABLE_UHV
  // This check has been moved to UHV
608873
  if (headers_with_underscores_action_ != envoy::config::core::v3::HttpProtocolOptions::ALLOW &&
608873
      Http::HeaderUtility::headerNameContainsUnderscore(header_name)) {
22
    if (headers_with_underscores_action_ ==
22
        envoy::config::core::v3::HttpProtocolOptions::DROP_HEADER) {
12
      ENVOY_CONN_LOG(debug, "Dropping header with invalid characters in its name: {}", connection_,
12
                     header_name);
12
      stats_.incDroppedHeadersWithUnderscores();
12
      return 0;
12
    }
10
    ENVOY_CONN_LOG(debug, "Rejecting request due to header name with underscores: {}", connection_,
10
                   header_name);
10
    stats_.incRequestsRejectedWithUnderscoresInHeaders();
10
    return ERR_TEMPORAL_CALLBACK_FAILURE;
22
  }
#else
  // Workaround for gcc not understanding [[maybe_unused]] for class members.
  (void)headers_with_underscores_action_;
#endif
608851
  return absl::nullopt;
608873
}
} // namespace Http2
} // namespace Http
} // namespace Envoy