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
341
  StaticHeaderNameLookup() {
45
341
    const auto& headers = Headers::get();
46
341
    const auto& custom_headers = CustomHeaders::get();
47

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

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

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

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

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

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

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

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

            
108
764115
const StaticHeaderNameLookup& getStaticHeaderNameLookup() {
109
764115
  CONSTRUCT_ON_FIRST_USE(StaticHeaderNameLookup);
110
764115
}
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
12775
int reasonToReset(StreamResetReason reason, bool response_end_stream_sent) {
177
12775
  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
12636
  default:
188
12636
    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
12630
    return response_end_stream_sent ? OGHTTP2_NO_ERROR : OGHTTP2_INTERNAL_ERROR;
194
12775
  }
195
12775
}
196

            
197
11517
StreamResetReason errorCodeToResetReason(int error_code) {
198
11517
  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
11335
  default:
206
11335
    return StreamResetReason::RemoteReset;
207
11517
  }
208
11517
}
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
28245
    absl::Span<const http2::adapter::Http2Setting> settings) {
235
89910
  for (const auto& [id, value] : settings) {
236
89604
    if (id == SETTINGS_MAX_CONCURRENT_STREAMS) {
237
27673
      concurrent_stream_limit_ = value;
238
27673
      break;
239
27673
    }
240
89604
  }
241
28245
}
242

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

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

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

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

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

            
282
void ProdNghttp2SessionFactory::init(ConnectionImpl* connection,
283
14963
                                     const envoy::config::core::v3::Http2ProtocolOptions& options) {
284
14963
  connection->sendSettings(options, true);
285
14963
}
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
149019
    : MultiplexedStreamImplBase(parent.connection_.dispatcher()), parent_(parent),
297
149019
      pending_recv_data_(parent_.connection_.dispatcher().getWatermarkFactory().createBuffer(
298
149019
          [this]() -> void { this->pendingRecvBufferLowWatermark(); },
299
149019
          [this]() -> void { this->pendingRecvBufferHighWatermark(); },
300
149019
          []() -> void { /* TODO(adisuissa): Handle overflow watermark */ })),
301
149019
      pending_send_data_(parent_.connection_.dispatcher().getWatermarkFactory().createBuffer(
302
149019
          [this]() -> void { this->pendingSendBufferLowWatermark(); },
303
149019
          [this]() -> void { this->pendingSendBufferHighWatermark(); },
304
149019
          []() -> void { /* TODO(adisuissa): Handle overflow watermark */ })),
305
149019
      local_end_stream_sent_(false), remote_end_stream_(false), remote_rst_(false),
306
149019
      data_deferred_(false), received_noninformational_headers_(false),
307
149019
      pending_receive_buffer_high_watermark_called_(false),
308
149019
      pending_send_buffer_high_watermark_called_(false), reset_due_to_messaging_error_(false),
309
149019
      extend_stream_lifetime_flag_(false) {
310
149019
  parent_.stats_.streams_active_.inc();
311
149021
  if (buffer_limit > 0) {
312
149021
    setWriteBufferWatermarks(buffer_limit);
313
149021
  }
314
149019
  stream_manager_.defer_processing_segment_size_ = parent.connection_.bufferLimit();
315
149019
}
316

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

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

            
326
95960
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
95960
  if (buffer_memory_account_) {
336
84
    buffer_memory_account_->clearDownstream();
337
84
  }
338

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

            
342
1259860
http2::adapter::HeaderRep getRep(const HeaderString& str) {
343
1259860
  if (str.isReference()) {
344
505906
    return str.getStringView();
345
811808
  } else {
346
753954
    return std::string(str.getStringView());
347
753954
  }
348
1259860
}
349

            
350
std::vector<http2::adapter::Header>
351
118963
ConnectionImpl::StreamImpl::buildHeaders(const HeaderMap& headers) {
352
118963
  std::vector<http2::adapter::Header> out;
353
118963
  out.reserve(headers.size());
354
629930
  headers.iterate([&out](const HeaderEntry& header) -> HeaderMap::Iterate {
355
629930
    out.push_back({getRep(header.key()), getRep(header.value())});
356
629930
    return HeaderMap::Iterate::Continue;
357
629930
  });
358
118963
  return out;
359
118963
}
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
117325
void ConnectionImpl::StreamImpl::encodeHeadersBase(const HeaderMap& headers, bool end_stream) {
367
117325
  local_end_stream_ = end_stream;
368

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

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

            
378
Status ConnectionImpl::ClientStreamImpl::encodeHeaders(const RequestHeaderMap& headers,
379
52442
                                                       bool end_stream) {
380
52442
  parent_.updateActiveStreamsOnEncode(*this);
381
52442
#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
52442
  RETURN_IF_ERROR(HeaderUtility::checkRequiredRequestHeaders(headers));
388
  // Verify that a filter hasn't added an invalid header key or value.
389
52428
  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
52424
  Http::RequestHeaderMapPtr modified_headers;
394
52424
  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
52403
  } 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
52189
  } else {
406
51636
    encodeHeadersBase(headers, end_stream);
407
51636
  }
408
#else
409
  encodeHeadersBase(headers, end_stream);
410
#endif
411
52424
  return okStatus();
412
52428
}
413

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

            
420
64902
#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
64902
  Http::ResponseHeaderMapPtr modified_headers;
425
64902
  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
64866
  } else {
430
64848
    encodeHeadersBase(headers, end_stream);
431
64848
  }
432
#else
433
  encodeHeadersBase(headers, end_stream);
434
#endif
435
64902
}
436

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

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

            
444
6617
  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
6593
  } else {
455
6593
    submitTrailers(trailers);
456
6593
    if (parent_.sendPendingFramesAndHandleError()) {
457
      // Intended to check through coverage that this error case is tested
458
4
      return;
459
4
    }
460
6593
  }
461
6617
}
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
452414
void ConnectionImpl::StreamImpl::processBufferedData() {
479
452414
  ENVOY_CONN_LOG(debug, "Stream {} processing buffered data.", parent_.connection_, stream_id_);
480

            
481
  // Restore crash dump context when processing buffered data.
482
452414
  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
452414
  ASSERT(dispatcher.trackedObjectStackIsEmpty());
486
452414
  Envoy::ScopeTrackedObjectStack stack;
487
452414
  stack.add(parent_.connection_);
488

            
489
452414
  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
452414
  if (!stream_manager_.buffered_on_stream_close_) {
493
448506
    ASSERT(!parent_.current_stream_id_.has_value());
494
448506
    parent_.current_stream_id_ = stream_id_;
495
448506
  }
496

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

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

            
504
452414
  if (stream_manager_.trailers_buffered_ && !stream_manager_.body_buffered_ &&
505
452414
      continueProcessingBufferedData()) {
506
16
    decodeTrailers();
507
16
    ASSERT(!stream_manager_.trailers_buffered_);
508
16
  }
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
452414
  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
452414
}
522

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

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

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

            
557
    // We schedule processing to occur in another callback to avoid
558
    // reentrant and deep call stacks.
559
465979
    if (schedule_next_iteration) {
560
261402
      process_buffered_data_callback_->scheduleCallbackNextIteration();
561
400407
    } else {
562
204577
      process_buffered_data_callback_->scheduleCallbackCurrentIteration();
563
204577
    }
564
465979
  }
565
470899
}
566

            
567
544
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
544
}
572

            
573
540
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
540
  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
540
    grantPeerAdditionalStreamWindow();
581
540
  }
582
540
}
583

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

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

            
596
654537
  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
654537
  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
654208
    const bool decode_data_in_chunk =
605
654208
        stream_manager_.decodeAsChunks() &&
606
654208
        pending_recv_data_->length() > stream_manager_.defer_processing_segment_size_;
607

            
608
654208
    StreamDecoder* stream_decoder = decoder();
609
654208
    if (decode_data_in_chunk) {
610
350865
      Buffer::OwnedImpl chunk_buffer;
611
      // TODO(kbaichoo): Consider implementing an approximate move for chunking.
612
350865
      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
350865
      stream_manager_.body_buffered_ = true;
617
350865
      ASSERT(pending_recv_data_->length() > 0);
618

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

            
624
350865
      if (!buffersOverrun()) {
625
261402
        scheduleProcessingOfBufferedData(true);
626
261402
      }
627
502962
    } else {
628
      // Send the entire buffer through.
629
303475
      if (stream_decoder) {
630
303475
        stream_decoder->decodeData(*pending_recv_data_, sendEndStream());
631
303475
      }
632
303343
    }
633
654208
  }
634

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

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

            
645
  // Extended CONNECT to H/1 upgrade transformation has moved to UHV
646
41688
  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
41688
  received_noninformational_headers_ =
666
41688
      !CodeUtility::is1xx(status) || status == enumToInt(Http::Code::SwitchingProtocols);
667

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

            
675
1567
bool ConnectionImpl::StreamImpl::maybeDeferDecodeTrailers() {
676
1567
  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
1567
  if (buffersOverrun() || stream_manager_.body_buffered_) {
683
17
    stream_manager_.trailers_buffered_ = true;
684
17
    ENVOY_CONN_LOG(trace, "Stream {} buffering decodeTrailers() call.", parent_.connection_,
685
17
                   stream_id_);
686
17
    return true;
687
17
  }
688

            
689
1550
  return false;
690
1567
}
691

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

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

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

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

            
719
689
void ConnectionImpl::ServerStreamImpl::decodeTrailers() {
720
689
  if (maybeDeferDecodeTrailers()) {
721
5
    return;
722
5
  }
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
67
void ConnectionImpl::StreamImpl::pendingSendBufferHighWatermark() {
736
67
  ENVOY_CONN_LOG(debug, "send buffer over limit ", parent_.connection_);
737
67
  ASSERT(!pending_send_buffer_high_watermark_called_);
738
67
  pending_send_buffer_high_watermark_called_ = true;
739
67
  runHighWatermarkCallbacks();
740
67
}
741

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

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

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

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

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

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

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

            
782
42809
  return okStatus();
783
42809
}
784

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

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

            
796
96673
Status ConnectionImpl::ServerStreamImpl::onBeginHeaders() {
797
96673
  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
96673
  return okStatus();
805
96673
}
806

            
807
95785
void ConnectionImpl::ServerStreamImpl::advanceHeadersState() {
808
95785
  RELEASE_ASSERT(headers_state_ == HeadersState::Request || headers_state_ == HeadersState::Headers,
809
95785
                 "");
810
95785
  headers_state_ = HeadersState::Headers;
811
95785
}
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
416776
void ConnectionImpl::StreamImpl::encodeData(Buffer::Instance& data, bool end_stream) {
829
416776
  parent_.updateActiveStreamsOnEncode(*this);
830
416776
  ASSERT(!local_end_stream_);
831
416776
  encodeDataHelper(data, end_stream,
832
                   /*skip_encoding_empty_trailers=*/
833
416776
                   false);
834
416776
}
835

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

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

            
849
419687
    data_deferred_ = false;
850
419687
  }
851

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

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

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

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

            
873
  // Higher layers expect calling resetStream() to immediately raise reset callbacks.
874
13591
  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
13591
  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
13583
  if (useDeferredReset() && local_end_stream_ && !local_end_stream_sent_ &&
897
13583
      reason != StreamResetReason::OverloadManager) {
898
303
    ASSERT(parent_.getStreamUnchecked(stream_id_) != nullptr);
899
303
    parent_.pending_deferred_reset_streams_.emplace(stream_id_, this);
900
303
    deferred_reset_ = reason;
901
303
    ENVOY_CONN_LOG(trace, "deferred reset stream", parent_.connection_);
902
13418
  } else {
903
13280
    resetStreamWorker(reason);
904
13280
  }
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
13583
  if (parent_.sendPendingFramesAndHandleError()) {
910
    // Intended to check through coverage that this error case is tested
911
58
    return;
912
58
  }
913
13583
}
914

            
915
13388
void ConnectionImpl::StreamImpl::resetStreamWorker(StreamResetReason reason) {
916
13388
  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
613
    ENVOY_CONN_LOG(trace, "Stream {} reset before headers sent.", parent_.connection_, stream_id_);
921
613
    Status status = parent_.onStreamClose(this, 0);
922
613
    ASSERT(status.ok());
923
613
    return;
924
613
  }
925
12775
  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
12775
  const bool response_end_stream_sent =
936
12775
      parent_.adapter_->IsServerSession() ? local_end_stream_sent_ : false;
937
12775
  parent_.adapter_->SubmitRst(stream_id_, static_cast<http2::adapter::Http2ErrorCode>(
938
12775
                                              reasonToReset(reason, response_end_stream_sent)));
939
12775
}
940

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

            
948
5509
MetadataDecoder& ConnectionImpl::StreamImpl::getMetadataDecoder() {
949
5509
  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
5509
  return *metadata_decoder_;
956
5509
}
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
94280
void ConnectionImpl::StreamImpl::setAccount(Buffer::BufferMemoryAccountSharedPtr account) {
972
94280
  buffer_memory_account_ = account;
973
94280
  pending_recv_data_->bindAccount(buffer_memory_account_);
974
94280
  pending_send_data_->bindAccount(buffer_memory_account_);
975
94280
}
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
30684
    : stats_(stats), connection_(connection), max_headers_kb_(max_headers_kb),
982
30684
      max_headers_count_(max_headers_count),
983
30684
      per_stream_buffer_limit_(http2_options.initial_stream_window_size().value()),
984
      stream_error_on_invalid_http_messaging_(
985
30684
          http2_options.override_stream_error_on_invalid_http_message().value()),
986
30684
      protocol_constraints_(stats, http2_options), dispatching_(false), raised_goaway_(false),
987
30684
      random_(random_generator),
988
30684
      last_received_data_time_(connection_.dispatcher().timeSource().monotonicTime()) {
989
30684
  if (http2_options.has_use_oghttp2_codec()) {
990
2
    use_oghttp2_library_ = http2_options.use_oghttp2_codec().value();
991
30682
  } else {
992
30682
    use_oghttp2_library_ =
993
30682
        Runtime::runtimeFeatureEnabled("envoy.reloadable_features.http2_use_oghttp2");
994
30682
  }
995
30684
  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
  }
30684
}
30684
ConnectionImpl::~ConnectionImpl() {
81594
  for (const auto& stream : active_streams_) {
57908
    stream->destroy();
57908
  }
30684
}
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
}
294641
Http::Status ConnectionImpl::dispatch(Buffer::Instance& data) {
294641
  ScopeTrackerScopeState scope(this, connection_.dispatcher());
294641
  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)).
294643
  Cleanup cleanup([this]() {
294642
    dispatching_ = false;
294642
    current_slice_ = nullptr;
294642
    current_stream_id_.reset();
294642
  });
294641
  last_received_data_time_ = connection_.dispatcher().timeSource().monotonicTime();
393074
  for (const Buffer::RawSlice& slice : data.getRawSlices()) {
393021
    current_slice_ = &slice;
393021
    dispatching_ = true;
393021
    ssize_t rc;
393021
    rc = adapter_->ProcessBytes(absl::string_view(static_cast<char*>(slice.mem_), slice.len_));
393021
    if (!codec_callback_status_.ok()) {
476
      return codec_callback_status_;
476
    }
392545
#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.
392545
    static const int ERR_FLOODED = -904;
392545
    if (rc == ERR_FLOODED) {
      return bufferFloodError(
          "Flooding was detected in this HTTP/2 session, and it must be closed"); // LCOV_EXCL_LINE
    }
392545
#endif
392545
    if (rc != static_cast<ssize_t>(slice.len_)) {
762
      return codecProtocolError(codecStrError(rc));
762
    }
391783
    current_slice_ = nullptr;
391783
    dispatching_ = false;
391783
    current_stream_id_.reset();
391783
  }
293403
  ENVOY_CONN_LOG(trace, "dispatched {} bytes", connection_, data.length());
293403
  data.drain(data.length());
  // Decoding incoming frames can generate outbound frames so flush pending.
293403
  return sendPendingFrames();
294641
}
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
}
2089109
ConnectionImpl::StreamImpl* ConnectionImpl::getStream(int32_t stream_id) {
2089109
  StreamImpl* stream = getStreamUnchecked(stream_id);
2089109
  SLOW_ASSERT(stream != nullptr || !slowContainsStreamId(stream_id));
2089109
  return stream;
2089109
}
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
}
4822125
ConnectionImpl::StreamImpl* ConnectionImpl::getStreamUnchecked(int32_t stream_id) {
4822125
  return static_cast<StreamImpl*>(adapter_->GetStreamUserData(stream_id));
4822125
}
457461
int ConnectionImpl::onData(int32_t stream_id, const uint8_t* data, size_t len) {
457461
  ASSERT(connection_.state() == Network::Connection::State::Open);
457461
  StreamImpl* stream = getStream(stream_id);
  // If this results in buffering too much data, the watermark buffer will call
  // pendingRecvBufferHighWatermark, resulting in ++read_disable_count_
457461
  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
457462
  if (stream->shouldAllowPeerAdditionalStreamWindow()) {
394887
    adapter_->MarkDataConsumedForStream(stream_id, len);
416425
  } else {
62575
    stream->unconsumed_bytes_ += len;
62575
  }
457461
  return 0;
457461
}
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,
619972
                                             uint8_t flags) {
619972
  ENVOY_CONN_LOG(trace, "about to recv frame type={}, flags={}, stream_id={}", connection_,
619972
                 static_cast<uint64_t>(type), static_cast<uint64_t>(flags), stream_id);
619972
  ASSERT(connection_.state() == Network::Connection::State::Open);
619972
  current_stream_id_ = stream_id;
619972
  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.
617464
  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().
617464
  auto status = okStatus();
617464
  if (type != OGHTTP2_DATA_FRAME_TYPE) {
266432
    status = trackInboundFrames(stream_id, length, type, flags, 0);
266432
  }
617464
  return status;
619972
}
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
}
12376
Status ConnectionImpl::onPing(uint64_t opaque_data, bool is_ack) {
12376
  ENVOY_CONN_LOG(trace, "recv frame type=PING", connection_);
12376
  ASSERT(connection_.state() == Network::Connection::State::Open);
12376
  if (is_ack) {
2508
    ENVOY_CONN_LOG(trace, "recv PING ACK {}", connection_, opaque_data);
2508
    onKeepaliveResponse();
2508
  }
12376
  return okStatus();
12376
}
Status ConnectionImpl::onBeginData(int32_t stream_id, size_t length, uint8_t flags,
350947
                                   size_t padding) {
350947
  ENVOY_CONN_LOG(trace, "recv frame type=DATA stream_id={}", connection_, stream_id);
350947
  RETURN_IF_ERROR(trackInboundFrames(stream_id, length, OGHTTP2_DATA_FRAME_TYPE, flags, padding));
350934
  StreamImpl* stream = getStreamUnchecked(stream_id);
350934
  if (!stream) {
    return okStatus();
  }
  // Track bytes received.
350934
  stream->bytes_meter_->addWireBytesReceived(length + H2_FRAME_HEADER_SIZE);
350934
  stream->remote_end_stream_ = flags & FLAG_END_STREAM;
350934
  stream->decodeData();
350934
  return okStatus();
350934
}
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
}
138340
Status ConnectionImpl::onHeaders(int32_t stream_id, size_t length, uint8_t flags) {
138340
  StreamImpl* stream = getStreamUnchecked(stream_id);
138340
  if (!stream) {
    return okStatus();
  }
  // Track bytes received.
138340
  stream->bytes_meter_->addWireBytesReceived(length + H2_FRAME_HEADER_SIZE);
138340
  stream->bytes_meter_->addHeaderBytesReceived(length + H2_FRAME_HEADER_SIZE);
138340
  stream->remote_end_stream_ = flags & FLAG_END_STREAM;
138340
  if (!stream->cookies_.empty()) {
252
    HeaderString key(Headers::get().Cookie);
252
    stream->headers().addViaMove(std::move(key), std::move(stream->cookies_));
252
  }
138340
  StreamImpl::HeadersState headers_state = stream->headersState();
138340
  switch (headers_state) {
41543
  case StreamImpl::HeadersState::Response:
136640
  case StreamImpl::HeadersState::Request: {
136640
    stream->decodeHeaders();
136640
    break;
41543
  }
1700
  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.
1700
    if (!stream->deferred_reset_) {
1696
      if (adapter_->IsServerSession() || stream->received_noninformational_headers_) {
1551
        ASSERT(stream->remote_end_stream_);
1551
        stream->decodeTrailers();
1583
      } else {
        // We're a client session and still waiting for non-informational headers.
145
        stream->decodeHeaders();
145
      }
1696
    }
1700
    break;
41543
  }
  default:
    // We do not currently support push.
    ENVOY_BUG(false, "push not supported"); // LCOV_EXCL_LINE
138340
  }
138340
  stream->advanceHeadersState();
138340
  return okStatus();
138340
}
4350
Status ConnectionImpl::onRstStream(int32_t stream_id, uint32_t error_code) {
4350
  ENVOY_CONN_LOG(trace, "recv frame type=RST_STREAM stream_id={}", connection_, stream_id);
4350
  StreamImpl* stream = getStreamUnchecked(stream_id);
4350
  if (!stream) {
51
    return okStatus();
51
  }
4299
  ENVOY_CONN_LOG(trace, "remote reset: {} {}", connection_, stream_id, error_code);
  // Track bytes received.
4299
  stream->bytes_meter_->addWireBytesReceived(/*frame_length=*/4 + H2_FRAME_HEADER_SIZE);
4299
  stream->remote_rst_ = true;
4299
  stats_.rx_reset_.inc();
4299
  return okStatus();
4350
}
int ConnectionImpl::onFrameSend(int32_t stream_id, size_t length, uint8_t type, uint8_t flags,
768818
                                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.
768818
  ENVOY_CONN_LOG(trace, "sent frame type={}, stream_id={}, length={}", connection_,
768818
                 static_cast<uint64_t>(type), stream_id, length);
768818
  StreamImpl* stream = getStreamUnchecked(stream_id);
768818
  if (stream != nullptr) {
663230
    if (type != METADATA_FRAME_TYPE) {
660156
      stream->bytes_meter_->addWireBytesSent(length + H2_FRAME_HEADER_SIZE);
660156
    }
663230
    if (type == OGHTTP2_HEADERS_FRAME_TYPE || type == OGHTTP2_CONTINUATION_FRAME_TYPE) {
118955
      stream->bytes_meter_->addHeaderBytesSent(length + H2_FRAME_HEADER_SIZE);
118955
    }
663230
  }
768818
  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
  }
7413
  case OGHTTP2_RST_STREAM_FRAME_TYPE: {
7350
    ENVOY_CONN_LOG(debug, "sent reset code={}", connection_, error_code);
7350
    stats_.tx_reset_.inc();
7350
    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.
6045
      stream->onResetEncoded(error_code);
6045
      stream->local_end_stream_sent_ = true;
6045
    }
7350
    break;
284
  }
118955
  case OGHTTP2_HEADERS_FRAME_TYPE:
647388
  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.
647389
    if (stream != nullptr) {
647389
      const bool end_stream_sent = flags & FLAG_END_STREAM;
647389
      stream->local_end_stream_sent_ = end_stream_sent;
647389
      if (end_stream_sent) {
82905
        stream->onEndStreamEncoded();
82905
      }
647389
    }
647388
    break;
118955
  }
768818
  }
768745
  return 0;
768818
}
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,
240383
                                      uint8_t flags) {
240383
  ENVOY_CONN_LOG(trace, "about to send frame type={}, flags={}", connection_,
240383
                 static_cast<uint64_t>(type), static_cast<uint64_t>(flags));
240383
  ASSERT(!is_outbound_flood_monitored_control_frame_);
  // Flag flood monitored outbound control frames.
240383
  is_outbound_flood_monitored_control_frame_ =
240384
      ((type == OGHTTP2_PING_FRAME_TYPE || type == OGHTTP2_SETTINGS_FRAME_TYPE) &&
240383
       flags & FLAG_ACK) ||
240383
      type == OGHTTP2_RST_STREAM_FRAME_TYPE;
240383
  return 0;
240383
}
void ConnectionImpl::addOutboundFrameFragment(Buffer::OwnedImpl& output, const uint8_t* data,
787423
                                              size_t length) {
  // Reset the outbound frame type (set in the onBeforeFrameSend callback) since the
  // onBeforeFrameSend callback is not called for DATA frames.
787423
  bool is_outbound_flood_monitored_control_frame = false;
787423
  std::swap(is_outbound_flood_monitored_control_frame, is_outbound_flood_monitored_control_frame_);
787423
  auto releasor =
787423
      protocol_constraints_.incrementOutboundFrameCount(is_outbound_flood_monitored_control_frame);
787423
  output.add(data, length);
787423
  output.addDrainTracker(releasor);
787423
}
Status ConnectionImpl::trackInboundFrames(int32_t stream_id, size_t length, uint8_t type,
617376
                                          uint8_t flags, uint32_t padding_length) {
617376
  Status result;
617376
  ENVOY_CONN_LOG(trace, "track inbound frame type={} flags={} length={} padding_length={}",
617376
                 connection_, static_cast<uint64_t>(type), static_cast<uint64_t>(flags),
617376
                 static_cast<uint64_t>(length), padding_length);
617376
  const bool end_stream = (type == OGHTTP2_DATA_FRAME_TYPE || type == OGHTTP2_HEADERS_FRAME_TYPE) &&
617376
                          (flags & FLAG_END_STREAM);
617376
  const bool is_empty = (length - padding_length) == 0;
617376
  result = protocol_constraints_.trackInboundFrame(type, end_stream, is_empty);
617376
  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
  }
617376
  return result;
617376
}
258985
ssize_t ConnectionImpl::onSend(const uint8_t* data, size_t length) {
258985
  ENVOY_CONN_LOG(trace, "send data: bytes={}", connection_, length);
258985
  Buffer::OwnedImpl buffer;
258985
  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.
258985
  connection_.write(buffer, false);
258985
  return length;
258985
}
91236
Status ConnectionImpl::onStreamClose(StreamImpl* stream, uint32_t error_code) {
91236
  if (stream) {
91222
    const int32_t stream_id = stream->stream_id_;
    // Consume buffered on stream_close.
91222
    if (stream->stream_manager_.buffered_on_stream_close_) {
109
      stream->stream_manager_.buffered_on_stream_close_ = false;
109
      stats_.deferred_stream_close_.dec();
109
    }
91222
    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.
91222
    bool should_reset_stream = !stream->remote_end_stream_ || !stream->local_end_stream_;
91222
    if (stream->extend_stream_lifetime_flag_) {
30667
      should_reset_stream = should_reset_stream || stream->remote_rst_;
30667
    }
91222
    if (should_reset_stream) {
11841
      StreamResetReason reason;
11841
      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();
11735
      } else {
11533
        if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.reset_with_error")) {
11517
          reason = errorCodeToResetReason(error_code);
11517
          if (error_code == OGHTTP2_REFUSED_STREAM) {
22
            stream->setDetails(Http2ResponseCodeDetails::get().remote_refused);
11495
          } else {
11495
            stream->setDetails(Http2ResponseCodeDetails::get().remote_reset);
11495
          }
11517
        } 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
        }
11533
      }
11841
      stream->runResetCallbacks(reason, absl::string_view());
84817
    } else if (!stream->reset_reason_.has_value() &&
79381
               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
    }
91113
    stream->destroy();
91113
    current_stream_id_.reset();
    // TODO(antoniovicente) Test coverage for onCloseStream before deferred reset handling happens.
91113
    pending_deferred_reset_streams_.erase(stream->stream_id_);
91113
    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.
91113
    ENVOY_CONN_LOG(debug, "Recouping {} bytes of flow control window for stream {}.", connection_,
91113
                   stream->unconsumed_bytes_, stream_id);
91113
    adapter_->MarkDataConsumedForStream(stream_id, stream->unconsumed_bytes_);
91113
    stream->unconsumed_bytes_ = 0;
91113
    adapter_->SetStreamUserData(stream->stream_id_, nullptr);
91113
  }
91127
  return okStatus();
91236
}
90514
Status ConnectionImpl::onStreamClose(int32_t stream_id, uint32_t error_code) {
90514
  return onStreamClose(getStreamUnchecked(stream_id), error_code);
90514
}
3160
int ConnectionImpl::onMetadataReceived(int32_t stream_id, const uint8_t* data, size_t len) {
3160
  ENVOY_CONN_LOG(trace, "recv {} bytes METADATA", connection_, len);
3160
  StreamImpl* stream = getStreamUnchecked(stream_id);
3160
  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
  }
3153
  bool success = stream->getMetadataDecoder().receiveMetadata(data, len);
3153
  return success ? 0 : ERR_CALLBACK_FAILURE;
3160
}
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
}
764109
int ConnectionImpl::saveHeader(int32_t stream_id, HeaderString&& name, HeaderString&& value) {
764109
  StreamImpl* stream = getStreamUnchecked(stream_id);
764109
  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;
  }
764109
  stream->bytes_meter_->addDecompressedHeaderBytesReceived(name.size() + value.size());
  // TODO(10646): Switch to use HeaderUtility::checkHeaderNameForUnderscores().
764109
  auto should_return = checkHeaderNameForUnderscores(name.getStringView());
764109
  if (should_return) {
22
    stream->setDetails(Http2ResponseCodeDetails::get().invalid_underscore);
22
    name.clear();
22
    value.clear();
22
    return should_return.value();
22
  }
764087
  stream->saveHeader(std::move(name), std::move(value));
764087
  if (stream->headers().byteSize() > max_headers_kb_ * 1024 ||
764088
      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;
764062
  } else {
764062
    return 0;
764062
  }
764087
}
1027265
Status ConnectionImpl::sendPendingFrames() {
1027265
  if (dispatching_ || connection_.state() == Network::Connection::State::Closed) {
14722
    return okStatus();
14722
  }
1012543
  const int rc = adapter_->Send();
1012543
  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.
1012474
  if (!pending_deferred_reset_streams_.empty()) {
190
    while (!pending_deferred_reset_streams_.empty()) {
95
      auto it = pending_deferred_reset_streams_.begin();
95
      auto* stream = it->second;
      // Sanity check: the stream's id matches the map key.
95
      ASSERT(it->first == stream->stream_id_);
95
      pending_deferred_reset_streams_.erase(it);
95
      ASSERT(stream->deferred_reset_);
95
      stream->resetStreamWorker(stream->deferred_reset_.value());
95
    }
95
    RETURN_IF_ERROR(sendPendingFrames());
95
  }
  // After all pending frames have been written into the outbound buffer check if any of
  // protocol constraints had been violated.
1012474
  Status status = protocol_constraints_.checkOutboundFrameLimits();
1012474
  if (!status.ok()) {
234
    ENVOY_CONN_LOG(debug, "error sending frames: Too many frames in the outbound queue.",
234
                   connection_);
234
  }
1012474
  return status;
1012474
}
733847
bool ConnectionImpl::sendPendingFramesAndHandleError() {
733847
  if (!sendPendingFrames().ok()) {
206
    scheduleProtocolConstraintViolationCallback();
206
    return true;
206
  }
733641
  return false;
733847
}
void ConnectionImpl::sendSettingsHelper(
30684
    const envoy::config::core::v3::Http2ProtocolOptions& http2_options, bool disable_push) {
30684
  absl::InlinedVector<http2::adapter::Http2Setting, 10> settings;
30684
  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.
30684
  if (disable_push) {
14963
    settings.push_back({static_cast<int32_t>(http2::adapter::ENABLE_PUSH), disable_push ? 0U : 1U});
14963
  }
30684
  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.
30684
  settings.insert(
30684
      settings.end(),
30684
      {{http2::adapter::HEADER_TABLE_SIZE, http2_options.hpack_table_size().value()},
30684
       {http2::adapter::ENABLE_CONNECT_PROTOCOL, http2_options.allow_connect()},
30684
       {http2::adapter::MAX_CONCURRENT_STREAMS, http2_options.max_concurrent_streams().value()},
30684
       {http2::adapter::INITIAL_WINDOW_SIZE, http2_options.initial_stream_window_size().value()}});
30684
  adapter_->SubmitSettings(settings);
30684
}
void ConnectionImpl::sendSettings(
30684
    const envoy::config::core::v3::Http2ProtocolOptions& http2_options, bool disable_push) {
30684
  sendSettingsHelper(http2_options, disable_push);
30684
  const uint32_t initial_connection_window_size =
30684
      http2_options.initial_connection_window_size().value();
  // Increase connection window size up to our default size.
30684
  if (initial_connection_window_size != INITIAL_CONNECTION_WINDOW_SIZE) {
30105
    ENVOY_CONN_LOG(debug, "updating connection-level initial window size to {}", connection_,
30105
                   initial_connection_window_size);
30105
    adapter_->SubmitWindowUpdate(0,
30105
                                 initial_connection_window_size - INITIAL_CONNECTION_WINDOW_SIZE);
30105
  }
30684
}
1351818
int ConnectionImpl::setAndCheckCodecCallbackStatus(Status&& status) {
  // Keep the error status that caused the original failure. Subsequent
  // error statuses are silently discarded.
1351818
  codec_callback_status_.Update(std::move(status));
1351820
  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
  }
1347648
  return codec_callback_status_.ok() ? 0 : ERR_CALLBACK_FAILURE;
1351818
}
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
}
76502
void ConnectionImpl::onUnderlyingConnectionBelowWriteBufferLowWatermark() {
  // Notify the streams based on least recently encoding to the connection.
254704
  for (auto it = active_streams_.rbegin(); it != active_streams_.rend(); ++it) {
178202
    (*it)->runLowWatermarkCallbacks();
178202
  }
76502
}
30684
ConnectionImpl::Http2Visitor::Http2Visitor(ConnectionImpl* connection) : connection_(connection) {}
258990
int64_t ConnectionImpl::Http2Visitor::OnReadyToSend(absl::string_view serialized) {
258990
  return connection_->onSend(reinterpret_cast<const uint8_t*>(serialized.data()),
258990
                             serialized.size());
258990
}
ConnectionImpl::Http2Visitor::DataFrameHeaderInfo
ConnectionImpl::Http2Visitor::OnReadyToSendDataForStream(Http2StreamId stream_id,
963676
                                                         size_t max_length) {
963676
  StreamImpl* stream = connection_->getStream(stream_id);
963676
  if (stream == nullptr) {
    return {/*payload_length=*/-1, /*end_data=*/false, /*end_stream=*/false};
  }
963676
  if (stream->pending_send_data_->length() == 0 && !stream->local_end_stream_) {
435243
    stream->data_deferred_ = true;
435243
    return {/*payload_length=*/0, /*end_data=*/false, /*end_stream=*/false};
435243
  }
528433
  const size_t length = std::min<size_t>(max_length, stream->pending_send_data_->length());
528433
  bool end_data = false;
528433
  bool end_stream = false;
528433
  if (stream->local_end_stream_ && length == stream->pending_send_data_->length()) {
18592
    end_data = true;
18592
    if (stream->pending_trailers_to_encode_) {
20
      stream->submitTrailers(*stream->pending_trailers_to_encode_);
20
      stream->pending_trailers_to_encode_.reset();
18572
    } else {
18572
      end_stream = true;
18572
    }
18592
  }
528433
  return {static_cast<int64_t>(length), end_data, end_stream};
963676
}
bool ConnectionImpl::Http2Visitor::SendDataFrame(Http2StreamId stream_id,
                                                 absl::string_view frame_header,
528434
                                                 size_t payload_length) {
528434
  connection_->protocol_constraints_.incrementOutboundDataFrameCount();
528434
  StreamImpl* stream = connection_->getStream(stream_id);
528434
  if (stream == nullptr) {
    ENVOY_CONN_LOG(error, "error sending data frame: stream {} not found", connection_->connection_,
                   stream_id);
    return false;
  }
528434
  Buffer::OwnedImpl output;
528434
  connection_->addOutboundFrameFragment(
528434
      output, reinterpret_cast<const uint8_t*>(frame_header.data()), frame_header.size());
528434
  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
  }
528434
  connection_->stats_.pending_send_bytes_.sub(payload_length);
528434
  output.move(*stream->pending_send_data_, payload_length);
528434
  connection_->connection_.write(output, false);
528434
  return true;
528434
}
bool ConnectionImpl::Http2Visitor::OnFrameHeader(Http2StreamId stream_id, size_t length,
619975
                                                 uint8_t type, uint8_t flags) {
619975
  ENVOY_CONN_LOG(trace, "Http2Visitor::OnFrameHeader({}, {}, {}, {})", connection_->connection_,
619975
                 stream_id, length, int(type), int(flags));
619975
  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;
611955
  } else {
611379
    current_frame_ = {stream_id, length, type, flags};
611379
    padding_length_ = 0;
611379
    remaining_data_payload_ = 0;
611379
  }
619975
  Status status = connection_->onBeforeFrameReceived(stream_id, length, type, flags);
619975
  return 0 == connection_->setAndCheckCodecCallbackStatus(std::move(status));
619975
}
139482
bool ConnectionImpl::Http2Visitor::OnBeginHeadersForStream(Http2StreamId stream_id) {
139482
  Status status = connection_->onBeginHeaders(stream_id);
139482
  return 0 == connection_->setAndCheckCodecCallbackStatus(std::move(status));
139482
}
OnHeaderResult ConnectionImpl::Http2Visitor::OnHeaderForStream(Http2StreamId stream_id,
                                                               absl::string_view name_view,
764115
                                                               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.
764115
  HeaderString name;
764115
  const LowerCaseString* static_name = getStaticHeaderNameLookup().lookup(name_view);
764115
  if (static_name != nullptr) {
    // Header name matches a well-known header. Use setReference to avoid copying.
552861
    name.setReference(static_name->get());
663372
  } else {
    // Unknown header name. Copy the data.
211254
    name.setCopy(name_view.data(), name_view.size());
211254
  }
  // Always copy the value, as header values are highly variable and the data from
  // the HTTP/2 adapter is only valid during this callback.
764115
  HeaderString value;
764115
  value.setCopy(value_view.data(), value_view.size());
764115
  const int result = connection_->onHeader(stream_id, std::move(name), std::move(value));
764115
  switch (result) {
764078
  case 0:
764078
    return OnHeaderResult::HEADER_OK;
35
  case ERR_TEMPORAL_CALLBACK_FAILURE:
35
    return OnHeaderResult::HEADER_RST_STREAM;
  default:
    return OnHeaderResult::HEADER_CONNECTION_ERROR;
764115
  }
764115
}
138340
bool ConnectionImpl::Http2Visitor::OnEndHeadersForStream(Http2StreamId stream_id) {
138340
  ENVOY_CONN_LOG(trace, "Http2Visitor::OnEndHeadersForStream({})", connection_->connection_,
138340
                 stream_id);
138340
  Status status = connection_->onHeaders(stream_id, current_frame_.length, current_frame_.flags);
138340
  return 0 == connection_->setAndCheckCodecCallbackStatus(std::move(status));
138340
}
bool ConnectionImpl::Http2Visitor::OnBeginDataForStream(Http2StreamId stream_id,
350965
                                                        size_t payload_length) {
350965
  ENVOY_CONN_LOG(trace, "Http2Visitor::OnBeginDataForStream({}, {})", connection_->connection_,
350965
                 stream_id, payload_length);
350965
  remaining_data_payload_ = payload_length;
350965
  padding_length_ = 0;
350965
  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
  }
350949
  ENVOY_CONN_LOG(debug, "Http2Visitor: remaining data payload: {}, stream_id: {}, end_stream: {}",
350949
                 connection_->connection_, remaining_data_payload_, stream_id,
350949
                 bool(current_frame_.flags & FLAG_END_STREAM));
350949
  return true;
350965
}
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,
457461
                                                   absl::string_view data) {
457461
  const int result =
457461
      connection_->onData(stream_id, reinterpret_cast<const uint8_t*>(data.data()), data.size());
457461
  remaining_data_payload_ -= data.size();
457468
  if (result == 0 && remaining_data_payload_ == 0 &&
457461
      (current_frame_.flags & FLAG_END_STREAM) == 0) {
332016
    ENVOY_CONN_LOG(trace, "Http2Visitor dispatching DATA for stream {}", connection_->connection_,
332016
                   stream_id);
332016
    Status status = connection_->onBeginData(stream_id, current_frame_.length, current_frame_.flags,
332016
                                             padding_length_);
332016
    return 0 == connection_->setAndCheckCodecCallbackStatus(std::move(status));
332016
  }
125445
  ENVOY_CONN_LOG(trace, "Http2Visitor: remaining data payload: {}, stream_id: {}, end_stream: {}",
125445
                 connection_->connection_, remaining_data_payload_, stream_id,
125445
                 bool(current_frame_.flags & FLAG_END_STREAM));
125445
  return result == 0;
457461
}
122762
bool ConnectionImpl::Http2Visitor::OnEndStream(Http2StreamId stream_id) {
122762
  ENVOY_CONN_LOG(trace, "Http2Visitor::OnEndStream({})", connection_->connection_, stream_id);
122762
  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.
18914
    ENVOY_CONN_LOG(trace, "Http2Visitor dispatching DATA for stream {}", connection_->connection_,
18914
                   stream_id);
18914
    Status status = connection_->onBeginData(stream_id, current_frame_.length, current_frame_.flags,
18914
                                             padding_length_);
18914
    return 0 == connection_->setAndCheckCodecCallbackStatus(std::move(status));
18914
  }
103848
  return true;
122762
}
4350
void ConnectionImpl::Http2Visitor::OnRstStream(Http2StreamId stream_id, Http2ErrorCode error_code) {
4350
  (void)connection_->onRstStream(stream_id, static_cast<uint32_t>(error_code));
4350
}
bool ConnectionImpl::Http2Visitor::OnCloseStream(Http2StreamId stream_id,
90514
                                                 Http2ErrorCode error_code) {
90514
  Status status = connection_->onStreamClose(stream_id, static_cast<uint32_t>(error_code));
90514
  ASSERT(status.ok());
90514
  if (stream_close_listener_) {
73194
    ENVOY_CONN_LOG(trace, "Http2Visitor invoking stream close listener for stream {}",
73194
                   connection_->connection_, stream_id);
73194
    stream_close_listener_(stream_id);
73194
  }
90514
  return 0 == connection_->setAndCheckCodecCallbackStatus(std::move(status));
90514
}
12376
void ConnectionImpl::Http2Visitor::OnPing(Http2PingId ping_id, bool is_ack) {
12376
  const uint64_t network_order_opaque_data = quiche::QuicheEndian::HostToNet64(ping_id);
12376
  Status status = connection_->onPing(network_order_opaque_data, is_ack);
12376
  connection_->setAndCheckCodecCallbackStatus(std::move(status));
12376
}
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,
240383
                                                    size_t length, uint8_t flags) {
240383
  return connection_->onBeforeFrameSend(stream_id, length, frame_type, flags);
240383
}
int ConnectionImpl::Http2Visitor::OnFrameSent(uint8_t frame_type, Http2StreamId stream_id,
768818
                                              size_t length, uint8_t flags, uint32_t error_code) {
768818
  return connection_->onFrameSend(stream_id, length, frame_type, flags, error_code);
768818
}
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,
3160
                                                       absl::string_view metadata) {
3160
  return 0 == connection_->onMetadataReceived(
3160
                  stream_id, reinterpret_cast<const uint8_t*>(metadata.data()), metadata.size());
3160
}
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(
30684
    const envoy::config::core::v3::Http2ProtocolOptions& http2_options, uint32_t max_headers_kb) {
30684
  og_options_.perspective = http2::adapter::Perspective::kServer;
30684
  og_options_.max_hpack_encoding_table_capacity = http2_options.hpack_table_size().value();
30684
  og_options_.max_header_list_bytes = max_headers_kb * 1024;
30684
  og_options_.max_header_field_size = max_headers_kb * 1024;
30684
  og_options_.allow_extended_connect = http2_options.allow_connect();
30684
  og_options_.allow_different_host_and_authority = true;
30684
  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
30684
#ifdef ENVOY_NGHTTP2
30684
  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.
30684
  nghttp2_option_set_no_closed_streams(options_, 1);
30684
  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.
30684
  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.
30684
  nghttp2_option_set_max_send_header_block_length(options_, 0x2000000);
30684
  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
  }
30684
  if (!PROTOBUF_GET_WRAPPED_OR_DEFAULT(http2_options, enable_huffman_encoding, true)) {
2
    nghttp2_option_set_disable_huffman_encoding(options_, 1);
2
  }
30684
  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
  }
30684
  if (http2_options.allow_metadata()) {
14382
    nghttp2_option_set_user_recv_extension_type(options_, METADATA_FRAME_TYPE);
17327
  } else {
16302
    ENVOY_LOG(trace, "Codec does not have Metadata frame support.");
16302
  }
  // 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.
30684
  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
30684
  nghttp2_option_set_max_continuations(options_, 512);
30684
#endif
30684
}
30684
ConnectionImpl::Http2Options::~Http2Options() {
30684
#ifdef ENVOY_NGHTTP2
30684
  nghttp2_option_del(options_);
30684
#endif
30684
}
ConnectionImpl::ClientHttp2Options::ClientHttp2Options(
    const envoy::config::core::v3::Http2ProtocolOptions& http2_options, uint32_t max_headers_kb)
14963
    : Http2Options(http2_options, max_headers_kb) {
14963
  og_options_.perspective = http2::adapter::Perspective::kClient;
14963
  og_options_.remote_max_concurrent_streams =
14963
      ::Envoy::Http2::Utility::OptionsLimits::MAX_MAX_CONCURRENT_STREAMS;
14963
#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.
14963
  nghttp2_option_set_peer_max_concurrent_streams(
14963
      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
14963
  nghttp2_option_set_max_continuations(options_, 1024);
14963
#endif
14963
}
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)
14963
    : ConnectionImpl(connection, stats, random_generator, http2_options, max_response_headers_kb,
14963
                     max_response_headers_count),
14963
      callbacks_(callbacks) {
14963
  ClientHttp2Options client_http2_options(http2_options, max_response_headers_kb);
14963
  if (!use_oghttp2_library_) {
11457
#ifdef ENVOY_NGHTTP2
11457
    adapter_ = http2_session_factory.create(base(), client_http2_options.options());
11457
#endif
11457
  }
14963
  if (!adapter_) {
3506
    adapter_ = http2_session_factory.create(base(), client_http2_options.ogOptions());
3506
  }
14963
  http2_session_factory.init(base(), http2_options);
14963
  allow_metadata_ = http2_options.allow_metadata();
14963
  max_metadata_size_ =
14963
      PROTOBUF_GET_WRAPPED_OR_DEFAULT(http2_options, max_metadata_size, 1024 * 1024);
14963
  idle_session_requires_ping_interval_ = std::chrono::milliseconds(PROTOBUF_GET_MS_OR_DEFAULT(
14963
      http2_options.connection_keepalive(), connection_idle_interval, 0));
14963
}
53061
RequestEncoder& ClientConnectionImpl::newStream(ResponseDecoder& decoder) {
  // If the connection has been idle long enough to trigger a ping, send one
  // ahead of creating the stream.
53061
  if (idle_session_requires_ping_interval_.count() != 0 &&
53061
      (connection_.dispatcher().timeSource().monotonicTime() - lastReceivedDataTime() >
4
       idle_session_requires_ping_interval_)) {
    sendKeepalive();
  }
53061
  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.
53061
  if (connection_.aboveHighWatermark()) {
29
    stream->runHighWatermarkCallbacks();
29
  }
53061
  ClientStreamImpl& stream_ref = *stream;
53061
  LinkedList::moveIntoList(std::move(stream), active_streams_);
53061
  protocol_constraints_.incrementOpenedStreamCount();
53061
  return stream_ref;
53061
}
42811
Status ClientConnectionImpl::onBeginHeaders(int32_t stream_id) {
42811
  StreamImpl* stream = getStream(stream_id);
42811
  if (stream != nullptr) {
42809
    return stream->onBeginHeaders();
42809
  }
2
  return codecClientError(absl::StrFormat("stream %d is already gone", stream_id));
42811
}
154607
int ClientConnectionImpl::onHeader(int32_t stream_id, HeaderString&& name, HeaderString&& value) {
154607
  ASSERT(connection_.state() == Network::Connection::State::Open);
154607
  return saveHeader(stream_id, std::move(name), std::move(value));
154607
}
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)
15721
    : ConnectionImpl(connection, stats, random_generator, http2_options, max_request_headers_kb,
15721
                     max_request_headers_count),
15721
      callbacks_(callbacks), headers_with_underscores_action_(headers_with_underscores_action),
15721
      should_send_go_away_on_dispatch_(overload_manager.getLoadShedPoint(
15721
          Server::LoadShedPointName::get().H2ServerGoAwayOnDispatch)),
15721
      should_send_go_away_and_close_on_dispatch_(overload_manager.getLoadShedPoint(
15721
          Server::LoadShedPointName::get().H2ServerGoAwayAndCloseOnDispatch)) {
15721
  ENVOY_LOG_ONCE_IF(trace, should_send_go_away_on_dispatch_ == nullptr,
15721
                    "LoadShedPoint envoy.load_shed_points.http2_server_go_away_on_dispatch is not "
15721
                    "found. Is it configured?");
15721
  ENVOY_LOG_ONCE_IF(
15721
      trace, should_send_go_away_and_close_on_dispatch_ == nullptr,
15721
      "LoadShedPoint envoy.load_shed_points.http2_server_go_away_and_close_on_dispatch is not "
15721
      "found. Is it configured?");
15721
  Http2Options h2_options(http2_options, max_request_headers_kb);
15721
  auto direct_visitor = std::make_unique<Http2Visitor>(this);
15721
#ifdef ENVOY_NGHTTP2
15721
  if (use_oghttp2_library_) {
3502
#endif
3502
    visitor_ = std::move(direct_visitor);
3502
    adapter_ = http2::adapter::OgHttp2Adapter::Create(*visitor_, h2_options.ogOptions());
3502
#ifdef ENVOY_NGHTTP2
13655
  } else {
12219
    auto adapter =
12219
        http2::adapter::NgHttp2Adapter::CreateServerAdapter(*direct_visitor, h2_options.options());
38461
    auto stream_close_listener = [p = adapter.get()](http2::adapter::Http2StreamId stream_id) {
36409
      p->RemoveStream(stream_id);
36409
    };
12219
    direct_visitor->setStreamCloseListener(std::move(stream_close_listener));
12219
    visitor_ = std::move(direct_visitor);
12219
    adapter_ = std::move(adapter);
12219
  }
15721
#endif
15721
  sendSettings(http2_options, false);
15721
  allow_metadata_ = http2_options.allow_metadata();
15721
  max_metadata_size_ =
15721
      PROTOBUF_GET_WRAPPED_OR_DEFAULT(http2_options, max_metadata_size, 1024 * 1024);
15721
}
96673
Status ServerConnectionImpl::onBeginHeaders(int32_t stream_id) {
96673
  ASSERT(connection_.state() == Network::Connection::State::Open);
96673
  StreamImpl* stream_ptr = getStream(stream_id);
96673
  if (stream_ptr != nullptr) {
713
    return stream_ptr->onBeginHeaders();
713
  }
95960
  ServerStreamImplPtr stream(new ServerStreamImpl(*this, per_stream_buffer_limit_));
95960
  if (connection_.aboveHighWatermark()) {
7
    stream->runHighWatermarkCallbacks();
7
  }
95960
  stream->setRequestDecoder(callbacks_.newStream(*stream));
95960
  stream->stream_id_ = stream_id;
95960
  LinkedList::moveIntoList(std::move(stream), active_streams_);
95960
  adapter_->SetStreamUserData(stream_id, active_streams_.front().get());
95960
  protocol_constraints_.incrementOpenedStreamCount();
95960
  return active_streams_.front()->onBeginHeaders();
96673
}
609505
int ServerConnectionImpl::onHeader(int32_t stream_id, HeaderString&& name, HeaderString&& value) {
609507
  if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.http2_discard_host_header")) {
609483
    StreamImpl* stream = getStreamUnchecked(stream_id);
609484
    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
    }
609483
  }
609501
  return saveHeader(stream_id, std::move(name), std::move(value));
609505
}
220786
Http::Status ServerConnectionImpl::dispatch(Buffer::Instance& data) {
  // Make sure downstream outbound queue was not flooded by the upstream frames.
220786
  RETURN_IF_ERROR(protocol_constraints_.checkOutboundFrameLimits());
220783
  if (should_send_go_away_and_close_on_dispatch_ != nullptr &&
220783
      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
  }
220779
  if (should_send_go_away_on_dispatch_ != nullptr && !sent_go_away_on_dispatch_ &&
220779
      should_send_go_away_on_dispatch_->shouldShedLoad()) {
4
    ConnectionImpl::goAway();
4
    sent_go_away_on_dispatch_ = true;
4
  }
220779
  return ConnectionImpl::dispatch(data);
220783
}
absl::optional<int> ServerConnectionImpl::checkHeaderNameForUnderscores(
609503
    [[maybe_unused]] absl::string_view header_name) {
609503
#ifndef ENVOY_ENABLE_UHV
  // This check has been moved to UHV
609503
  if (headers_with_underscores_action_ != envoy::config::core::v3::HttpProtocolOptions::ALLOW &&
609503
      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
609481
  return absl::nullopt;
609503
}
} // namespace Http2
} // namespace Http
} // namespace Envoy