Skip to content

health check: use the ALPN-negotiated protocol for HTTP health checks - #46903

Draft
zhaohuabing wants to merge 1 commit into
envoyproxy:mainfrom
zhaohuabing:health-check-alpn-negotiated-codec
Draft

health check: use the ALPN-negotiated protocol for HTTP health checks#46903
zhaohuabing wants to merge 1 commit into
envoyproxy:mainfrom
zhaohuabing:health-check-alpn-negotiated-codec

Conversation

@zhaohuabing

@zhaohuabing zhaohuabing commented Aug 24, 2026

Copy link
Copy Markdown
Member

HTTP health checks negotiate ALPN whenever TLS is in use, then discard the result and speak whatever codec_client_type says. That means heterogeneous and mid-migration upstreams cannot be health checked with a single configuration — a pinned codec marks every host of the other protocol unhealthy, and the codec cannot vary per host — and contradictory config such as codec_client_type: HTTP2 alongside tls_options.alpn_protocols: ["http/1.1"] loads cleanly and then fails forever with nothing in the logs pointing at ALPN.

This uses the negotiated protocol to select the health check codec (h2 → HTTP/2, http/1.1 → HTTP/1.1), and keeps codec_client_type as the value used when nothing is negotiated, i.e. plaintext health checks and peers that do not do ALPN. A negotiated protocol this health checker cannot speak also falls back to codec_client_type, so upstreams that route health checks by a custom ALPN identifier are unaffected. codec_client_type: HTTP3 is unchanged, and gRPC health checks are unaffected since gRPC requires HTTP/2.

The codec can only be chosen once the handshake has completed, so a TLS health check connection is now connected first and the codec client attached on the Connected event — the same handoff HttpConnPoolImplMixed already does on the data plane. Plaintext and HTTP/3 health checks keep creating the codec client up front, so their behavior is unchanged.

I used Claude Code to help write this change and its tests. I have reviewed and understand the code and take full ownership of it.

Commit Message: health check: use the ALPN-negotiated protocol for HTTP health checks
Additional Description: codec_client_type changes meaning from "the codec to use" to "the codec to use when ALPN negotiates nothing". Behavior only differs for TLS health checks whose peer negotiates a protocol other than the configured one — a case that fails today — so no working configuration changes.
Risk Level: Medium (changes how TLS health check connections are established; runtime guarded)
Testing: unit tests in test/common/upstream/health_checker_impl_test.cc covering negotiated h2/http1 against the opposite configured codec, no-ALPN and unknown-ALPN fallback, plaintext, HTTP/3, both runtime guard paths, and the pending-connection close/timeout/zero-RTT/teardown paths; ALPN-over-TLS integration tests in test/integration/health_check_integration_test.cc.
Docs Changes: updated the codec_client_type API comment.
Release Notes: added.
Platform Specific Features: no
Optional Runtime guard: envoy.reloadable_features.health_check_use_negotiated_protocol

Fixes #46848

@repokitteh-read-only

Copy link
Copy Markdown

As a reminder, PRs marked as draft will not be automatically assigned reviewers,
or be handled by maintainer-oncall triage.

Please mark your PR as ready when you want it to be reviewed!

🐱

Caused by: #46903 was opened by zhaohuabing.

see: more, trace.

@zhaohuabing
zhaohuabing force-pushed the health-check-alpn-negotiated-codec branch 4 times, most recently from 0fe64a9 to 3700377 Compare August 24, 2026 04:28
HTTP health checks negotiated ALPN whenever TLS was in use and then
discarded the result, always speaking whatever `codec_client_type` said.
That made heterogeneous and mid-migration upstreams impossible to health
check with a single configuration, and let contradictory config
(`codec_client_type: HTTP2` with `tls_options.alpn_protocols:
["http/1.1"]`) load cleanly and then fail forever.

Use the negotiated protocol to select the health check codec, with
`codec_client_type` retained as the value used when nothing is
negotiated, i.e. plaintext health checks and peers that do not do ALPN.
A protocol this health checker cannot speak also falls back to
`codec_client_type`, so upstreams that route health checks by a custom
ALPN identifier are unaffected.

The ALPN offered on health check connections is deliberately left
unchanged, so no configuration that works today changes behavior on the
wire.

Since the codec can only be chosen once the handshake has completed, a
TLS health check connection is now connected first and the codec client
attached on the Connected event, mirroring what `HttpConnPoolImplMixed`
already does on the data plane. Plaintext and HTTP/3 health checks keep
creating the codec client up front.

Guarded by
`envoy.reloadable_features.health_check_use_negotiated_protocol`.

Fixes envoyproxy#46848

Signed-off-by: Huabing (Robin) Zhao <zhaohuabing@gmail.com>
Signed-off-by: Huabing (Robin) Zhao <huabing@tetrate.io>
@zhaohuabing
zhaohuabing force-pushed the health-check-alpn-negotiated-codec branch from 3700377 to 614a933 Compare August 24, 2026 04:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

health check: use the ALPN-negotiated protocol for HTTP health checks

1 participant