Skip to content

xds: implement HeaderMutator to apply HTTP header mutations (A102)#9192

Open
mbissa wants to merge 4 commits into
grpc:masterfrom
mbissa:split-pr2-header-mutator
Open

xds: implement HeaderMutator to apply HTTP header mutations (A102)#9192
mbissa wants to merge 4 commits into
grpc:masterfrom
mbissa:split-pr2-header-mutator

Conversation

@mbissa

@mbissa mbissa commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Series Context

This is a standalone PR implementing the HTTP header mutation engine for
gRFC A102 (xDS GrpcService Support and Header Representations). It does not
depend on any other pending A102 PRs.

Overview

Implements the HeaderMutator engine in internal/xds/httpfilter/headers.go
as specified in gRFC A102. It evaluates Envoy's HeaderMutationRules against a
set of HeaderValueOption mutations and applies the allowed ones to gRPC
metadata. The mutator operates on a copy of the metadata and holds only
read-only compiled state, so it is safe for concurrent use — intended for the
side-channel call paths (e.g. ext_proc) that A102 enables.

Key Changes

  • Mutation rules: Constructed from HeaderMutationRules (compiled
    allow_expression / disallow_expression regexes), honoring disallow_all
    and disallow_is_error.
  • Security boundaries: Mutations to pseudo-headers (:-prefixed) and the
    host header are always blocked, regardless of control-plane settings. Other
    headers (including grpc-*) are subject to the allow/disallow rules.
  • Mutation actions: Supports all four append actions —
    APPEND_IF_EXISTS_OR_ADD, ADD_IF_ABSENT, OVERWRITE_IF_EXISTS_OR_ADD, and
    OVERWRITE_IF_EXISTS.
  • Value handling: raw_value takes precedence over the legacy value
    field; headers are retained even when a mutation yields an empty value
    (keep_empty_value is intentionally unsupported).

Testing

  • Table-driven unit tests in internal/xds/httpfilter/headers_test.go covering
    all four actions, empty-value retention, raw_value precedence, the
    always-protected headers, the allow/disallow rule combinations, and that the
    input metadata is left unmodified.

RELEASE NOTES: n/a

@mbissa mbissa added this to the 1.83 Release milestone Jun 23, 2026
@mbissa mbissa added the Type: Feature New features or improvements in behavior label Jun 23, 2026
@mbissa

mbissa commented Jun 23, 2026

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces HeaderMutator in internal/xds/httpfilter to compile and apply header mutations on gRPC metadata based on Envoy's mutation rules. The review feedback highlights a critical missing feature: according to gRFC A102, binary headers (keys ending in -bin) must be base64-decoded before being added to the metadata to prevent double-encoding. The reviewer suggests adding the necessary "encoding/base64" import, implementing the decoding logic to support both padded and unpadded base64 formats, and adding comprehensive test cases to verify this behavior.

Comment thread internal/xds/httpfilter/headers.go
Comment thread internal/xds/httpfilter/headers.go Outdated
Comment thread internal/xds/httpfilter/headers_test.go Outdated
@codecov

codecov Bot commented Jun 23, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.66667% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 83.22%. Comparing base (8d66cfc) to head (4d6c34a).
⚠️ Report is 8 commits behind head on master.

Files with missing lines Patch % Lines
internal/xds/httpfilter/headers.go 96.66% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #9192      +/-   ##
==========================================
- Coverage   83.27%   83.22%   -0.05%     
==========================================
  Files         419      420       +1     
  Lines       33863    33923      +60     
==========================================
+ Hits        28198    28234      +36     
- Misses       4252     4266      +14     
- Partials     1413     1423      +10     
Files with missing lines Coverage Δ
internal/xds/httpfilter/headers.go 96.66% <96.66%> (ø)

... and 26 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@mbissa

mbissa commented Jun 23, 2026

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces the HeaderMutator utility to apply header mutations on gRPC metadata based on Envoy's mutation rules. The feedback suggests two key improvements: first, correcting the raw_value precedence check to properly handle empty byte slices (and adding a test case for this scenario); second, optimizing the Mutate function using a copy-on-write pattern to prevent unnecessary metadata copies when no mutations are performed.

Comment thread internal/xds/httpfilter/headers.go
Comment thread internal/xds/httpfilter/headers_test.go
@mbissa

mbissa commented Jun 23, 2026

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces the HeaderMutator utility in internal/xds/httpfilter to apply header mutations on gRPC metadata based on Envoy's header mutation rules, utilizing a copy-on-write strategy to avoid unnecessary allocations. The feedback suggests optimizing the mutation loop by performing direct map operations on the metadata instead of calling helper methods, which avoids redundant lowercasing and slice allocations in the hot path.

Comment thread internal/xds/httpfilter/headers.go
@mbissa mbissa requested a review from easwars June 23, 2026 19:28
@mbissa mbissa changed the title xds: implement HeaderMutator to apply HTTP header mutations xds: implement HeaderMutator to apply HTTP header mutations (A102) Jun 23, 2026
@easwars easwars assigned eshitachandwani and unassigned easwars Jun 25, 2026
// with ":") or the "host" header, regardless of the configured mutation
// rules. Other headers (including "grpc-*") are subject to the
// allow/disallow rules below.
if strings.HasPrefix(key, ":") || key == "host" {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the gRFC says that headers with grpc- prefix are also invalid

[key](https://github.com/envoyproxy/envoy/blob/cdd19052348f7f6d85910605d957ba4fe0538aec/api/envoy/config/core/v3/base.proto#L404): The header name. When reading, the entry will be considered invalid if empty, if not all lower-case, if length exceeds 16384, if the key is host, if it starts with : or grpc-, or if it is not a valid gRPC header name.


if m.rules.AllowExpr != nil {
if !m.rules.AllowExpr.MatchString(key) {
if m.rules.DisallowIsError {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dont think we should check the DisallowIsError here , because the gRFC says that

[disallow_is_error]: If false, a disallowed header mutation will simply be ignored.

and what I understand from it is if a header mutation is under the Disallowed array , only then we fail.
But under current scenario if a header key is not part of either Allowed or Disallowed will also fail this check. WHich I think should not happen.
WDYT ?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the gRFC mentions check like if the key exceed 16384 length it is considered invalid. Where will these checks be added ?

}
}

func TestHeaderMutator_Mutate(t *testing.T) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should make these tests function of s

)

// hvo builds a HeaderValueOption with the legacy value field.
func hvo(key, val string, action v3corepb.HeaderValueOption_HeaderAppendAction) *v3corepb.HeaderValueOption {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we also have a test that uses values stored in RawValue

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Type: Feature New features or improvements in behavior

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants