Skip to content

SQL-522: serve HTTP/2 on environmentd and balancerd HTTP endpoints - #37913

Draft
jubrad wants to merge 4 commits into
MaterializeInc:mainfrom
jubrad:justin/sql-522-add-adapter-support-for-http2
Draft

SQL-522: serve HTTP/2 on environmentd and balancerd HTTP endpoints#37913
jubrad wants to merge 4 commits into
MaterializeInc:mainfrom
jubrad:justin/sql-522-add-adapter-support-for-http2

Conversation

@jubrad

@jubrad jubrad commented Jul 28, 2026

Copy link
Copy Markdown
Member

Summary

  • Negotiate HTTP/2 or HTTP/1.1 via TLS ALPN, serve both protocols with hyper's auto builder (h2c via preface sniffing on plaintext listeners)
  • HTTP/1.1-only clients are unaffected; pgwire listeners share the TLS context but pgwire clients do not send ALPN
  • balancerd remains a byte proxy for HTTPS; HTTP/2 frames pass through unchanged
  • Accept WebSockets over HTTP/2 (RFC 8441 extended CONNECT)
  • Updated dependencies: axum 0.8.9, tungstenite 0.29, hyper-util with server-auto feature

Test plan

  • cargo check passes
  • cargo fmt passes
  • CI tests pass (including new HTTP/2 tests: test_http2_tls, test_http2_cleartext, test_http2_websocket_auth)
  • balancerd tests pass

Closes https://linear.app/materializeinc/issue/SQL-522/add-adapter-support-for-http2

Supersedes #37541

🤖 Generated with Claude Code

jubrad and others added 4 commits July 28, 2026 14:10
Negotiate HTTP/2 or HTTP/1.1 via TLS ALPN and serve both protocols with
hyper's auto builder (h2c via preface sniffing on plaintext listeners).
HTTP/1.1-only clients are unaffected. pgwire listeners share the TLS
context, but pgwire clients do not send ALPN, so the negotiation
callback no-ops for them.

balancerd remains a byte proxy for HTTPS: HTTP/2 frames pass through to
environmentd unchanged. Its raw HTTP/1.1 502 fallback is now skipped
for clients that negotiated h2, and its internal HTTP server serves
h2c.

Also accept WebSockets over HTTP/2 (RFC 8441 extended CONNECT):
environmentd advertises SETTINGS_ENABLE_CONNECT_PROTOCOL and the
/api/experimental/sql route accepts CONNECT in addition to GET. This
requires axum >= 0.8.9, as 0.8.8 has a method-routing bug that
registers CONNECT handlers under OPTIONS. The axum bump pulls
tungstenite 0.29, which duplicates the 0.28 pinned by kube-client, so
deny.toml gains skip entries for the old versions.

reqwest gains the native-tls-alpn feature: without it the native-tls
backend never offers ALPN and clients silently stay on HTTP/1.1, which
the new tests would catch in CI.

Tests: HTTP/2 over TLS with HTTP/1.1 downgrade, h2c prior knowledge,
WebSocket authentication (valid and invalid credentials) over both
HTTP/2 and HTTP/1.1, and h2/h1.1/h2c assertions through balancerd.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
balancerd is a byte proxy: it terminates TLS and forwards the decrypted
stream to environmentd. If balancerd advertises HTTP/2 via ALPN before
environmentd supports it, clients negotiate h2 but environmentd receives
frames it cannot parse ("invalid HTTP version parsed (found HTTP2
preface)").

Add `balancerd_https_enable_http2_alpn` dyncfg (default false) to
control whether balancerd advertises h2. During upgrade:
1. Upgrade all environmentd instances (they now support h2)
2. Enable the dyncfg via LaunchDarkly
3. Restart balancerd instances (the dyncfg is read at startup)

Also add `enable_http2_alpn` parameter to `TlsCertConfig::load_context`
so callers can control ALPN advertisement. environmentd passes `true`
since it handles HTTP/2 directly via hyper's auto builder.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
bin/lint-test-flags requires every mz_dyncfg::Config to be known to
parallel-workload's FlipFlagsAction and to mzcompose's system parameter
lists. balancerd dyncfgs are not SQL-settable system parameters, so add
the new flag alongside the other balancerd_* entries in the
uninteresting lists.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The new balancerd_https_enable_http2_alpn dyncfg defaults off, which
broke the balancerd server test's assertion that a request through
balancerd comes back as HTTP/2.

Follow the convention that a new flag defaults off in production but on
in the test configuration: teach set_defaults to accept the flag so it
can be set via --default-config, and have the test enable it.

Also document that the flag is read once when the TLS context is built,
so a change only takes effect after balancerd restarts.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.

1 participant