Skip to content

http2: add keepalive observer hook

8eab6d2
Select commit
Loading
Failed to load commit list.
Open

http2: add keepalive observer hook #44299

http2: add keepalive observer hook
8eab6d2
Select commit
Loading
Failed to load commit list.
CI (Envoy) / Mobile/Android succeeded Apr 7, 2026 in 15m 15s

Mobile/Android (success)

Check has finished

Details

Check run finished (success ✔️)

The check run can be viewed here:

Mobile/Android (pr/44299/main@8eab6d2)

Check started by

Request (pr/44299/main@8eab6d2)

roll-no-21 @roll-no-21 8eab6d2 #44299 merge main@c33796b

http2: add keepalive observer hook

Expose HTTP/2 keepalive send and ack events through a small observer interface so higher layers can react to codec keepalives without coupling reverse-tunnel logic into the core codec.

Commit Message: http2: add keepalive observer hook
Additional Description:

Today, higher-level extensions (e.g. reverse tunnel lifecycle logging) that
need to react to HTTP/2 keepalive events must either parse raw HTTP/2 frames
from network filter buffers or embed extension-specific logic directly in the
codec. Both approaches are undesirable — the first is fragile and works against
Envoy's layered architecture, and the second couples domain logic into core
protocol code.

This PR introduces a lightweight KeepaliveObserver interface that the HTTP/2
codec queries via connection FilterState on keepalive ping send and ack events.
If no observer is registered, the codec does nothing (null-check only). If an
observer is present, it receives callbacks with the connection's StreamInfo
and the PING opaque data.

New interface (keepalive_observer.h)

KeepaliveObserver extends StreamInfo::FilterState::Object and defines:

  • onKeepalivePingSent(StreamInfo::StreamInfo&, uint64_t opaque_data) — called
    immediately after the codec submits an HTTP/2 PING frame via the adapter.
  • onKeepalivePingAck(StreamInfo::StreamInfo&, uint64_t opaque_data) — called
    when the codec receives a PING ACK frame from the peer.
  • pendingKeepalivePingId() — returns the opaque data of the outstanding PING,
    if any, allowing the observer to correlate send/ack pairs.

The observer is looked up from FilterState under the key
envoy.http2.keepalive_observer.

Codec changes (codec_impl.cc)

  • sendKeepalive(): after adapter_->SubmitPing(), checks for an observer
    and calls onKeepalivePingSent() (~3 lines added).
  • onPing() (ACK path): after logging the PING ACK, checks for an observer
    and calls onKeepalivePingAck() (~3 lines added).
  • New private helper keepaliveObserver() retrieves the observer from
    connection_.streamInfo().filterState() (~6 lines).

Usage pattern

An upstream network filter sets a KeepaliveObserver implementation in
the connection's FilterState during onNewConnection(). The codec picks it
up automatically on the next keepalive cycle. Example:

filter_state.setData(
    kKeepaliveObserverFilterStateKey,
    std::make_shared<MyObserverImpl>(),
    StreamInfo::FilterState::StateType::ReadOnly,
    StreamInfo::FilterState::LifeSpan::Connection);

No observer registered → zero overhead beyond a null-check per ping event.

Risk Level: Low — opt-in via FilterState; no behavioral change when no observer
  is registered. The null-check in the keepalive hot path is negligible.
Testing: New ConnectionKeepaliveObserver test in codec_impl_test.cc
  validates that onKeepalivePingSent and onKeepalivePingAck are called in
  correct sequence with proper timer interactions using a MockKeepaliveObserver.
Docs Changes: N/A
Release Notes: N/A
Platform Specific Features: N/A
Environment

Request variables

Key Value
ref 281511f
sha 8eab6d2
pr 44299
base-sha c33796b
actor roll-no-21 @roll-no-21
message http2: add keepalive observer hook...
started 1775548661.206039
target-branch main
trusted false
Build image

Container image/s (as used in this CI run)

Key Value
default docker.io/envoyproxy/envoy-build:86873047235e9b8232df989a5999b9bebf9db69c
mobile docker.io/envoyproxy/envoy-build:mobile-86873047235e9b8232df989a5999b9bebf9db69c
Version

Envoy version (as used in this CI run)

Key Value
major 1
minor 38
patch 0
dev true