ProdNghttp2SessionFactory::create(ConnectionImpl* connection, const nghttp2_option* options) {
auto stream_close_listener = [p = adapter.get()](http2::adapter::Http2StreamId stream_id) {
* Helper to remove const during a cast. nghttp2 takes non-const pointers for headers even though
void ConnectionImpl::StreamImpl::encodeHeadersBase(const HeaderMap& headers, bool end_stream) {
// Headers are now validated by UHV before encoding by the codec. Two checks below are not needed
// Required headers must be present. This can only happen by some erroneous processing after the
void ConnectionImpl::StreamImpl::encodeMetadata(const MetadataMapVector& metadata_map_vector) {
ENVOY_CONN_LOG(debug, "Stream {} processing buffered data.", parent_.connection_, stream_id_);
absl::Cleanup clear_current_stream_id = [this]() { parent_.current_stream_id_.reset(); };
if (stream_manager_.buffered_on_stream_close_ && !stream_manager_.hasBufferedBodyOrTrailers()) {
void ConnectionImpl::StreamImpl::scheduleProcessingOfBufferedData(bool schedule_next_iteration) {
process_buffered_data_callback_ = parent_.connection_.dispatcher().createSchedulableCallback(
// In case the status is invalid or missing, the response_decoder_.decodeHeaders() will fail the
// Non-informational headers are non-1xx OR 101-SwitchingProtocols, since 101 implies that further
void ConnectionImpl::ClientStreamImpl::submitHeaders(const HeaderMap& headers, bool end_stream) {
void ConnectionImpl::ServerStreamImpl::submitHeaders(const HeaderMap& headers, bool end_stream) {
RELEASE_ASSERT(headers_state_ == HeadersState::Request || headers_state_ == HeadersState::Headers,
// We must still call sendPendingFrames() in both the deferred and not deferred path. This forces
// the cleanup logic to run which will reset the stream in all cases if all data frames could not
ENVOY_CONN_LOG(trace, "Stream {} reset before headers sent.", parent_.connection_, stream_id_);
ENVOY_CONN_LOG(debug, "decode metadata called with empty map, skipping", parent_.connection_);
void ConnectionImpl::StreamImpl::setAccount(Buffer::BufferMemoryAccountSharedPtr account) {
keepalive_send_timer_ = connection.dispatcher().createTimer([this]() { sendKeepalive(); });
const uint64_t jitter_percent_mod = keepalive_interval_jitter_percent_ * interval_ms / 100;
ENVOY_CONN_LOG_EVENT(debug, "h2_ping_timeout", "Closing connection due to keepalive timeout",
// ConnectionImpl::dispatch returns early or throws an exception (consider removing if there is a
rc = adapter_->ProcessBytes(absl::string_view(static_cast<char*>(slice.mem_), slice.len_));
const ConnectionImpl::StreamImpl* ConnectionImpl::getStreamUnchecked(int32_t stream_id) const {
// Update the window to the peer unless some consumer of this stream's data has hit a flow control
ENVOY_CONN_LOG(trace, "about to recv frame type={}, flags={}, stream_id={}", connection_,
RETURN_IF_ERROR(trackInboundFrames(stream_id, length, OGHTTP2_DATA_FRAME_TYPE, flags, padding));
// data from our peer. Sometimes it raises the invalid frame callback, and sometimes it does not.
// an outgoing frame of this type, we will return an error code so that we can abort execution.
ENVOY_CONN_LOG(debug, "invalid frame: {} on stream {}", connection_, codecStrError(error_code),
std::swap(is_outbound_flood_monitored_control_frame, is_outbound_flood_monitored_control_frame_);
protocol_constraints_.incrementOutboundFrameCount(is_outbound_flood_monitored_control_frame);
ENVOY_CONN_LOG(trace, "track inbound frame type={} flags={} length={} padding_length={}",
const bool end_stream = (type == OGHTTP2_DATA_FRAME_TYPE || type == OGHTTP2_HEADERS_FRAME_TYPE) &&
ENVOY_CONN_LOG(trace, "error reading frame: {} received in this HTTP/2 session.", connection_,
// destroyed before the filter_manager_ which owns the codec through Http::ConnectionManagerImpl.
// TODO(antoniovicente) Test coverage for onCloseStream before deferred reset handling happens.
ENVOY_CONN_LOG(debug, "Recouping {} bytes of flow control window for stream {}.", connection_,
int ConnectionImpl::onMetadataReceived(int32_t stream_id, const uint8_t* data, size_t len) {
int ConnectionImpl::saveHeader(int32_t stream_id, HeaderString&& name, HeaderString&& value) {
// 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
// See ConnectionImpl::StreamImpl::resetStream() for why we do this. This is an uncommon event,
// deal. Furthermore, queueing a reset frame does not actually invoke the close stream callback.
auto insertParameter = [&settings](const http2::adapter::Http2Setting& entry) mutable -> bool {
[&entry](const http2::adapter::Http2Setting& existing) { return entry.id == existing.id; });
settings.push_back({static_cast<int32_t>(http2::adapter::ENABLE_PUSH), disable_push ? 0U : 1U});
{http2::adapter::MAX_CONCURRENT_STREAMS, http2_options.max_concurrent_streams().value()},
{http2::adapter::INITIAL_WINDOW_SIZE, http2_options.initial_stream_window_size().value()}});
ENVOY_CONN_LOG(debug, "updating connection-level initial window size to {}", connection_,
if (codec_callback_status_.ok() && connection_.state() != Network::Connection::State::Open) {
codec_callback_status_ = codecProtocolError("Connection was closed while dispatching frames");
protocol_constraint_violation_callback_ = connection_.dispatcher().createSchedulableCallback(
ConnectionImpl::Http2Visitor::Http2Visitor(ConnectionImpl* connection) : connection_(connection) {}
const size_t length = std::min<size_t>(max_length, stream->pending_send_data_->length());
ENVOY_CONN_LOG(error, "error sending data frame: stream {} not found", connection_->connection_,
ENVOY_CONN_LOG(trace, "Http2Visitor::OnFrameHeader({}, {}, {}, {})", connection_->connection_,
ENVOY_CONN_LOG(trace, "Http2Visitor::OnEndHeadersForStream({})", connection_->connection_,
Status status = connection_->onHeaders(stream_id, current_frame_.length, current_frame_.flags);
ENVOY_CONN_LOG(trace, "Http2Visitor::OnBeginDataForStream({}, {})", connection_->connection_,
ENVOY_CONN_LOG(trace, "Http2Visitor dispatching DATA for stream {}", connection_->connection_,
Status status = connection_->onBeginData(stream_id, current_frame_.length, current_frame_.flags,
ENVOY_CONN_LOG(debug, "Http2Visitor: remaining data payload: {}, stream_id: {}, end_stream: {}",
ENVOY_CONN_LOG(trace, "Http2Visitor dispatching DATA for stream {}", connection_->connection_,
Status status = connection_->onBeginData(stream_id, current_frame_.length, current_frame_.flags,
ENVOY_CONN_LOG(trace, "Http2Visitor: remaining data payload: {}, stream_id: {}, end_stream: {}",
connection_->onData(stream_id, reinterpret_cast<const uint8_t*>(data.data()), data.size());
ENVOY_CONN_LOG(trace, "Http2Visitor dispatching DATA for stream {}", connection_->connection_,
Status status = connection_->onBeginData(stream_id, current_frame_.length, current_frame_.flags,
ENVOY_CONN_LOG(trace, "Http2Visitor: remaining data payload: {}, stream_id: {}, end_stream: {}",
ENVOY_CONN_LOG(trace, "Http2Visitor::OnEndStream({})", connection_->connection_, stream_id);
ENVOY_CONN_LOG(trace, "Http2Visitor dispatching DATA for stream {}", connection_->connection_,
Status status = connection_->onBeginData(stream_id, current_frame_.length, current_frame_.flags,
void ConnectionImpl::Http2Visitor::OnRstStream(Http2StreamId stream_id, Http2ErrorCode error_code) {
Status status = connection_->onStreamClose(stream_id, static_cast<uint32_t>(error_code));
int ConnectionImpl::Http2Visitor::OnBeforeFrameSent(uint8_t frame_type, Http2StreamId stream_id,
return 0 == connection_->onInvalidFrame(stream_id, http2::adapter::ToNgHttp2ErrorCode(error));
const envoy::config::core::v3::Http2ProtocolOptions& http2_options, uint32_t max_headers_kb) {
og_options_.max_hpack_encoding_table_capacity = http2_options.hpack_table_size().value();
// TODO(PiotrSikora): remove this once multiple upstream connections or queuing are implemented.
<< DUMP_MEMBER(raised_goaway_) << DUMP_MEMBER(pending_deferred_reset_streams_.size()) << '\n';
: ConnectionImpl(connection, stats, random_generator, http2_options, max_response_headers_kb,
idle_session_requires_ping_interval_ = std::chrono::milliseconds(PROTOBUF_GET_MS_OR_DEFAULT(
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
int ClientConnectionImpl::onHeader(int32_t stream_id, HeaderString&& name, HeaderString&& value) {
connection_.streamInfo().setResponseFlag(StreamInfo::CoreResponseFlag::UpstreamProtocolError);
Network::Connection& connection, Http::ServerConnectionCallbacks& callbacks, CodecStats& stats,
: ConnectionImpl(connection, stats, random_generator, http2_options, max_request_headers_kb,
callbacks_(callbacks), headers_with_underscores_action_(headers_with_underscores_action),
"LoadShedPoint envoy.load_shed_points.http2_server_go_away_and_close_on_dispatch is not "
http2::adapter::NgHttp2Adapter::CreateServerAdapter(*direct_visitor, h2_options.options());
auto stream_close_listener = [p = adapter.get()](http2::adapter::Http2StreamId stream_id) {
int ServerConnectionImpl::onHeader(int32_t stream_id, HeaderString&& name, HeaderString&& value) {
if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.http2_discard_host_header")) {
if (stream && name == static_cast<absl::string_view>(Http::Headers::get().HostLegacy)) {
if (headers_with_underscores_action_ != envoy::config::core::v3::HttpProtocolOptions::ALLOW &&
ENVOY_CONN_LOG(debug, "Dropping header with invalid characters in its name: {}", connection_,
ENVOY_CONN_LOG(debug, "Rejecting request due to header name with underscores: {}", connection_,