diff --git a/api/v1alpha1/httproutefilter_types.go b/api/v1alpha1/httproutefilter_types.go
index 9ae8be59842..3259fabc8f4 100644
--- a/api/v1alpha1/httproutefilter_types.go
+++ b/api/v1alpha1/httproutefilter_types.go
@@ -43,7 +43,6 @@ type HTTPURLRewriteFilter struct {
// forwarding.
//
// +optional
- // +notImplementedHide
Hostname *HTTPHostnameModifier `json:"hostname,omitempty"`
// Path defines a path rewrite.
//
@@ -83,12 +82,12 @@ const (
type HTTPHostnameModifierType string
const (
- // HeaderHTTPHostnameModifier indicates that the Host header value would be replaced with the value of the header specified in setFromHeader.
+ // HeaderHTTPHostnameModifier indicates that the Host header value would be replaced with the value of the header specified in header.
// https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#envoy-v3-api-field-config-route-v3-routeaction-host-rewrite-header
- HeaderHTTPHostnameModifier HTTPHostnameModifierType = "SetFromHeader"
+ HeaderHTTPHostnameModifier HTTPHostnameModifierType = "Header"
// BackendHTTPHostnameModifier indicates that the Host header value would be replaced by the DNS name of the backend if it exists.
// https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#envoy-v3-api-field-config-route-v3-routeaction-auto-host-rewrite
- BackendHTTPHostnameModifier HTTPHostnameModifierType = "SetFromBackend"
+ BackendHTTPHostnameModifier HTTPHostnameModifierType = "Backend"
)
type ReplaceRegexMatch struct {
@@ -129,16 +128,16 @@ type HTTPPathModifier struct {
ReplaceRegexMatch *ReplaceRegexMatch `json:"replaceRegexMatch,omitempty"`
}
-// +kubebuilder:validation:XValidation:message="setFromHeader must be nil if the type is not SetFromHeader",rule="!(has(self.setFromHeader) && self.type != 'SetFromHeader')"
-// +kubebuilder:validation:XValidation:message="setFromHeader must be specified for SetFromHeader type",rule="!(!has(self.setFromHeader) && self.type == 'SetFromHeader')"
+// +kubebuilder:validation:XValidation:message="header must be nil if the type is not Header",rule="!(has(self.header) && self.type != 'Header')"
+// +kubebuilder:validation:XValidation:message="header must be specified for Header type",rule="!(!has(self.header) && self.type == 'Header')"
type HTTPHostnameModifier struct {
- // +kubebuilder:validation:Enum=SetFromHeader;SetFromBackend
+ // +kubebuilder:validation:Enum=Header;Backend
// +kubebuilder:validation:Required
Type HTTPHostnameModifierType `json:"type"`
- // SetFromHeader is the name of the header whose value would be used to rewrite the Host header
+ // Header is the name of the header whose value would be used to rewrite the Host header
// +optional
- SetFromHeader *string `json:"setFromHeader,omitempty"`
+ Header *string `json:"header,omitempty"`
}
//+kubebuilder:object:root=true
diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go
index c225d65d39e..f2cf9072fa6 100644
--- a/api/v1alpha1/zz_generated.deepcopy.go
+++ b/api/v1alpha1/zz_generated.deepcopy.go
@@ -2747,8 +2747,8 @@ func (in *HTTPExtAuthService) DeepCopy() *HTTPExtAuthService {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *HTTPHostnameModifier) DeepCopyInto(out *HTTPHostnameModifier) {
*out = *in
- if in.SetFromHeader != nil {
- in, out := &in.SetFromHeader, &out.SetFromHeader
+ if in.Header != nil {
+ in, out := &in.Header, &out.Header
*out = new(string)
**out = **in
}
diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_httproutefilters.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_httproutefilters.yaml
index 672cfb59df8..195bf24ece8 100644
--- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_httproutefilters.yaml
+++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_httproutefilters.yaml
@@ -137,25 +137,25 @@ spec:
Hostname is the value to be used to replace the Host header value during
forwarding.
properties:
- setFromHeader:
- description: SetFromHeader is the name of the header whose
- value would be used to rewrite the Host header
+ header:
+ description: Header is the name of the header whose value
+ would be used to rewrite the Host header
type: string
type:
description: HTTPPathModifierType defines the type of Hostname
rewrite.
enum:
- - SetFromHeader
- - SetFromBackend
+ - Header
+ - Backend
type: string
required:
- type
type: object
x-kubernetes-validations:
- - message: setFromHeader must be nil if the type is not SetFromHeader
- rule: '!(has(self.setFromHeader) && self.type != ''SetFromHeader'')'
- - message: setFromHeader must be specified for SetFromHeader type
- rule: '!(!has(self.setFromHeader) && self.type == ''SetFromHeader'')'
+ - message: header must be nil if the type is not Header
+ rule: '!(has(self.header) && self.type != ''Header'')'
+ - message: header must be specified for Header type
+ rule: '!(!has(self.header) && self.type == ''Header'')'
path:
description: Path defines a path rewrite.
properties:
diff --git a/internal/gatewayapi/filters.go b/internal/gatewayapi/filters.go
index 41acc4c76ba..e969b7365fc 100644
--- a/internal/gatewayapi/filters.go
+++ b/internal/gatewayapi/filters.go
@@ -11,6 +11,7 @@ import (
"strings"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/utils/ptr"
gwapiv1 "sigs.k8s.io/gateway-api/apis/v1"
egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1"
@@ -147,14 +148,46 @@ func (t *Translator) ProcessGRPCFilters(parentRef *RouteParentContext,
return httpFiltersContext
}
+// Checks if the context and the rewrite both contain a core gw-api HTTP URL rewrite
+func hasMultipleCoreRewrites(rewrite *gwapiv1.HTTPURLRewriteFilter, contextRewrite *ir.URLRewrite) bool {
+ contextHasCoreRewrites := contextRewrite.Path != nil && (contextRewrite.Path.FullReplace != nil ||
+ contextRewrite.Path.PrefixMatchReplace != nil) || (contextRewrite.Host != nil && contextRewrite.Host.Name != nil)
+ rewriteHasCoreRewrites := rewrite.Hostname != nil || rewrite.Path != nil
+ return contextHasCoreRewrites && rewriteHasCoreRewrites
+}
+
+// Checks if the context and the rewrite both contain a envoy-gateway extended HTTP URL rewrite
+func hasMultipleExtensionRewrites(rewrite *egv1a1.HTTPURLRewriteFilter, contextRewrite *ir.URLRewrite) bool {
+ contextHasExtensionRewrites := (contextRewrite.Path != nil && contextRewrite.Path.RegexMatchReplace != nil) ||
+ (contextRewrite.Host != nil && (contextRewrite.Host.Header != nil || contextRewrite.Host.Backend != nil))
+
+ return contextHasExtensionRewrites && (rewrite.Hostname != nil || rewrite.Path != nil)
+}
+
+// Checks if the context and the gw-api core rewrite both contain an HTTP URL rewrite that creates a conflict (e.g. both rewrite path)
+func hasConflictingCoreAndExtensionRewrites(rewrite *gwapiv1.HTTPURLRewriteFilter, contextRewrite *ir.URLRewrite) bool {
+ contextHasExtensionPathRewrites := contextRewrite.Path != nil && contextRewrite.Path.RegexMatchReplace != nil
+ contextHasExtensionHostRewrites := contextRewrite.Host != nil && (contextRewrite.Host.Header != nil ||
+ contextRewrite.Host.Backend != nil)
+ return (rewrite.Hostname != nil && contextHasExtensionHostRewrites) || (rewrite.Path != nil && contextHasExtensionPathRewrites)
+}
+
+// Checks if the context and the envoy-gateway extended rewrite both contain an HTTP URL rewrite that creates a conflict (e.g. both rewrite path)
+func hasConflictingExtensionAndCoreRewrites(rewrite *egv1a1.HTTPURLRewriteFilter, contextRewrite *ir.URLRewrite) bool {
+ contextHasCorePathRewrites := contextRewrite.Path != nil && (contextRewrite.Path.FullReplace != nil ||
+ contextRewrite.Path.PrefixMatchReplace != nil)
+ contextHasCoreHostnameRewrites := contextRewrite.Host != nil && contextRewrite.Host.Name != nil
+
+ return (rewrite.Hostname != nil && contextHasCoreHostnameRewrites) || (rewrite.Path != nil && contextHasCorePathRewrites)
+}
+
func (t *Translator) processURLRewriteFilter(
rewrite *gwapiv1.HTTPURLRewriteFilter,
filterContext *HTTPFiltersContext,
) {
if filterContext.URLRewrite != nil {
- if filterContext.URLRewrite.Hostname != nil ||
- filterContext.URLRewrite.Path.FullReplace != nil ||
- filterContext.URLRewrite.Path.PrefixMatchReplace != nil {
+ if hasMultipleCoreRewrites(rewrite, filterContext.URLRewrite) ||
+ hasConflictingCoreAndExtensionRewrites(rewrite, filterContext.URLRewrite) {
routeStatus := GetRouteStatus(filterContext.Route)
status.SetRouteStatusCondition(routeStatus,
filterContext.ParentRef.routeParentStatusIdx,
@@ -188,7 +221,9 @@ func (t *Translator) processURLRewriteFilter(
return
}
redirectHost := string(*rewrite.Hostname)
- newURLRewrite.Hostname = &redirectHost
+ newURLRewrite.Host = &ir.HTTPHostModifier{
+ Name: &redirectHost,
+ }
}
if rewrite.Path != nil {
@@ -751,48 +786,12 @@ func (t *Translator) processExtensionRefHTTPFilter(extFilter *gwapiv1.LocalObjec
if string(extFilter.Kind) == egv1a1.KindHTTPRouteFilter {
for _, hrf := range resources.HTTPRouteFilters {
- if hrf.Namespace == filterNs && hrf.Name == string(extFilter.Name) &&
- hrf.Spec.URLRewrite.Path.Type == egv1a1.RegexHTTPPathModifier {
-
- if hrf.Spec.URLRewrite.Path.ReplaceRegexMatch == nil ||
- hrf.Spec.URLRewrite.Path.ReplaceRegexMatch.Pattern == "" {
- errMsg := "ReplaceRegexMatch Pattern must be set when rewrite path type is \"ReplaceRegexMatch\""
- routeStatus := GetRouteStatus(filterContext.Route)
- status.SetRouteStatusCondition(routeStatus,
- filterContext.ParentRef.routeParentStatusIdx,
- filterContext.Route.GetGeneration(),
- gwapiv1.RouteConditionAccepted,
- metav1.ConditionFalse,
- gwapiv1.RouteReasonUnsupportedValue,
- errMsg,
- )
- return
- } else if _, err := regexp.Compile(hrf.Spec.URLRewrite.Path.ReplaceRegexMatch.Pattern); err != nil {
- // Avoid envoy NACKs due to invalid regex.
- // Golang's regexp is almost identical to RE2: https://pkg.go.dev/regexp/syntax
- errMsg := "ReplaceRegexMatch must be a valid RE2 regular expression"
- routeStatus := GetRouteStatus(filterContext.Route)
- status.SetRouteStatusCondition(routeStatus,
- filterContext.ParentRef.routeParentStatusIdx,
- filterContext.Route.GetGeneration(),
- gwapiv1.RouteConditionAccepted,
- metav1.ConditionFalse,
- gwapiv1.RouteReasonUnsupportedValue,
- errMsg,
- )
- return
- }
-
- rmr := &ir.RegexMatchReplace{
- Pattern: hrf.Spec.URLRewrite.Path.ReplaceRegexMatch.Pattern,
- Substitution: hrf.Spec.URLRewrite.Path.ReplaceRegexMatch.Substitution,
- }
+ if hrf.Namespace == filterNs && hrf.Name == string(extFilter.Name) {
+ if hrf.Spec.URLRewrite != nil {
- if filterContext.HTTPFilterIR.URLRewrite != nil {
- // If path IR is already set - check for a conflict
- if filterContext.HTTPFilterIR.URLRewrite.Path != nil {
- path := filterContext.HTTPFilterIR.URLRewrite.Path
- if path.RegexMatchReplace != nil || path.PrefixMatchReplace != nil || path.FullReplace != nil {
+ if filterContext.URLRewrite != nil {
+ if hasMultipleExtensionRewrites(hrf.Spec.URLRewrite, filterContext.URLRewrite) ||
+ hasConflictingExtensionAndCoreRewrites(hrf.Spec.URLRewrite, filterContext.URLRewrite) {
routeStatus := GetRouteStatus(filterContext.Route)
status.SetRouteStatusCondition(routeStatus,
filterContext.ParentRef.routeParentStatusIdx,
@@ -804,19 +803,100 @@ func (t *Translator) processExtensionRefHTTPFilter(extFilter *gwapiv1.LocalObjec
)
return
}
- } else { // no path
- filterContext.HTTPFilterIR.URLRewrite.Path = &ir.ExtendedHTTPPathModifier{
- RegexMatchReplace: rmr,
+ }
+
+ if hrf.Spec.URLRewrite.Path != nil {
+ if hrf.Spec.URLRewrite.Path.Type == egv1a1.RegexHTTPPathModifier {
+ if hrf.Spec.URLRewrite.Path.ReplaceRegexMatch == nil ||
+ hrf.Spec.URLRewrite.Path.ReplaceRegexMatch.Pattern == "" {
+ errMsg := "ReplaceRegexMatch Pattern must be set when rewrite path type is \"ReplaceRegexMatch\""
+ routeStatus := GetRouteStatus(filterContext.Route)
+ status.SetRouteStatusCondition(routeStatus,
+ filterContext.ParentRef.routeParentStatusIdx,
+ filterContext.Route.GetGeneration(),
+ gwapiv1.RouteConditionAccepted,
+ metav1.ConditionFalse,
+ gwapiv1.RouteReasonUnsupportedValue,
+ errMsg,
+ )
+ return
+ } else if _, err := regexp.Compile(hrf.Spec.URLRewrite.Path.ReplaceRegexMatch.Pattern); err != nil {
+ // Avoid envoy NACKs due to invalid regex.
+ // Golang's regexp is almost identical to RE2: https://pkg.go.dev/regexp/syntax
+ errMsg := "ReplaceRegexMatch must be a valid RE2 regular expression"
+ routeStatus := GetRouteStatus(filterContext.Route)
+ status.SetRouteStatusCondition(routeStatus,
+ filterContext.ParentRef.routeParentStatusIdx,
+ filterContext.Route.GetGeneration(),
+ gwapiv1.RouteConditionAccepted,
+ metav1.ConditionFalse,
+ gwapiv1.RouteReasonUnsupportedValue,
+ errMsg,
+ )
+ return
+ }
+
+ rmr := &ir.RegexMatchReplace{
+ Pattern: hrf.Spec.URLRewrite.Path.ReplaceRegexMatch.Pattern,
+ Substitution: hrf.Spec.URLRewrite.Path.ReplaceRegexMatch.Substitution,
+ }
+
+ if filterContext.HTTPFilterIR.URLRewrite != nil {
+ if filterContext.HTTPFilterIR.URLRewrite.Path == nil {
+ filterContext.HTTPFilterIR.URLRewrite.Path = &ir.ExtendedHTTPPathModifier{
+ RegexMatchReplace: rmr,
+ }
+ return
+ }
+ } else { // no url rewrite
+ filterContext.HTTPFilterIR.URLRewrite = &ir.URLRewrite{
+ Path: &ir.ExtendedHTTPPathModifier{
+ RegexMatchReplace: rmr,
+ },
+ }
+ return
+ }
}
- return
}
- } else { // no url rewrite
- filterContext.HTTPFilterIR.URLRewrite = &ir.URLRewrite{
- Path: &ir.ExtendedHTTPPathModifier{
- RegexMatchReplace: rmr,
- },
+
+ if hrf.Spec.URLRewrite.Hostname != nil {
+ var hm *ir.HTTPHostModifier
+ if hrf.Spec.URLRewrite.Hostname.Type == egv1a1.HeaderHTTPHostnameModifier {
+ if hrf.Spec.URLRewrite.Hostname.Header == nil {
+ errMsg := "Header must be set when rewrite path type is \"Header\""
+ routeStatus := GetRouteStatus(filterContext.Route)
+ status.SetRouteStatusCondition(routeStatus,
+ filterContext.ParentRef.routeParentStatusIdx,
+ filterContext.Route.GetGeneration(),
+ gwapiv1.RouteConditionAccepted,
+ metav1.ConditionFalse,
+ gwapiv1.RouteReasonUnsupportedValue,
+ errMsg,
+ )
+ return
+ }
+ hm = &ir.HTTPHostModifier{
+ Header: hrf.Spec.URLRewrite.Hostname.Header,
+ }
+ } else if hrf.Spec.URLRewrite.Hostname.Type == egv1a1.BackendHTTPHostnameModifier {
+ hm = &ir.HTTPHostModifier{
+ Backend: ptr.To(true),
+ }
+ }
+
+ if filterContext.HTTPFilterIR.URLRewrite != nil {
+ if filterContext.HTTPFilterIR.URLRewrite.Host == nil {
+ filterContext.HTTPFilterIR.URLRewrite.Host = hm
+ return
+ }
+ } else { // no url rewrite
+ filterContext.HTTPFilterIR.URLRewrite = &ir.URLRewrite{
+ Host: hm,
+ }
+ return
+ }
}
- return
+
}
}
}
diff --git a/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-hostname-prefix-replace.out.yaml b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-hostname-prefix-replace.out.yaml
index 1577ab27e64..8e3079c9bbe 100644
--- a/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-hostname-prefix-replace.out.yaml
+++ b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-hostname-prefix-replace.out.yaml
@@ -144,7 +144,8 @@ xdsIR:
name: ""
prefix: /
urlRewrite:
- hostname: rewrite.com
+ host:
+ name: rewrite.com
path:
fullReplace: null
prefixMatchReplace: /rewrite
diff --git a/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-hostname.out.yaml b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-hostname.out.yaml
index 658725825f3..c0d8cce8b8a 100644
--- a/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-hostname.out.yaml
+++ b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-hostname.out.yaml
@@ -141,4 +141,5 @@ xdsIR:
name: ""
prefix: /
urlRewrite:
- hostname: rewrite.com
+ host:
+ name: rewrite.com
diff --git a/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-invalid-filter-type.out.yaml b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-invalid-filter-type.out.yaml
index d0d62e98a27..7cbff74f25b 100644
--- a/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-invalid-filter-type.out.yaml
+++ b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-invalid-filter-type.out.yaml
@@ -141,4 +141,5 @@ xdsIR:
name: ""
prefix: /
urlRewrite:
- hostname: urlrewrite.envoyproxy.io
+ host:
+ name: urlrewrite.envoyproxy.io
diff --git a/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-regex-match-replace-http.out.yaml b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-regex-match-replace-http.out.yaml
index 5c8d2527a1f..c42f3934568 100644
--- a/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-regex-match-replace-http.out.yaml
+++ b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-regex-match-replace-http.out.yaml
@@ -308,7 +308,8 @@ xdsIR:
name: ""
prefix: /host-and-regex-path
urlRewrite:
- hostname: rewrite.com
+ host:
+ name: rewrite.com
path:
fullReplace: null
prefixMatchReplace: null
@@ -336,7 +337,8 @@ xdsIR:
name: ""
prefix: /regex-path-and-host
urlRewrite:
- hostname: rewrite.com
+ host:
+ name: rewrite.com
- destination:
name: httproute/default/httproute-1/rule/0
settings:
diff --git a/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-regex-match-replace-invalid.out.yaml b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-regex-match-replace-invalid.out.yaml
index fb9e85a632d..17ffc680f52 100644
--- a/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-regex-match-replace-invalid.out.yaml
+++ b/internal/gatewayapi/testdata/httproute-with-urlrewrite-filter-regex-match-replace-invalid.out.yaml
@@ -164,9 +164,9 @@ httpRoutes:
parents:
- conditions:
- lastTransitionTime: null
- message: Route is accepted
- reason: Accepted
- status: "True"
+ message: Cannot configure multiple urlRewrite filters for a single HTTPRouteRule
+ reason: UnsupportedValue
+ status: "False"
type: Accepted
- lastTransitionTime: null
message: Resolved all the Object references for the Route
@@ -356,28 +356,3 @@ xdsIR:
escapedSlashesAction: UnescapeAndRedirect
mergeSlashes: true
port: 10080
- routes:
- - destination:
- name: httproute/default/httproute-multiple-path-rewrites-1/rule/0
- settings:
- - addressType: IP
- endpoints:
- - host: 7.7.7.7
- port: 8080
- protocol: HTTP
- weight: 1
- hostname: gateway.envoyproxy.io
- isHTTP2: false
- metadata:
- kind: HTTPRoute
- name: httproute-multiple-path-rewrites-1
- namespace: default
- name: httproute/default/httproute-multiple-path-rewrites-1/rule/0/match/0/gateway_envoyproxy_io
- pathMatch:
- distinct: false
- name: ""
- prefix: /ext-first
- urlRewrite:
- path:
- fullReplace: null
- prefixMatchReplace: /rewrite
diff --git a/internal/gatewayapi/testdata/httproute-with-urlrewrite-hostname-filter-invalid.in.yaml b/internal/gatewayapi/testdata/httproute-with-urlrewrite-hostname-filter-invalid.in.yaml
new file mode 100644
index 00000000000..5a7f4499048
--- /dev/null
+++ b/internal/gatewayapi/testdata/httproute-with-urlrewrite-hostname-filter-invalid.in.yaml
@@ -0,0 +1,236 @@
+gateways:
+ - apiVersion: gateway.networking.k8s.io/v1
+ kind: Gateway
+ metadata:
+ namespace: envoy-gateway
+ name: gateway-1
+ spec:
+ gatewayClassName: envoy-gateway-class
+ listeners:
+ - name: http
+ protocol: HTTP
+ port: 80
+ hostname: "*.envoyproxy.io"
+ allowedRoutes:
+ namespaces:
+ from: All
+httpRoutes:
+ - apiVersion: gateway.networking.k8s.io/v1
+ kind: HTTPRoute
+ metadata:
+ namespace: default
+ name: httproute-invalid-header
+ spec:
+ hostnames:
+ - gateway.envoyproxy.io
+ parentRefs:
+ - namespace: envoy-gateway
+ name: gateway-1
+ sectionName: http
+ rules:
+ - matches:
+ - path:
+ value: "/invalid-header"
+ backendRefs:
+ - name: service-1
+ port: 8080
+ filters:
+ - type: ExtensionRef
+ extensionRef:
+ group: gateway.envoyproxy.io
+ kind: HTTPRouteFilter
+ name: invalid-header
+ - apiVersion: gateway.networking.k8s.io/v1
+ kind: HTTPRoute
+ metadata:
+ namespace: default
+ name: httproute-multiple-host-rewrites-1
+ spec:
+ hostnames:
+ - gateway.envoyproxy.io
+ parentRefs:
+ - namespace: envoy-gateway
+ name: gateway-1
+ sectionName: http
+ rules:
+ - matches:
+ - path:
+ value: "/ext-first"
+ backendRefs:
+ - name: service-1
+ port: 8080
+ filters:
+ - type: ExtensionRef
+ extensionRef:
+ group: gateway.envoyproxy.io
+ kind: HTTPRouteFilter
+ name: valid-header
+ - type: URLRewrite
+ urlRewrite:
+ hostname: "rewrite.com"
+ - apiVersion: gateway.networking.k8s.io/v1
+ kind: HTTPRoute
+ metadata:
+ namespace: default
+ name: httproute-multiple-path-rewrites-2
+ spec:
+ hostnames:
+ - gateway.envoyproxy.io
+ parentRefs:
+ - namespace: envoy-gateway
+ name: gateway-1
+ sectionName: http
+ rules:
+ - matches:
+ - path:
+ value: "/inline-first"
+ backendRefs:
+ - name: service-1
+ port: 8080
+ filters:
+ - type: URLRewrite
+ urlRewrite:
+ hostname: "rewrite.com"
+ - type: ExtensionRef
+ extensionRef:
+ group: gateway.envoyproxy.io
+ kind: HTTPRouteFilter
+ name: valid-header
+ - apiVersion: gateway.networking.k8s.io/v1
+ kind: HTTPRoute
+ metadata:
+ namespace: default
+ name: httproute-multiple-header-host-rewrites
+ spec:
+ hostnames:
+ - gateway.envoyproxy.io
+ parentRefs:
+ - namespace: envoy-gateway
+ name: gateway-1
+ sectionName: http
+ rules:
+ - matches:
+ - path:
+ value: "/two-headers"
+ backendRefs:
+ - name: service-1
+ port: 8080
+ filters:
+ - type: ExtensionRef
+ extensionRef:
+ group: gateway.envoyproxy.io
+ kind: HTTPRouteFilter
+ name: valid-header
+ - type: ExtensionRef
+ extensionRef:
+ group: gateway.envoyproxy.io
+ kind: HTTPRouteFilter
+ name: valid-header-2
+ - apiVersion: gateway.networking.k8s.io/v1
+ kind: HTTPRoute
+ metadata:
+ namespace: default
+ name: httproute-multiple-header-host-rewrites
+ spec:
+ hostnames:
+ - gateway.envoyproxy.io
+ parentRefs:
+ - namespace: envoy-gateway
+ name: gateway-1
+ sectionName: http
+ rules:
+ - matches:
+ - path:
+ value: "/two-backends"
+ backendRefs:
+ - name: service-1
+ port: 8080
+ filters:
+ - type: ExtensionRef
+ extensionRef:
+ group: gateway.envoyproxy.io
+ kind: HTTPRouteFilter
+ name: valid-backend
+ - type: ExtensionRef
+ extensionRef:
+ group: gateway.envoyproxy.io
+ kind: HTTPRouteFilter
+ name: valid-backend-2
+ - apiVersion: gateway.networking.k8s.io/v1
+ kind: HTTPRoute
+ metadata:
+ namespace: default
+ name: httproute-header-and-backend-host-rewrites
+ spec:
+ hostnames:
+ - gateway.envoyproxy.io
+ parentRefs:
+ - namespace: envoy-gateway
+ name: gateway-1
+ sectionName: http
+ rules:
+ - matches:
+ - path:
+ value: "/header-and-backend"
+ backendRefs:
+ - name: service-1
+ port: 8080
+ filters:
+ - type: ExtensionRef
+ extensionRef:
+ group: gateway.envoyproxy.io
+ kind: HTTPRouteFilter
+ name: valid-header
+ - type: ExtensionRef
+ extensionRef:
+ group: gateway.envoyproxy.io
+ kind: HTTPRouteFilter
+ name: valid-header
+httpFilters:
+ - apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: HTTPRouteFilter
+ metadata:
+ name: valid-header
+ namespace: default
+ spec:
+ urlRewrite:
+ hostname:
+ type: Header
+ header: my-host
+ - apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: HTTPRouteFilter
+ metadata:
+ name: valid-header-2
+ namespace: default
+ spec:
+ urlRewrite:
+ hostname:
+ type: Header
+ header: my-host2
+ - apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: HTTPRouteFilter
+ metadata:
+ name: valid-backend
+ namespace: default
+ spec:
+ urlRewrite:
+ hostname:
+ type: Backend
+ - apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: HTTPRouteFilter
+ metadata:
+ name: valid-backend-2
+ namespace: default
+ spec:
+ urlRewrite:
+ hostname:
+ type: Backend
+ - apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: HTTPRouteFilter
+ metadata:
+ name: invalid-header
+ namespace: default
+ spec:
+ urlRewrite:
+ hostname:
+ type: Header
diff --git a/internal/gatewayapi/testdata/httproute-with-urlrewrite-hostname-filter-invalid.out.yaml b/internal/gatewayapi/testdata/httproute-with-urlrewrite-hostname-filter-invalid.out.yaml
new file mode 100644
index 00000000000..ab24ec0e81d
--- /dev/null
+++ b/internal/gatewayapi/testdata/httproute-with-urlrewrite-hostname-filter-invalid.out.yaml
@@ -0,0 +1,364 @@
+gateways:
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: Gateway
+ metadata:
+ creationTimestamp: null
+ name: gateway-1
+ namespace: envoy-gateway
+ spec:
+ gatewayClassName: envoy-gateway-class
+ listeners:
+ - allowedRoutes:
+ namespaces:
+ from: All
+ hostname: '*.envoyproxy.io'
+ name: http
+ port: 80
+ protocol: HTTP
+ status:
+ listeners:
+ - attachedRoutes: 6
+ conditions:
+ - lastTransitionTime: null
+ message: Sending translated listener configuration to the data plane
+ reason: Programmed
+ status: "True"
+ type: Programmed
+ - lastTransitionTime: null
+ message: Listener has been successfully translated
+ reason: Accepted
+ status: "True"
+ type: Accepted
+ - lastTransitionTime: null
+ message: Listener references have been resolved
+ reason: ResolvedRefs
+ status: "True"
+ type: ResolvedRefs
+ name: http
+ supportedKinds:
+ - group: gateway.networking.k8s.io
+ kind: HTTPRoute
+ - group: gateway.networking.k8s.io
+ kind: GRPCRoute
+httpRoutes:
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: HTTPRoute
+ metadata:
+ creationTimestamp: null
+ name: httproute-invalid-header
+ namespace: default
+ spec:
+ hostnames:
+ - gateway.envoyproxy.io
+ parentRefs:
+ - name: gateway-1
+ namespace: envoy-gateway
+ sectionName: http
+ rules:
+ - backendRefs:
+ - name: service-1
+ port: 8080
+ filters:
+ - extensionRef:
+ group: gateway.envoyproxy.io
+ kind: HTTPRouteFilter
+ name: invalid-header
+ type: ExtensionRef
+ matches:
+ - path:
+ value: /invalid-header
+ status:
+ parents:
+ - conditions:
+ - lastTransitionTime: null
+ message: Header must be set when rewrite path type is "Header"
+ reason: UnsupportedValue
+ status: "False"
+ type: Accepted
+ - lastTransitionTime: null
+ message: Resolved all the Object references for the Route
+ reason: ResolvedRefs
+ status: "True"
+ type: ResolvedRefs
+ controllerName: gateway.envoyproxy.io/gatewayclass-controller
+ parentRef:
+ name: gateway-1
+ namespace: envoy-gateway
+ sectionName: http
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: HTTPRoute
+ metadata:
+ creationTimestamp: null
+ name: httproute-multiple-host-rewrites-1
+ namespace: default
+ spec:
+ hostnames:
+ - gateway.envoyproxy.io
+ parentRefs:
+ - name: gateway-1
+ namespace: envoy-gateway
+ sectionName: http
+ rules:
+ - backendRefs:
+ - name: service-1
+ port: 8080
+ filters:
+ - extensionRef:
+ group: gateway.envoyproxy.io
+ kind: HTTPRouteFilter
+ name: valid-header
+ type: ExtensionRef
+ - type: URLRewrite
+ urlRewrite:
+ hostname: rewrite.com
+ matches:
+ - path:
+ value: /ext-first
+ status:
+ parents:
+ - conditions:
+ - lastTransitionTime: null
+ message: Cannot configure multiple urlRewrite filters for a single HTTPRouteRule
+ reason: UnsupportedValue
+ status: "False"
+ type: Accepted
+ - lastTransitionTime: null
+ message: Resolved all the Object references for the Route
+ reason: ResolvedRefs
+ status: "True"
+ type: ResolvedRefs
+ controllerName: gateway.envoyproxy.io/gatewayclass-controller
+ parentRef:
+ name: gateway-1
+ namespace: envoy-gateway
+ sectionName: http
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: HTTPRoute
+ metadata:
+ creationTimestamp: null
+ name: httproute-multiple-path-rewrites-2
+ namespace: default
+ spec:
+ hostnames:
+ - gateway.envoyproxy.io
+ parentRefs:
+ - name: gateway-1
+ namespace: envoy-gateway
+ sectionName: http
+ rules:
+ - backendRefs:
+ - name: service-1
+ port: 8080
+ filters:
+ - type: URLRewrite
+ urlRewrite:
+ hostname: rewrite.com
+ - extensionRef:
+ group: gateway.envoyproxy.io
+ kind: HTTPRouteFilter
+ name: valid-header
+ type: ExtensionRef
+ matches:
+ - path:
+ value: /inline-first
+ status:
+ parents:
+ - conditions:
+ - lastTransitionTime: null
+ message: Cannot configure multiple urlRewrite filters for a single HTTPRouteRule
+ reason: UnsupportedValue
+ status: "False"
+ type: Accepted
+ - lastTransitionTime: null
+ message: Resolved all the Object references for the Route
+ reason: ResolvedRefs
+ status: "True"
+ type: ResolvedRefs
+ controllerName: gateway.envoyproxy.io/gatewayclass-controller
+ parentRef:
+ name: gateway-1
+ namespace: envoy-gateway
+ sectionName: http
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: HTTPRoute
+ metadata:
+ creationTimestamp: null
+ name: httproute-multiple-header-host-rewrites
+ namespace: default
+ spec:
+ hostnames:
+ - gateway.envoyproxy.io
+ parentRefs:
+ - name: gateway-1
+ namespace: envoy-gateway
+ sectionName: http
+ rules:
+ - backendRefs:
+ - name: service-1
+ port: 8080
+ filters:
+ - extensionRef:
+ group: gateway.envoyproxy.io
+ kind: HTTPRouteFilter
+ name: valid-header
+ type: ExtensionRef
+ - extensionRef:
+ group: gateway.envoyproxy.io
+ kind: HTTPRouteFilter
+ name: valid-header-2
+ type: ExtensionRef
+ matches:
+ - path:
+ value: /two-headers
+ status:
+ parents:
+ - conditions:
+ - lastTransitionTime: null
+ message: Cannot configure multiple urlRewrite filters for a single HTTPRouteRule
+ reason: UnsupportedValue
+ status: "False"
+ type: Accepted
+ - lastTransitionTime: null
+ message: Resolved all the Object references for the Route
+ reason: ResolvedRefs
+ status: "True"
+ type: ResolvedRefs
+ controllerName: gateway.envoyproxy.io/gatewayclass-controller
+ parentRef:
+ name: gateway-1
+ namespace: envoy-gateway
+ sectionName: http
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: HTTPRoute
+ metadata:
+ creationTimestamp: null
+ name: httproute-multiple-header-host-rewrites
+ namespace: default
+ spec:
+ hostnames:
+ - gateway.envoyproxy.io
+ parentRefs:
+ - name: gateway-1
+ namespace: envoy-gateway
+ sectionName: http
+ rules:
+ - backendRefs:
+ - name: service-1
+ port: 8080
+ filters:
+ - extensionRef:
+ group: gateway.envoyproxy.io
+ kind: HTTPRouteFilter
+ name: valid-backend
+ type: ExtensionRef
+ - extensionRef:
+ group: gateway.envoyproxy.io
+ kind: HTTPRouteFilter
+ name: valid-backend-2
+ type: ExtensionRef
+ matches:
+ - path:
+ value: /two-backends
+ status:
+ parents:
+ - conditions:
+ - lastTransitionTime: null
+ message: Cannot configure multiple urlRewrite filters for a single HTTPRouteRule
+ reason: UnsupportedValue
+ status: "False"
+ type: Accepted
+ - lastTransitionTime: null
+ message: Resolved all the Object references for the Route
+ reason: ResolvedRefs
+ status: "True"
+ type: ResolvedRefs
+ controllerName: gateway.envoyproxy.io/gatewayclass-controller
+ parentRef:
+ name: gateway-1
+ namespace: envoy-gateway
+ sectionName: http
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: HTTPRoute
+ metadata:
+ creationTimestamp: null
+ name: httproute-header-and-backend-host-rewrites
+ namespace: default
+ spec:
+ hostnames:
+ - gateway.envoyproxy.io
+ parentRefs:
+ - name: gateway-1
+ namespace: envoy-gateway
+ sectionName: http
+ rules:
+ - backendRefs:
+ - name: service-1
+ port: 8080
+ filters:
+ - extensionRef:
+ group: gateway.envoyproxy.io
+ kind: HTTPRouteFilter
+ name: valid-header
+ type: ExtensionRef
+ - extensionRef:
+ group: gateway.envoyproxy.io
+ kind: HTTPRouteFilter
+ name: valid-header
+ type: ExtensionRef
+ matches:
+ - path:
+ value: /header-and-backend
+ status:
+ parents:
+ - conditions:
+ - lastTransitionTime: null
+ message: Cannot configure multiple urlRewrite filters for a single HTTPRouteRule
+ reason: UnsupportedValue
+ status: "False"
+ type: Accepted
+ - lastTransitionTime: null
+ message: Resolved all the Object references for the Route
+ reason: ResolvedRefs
+ status: "True"
+ type: ResolvedRefs
+ controllerName: gateway.envoyproxy.io/gatewayclass-controller
+ parentRef:
+ name: gateway-1
+ namespace: envoy-gateway
+ sectionName: http
+infraIR:
+ envoy-gateway/gateway-1:
+ proxy:
+ listeners:
+ - address: null
+ name: envoy-gateway/gateway-1/http
+ ports:
+ - containerPort: 10080
+ name: http-80
+ protocol: HTTP
+ servicePort: 80
+ metadata:
+ labels:
+ gateway.envoyproxy.io/owning-gateway-name: gateway-1
+ gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway
+ name: envoy-gateway/gateway-1
+xdsIR:
+ envoy-gateway/gateway-1:
+ accessLog:
+ text:
+ - path: /dev/stdout
+ http:
+ - address: 0.0.0.0
+ hostnames:
+ - '*.envoyproxy.io'
+ isHTTP2: false
+ metadata:
+ kind: Gateway
+ name: gateway-1
+ namespace: envoy-gateway
+ sectionName: http
+ name: envoy-gateway/gateway-1/http
+ path:
+ escapedSlashesAction: UnescapeAndRedirect
+ mergeSlashes: true
+ port: 10080
diff --git a/internal/gatewayapi/testdata/httproute-with-urlrewrite-hostname-filter.in.yaml b/internal/gatewayapi/testdata/httproute-with-urlrewrite-hostname-filter.in.yaml
new file mode 100644
index 00000000000..f39c951e5f8
--- /dev/null
+++ b/internal/gatewayapi/testdata/httproute-with-urlrewrite-hostname-filter.in.yaml
@@ -0,0 +1,147 @@
+gateways:
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: Gateway
+ metadata:
+ namespace: envoy-gateway
+ name: gateway-1
+ spec:
+ gatewayClassName: envoy-gateway-class
+ listeners:
+ - name: http
+ protocol: HTTP
+ port: 80
+ hostname: "*.envoyproxy.io"
+ allowedRoutes:
+ namespaces:
+ from: All
+httpRoutes:
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: HTTPRoute
+ metadata:
+ namespace: default
+ name: httproute-1
+ spec:
+ hostnames:
+ - gateway.envoyproxy.io
+ parentRefs:
+ - namespace: envoy-gateway
+ name: gateway-1
+ sectionName: http
+ rules:
+ - matches:
+ - path:
+ value: "/valid-header"
+ backendRefs:
+ - name: service-1
+ port: 8080
+ filters:
+ - type: ExtensionRef
+ extensionRef:
+ group: gateway.envoyproxy.io
+ kind: HTTPRouteFilter
+ name: valid-header
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: HTTPRoute
+ metadata:
+ namespace: default
+ name: httproute-2
+ spec:
+ hostnames:
+ - gateway.envoyproxy.io
+ parentRefs:
+ - namespace: envoy-gateway
+ name: gateway-1
+ sectionName: http
+ rules:
+ - matches:
+ - path:
+ value: "/valid-backend"
+ backendRefs:
+ - name: service-1
+ port: 8080
+ filters:
+ - type: ExtensionRef
+ extensionRef:
+ group: gateway.envoyproxy.io
+ kind: HTTPRouteFilter
+ name: valid-backend
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: HTTPRoute
+ metadata:
+ namespace: default
+ name: httproute-3
+ spec:
+ hostnames:
+ - gateway.envoyproxy.io
+ parentRefs:
+ - namespace: envoy-gateway
+ name: gateway-1
+ sectionName: http
+ rules:
+ - matches:
+ - path:
+ value: "/path-and-header-host"
+ backendRefs:
+ - name: service-1
+ port: 8080
+ filters:
+ - type: URLRewrite
+ urlRewrite:
+ path:
+ type: ReplacePrefixMatch
+ replacePrefixMatch: /rewrite
+ - type: ExtensionRef
+ extensionRef:
+ group: gateway.envoyproxy.io
+ kind: HTTPRouteFilter
+ name: valid-header
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: HTTPRoute
+ metadata:
+ namespace: default
+ name: httproute-4
+ spec:
+ hostnames:
+ - gateway.envoyproxy.io
+ parentRefs:
+ - namespace: envoy-gateway
+ name: gateway-1
+ sectionName: http
+ rules:
+ - matches:
+ - path:
+ value: "/header-host-and-path"
+ backendRefs:
+ - name: service-1
+ port: 8080
+ filters:
+ - type: ExtensionRef
+ extensionRef:
+ group: gateway.envoyproxy.io
+ kind: HTTPRouteFilter
+ name: valid-header
+ - type: URLRewrite
+ urlRewrite:
+ path:
+ type: ReplacePrefixMatch
+ replacePrefixMatch: /rewrite
+httpFilters:
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: HTTPRouteFilter
+ metadata:
+ name: valid-header
+ namespace: default
+ spec:
+ urlRewrite:
+ hostname:
+ type: Header
+ header: my-host
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: HTTPRouteFilter
+ metadata:
+ name: valid-backend
+ namespace: default
+ spec:
+ urlRewrite:
+ hostname:
+ type: Backend
diff --git a/internal/gatewayapi/testdata/httproute-with-urlrewrite-hostname-filter.out.yaml b/internal/gatewayapi/testdata/httproute-with-urlrewrite-hostname-filter.out.yaml
new file mode 100644
index 00000000000..916f7d0cefe
--- /dev/null
+++ b/internal/gatewayapi/testdata/httproute-with-urlrewrite-hostname-filter.out.yaml
@@ -0,0 +1,362 @@
+gateways:
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: Gateway
+ metadata:
+ creationTimestamp: null
+ name: gateway-1
+ namespace: envoy-gateway
+ spec:
+ gatewayClassName: envoy-gateway-class
+ listeners:
+ - allowedRoutes:
+ namespaces:
+ from: All
+ hostname: '*.envoyproxy.io'
+ name: http
+ port: 80
+ protocol: HTTP
+ status:
+ listeners:
+ - attachedRoutes: 4
+ conditions:
+ - lastTransitionTime: null
+ message: Sending translated listener configuration to the data plane
+ reason: Programmed
+ status: "True"
+ type: Programmed
+ - lastTransitionTime: null
+ message: Listener has been successfully translated
+ reason: Accepted
+ status: "True"
+ type: Accepted
+ - lastTransitionTime: null
+ message: Listener references have been resolved
+ reason: ResolvedRefs
+ status: "True"
+ type: ResolvedRefs
+ name: http
+ supportedKinds:
+ - group: gateway.networking.k8s.io
+ kind: HTTPRoute
+ - group: gateway.networking.k8s.io
+ kind: GRPCRoute
+httpRoutes:
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: HTTPRoute
+ metadata:
+ creationTimestamp: null
+ name: httproute-1
+ namespace: default
+ spec:
+ hostnames:
+ - gateway.envoyproxy.io
+ parentRefs:
+ - name: gateway-1
+ namespace: envoy-gateway
+ sectionName: http
+ rules:
+ - backendRefs:
+ - name: service-1
+ port: 8080
+ filters:
+ - extensionRef:
+ group: gateway.envoyproxy.io
+ kind: HTTPRouteFilter
+ name: valid-header
+ type: ExtensionRef
+ matches:
+ - path:
+ value: /valid-header
+ status:
+ parents:
+ - conditions:
+ - lastTransitionTime: null
+ message: Route is accepted
+ reason: Accepted
+ status: "True"
+ type: Accepted
+ - lastTransitionTime: null
+ message: Resolved all the Object references for the Route
+ reason: ResolvedRefs
+ status: "True"
+ type: ResolvedRefs
+ controllerName: gateway.envoyproxy.io/gatewayclass-controller
+ parentRef:
+ name: gateway-1
+ namespace: envoy-gateway
+ sectionName: http
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: HTTPRoute
+ metadata:
+ creationTimestamp: null
+ name: httproute-2
+ namespace: default
+ spec:
+ hostnames:
+ - gateway.envoyproxy.io
+ parentRefs:
+ - name: gateway-1
+ namespace: envoy-gateway
+ sectionName: http
+ rules:
+ - backendRefs:
+ - name: service-1
+ port: 8080
+ filters:
+ - extensionRef:
+ group: gateway.envoyproxy.io
+ kind: HTTPRouteFilter
+ name: valid-backend
+ type: ExtensionRef
+ matches:
+ - path:
+ value: /valid-backend
+ status:
+ parents:
+ - conditions:
+ - lastTransitionTime: null
+ message: Route is accepted
+ reason: Accepted
+ status: "True"
+ type: Accepted
+ - lastTransitionTime: null
+ message: Resolved all the Object references for the Route
+ reason: ResolvedRefs
+ status: "True"
+ type: ResolvedRefs
+ controllerName: gateway.envoyproxy.io/gatewayclass-controller
+ parentRef:
+ name: gateway-1
+ namespace: envoy-gateway
+ sectionName: http
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: HTTPRoute
+ metadata:
+ creationTimestamp: null
+ name: httproute-3
+ namespace: default
+ spec:
+ hostnames:
+ - gateway.envoyproxy.io
+ parentRefs:
+ - name: gateway-1
+ namespace: envoy-gateway
+ sectionName: http
+ rules:
+ - backendRefs:
+ - name: service-1
+ port: 8080
+ filters:
+ - type: URLRewrite
+ urlRewrite:
+ path:
+ replacePrefixMatch: /rewrite
+ type: ReplacePrefixMatch
+ - extensionRef:
+ group: gateway.envoyproxy.io
+ kind: HTTPRouteFilter
+ name: valid-header
+ type: ExtensionRef
+ matches:
+ - path:
+ value: /path-and-header-host
+ status:
+ parents:
+ - conditions:
+ - lastTransitionTime: null
+ message: Route is accepted
+ reason: Accepted
+ status: "True"
+ type: Accepted
+ - lastTransitionTime: null
+ message: Resolved all the Object references for the Route
+ reason: ResolvedRefs
+ status: "True"
+ type: ResolvedRefs
+ controllerName: gateway.envoyproxy.io/gatewayclass-controller
+ parentRef:
+ name: gateway-1
+ namespace: envoy-gateway
+ sectionName: http
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: HTTPRoute
+ metadata:
+ creationTimestamp: null
+ name: httproute-4
+ namespace: default
+ spec:
+ hostnames:
+ - gateway.envoyproxy.io
+ parentRefs:
+ - name: gateway-1
+ namespace: envoy-gateway
+ sectionName: http
+ rules:
+ - backendRefs:
+ - name: service-1
+ port: 8080
+ filters:
+ - extensionRef:
+ group: gateway.envoyproxy.io
+ kind: HTTPRouteFilter
+ name: valid-header
+ type: ExtensionRef
+ - type: URLRewrite
+ urlRewrite:
+ path:
+ replacePrefixMatch: /rewrite
+ type: ReplacePrefixMatch
+ matches:
+ - path:
+ value: /header-host-and-path
+ status:
+ parents:
+ - conditions:
+ - lastTransitionTime: null
+ message: Route is accepted
+ reason: Accepted
+ status: "True"
+ type: Accepted
+ - lastTransitionTime: null
+ message: Resolved all the Object references for the Route
+ reason: ResolvedRefs
+ status: "True"
+ type: ResolvedRefs
+ controllerName: gateway.envoyproxy.io/gatewayclass-controller
+ parentRef:
+ name: gateway-1
+ namespace: envoy-gateway
+ sectionName: http
+infraIR:
+ envoy-gateway/gateway-1:
+ proxy:
+ listeners:
+ - address: null
+ name: envoy-gateway/gateway-1/http
+ ports:
+ - containerPort: 10080
+ name: http-80
+ protocol: HTTP
+ servicePort: 80
+ metadata:
+ labels:
+ gateway.envoyproxy.io/owning-gateway-name: gateway-1
+ gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway
+ name: envoy-gateway/gateway-1
+xdsIR:
+ envoy-gateway/gateway-1:
+ accessLog:
+ text:
+ - path: /dev/stdout
+ http:
+ - address: 0.0.0.0
+ hostnames:
+ - '*.envoyproxy.io'
+ isHTTP2: false
+ metadata:
+ kind: Gateway
+ name: gateway-1
+ namespace: envoy-gateway
+ sectionName: http
+ name: envoy-gateway/gateway-1/http
+ path:
+ escapedSlashesAction: UnescapeAndRedirect
+ mergeSlashes: true
+ port: 10080
+ routes:
+ - destination:
+ name: httproute/default/httproute-3/rule/0
+ settings:
+ - addressType: IP
+ endpoints:
+ - host: 7.7.7.7
+ port: 8080
+ protocol: HTTP
+ weight: 1
+ hostname: gateway.envoyproxy.io
+ isHTTP2: false
+ metadata:
+ kind: HTTPRoute
+ name: httproute-3
+ namespace: default
+ name: httproute/default/httproute-3/rule/0/match/0/gateway_envoyproxy_io
+ pathMatch:
+ distinct: false
+ name: ""
+ prefix: /path-and-header-host
+ urlRewrite:
+ host:
+ header: my-host
+ path:
+ fullReplace: null
+ prefixMatchReplace: /rewrite
+ - destination:
+ name: httproute/default/httproute-4/rule/0
+ settings:
+ - addressType: IP
+ endpoints:
+ - host: 7.7.7.7
+ port: 8080
+ protocol: HTTP
+ weight: 1
+ hostname: gateway.envoyproxy.io
+ isHTTP2: false
+ metadata:
+ kind: HTTPRoute
+ name: httproute-4
+ namespace: default
+ name: httproute/default/httproute-4/rule/0/match/0/gateway_envoyproxy_io
+ pathMatch:
+ distinct: false
+ name: ""
+ prefix: /header-host-and-path
+ urlRewrite:
+ path:
+ fullReplace: null
+ prefixMatchReplace: /rewrite
+ - destination:
+ name: httproute/default/httproute-2/rule/0
+ settings:
+ - addressType: IP
+ endpoints:
+ - host: 7.7.7.7
+ port: 8080
+ protocol: HTTP
+ weight: 1
+ hostname: gateway.envoyproxy.io
+ isHTTP2: false
+ metadata:
+ kind: HTTPRoute
+ name: httproute-2
+ namespace: default
+ name: httproute/default/httproute-2/rule/0/match/0/gateway_envoyproxy_io
+ pathMatch:
+ distinct: false
+ name: ""
+ prefix: /valid-backend
+ urlRewrite:
+ host:
+ backend: true
+ - destination:
+ name: httproute/default/httproute-1/rule/0
+ settings:
+ - addressType: IP
+ endpoints:
+ - host: 7.7.7.7
+ port: 8080
+ protocol: HTTP
+ weight: 1
+ hostname: gateway.envoyproxy.io
+ isHTTP2: false
+ metadata:
+ kind: HTTPRoute
+ name: httproute-1
+ namespace: default
+ name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io
+ pathMatch:
+ distinct: false
+ name: ""
+ prefix: /valid-header
+ urlRewrite:
+ host:
+ header: my-host
diff --git a/internal/ir/xds.go b/internal/ir/xds.go
index cb5021f4c9f..00edaf21b57 100644
--- a/internal/ir/xds.go
+++ b/internal/ir/xds.go
@@ -55,6 +55,7 @@ var (
ErrHTTPPathModifierDoubleReplace = errors.New("redirect filter cannot have a path modifier that supplies more than one of fullPathReplace, prefixMatchReplace and regexMatchReplace")
ErrHTTPPathModifierNoReplace = errors.New("redirect filter cannot have a path modifier that does not supply either fullPathReplace, prefixMatchReplace or regexMatchReplace")
ErrHTTPPathRegexModifierNoSetting = errors.New("redirect filter cannot have a path modifier that does not supply either fullPathReplace, prefixMatchReplace or regexMatchReplace")
+ ErrHTTPHostModifierDoubleReplace = errors.New("redirect filter cannot have a host modifier that supplies more than one of Hostname, Header and Backend")
ErrAddHeaderEmptyName = errors.New("header modifier filter cannot configure a header without a name to be added")
ErrAddHeaderDuplicate = errors.New("header modifier filter attempts to add the same header more than once (case insensitive)")
ErrRemoveHeaderDuplicate = errors.New("header modifier filter attempts to remove the same header more than once (case insensitive)")
@@ -1382,8 +1383,8 @@ func (r DirectResponse) Validate() error {
type URLRewrite struct {
// Path contains config for rewriting the path of the request.
Path *ExtendedHTTPPathModifier `json:"path,omitempty" yaml:"path,omitempty"`
- // Hostname configures the replacement of the request's hostname.
- Hostname *string `json:"hostname,omitempty" yaml:"hostname,omitempty"`
+ // Host configures the replacement of the request's host header.
+ Host *HTTPHostModifier `json:"host,omitempty" yaml:"host,omitempty"`
}
// Validate the fields within the URLRewrite structure
@@ -1396,6 +1397,12 @@ func (r URLRewrite) Validate() error {
}
}
+ if r.Host != nil {
+ if err := r.Host.Validate(); err != nil {
+ errs = errors.Join(errs, err)
+ }
+ }
+
return errs
}
@@ -1499,6 +1506,34 @@ func (r ExtendedHTTPPathModifier) Validate() error {
return errs
}
+// HTTPHostModifier holds instructions for how to modify the host of a request
+// with both core gateway-api and extended envoy gateway capabilities
+// +k8s:deepcopy-gen=true
+type HTTPHostModifier struct {
+ Name *string `json:"name,omitempty" yaml:"name,omitempty"`
+ Header *string `json:"header,omitempty" yaml:"header,omitempty"`
+ Backend *bool `json:"backend,omitempty" yaml:"backend,omitempty"`
+}
+
+// Validate the fields within the HTTPPathModifier structure
+func (r HTTPHostModifier) Validate() error {
+ var errs error
+
+ rewrites := []bool{r.Name != nil, r.Header != nil, r.Backend != nil}
+ rwc := 0
+ for _, rw := range rewrites {
+ if rw {
+ rwc++
+ }
+ }
+
+ if rwc > 1 {
+ errs = errors.Join(errs, ErrHTTPHostModifierDoubleReplace)
+ }
+
+ return errs
+}
+
// StringMatch holds the various match conditions.
// Only one of Exact, Prefix, SafeRegex or Distinct can be set.
// +k8s:deepcopy-gen=true
diff --git a/internal/ir/xds_test.go b/internal/ir/xds_test.go
index 5ff9a8736ef..b4593152593 100644
--- a/internal/ir/xds_test.go
+++ b/internal/ir/xds_test.go
@@ -308,7 +308,9 @@ var (
Exact: ptr.To("rewrite"),
},
URLRewrite: &URLRewrite{
- Hostname: ptr.To("rewrite.example.com"),
+ Host: &HTTPHostModifier{
+ Name: ptr.To("rewrite.example.com"),
+ },
Path: &ExtendedHTTPPathModifier{
HTTPPathModifier: HTTPPathModifier{
FullReplace: ptr.To("/rewrite"),
@@ -324,7 +326,9 @@ var (
Exact: ptr.To("rewrite"),
},
URLRewrite: &URLRewrite{
- Hostname: ptr.To("rewrite.example.com"),
+ Host: &HTTPHostModifier{
+ Name: ptr.To("rewrite.example.com"),
+ },
Path: &ExtendedHTTPPathModifier{
HTTPPathModifier: HTTPPathModifier{
FullReplace: ptr.To("/rewrite"),
diff --git a/internal/ir/zz_generated.deepcopy.go b/internal/ir/zz_generated.deepcopy.go
index 791b6d5dd68..111f2661377 100644
--- a/internal/ir/zz_generated.deepcopy.go
+++ b/internal/ir/zz_generated.deepcopy.go
@@ -1270,6 +1270,36 @@ func (in *HTTPHealthChecker) DeepCopy() *HTTPHealthChecker {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *HTTPHostModifier) DeepCopyInto(out *HTTPHostModifier) {
+ *out = *in
+ if in.Name != nil {
+ in, out := &in.Name, &out.Name
+ *out = new(string)
+ **out = **in
+ }
+ if in.Header != nil {
+ in, out := &in.Header, &out.Header
+ *out = new(string)
+ **out = **in
+ }
+ if in.Backend != nil {
+ in, out := &in.Backend, &out.Backend
+ *out = new(bool)
+ **out = **in
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPHostModifier.
+func (in *HTTPHostModifier) DeepCopy() *HTTPHostModifier {
+ if in == nil {
+ return nil
+ }
+ out := new(HTTPHostModifier)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *HTTPListener) DeepCopyInto(out *HTTPListener) {
*out = *in
@@ -3341,10 +3371,10 @@ func (in *URLRewrite) DeepCopyInto(out *URLRewrite) {
*out = new(ExtendedHTTPPathModifier)
(*in).DeepCopyInto(*out)
}
- if in.Hostname != nil {
- in, out := &in.Hostname, &out.Hostname
- *out = new(string)
- **out = **in
+ if in.Host != nil {
+ in, out := &in.Host, &out.Host
+ *out = new(HTTPHostModifier)
+ (*in).DeepCopyInto(*out)
}
}
diff --git a/internal/xds/translator/route.go b/internal/xds/translator/route.go
index a8ec4a291d5..e1d790268dc 100644
--- a/internal/xds/translator/route.go
+++ b/internal/xds/translator/route.go
@@ -417,9 +417,21 @@ func buildXdsURLRewriteAction(destName string, urlRewrite *ir.URLRewrite, pathMa
}
}
- if urlRewrite.Hostname != nil {
- routeAction.HostRewriteSpecifier = &routev3.RouteAction_HostRewriteLiteral{
- HostRewriteLiteral: *urlRewrite.Hostname,
+ if urlRewrite.Host != nil {
+
+ switch {
+ case urlRewrite.Host.Name != nil:
+ routeAction.HostRewriteSpecifier = &routev3.RouteAction_HostRewriteLiteral{
+ HostRewriteLiteral: *urlRewrite.Host.Name,
+ }
+ case urlRewrite.Host.Header != nil:
+ routeAction.HostRewriteSpecifier = &routev3.RouteAction_HostRewriteHeader{
+ HostRewriteHeader: *urlRewrite.Host.Header,
+ }
+ case urlRewrite.Host.Backend != nil:
+ routeAction.HostRewriteSpecifier = &routev3.RouteAction_AutoHostRewrite{
+ AutoHostRewrite: wrapperspb.Bool(true),
+ }
}
routeAction.AppendXForwardedHost = true
diff --git a/internal/xds/translator/testdata/in/xds-ir/http-route-rewrite-url-host.yaml b/internal/xds/translator/testdata/in/xds-ir/http-route-rewrite-url-host.yaml
index 8cc673a7e5b..525a22210b9 100644
--- a/internal/xds/translator/testdata/in/xds-ir/http-route-rewrite-url-host.yaml
+++ b/internal/xds/translator/testdata/in/xds-ir/http-route-rewrite-url-host.yaml
@@ -23,6 +23,43 @@ http:
- host: "1.2.3.4"
port: 50000
urlRewrite:
- hostname: "3.3.3.3"
+ host:
+ name: "3.3.3.3"
+ path:
+ prefixMatchReplace: /rewrite
+ - name: "rewrite-host-header"
+ pathMatch:
+ prefix: "/host-header"
+ hostname: gateway.envoyproxy.io
+ headerMatches:
+ - name: ":authority"
+ exact: gateway.envoyproxy.io
+ destination:
+ name: "rewrite-route-dest"
+ settings:
+ - endpoints:
+ - host: "1.2.3.4"
+ port: 50000
+ urlRewrite:
+ host:
+ header: "foo"
+ path:
+ prefixMatchReplace: /rewrite
+ - name: "rewrite-host-backend"
+ pathMatch:
+ prefix: "/host-backend"
+ hostname: gateway.envoyproxy.io
+ headerMatches:
+ - name: ":authority"
+ exact: gateway.envoyproxy.io
+ destination:
+ name: "rewrite-route-dest"
+ settings:
+ - endpoints:
+ - host: "1.2.3.4"
+ port: 50000
+ urlRewrite:
+ host:
+ backend: true
path:
prefixMatchReplace: /rewrite
diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-rewrite-url-host.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-rewrite-url-host.routes.yaml
index 680a67404ee..a3e1e29e821 100644
--- a/internal/xds/translator/testdata/out/xds-ir/http-route-rewrite-url-host.routes.yaml
+++ b/internal/xds/translator/testdata/out/xds-ir/http-route-rewrite-url-host.routes.yaml
@@ -19,3 +19,31 @@
prefixRewrite: /rewrite
upgradeConfigs:
- upgradeType: websocket
+ - match:
+ headers:
+ - name: :authority
+ stringMatch:
+ exact: gateway.envoyproxy.io
+ pathSeparatedPrefix: /host-header
+ name: rewrite-host-header
+ route:
+ appendXForwardedHost: true
+ cluster: rewrite-route-dest
+ hostRewriteHeader: foo
+ prefixRewrite: /rewrite
+ upgradeConfigs:
+ - upgradeType: websocket
+ - match:
+ headers:
+ - name: :authority
+ stringMatch:
+ exact: gateway.envoyproxy.io
+ pathSeparatedPrefix: /host-backend
+ name: rewrite-host-backend
+ route:
+ appendXForwardedHost: true
+ autoHostRewrite: true
+ cluster: rewrite-route-dest
+ prefixRewrite: /rewrite
+ upgradeConfigs:
+ - upgradeType: websocket
diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md
index c183a4f0b8f..f90ee0702ad 100644
--- a/site/content/en/latest/api/extension_types.md
+++ b/site/content/en/latest/api/extension_types.md
@@ -1971,7 +1971,7 @@ _Appears in:_
| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `type` | _[HTTPHostnameModifierType](#httphostnamemodifiertype)_ | true | |
-| `setFromHeader` | _string_ | false | SetFromHeader is the name of the header whose value would be used to rewrite the Host header |
+| `header` | _string_ | false | Header is the name of the header whose value would be used to rewrite the Host header |
#### HTTPHostnameModifierType
@@ -1985,8 +1985,8 @@ _Appears in:_
| Value | Description |
| ----- | ----------- |
-| `SetFromHeader` | HeaderHTTPHostnameModifier indicates that the Host header value would be replaced with the value of the header specified in setFromHeader.
https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#envoy-v3-api-field-config-route-v3-routeaction-host-rewrite-header
|
-| `SetFromBackend` | BackendHTTPHostnameModifier indicates that the Host header value would be replaced by the DNS name of the backend if it exists.
https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#envoy-v3-api-field-config-route-v3-routeaction-auto-host-rewrite
|
+| `Header` | HeaderHTTPHostnameModifier indicates that the Host header value would be replaced with the value of the header specified in header.
https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#envoy-v3-api-field-config-route-v3-routeaction-host-rewrite-header
|
+| `Backend` | BackendHTTPHostnameModifier indicates that the Host header value would be replaced by the DNS name of the backend if it exists.
https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#envoy-v3-api-field-config-route-v3-routeaction-auto-host-rewrite
|
#### HTTPPathModifier
@@ -2106,6 +2106,7 @@ _Appears in:_
| Field | Type | Required | Description |
| --- | --- | --- | --- |
+| `hostname` | _[HTTPHostnameModifier](#httphostnamemodifier)_ | false | Hostname is the value to be used to replace the Host header value during
forwarding. |
| `path` | _[HTTPPathModifier](#httppathmodifier)_ | false | Path defines a path rewrite. |
diff --git a/site/content/en/latest/tasks/traffic/http-urlrewrite.md b/site/content/en/latest/tasks/traffic/http-urlrewrite.md
index a643d775a57..3515bd9caa4 100644
--- a/site/content/en/latest/tasks/traffic/http-urlrewrite.md
+++ b/site/content/en/latest/tasks/traffic/http-urlrewrite.md
@@ -374,14 +374,14 @@ spec:
The HTTPRoute status should indicate that it has been accepted and is bound to the example Gateway.
```shell
-kubectl get httproute/http-filter-url-rewrite -o yaml
+kubectl get httproute/http-filter-url-regex-rewrite -o yaml
```
-Querying `http://${GATEWAY_HOST}/get/origin/path/extra` should rewrite the request to
-`http://${GATEWAY_HOST}/force/replace/fullpath`.
+Querying `http://${GATEWAY_HOST}/service/foo/v1/api` should rewrite the request to
+`http://${GATEWAY_HOST}/service/foo/v1/api`.
```console
-$ curl -L -vvv --header "Host: path.regex.rewrite.example" "http://${GATEWAY_HOST}/get/origin/path/extra"
+$ curl -L -vvv --header "Host: path.regex.rewrite.example" "http://${GATEWAY_HOST}/service/foo/v1/api"
...
> GET /service/foo/v1/api HTTP/1.1
> Host: path.regex.rewrite.example
@@ -555,6 +555,145 @@ $ curl -L -vvv --header "Host: path.rewrite.example" "http://${GATEWAY_HOST}/get
You can see that the `X-Forwarded-Host` is `path.rewrite.example`, but the actual host is `envoygateway.io`.
+## Rewrite URL Host Name by Header or Backend
+
+In addition to core Gateway-API rewrite options, Envoy Gateway supports extended rewrite options through the [HTTPRouteFilter][] API.
+The `HTTPRouteFilter` API can be configured to rewrite the Host header value to:
+- The value of a different request header
+- The DNS name of the backend that the request is routed to
+
+In the following example, the host header is rewritten to the value of the x-custom-host header.
+
+{{< tabpane text=true >}}
+{{% tab header="Apply from stdin" %}}
+
+```shell
+cat <}}
+
+The HTTPRoute status should indicate that it has been accepted and is bound to the example Gateway.
+
+```shell
+kubectl get httproute/http-filter-header-host-rewrite -o yaml
+```
+
+Querying `http://${GATEWAY_HOST}/header` and providing a custom host rewrite header x-custom-host should rewrite the
+request host header to the value of the x-custom-host header.
+
+```console
+$ curl -L -vvv --header "Host: host.header.rewrite.example" --header "x-custom-host: foo" "http://${GATEWAY_HOST}/header"
+...
+> GET /header HTTP/1.1
+> Host: host.header.rewrite.example
+> User-Agent: curl/8.7.1
+> Accept: */*
+> x-custom-host: foo
+>
+* Request completely sent off
+< HTTP/1.1 200 OK
+<
+{
+ "path": "/header",
+ "host": "foo",
+ "method": "GET",
+ "proto": "HTTP/1.1",
+ "headers": {
+ "X-Custom-Host": [
+ "foo"
+ ],
+ "X-Forwarded-Host": [
+ "host.header.rewrite.example"
+ ],
+ },
+ "namespace": "default",
+ "ingress": "",
+ "service": "",
+ "pod": "backend-765694d47f-5t6f2"
+...
+```
+
+You can see that the host is rewritten from `host.header.rewrite.example`, to the value of the provided
+`x-custom-host` header `foo`. The original host header is preserved in the `X-Forwarded-Host` header.
+
+
[HTTPURLRewriteFilter]: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.HTTPURLRewriteFilter
[HTTPRouteFilter]: ../../../api/extension_types#httproutefilter
[RE2]: https://github.com/google/re2/wiki/Syntax
\ No newline at end of file
diff --git a/site/content/zh/latest/api/extension_types.md b/site/content/zh/latest/api/extension_types.md
index c183a4f0b8f..f90ee0702ad 100644
--- a/site/content/zh/latest/api/extension_types.md
+++ b/site/content/zh/latest/api/extension_types.md
@@ -1971,7 +1971,7 @@ _Appears in:_
| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `type` | _[HTTPHostnameModifierType](#httphostnamemodifiertype)_ | true | |
-| `setFromHeader` | _string_ | false | SetFromHeader is the name of the header whose value would be used to rewrite the Host header |
+| `header` | _string_ | false | Header is the name of the header whose value would be used to rewrite the Host header |
#### HTTPHostnameModifierType
@@ -1985,8 +1985,8 @@ _Appears in:_
| Value | Description |
| ----- | ----------- |
-| `SetFromHeader` | HeaderHTTPHostnameModifier indicates that the Host header value would be replaced with the value of the header specified in setFromHeader.
https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#envoy-v3-api-field-config-route-v3-routeaction-host-rewrite-header
|
-| `SetFromBackend` | BackendHTTPHostnameModifier indicates that the Host header value would be replaced by the DNS name of the backend if it exists.
https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#envoy-v3-api-field-config-route-v3-routeaction-auto-host-rewrite
|
+| `Header` | HeaderHTTPHostnameModifier indicates that the Host header value would be replaced with the value of the header specified in header.
https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#envoy-v3-api-field-config-route-v3-routeaction-host-rewrite-header
|
+| `Backend` | BackendHTTPHostnameModifier indicates that the Host header value would be replaced by the DNS name of the backend if it exists.
https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#envoy-v3-api-field-config-route-v3-routeaction-auto-host-rewrite
|
#### HTTPPathModifier
@@ -2106,6 +2106,7 @@ _Appears in:_
| Field | Type | Required | Description |
| --- | --- | --- | --- |
+| `hostname` | _[HTTPHostnameModifier](#httphostnamemodifier)_ | false | Hostname is the value to be used to replace the Host header value during
forwarding. |
| `path` | _[HTTPPathModifier](#httppathmodifier)_ | false | Path defines a path rewrite. |
diff --git a/test/cel-validation/httproutefilter_test.go b/test/cel-validation/httproutefilter_test.go
index 7f84deb71e5..c0b6e1ec817 100644
--- a/test/cel-validation/httproutefilter_test.go
+++ b/test/cel-validation/httproutefilter_test.go
@@ -86,13 +86,13 @@ func TestHTTPRouteFilter(t *testing.T) {
},
},
{
- desc: "Valid SetFromHeader",
+ desc: "Valid Header",
mutate: func(httproutefilter *egv1a1.HTTPRouteFilter) {
httproutefilter.Spec = egv1a1.HTTPRouteFilterSpec{
URLRewrite: &egv1a1.HTTPURLRewriteFilter{
Hostname: &egv1a1.HTTPHostnameModifier{
- Type: egv1a1.HeaderHTTPHostnameModifier,
- SetFromHeader: ptr.To("foo"),
+ Type: egv1a1.HeaderHTTPHostnameModifier,
+ Header: ptr.To("foo"),
},
},
}
@@ -113,7 +113,7 @@ func TestHTTPRouteFilter(t *testing.T) {
wantErrors: []string{},
},
{
- desc: "invalid SetFromHeader missing settings",
+ desc: "invalid Header missing settings",
mutate: func(httproutefilter *egv1a1.HTTPRouteFilter) {
httproutefilter.Spec = egv1a1.HTTPRouteFilterSpec{
URLRewrite: &egv1a1.HTTPURLRewriteFilter{
@@ -123,7 +123,7 @@ func TestHTTPRouteFilter(t *testing.T) {
},
}
},
- wantErrors: []string{"spec.urlRewrite.hostname: Invalid value: \"object\": setFromHeader must be specified for SetFromHeader type"},
+ wantErrors: []string{"spec.urlRewrite.hostname: Invalid value: \"object\": header must be specified for Header type"},
},
{
desc: "invalid SetFromBackend type",
@@ -131,13 +131,13 @@ func TestHTTPRouteFilter(t *testing.T) {
httproutefilter.Spec = egv1a1.HTTPRouteFilterSpec{
URLRewrite: &egv1a1.HTTPURLRewriteFilter{
Hostname: &egv1a1.HTTPHostnameModifier{
- Type: egv1a1.BackendHTTPHostnameModifier,
- SetFromHeader: ptr.To("foo"),
+ Type: egv1a1.BackendHTTPHostnameModifier,
+ Header: ptr.To("foo"),
},
},
}
},
- wantErrors: []string{"spec.urlRewrite.hostname: Invalid value: \"object\": setFromHeader must be nil if the type is not SetFromHeader"},
+ wantErrors: []string{"spec.urlRewrite.hostname: Invalid value: \"object\": header must be nil if the type is not Header"},
},
}
diff --git a/test/e2e/testdata/httproute-rewrite-host.yaml b/test/e2e/testdata/httproute-rewrite-host.yaml
new file mode 100644
index 00000000000..871b2008b3b
--- /dev/null
+++ b/test/e2e/testdata/httproute-rewrite-host.yaml
@@ -0,0 +1,68 @@
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ name: rewrite-host
+ namespace: gateway-conformance-infra
+spec:
+ parentRefs:
+ - name: same-namespace
+ rules:
+ - matches:
+ - path:
+ type: PathPrefix
+ value: /header
+ filters:
+ - type: ExtensionRef
+ extensionRef:
+ group: gateway.envoyproxy.io
+ kind: HTTPRouteFilter
+ name: header-host-rewrite
+ backendRefs:
+ - name: infra-backend-v1
+ port: 8080
+ - matches:
+ - path:
+ type: PathPrefix
+ value: /backend
+ filters:
+ - type: ExtensionRef
+ extensionRef:
+ group: gateway.envoyproxy.io
+ kind: HTTPRouteFilter
+ name: backend-host-rewrite
+ backendRefs:
+ - group: gateway.envoyproxy.io
+ kind: Backend
+ name: backend-fqdn
+---
+apiVersion: gateway.envoyproxy.io/v1alpha1
+kind: HTTPRouteFilter
+metadata:
+ name: header-host-rewrite
+ namespace: gateway-conformance-infra
+spec:
+ urlRewrite:
+ hostname:
+ type: Header
+ header: x-custom-host
+---
+apiVersion: gateway.envoyproxy.io/v1alpha1
+kind: HTTPRouteFilter
+metadata:
+ name: backend-host-rewrite
+ namespace: gateway-conformance-infra
+spec:
+ urlRewrite:
+ hostname:
+ type: Backend
+---
+apiVersion: gateway.envoyproxy.io/v1alpha1
+kind: Backend
+metadata:
+ name: backend-fqdn
+ namespace: gateway-conformance-infra
+spec:
+ endpoints:
+ - fqdn:
+ hostname: infra-backend-v1.gateway-conformance-infra.svc.cluster.local
+ port: 8080
diff --git a/test/e2e/tests/httproute_rewrite_host.go b/test/e2e/tests/httproute_rewrite_host.go
new file mode 100644
index 00000000000..643d0891e9f
--- /dev/null
+++ b/test/e2e/tests/httproute_rewrite_host.go
@@ -0,0 +1,75 @@
+// Copyright Envoy Gateway Authors
+// SPDX-License-Identifier: Apache-2.0
+// The full text of the Apache license is available in the LICENSE file at
+// the root of the repo.
+
+//go:build e2e
+
+package tests
+
+import (
+ "testing"
+
+ "k8s.io/apimachinery/pkg/types"
+ "sigs.k8s.io/gateway-api/conformance/utils/http"
+ "sigs.k8s.io/gateway-api/conformance/utils/kubernetes"
+ "sigs.k8s.io/gateway-api/conformance/utils/suite"
+)
+
+func init() {
+ ConformanceTests = append(ConformanceTests, HTTPRouteRewriteHostHeader)
+}
+
+var HTTPRouteRewriteHostHeader = suite.ConformanceTest{
+ ShortName: "HTTPRouteRewriteHostHeader",
+ Description: "An HTTPRoute with host rewrite filter to rewrite a host header",
+ Manifests: []string{"testdata/httproute-rewrite-host.yaml"},
+ Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
+ ns := "gateway-conformance-infra"
+ routeNN := types.NamespacedName{Name: "rewrite-host", Namespace: ns}
+ gwNN := types.NamespacedName{Name: "same-namespace", Namespace: ns}
+ gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN)
+ kubernetes.HTTPRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN)
+
+ testCases := []http.ExpectedResponse{
+ {
+ Request: http.Request{
+ Path: "/header",
+ Headers: map[string]string{
+ "x-custom-host": "custom-host",
+ },
+ },
+ ExpectedRequest: &http.ExpectedRequest{
+ Request: http.Request{
+ Path: "/header",
+ Host: "custom-host",
+ },
+ },
+ Backend: "infra-backend-v1",
+ Namespace: ns,
+ },
+ {
+ Request: http.Request{
+ Path: "/backend",
+ },
+ ExpectedRequest: &http.ExpectedRequest{
+ Request: http.Request{
+ Path: "/backend",
+ Host: "infra-backend-v1.gateway-conformance-infra.svc.cluster.local",
+ },
+ },
+ Backend: "infra-backend-v1",
+ Namespace: ns,
+ },
+ }
+ for i := range testCases {
+ // Declare tc here to avoid loop variable
+ // reuse issues across parallel tests.
+ tc := testCases[i]
+ t.Run(tc.GetTestCaseName(i), func(t *testing.T) {
+ t.Parallel()
+ http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, tc)
+ })
+ }
+ },
+}