Skip to content

fix(proxy): add max_header_list_size config for HTTP/2 client#4542

Open
wahajahmed010 wants to merge 1 commit into
linkerd:mainfrom
wahajahmed010:fix/15199-client-max-header-list-size
Open

fix(proxy): add max_header_list_size config for HTTP/2 client#4542
wahajahmed010 wants to merge 1 commit into
linkerd:mainfrom
wahajahmed010:fix/15199-client-max-header-list-size

Conversation

@wahajahmed010

Copy link
Copy Markdown

Summary

After the Hyper 0.14→1.x migration, the HTTP/2 client defaults to DEFAULT_MAX_HEADER_LIST_SIZE = 16 KiB, which is too restrictive for gRPC services with large trailers. The server side already has env var support for max_header_list_size, but the client side did not.

This PR adds client-side configurability via environment variables, following the same pattern already used for server-side h2 params.

Changes

linkerd/http/h2/src/lib.rs

  • Added max_header_list_size: Option<u32> to ClientParams struct
  • Included max_header_list_size in the override_from method

linkerd/app/src/env/http2.rs

  • Added parse_client() function for client-side h2 params parsing (mirrors parse_server())
  • Supports env vars for flow control, keep-alive, max frame size, max header list size, and max send buf size
  • Includes test coverage

linkerd/app/src/env.rs

  • Replaced inline h2::ClientParams construction in outbound/inbound connect configs with calls to http2::parse_client()
  • No behavioral change — same defaults when no env vars are set

linkerd/proxy/http/src/h2.rs

  • Destructure max_header_list_size from ClientParams
  • Apply via builder.max_header_list_size(sz) in the h2 connection setup

Environment Variables

Client params use the prefix LINKERD2_PROXY_OUTBOUND_CONNECT_HTTP2_ and LINKERD2_PROXY_INBOUND_CONNECT_HTTP2_:

  • *_MAX_HEADER_LIST_SIZE (e.g., 65536)
  • *_MAX_FRAME_SIZE
  • *_MAX_SEND_BUF_SIZE
  • *_KEEP_ALIVE_TIMEOUT / *_KEEP_ALIVE_INTERVAL / *_KEEP_ALIVE_WHILE_IDLE
  • *_ADAPTIVE_FLOW_CONTROL / *_INITIAL_STREAM_WINDOW_SIZE / *_INITIAL_CONNECTION_WINDOW_SIZE

Fixes linkerd/linkerd2#15199

Adds client-side configurability for the h2 max header list size,
which defaults to 16 KiB in Hyper 1.x and is insufficient for gRPC
services with large trailers.

Changes:
- linkerd/http/h2: Add max_header_list_size to ClientParams struct
  and include it in override_from method
- linkerd/app/env: Add http2::parse_client() for client params parsing
  and wire it to LINKERD2_PROXY_OUTBOUND_CONNECT_HTTP2_*
  and LINKERD2_PROXY_INBOUND_CONNECT_HTTP2_* env vars
- linkerd/proxy/http: Apply max_header_list_size in h2 Connect::call

Env vars (follow server pattern):
  *_MAX_HEADER_LIST_SIZE
  *_MAX_FRAME_SIZE
  *_MAX_SEND_BUF_SIZE
  *_KEEP_ALIVE_TIMEOUT / *_KEEP_ALIVE_INTERVAL / *_KEEP_ALIVE_WHILE_IDLE

Fixes linkerd/linkerd2#15199
@wahajahmed010 wahajahmed010 requested a review from a team as a code owner May 27, 2026 13:46
@cratelyn cratelyn self-assigned this May 27, 2026
@kostyay

kostyay commented May 27, 2026

Copy link
Copy Markdown

Thanks for this — the approach looks right, and I was able to validate it locally against the max-header repro (HTTP 22 KiB response headers + gRPC 22 KiB trailers) once the custom proxy was deployed with the new env vars set.

While building the branch, I hit a couple of issues that block a clean cargo build --release:

1. Missing field in api-resolve

Adding max_header_list_size to ClientParams breaks compilation in linkerd/proxy/api-resolve/src/pb.rs:

error[E0063]: missing field `max_header_list_size` in initializer of `ClientParams`
  --> linkerd/proxy/api-resolve/src/pb.rs:166:5

to_http2_client_params() needs to populate the new field (likely None until the proto/API surface exposes it).

2. Dead code / unused imports in env.rs

Switching inbound/outbound connect config to http2::parse_client() leaves behind unused code in linkerd/app/src/env.rs:

  • unused import: h2
  • unused variables: initial_stream_window_size, initial_connection_window_size
  • unused constants: ENV_INITIAL_STREAM_WINDOW_SIZE, ENV_INITIAL_CONNECTION_WINDOW_SIZE

These fail the build with -D warnings.

3. (Optional) Behavior change to call out

Previously, connect flow-control window sizes were driven by the global env vars:

LINKERD2_PROXY_HTTP2_INITIAL_STREAM_WINDOW_SIZE
LINKERD2_PROXY_HTTP2_INITIAL_CONNECTION_WINDOW_SIZE

After this change, connect-side settings only come from the prefixed vars:

LINKERD2_PROXY_OUTBOUND_CONNECT_HTTP2_*
LINKERD2_PROXY_INBOUND_CONNECT_HTTP2_*

If anyone was relying on the global vars for connect tuning, that would silently stop working. Worth documenting in the PR description or preserving backward compatibility.

@kostyay

kostyay commented May 27, 2026

Copy link
Copy Markdown

You can find a repo to test the issue here, with instructions on how to test it with this custom fixed branch:
https://github.com/kostyay/linkerd-issue-max-header-reproduction

@kostyay

kostyay commented May 31, 2026

Copy link
Copy Markdown

hi @cratelyn
will you have time to review/merge this?
would love for this to be merged as this bug had been around for a while now

@wahajahmed010 looks like the build is failing for the DCO action.

There is one commit incorrectly signed off. This means that the author of this commit failed to include a Signed-off-by line in the commit message.

Can you resolve that please?

@kostyay

kostyay commented Jun 9, 2026

Copy link
Copy Markdown

@cratelyn
bumping this again, would love to get your feedback on it

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.

linkerd-proxy mangles error code / message / trailer forwarding for sufficiently large messages

3 participants