Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions api/v1alpha1/shared_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -1052,6 +1052,20 @@ type CustomRedirect struct {
// multiple values for a header must use RFC 7230 header value formatting,
// separating each value with a comma.
type HTTPHeaderFilter struct {
// Mutations is an ordered list of header operations that are applied in
// exactly the order specified. Use this field when the sequence of
// operations matters, for example setting a header and then appending to
// it, or removing a header and then re-adding it.

@zhaohuabing zhaohuabing Jul 23, 2026

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.

Thanks for putting this up. Before we settle the shape, the two examples seem not justify the API.

Both examples are already covered by the existing API:

  • "set then append" → the result is just a final value: set: {value: "a,b"} does it in one op.
  • "remove then re-add" → net effect is "header ends up with exactly my value" — that's the definition of set (OVERWRITE_IF_EXISTS_OR_ADD).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Hello, @zhaohuabing . Good point. But note, although we may coalesce multiple values with , (only for inline headers). It's not means they have completely same semantics to for x: a,b and x: a x: b. Esp after we enabled the envoy.reloadable_features.match_headers_individually.

"remove then re-add" → net effect is "header ends up with exactly my value" — that's the definition of set (OVERWRITE_IF_EXISTS_OR_ADD).

When I see remove there, I mean to use remove or remove_on_match. It's very possible for a users to remove a list of headers (like all headers with x-custom- prefix) with remove_on_match and then add a specific one x-custom-specific.

//
// Mutations are always applied FIRST, in list order. The Set, Add,
// AddIfAbsent, Remove and RemoveOnMatch fields below are then applied after
// the mutations, preserving their existing ordering (Add, then Set, then
// AddIfAbsent, then Remove, then RemoveOnMatch).
//
// +optional
// +kubebuilder:validation:MaxItems=64
Mutations []HTTPHeaderMutation `json:"mutations,omitempty"`
Comment on lines +1065 to +1067

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Wire mutations into ClientTrafficPolicy translation

When a ClientTrafficPolicy uses earlyRequestHeaders or lateResponseHeaders with only the new mutations field, the CRD accepts it (and the added CEL test treats it as valid), but the controller still calls translateHeaderModifier, which only reads Add, Set, AddIfAbsent, Remove, and RemoveOnMatch and then reports did not provide valid configuration when those legacy slices are empty (internal/gatewayapi/clienttrafficpolicy.go:1339-1512). In that scenario the newly documented ordered operations never reach the IR/xDS and the policy is rejected or has no header effect, so this API needs corresponding translation/status/testdata support before it is exposed.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This is for API only discussion.

Comment on lines +1055 to +1067

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Add a release-note fragment for the new API

This commit exposes a new user-facing ClientTrafficPolicy header-mutation API and updates the generated docs/CRDs, but it does not add any release-notes/current/new_features/... fragment. The repository's release-note workflow expects notable new capabilities and existing API changes to be documented there (release-notes/current/README.md:8-25), so this feature can otherwise ship without appearing in the next release notes.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This is API only for discussion.


// Set overwrites the request with the given header (name, value)
// before the action.
//
Expand Down Expand Up @@ -1156,6 +1170,75 @@ type HTTPHeaderFilter struct {
RemoveOnMatch []StringMatch `json:"removeOnMatch,omitempty"`
}

// HTTPHeaderMutation defines a single header mutation operation.
//
// +kubebuilder:validation:MaxProperties=1
// +kubebuilder:validation:MinProperties=1
type HTTPHeaderMutation struct {
// Write adds or modifies a header using the specified action.
//
// +optional
Write *HTTPHeaderWrite `json:"write,omitempty"`

// Remove removes the named header if it exists. Header names are
// case-insensitive.
//
// +optional
Remove *string `json:"remove,omitempty"`

// RemoveOnMatch removes every header whose name matches the specified string
// matcher. Matching is performed on the header name (case-insensitive).
//
// +optional
RemoveOnMatch *StringMatch `json:"removeOnMatch,omitempty"`
}

// HTTPHeaderWrite defines a header to write and how it should be applied when a
// header with the same name already exists. It mirrors Envoy's
// core.v3.HeaderValueOption.
type HTTPHeaderWrite struct {
// Header is the header name and value to write.
Header gwapiv1.HTTPHeader `json:"header"`

// Action controls how the header value is written when a header with the
// same name already exists. Defaults to Append.
//
// +optional
// +kubebuilder:default=Append
Action HeaderWriteAction `json:"action,omitempty"`

// KeepEmptyValue controls whether a header with an empty value is kept.
// When unset, an empty value is kept only if the provided value is empty.
//
// +optional
KeepEmptyValue *bool `json:"keepEmptyValue,omitempty"`
}

// HeaderWriteAction controls how a header value is written when a header with
// the same name already exists. The values mirror Envoy's
// HeaderValueOption.HeaderAppendAction.
//
// +kubebuilder:validation:Enum=Append;Overwrite;AddIfAbsent;OverwriteIfExists
type HeaderWriteAction string

const (
// HeaderWriteAppend appends the value if the header exists, or adds the
// header otherwise. (Envoy: APPEND_IF_EXISTS_OR_ADD)
HeaderWriteAppend HeaderWriteAction = "Append"

// HeaderWriteOverwrite overwrites the value if the header exists, or adds
// the header otherwise. (Envoy: OVERWRITE_IF_EXISTS_OR_ADD)
HeaderWriteOverwrite HeaderWriteAction = "Overwrite"

// HeaderWriteAddIfAbsent adds the header only if it is not already present.
// (Envoy: ADD_IF_ABSENT)
HeaderWriteAddIfAbsent HeaderWriteAction = "AddIfAbsent"

// HeaderWriteOverwriteIfExists overwrites the value only if the header is
// already present, and does nothing otherwise. (Envoy: OVERWRITE_IF_EXISTS)
HeaderWriteOverwriteIfExists HeaderWriteAction = "OverwriteIfExists"
)

// LocalObjectKeyReference selects a key from a local object.
type LocalObjectKeyReference struct {
// The local object to select from.
Expand Down
58 changes: 58 additions & 0 deletions api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,112 @@ spec:
x-kubernetes-list-map-keys:
- name
x-kubernetes-list-type: map
mutations:
description: |-
Mutations is an ordered list of header operations that are applied in
exactly the order specified. Use this field when the sequence of
operations matters, for example setting a header and then appending to
it, or removing a header and then re-adding it.

Mutations are always applied FIRST, in list order. The Set, Add,
AddIfAbsent, Remove and RemoveOnMatch fields below are then applied after
the mutations, preserving their existing ordering (Add, then Set, then
AddIfAbsent, then Remove, then RemoveOnMatch).
items:
description: HTTPHeaderMutation defines a single header
mutation operation.
maxProperties: 1
minProperties: 1
properties:
remove:
description: |-
Remove removes the named header if it exists. Header names are
case-insensitive.
type: string
removeOnMatch:
description: |-
RemoveOnMatch removes every header whose name matches the specified string
matcher. Matching is performed on the header name (case-insensitive).
properties:
type:
default: Exact
description: Type specifies how to match against
a string.
enum:
- Exact
- Prefix
- Suffix
- RegularExpression
type: string
value:
description: Value specifies the string value that
the match must have.
maxLength: 1024
minLength: 1
type: string
required:
- value
type: object
write:
description: Write adds or modifies a header using the
specified action.
properties:
action:
default: Append
description: |-
Action controls how the header value is written when a header with the
same name already exists. Defaults to Append.
enum:
- Append
- Overwrite
- AddIfAbsent
- OverwriteIfExists
type: string
header:
description: Header is the header name and value
to write.
properties:
name:
description: |-
Name is the name of the HTTP Header to be matched. Name matching MUST be
case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2).

If multiple entries specify equivalent header names, the first entry with
an equivalent name MUST be considered for a match. Subsequent entries
with an equivalent header name MUST be ignored. Due to the
case-insensitivity of header names, "foo" and "Foo" are considered
equivalent.
maxLength: 256
minLength: 1
pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$
type: string
value:
description: |-
Value is the value of HTTP Header to be matched.
<gateway:experimental:description>
Must consist of printable US-ASCII characters, optionally separated
by single tabs or spaces. See: https://tools.ietf.org/html/rfc7230#section-3.2
</gateway:experimental:description>

<gateway:experimental:validation:Pattern=`^[!-~]+([\t ]?[!-~]+)*$`>
maxLength: 4096
minLength: 1
type: string
required:
- name
- value
type: object
keepEmptyValue:
description: |-
KeepEmptyValue controls whether a header with an empty value is kept.
When unset, an empty value is kept only if the provided value is empty.
type: boolean
required:
- header
type: object
type: object
maxItems: 64
type: array
remove:
description: |-
Remove the given header(s) from the HTTP request before the action. The
Expand Down Expand Up @@ -634,6 +740,112 @@ spec:
x-kubernetes-list-map-keys:
- name
x-kubernetes-list-type: map
mutations:
description: |-
Mutations is an ordered list of header operations that are applied in
exactly the order specified. Use this field when the sequence of
operations matters, for example setting a header and then appending to
it, or removing a header and then re-adding it.

Mutations are always applied FIRST, in list order. The Set, Add,
AddIfAbsent, Remove and RemoveOnMatch fields below are then applied after
the mutations, preserving their existing ordering (Add, then Set, then
AddIfAbsent, then Remove, then RemoveOnMatch).
items:
description: HTTPHeaderMutation defines a single header
mutation operation.
maxProperties: 1
minProperties: 1
properties:
remove:
description: |-
Remove removes the named header if it exists. Header names are
case-insensitive.
type: string
removeOnMatch:
description: |-
RemoveOnMatch removes every header whose name matches the specified string
matcher. Matching is performed on the header name (case-insensitive).
properties:
type:
default: Exact
description: Type specifies how to match against
a string.
enum:
- Exact
- Prefix
- Suffix
- RegularExpression
type: string
value:
description: Value specifies the string value that
the match must have.
maxLength: 1024
minLength: 1
type: string
required:
- value
type: object
write:
description: Write adds or modifies a header using the
specified action.
properties:
action:
default: Append
description: |-
Action controls how the header value is written when a header with the
same name already exists. Defaults to Append.
enum:
- Append
- Overwrite
- AddIfAbsent
- OverwriteIfExists
type: string
header:
description: Header is the header name and value
to write.
properties:
name:
description: |-
Name is the name of the HTTP Header to be matched. Name matching MUST be
case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2).

If multiple entries specify equivalent header names, the first entry with
an equivalent name MUST be considered for a match. Subsequent entries
with an equivalent header name MUST be ignored. Due to the
case-insensitivity of header names, "foo" and "Foo" are considered
equivalent.
maxLength: 256
minLength: 1
pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$
type: string
value:
description: |-
Value is the value of HTTP Header to be matched.
<gateway:experimental:description>
Must consist of printable US-ASCII characters, optionally separated
by single tabs or spaces. See: https://tools.ietf.org/html/rfc7230#section-3.2
</gateway:experimental:description>

<gateway:experimental:validation:Pattern=`^[!-~]+([\t ]?[!-~]+)*$`>
maxLength: 4096
minLength: 1
type: string
required:
- name
- value
type: object
keepEmptyValue:
description: |-
KeepEmptyValue controls whether a header with an empty value is kept.
When unset, an empty value is kept only if the provided value is empty.
type: boolean
required:
- header
type: object
type: object
maxItems: 64
type: array
remove:
description: |-
Remove the given header(s) from the HTTP request before the action. The
Expand Down
Loading