diff --git a/api/v1alpha1/backendtrafficpolicy_types.go b/api/v1alpha1/backendtrafficpolicy_types.go
index d230e2a4ca..2d4c4ab5bf 100644
--- a/api/v1alpha1/backendtrafficpolicy_types.go
+++ b/api/v1alpha1/backendtrafficpolicy_types.go
@@ -77,7 +77,6 @@ type BackendTrafficPolicySpec struct {
// If not specified, the default upgrade configuration(websocket) will be used.
//
// +optional
- // +notImplementedHide
HTTPUpgrade []*ProtocolUpgradeConfig `json:"httpUpgrade,omitempty"`
}
@@ -87,9 +86,8 @@ type ProtocolUpgradeConfig struct {
//
// +kubebuilder:validation:Required
Type string `json:"type"`
- // Disabled indicates whether the upgrade is disabled.
- // +optional
- Disabled *bool `json:"disabled"`
+
+ // TODO: support more options for CONNECT
}
// BackendTrafficPolicyList contains a list of BackendTrafficPolicy resources.
diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go
index 771ef318b8..f2e87c3e3c 100644
--- a/api/v1alpha1/zz_generated.deepcopy.go
+++ b/api/v1alpha1/zz_generated.deepcopy.go
@@ -633,7 +633,7 @@ func (in *BackendTrafficPolicySpec) DeepCopyInto(out *BackendTrafficPolicySpec)
if (*in)[i] != nil {
in, out := &(*in)[i], &(*out)[i]
*out = new(ProtocolUpgradeConfig)
- (*in).DeepCopyInto(*out)
+ **out = **in
}
}
}
@@ -4484,11 +4484,6 @@ func (in *ProcessingModeOptions) DeepCopy() *ProcessingModeOptions {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ProtocolUpgradeConfig) DeepCopyInto(out *ProtocolUpgradeConfig) {
*out = *in
- if in.Disabled != nil {
- in, out := &in.Disabled, &out.Disabled
- *out = new(bool)
- **out = **in
- }
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProtocolUpgradeConfig.
diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml
index 025e18e2f2..f972d58608 100644
--- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml
+++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml
@@ -538,9 +538,6 @@ spec:
If not specified, the default upgrade configuration(websocket) will be used.
items:
properties:
- disabled:
- description: Disabled indicates whether the upgrade is disabled.
- type: boolean
type:
description: |-
Type is the case-insensitive type of protocol upgrade.
diff --git a/internal/gatewayapi/backendtrafficpolicy.go b/internal/gatewayapi/backendtrafficpolicy.go
index 4d3c95c49c..ca72ac8287 100644
--- a/internal/gatewayapi/backendtrafficpolicy.go
+++ b/internal/gatewayapi/backendtrafficpolicy.go
@@ -292,21 +292,22 @@ func (t *Translator) translateBackendTrafficPolicyForRoute(
resources *resource.Resources,
) error {
var (
- rl *ir.RateLimit
- lb *ir.LoadBalancer
- pp *ir.ProxyProtocol
- hc *ir.HealthCheck
- cb *ir.CircuitBreaker
- fi *ir.FaultInjection
- to *ir.Timeout
- ka *ir.TCPKeepalive
- rt *ir.Retry
- bc *ir.BackendConnection
- ds *ir.DNS
- h2 *ir.HTTP2Settings
- ro *ir.ResponseOverride
- cp []*ir.Compression
- err, errs error
+ rl *ir.RateLimit
+ lb *ir.LoadBalancer
+ pp *ir.ProxyProtocol
+ hc *ir.HealthCheck
+ cb *ir.CircuitBreaker
+ fi *ir.FaultInjection
+ to *ir.Timeout
+ ka *ir.TCPKeepalive
+ rt *ir.Retry
+ bc *ir.BackendConnection
+ ds *ir.DNS
+ h2 *ir.HTTP2Settings
+ ro *ir.ResponseOverride
+ cp []*ir.Compression
+ httpUpgrade []string
+ err, errs error
)
// Build IR
@@ -359,6 +360,7 @@ func (t *Translator) translateBackendTrafficPolicyForRoute(
errs = errors.Join(errs, err)
}
cp = buildCompression(policy.Spec.Compression)
+ httpUpgrade = buildHTTPProtocolUpgradeConfig(policy.Spec.HTTPUpgrade)
ds = translateDNS(policy.Spec.ClusterSettings)
@@ -423,6 +425,7 @@ func (t *Translator) translateBackendTrafficPolicyForRoute(
Timeout: to,
ResponseOverride: ro,
Compression: cp,
+ HTTPUpgrade: httpUpgrade,
}
// Update the Host field in HealthCheck, now that we have access to the Route Hostname.
@@ -440,27 +443,25 @@ func (t *Translator) translateBackendTrafficPolicyForRoute(
}
func (t *Translator) translateBackendTrafficPolicyForGateway(
- policy *egv1a1.BackendTrafficPolicy,
- target gwapiv1a2.LocalPolicyTargetReferenceWithSectionName,
- gateway *GatewayContext,
- xdsIR resource.XdsIRMap,
- resources *resource.Resources,
+ policy *egv1a1.BackendTrafficPolicy, target gwapiv1a2.LocalPolicyTargetReferenceWithSectionName,
+ gateway *GatewayContext, xdsIR resource.XdsIRMap, resources *resource.Resources,
) error {
var (
- rl *ir.RateLimit
- lb *ir.LoadBalancer
- pp *ir.ProxyProtocol
- hc *ir.HealthCheck
- cb *ir.CircuitBreaker
- fi *ir.FaultInjection
- ct *ir.Timeout
- ka *ir.TCPKeepalive
- rt *ir.Retry
- ds *ir.DNS
- h2 *ir.HTTP2Settings
- ro *ir.ResponseOverride
- cp []*ir.Compression
- err, errs error
+ rl *ir.RateLimit
+ lb *ir.LoadBalancer
+ pp *ir.ProxyProtocol
+ hc *ir.HealthCheck
+ cb *ir.CircuitBreaker
+ fi *ir.FaultInjection
+ ct *ir.Timeout
+ ka *ir.TCPKeepalive
+ rt *ir.Retry
+ ds *ir.DNS
+ h2 *ir.HTTP2Settings
+ ro *ir.ResponseOverride
+ cp []*ir.Compression
+ httpUpgrade []string
+ err, errs error
)
// Build IR
@@ -506,6 +507,7 @@ func (t *Translator) translateBackendTrafficPolicyForGateway(
errs = errors.Join(errs, err)
}
cp = buildCompression(policy.Spec.Compression)
+ httpUpgrade = buildHTTPProtocolUpgradeConfig(policy.Spec.HTTPUpgrade)
ds = translateDNS(policy.Spec.ClusterSettings)
@@ -591,6 +593,7 @@ func (t *Translator) translateBackendTrafficPolicyForGateway(
DNS: ds,
ResponseOverride: ro,
Compression: cp,
+ HTTPUpgrade: httpUpgrade,
}
// Update the Host field in HealthCheck, now that we have access to the Route Hostname.
@@ -971,3 +974,16 @@ func buildCompression(compression []*egv1a1.Compression) []*ir.Compression {
return irCompression
}
+
+func buildHTTPProtocolUpgradeConfig(cfgs []*egv1a1.ProtocolUpgradeConfig) []string {
+ if len(cfgs) == 0 {
+ return nil
+ }
+
+ result := make([]string, 0, len(cfgs))
+ for _, cfg := range cfgs {
+ result = append(result, cfg.Type)
+ }
+
+ return result
+}
diff --git a/internal/gatewayapi/backendtrafficpolicy_test.go b/internal/gatewayapi/backendtrafficpolicy_test.go
index e104f3b0ba..c03796f578 100644
--- a/internal/gatewayapi/backendtrafficpolicy_test.go
+++ b/internal/gatewayapi/backendtrafficpolicy_test.go
@@ -132,3 +132,57 @@ func Test_translateRateLimitCost(t *testing.T) {
})
}
}
+
+func TestBuildHTTPProtocolUpgradeConfig(t *testing.T) {
+ cases := []struct {
+ name string
+ cfgs []*egv1a1.ProtocolUpgradeConfig
+ expected []string
+ }{
+ {
+ name: "empty",
+ cfgs: nil,
+ expected: nil,
+ },
+ {
+ name: "spdy",
+ cfgs: []*egv1a1.ProtocolUpgradeConfig{
+ {
+ Type: "spdy/3.1",
+ },
+ },
+ expected: []string{"spdy/3.1"},
+ },
+ {
+ name: "websockets-spdy",
+ cfgs: []*egv1a1.ProtocolUpgradeConfig{
+ {
+ Type: "websockets",
+ },
+ {
+ Type: "spdy/3.1",
+ },
+ },
+ expected: []string{"websockets", "spdy/3.1"},
+ },
+ {
+ name: "spdy-websockets",
+ cfgs: []*egv1a1.ProtocolUpgradeConfig{
+ {
+ Type: "spdy/3.1",
+ },
+ {
+ Type: "websockets",
+ },
+ },
+ expected: []string{"spdy/3.1", "websockets"},
+ },
+ }
+
+ for _, tc := range cases {
+ t.Run(tc.name, func(t *testing.T) {
+ got := buildHTTPProtocolUpgradeConfig(tc.cfgs)
+ require.Equal(t, tc.expected, got)
+ })
+ }
+}
diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-http-upgrade-spdy.in.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-http-upgrade-spdy.in.yaml
new file mode 100644
index 0000000000..72650c5dc1
--- /dev/null
+++ b/internal/gatewayapi/testdata/backendtrafficpolicy-http-upgrade-spdy.in.yaml
@@ -0,0 +1,48 @@
+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
+ 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: "/"
+ backendRefs:
+ - name: service-1
+ port: 8080
+backendTrafficPolicies:
+ - apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: BackendTrafficPolicy
+ metadata:
+ namespace: default
+ name: policy-for-route
+ spec:
+ targetRef:
+ group: gateway.networking.k8s.io
+ kind: HTTPRoute
+ name: httproute-1
+ httpUpgrade:
+ - type: "spdy/3.1"
diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-http-upgrade-spdy.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-http-upgrade-spdy.out.yaml
new file mode 100644
index 0000000000..dcb4b20e0d
--- /dev/null
+++ b/internal/gatewayapi/testdata/backendtrafficpolicy-http-upgrade-spdy.out.yaml
@@ -0,0 +1,175 @@
+backendTrafficPolicies:
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: BackendTrafficPolicy
+ metadata:
+ creationTimestamp: null
+ name: policy-for-route
+ namespace: default
+ spec:
+ httpUpgrade:
+ - type: spdy/3.1
+ targetRef:
+ group: gateway.networking.k8s.io
+ kind: HTTPRoute
+ name: httproute-1
+ status:
+ ancestors:
+ - ancestorRef:
+ group: gateway.networking.k8s.io
+ kind: Gateway
+ name: gateway-1
+ namespace: envoy-gateway
+ sectionName: http
+ conditions:
+ - lastTransitionTime: null
+ message: Policy has been accepted.
+ reason: Accepted
+ status: "True"
+ type: Accepted
+ controllerName: gateway.envoyproxy.io/gatewayclass-controller
+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
+ name: http
+ port: 80
+ protocol: HTTP
+ status:
+ listeners:
+ - attachedRoutes: 1
+ 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
+ matches:
+ - path:
+ value: /
+ 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:
+ json:
+ - path: /dev/stdout
+ http:
+ - address: 0.0.0.0
+ hostnames:
+ - '*'
+ 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-1/rule/0
+ settings:
+ - addressType: IP
+ endpoints:
+ - host: 7.7.7.7
+ port: 8080
+ name: httproute/default/httproute-1/rule/0/backend/0
+ 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: /
+ traffic:
+ httpUpgrade:
+ - spdy/3.1
+ readyListener:
+ address: 0.0.0.0
+ ipFamily: IPv4
+ path: /ready
+ port: 19003
diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-http-upgrade-websocket.in.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-http-upgrade-websocket.in.yaml
new file mode 100644
index 0000000000..230d2358d0
--- /dev/null
+++ b/internal/gatewayapi/testdata/backendtrafficpolicy-http-upgrade-websocket.in.yaml
@@ -0,0 +1,49 @@
+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
+ 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: "/"
+ backendRefs:
+ - name: service-1
+ port: 8080
+backendTrafficPolicies:
+ - apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: BackendTrafficPolicy
+ metadata:
+ namespace: default
+ name: policy-for-route
+ spec:
+ targetRef:
+ group: gateway.networking.k8s.io
+ kind: HTTPRoute
+ name: httproute-1
+ httpUpgrade:
+ - type: websocket
+ - type: spdy/3.1
diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-http-upgrade-websocket.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-http-upgrade-websocket.out.yaml
new file mode 100644
index 0000000000..ea1d4d6a6e
--- /dev/null
+++ b/internal/gatewayapi/testdata/backendtrafficpolicy-http-upgrade-websocket.out.yaml
@@ -0,0 +1,177 @@
+backendTrafficPolicies:
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: BackendTrafficPolicy
+ metadata:
+ creationTimestamp: null
+ name: policy-for-route
+ namespace: default
+ spec:
+ httpUpgrade:
+ - type: websocket
+ - type: spdy/3.1
+ targetRef:
+ group: gateway.networking.k8s.io
+ kind: HTTPRoute
+ name: httproute-1
+ status:
+ ancestors:
+ - ancestorRef:
+ group: gateway.networking.k8s.io
+ kind: Gateway
+ name: gateway-1
+ namespace: envoy-gateway
+ sectionName: http
+ conditions:
+ - lastTransitionTime: null
+ message: Policy has been accepted.
+ reason: Accepted
+ status: "True"
+ type: Accepted
+ controllerName: gateway.envoyproxy.io/gatewayclass-controller
+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
+ name: http
+ port: 80
+ protocol: HTTP
+ status:
+ listeners:
+ - attachedRoutes: 1
+ 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
+ matches:
+ - path:
+ value: /
+ 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:
+ json:
+ - path: /dev/stdout
+ http:
+ - address: 0.0.0.0
+ hostnames:
+ - '*'
+ 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-1/rule/0
+ settings:
+ - addressType: IP
+ endpoints:
+ - host: 7.7.7.7
+ port: 8080
+ name: httproute/default/httproute-1/rule/0/backend/0
+ 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: /
+ traffic:
+ httpUpgrade:
+ - websocket
+ - spdy/3.1
+ readyListener:
+ address: 0.0.0.0
+ ipFamily: IPv4
+ path: /ready
+ port: 19003
diff --git a/internal/ir/xds.go b/internal/ir/xds.go
index 15a8e28c8c..e37a9e82c8 100644
--- a/internal/ir/xds.go
+++ b/internal/ir/xds.go
@@ -828,6 +828,8 @@ type TrafficFeatures struct {
ResponseOverride *ResponseOverride `json:"responseOverride,omitempty" yaml:"responseOverride,omitempty"`
// Compression settings for HTTP Response
Compression []*Compression `json:"compression,omitempty" yaml:"compression,omitempty"`
+ // HTTPUpgrade defines the schema for upgrading the HTTP protocol.
+ HTTPUpgrade []string `json:"httpUpgrade,omitempty" yaml:"httpUpgrade,omitempty"`
}
func (b *TrafficFeatures) Validate() error {
diff --git a/internal/ir/zz_generated.deepcopy.go b/internal/ir/zz_generated.deepcopy.go
index c2ae3f0405..656141da01 100644
--- a/internal/ir/zz_generated.deepcopy.go
+++ b/internal/ir/zz_generated.deepcopy.go
@@ -3658,6 +3658,11 @@ func (in *TrafficFeatures) DeepCopyInto(out *TrafficFeatures) {
}
}
}
+ if in.HTTPUpgrade != nil {
+ in, out := &in.HTTPUpgrade, &out.HTTPUpgrade
+ *out = make([]string, len(*in))
+ copy(*out, *in)
+ }
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrafficFeatures.
diff --git a/internal/xds/translator/route.go b/internal/xds/translator/route.go
index 2bff31761d..722e1aa91f 100644
--- a/internal/xds/translator/route.go
+++ b/internal/xds/translator/route.go
@@ -26,8 +26,18 @@ const (
retryDefaultRetryOn = "connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes"
retryDefaultRetriableStatusCode = 503
retryDefaultNumRetries = 2
+
+ websocketUpgradeType = "websocket"
)
+// Allow websocket upgrades for HTTP 1.1
+// Reference: https://developer.mozilla.org/en-US/docs/Web/HTTP/Protocol_upgrade_mechanism
+var defaultUpgradeConfig = []*routev3.RouteAction_UpgradeConfig{
+ {
+ UpgradeType: websocketUpgradeType,
+ },
+}
+
func buildXdsRoute(httpRoute *ir.HTTPRoute) (*routev3.Route, error) {
router := &routev3.Route{
Name: httpRoute.Name,
@@ -61,13 +71,7 @@ func buildXdsRoute(httpRoute *ir.HTTPRoute) (*routev3.Route, error) {
}
if !httpRoute.IsHTTP2 {
- // Allow websocket upgrades for HTTP 1.1
- // Reference: https://developer.mozilla.org/en-US/docs/Web/HTTP/Protocol_upgrade_mechanism
- routeAction.UpgradeConfigs = []*routev3.RouteAction_UpgradeConfig{
- {
- UpgradeType: "websocket",
- },
- }
+ routeAction.UpgradeConfigs = buildUpgradeConfig(httpRoute.Traffic)
}
router.Action = &routev3.Route_Route{Route: routeAction}
@@ -80,13 +84,7 @@ func buildXdsRoute(httpRoute *ir.HTTPRoute) (*routev3.Route, error) {
routeAction.RequestMirrorPolicies = buildXdsRequestMirrorPolicies(httpRoute.Mirrors)
}
if !httpRoute.IsHTTP2 {
- // Allow websocket upgrades for HTTP 1.1
- // Reference: https://developer.mozilla.org/en-US/docs/Web/HTTP/Protocol_upgrade_mechanism
- routeAction.UpgradeConfigs = []*routev3.RouteAction_UpgradeConfig{
- {
- UpgradeType: "websocket",
- },
- }
+ routeAction.UpgradeConfigs = buildUpgradeConfig(httpRoute.Traffic)
}
router.Action = &routev3.Route_Route{Route: routeAction}
}
@@ -122,6 +120,21 @@ func buildXdsRoute(httpRoute *ir.HTTPRoute) (*routev3.Route, error) {
return router, nil
}
+func buildUpgradeConfig(trafficFeatures *ir.TrafficFeatures) []*routev3.RouteAction_UpgradeConfig {
+ if trafficFeatures == nil || trafficFeatures.HTTPUpgrade == nil {
+ return defaultUpgradeConfig
+ }
+
+ upgradeConfigs := make([]*routev3.RouteAction_UpgradeConfig, 0, len(trafficFeatures.HTTPUpgrade))
+ for _, protocol := range trafficFeatures.HTTPUpgrade {
+ upgradeConfigs = append(upgradeConfigs, &routev3.RouteAction_UpgradeConfig{
+ UpgradeType: protocol,
+ })
+ }
+
+ return upgradeConfigs
+}
+
func buildXdsRouteMatch(pathMatch *ir.StringMatch, headerMatches []*ir.StringMatch, queryParamMatches []*ir.StringMatch) *routev3.RouteMatch {
outMatch := &routev3.RouteMatch{}
@@ -236,7 +249,7 @@ func buildXdsRouteAction(backendWeights *ir.BackendWeights, settings []*ir.Desti
}
func buildXdsWeightedRouteAction(backendWeights *ir.BackendWeights, settings []*ir.DestinationSetting) *routev3.RouteAction {
- weightedClusters := []*routev3.WeightedCluster_ClusterWeight{}
+ weightedClusters := make([]*routev3.WeightedCluster_ClusterWeight, 0, len(settings))
if backendWeights.Invalid > 0 {
invalidCluster := &routev3.WeightedCluster_ClusterWeight{
Name: "invalid-backend-cluster",
diff --git a/internal/xds/translator/route_test.go b/internal/xds/translator/route_test.go
index 3d7d6a9727..817426c97b 100644
--- a/internal/xds/translator/route_test.go
+++ b/internal/xds/translator/route_test.go
@@ -15,7 +15,7 @@ import (
"github.com/envoyproxy/gateway/internal/ir"
)
-func Test_buildHashPolicy(t *testing.T) {
+func TestBuildHashPolicy(t *testing.T) {
tests := []struct {
name string
httpRoute *ir.HTTPRoute
@@ -102,3 +102,72 @@ func Test_buildHashPolicy(t *testing.T) {
})
}
}
+
+func TestBuildUpgradeConfig(t *testing.T) {
+ cases := []struct {
+ name string
+ trafficFeature *ir.TrafficFeatures
+ expected []*routev3.RouteAction_UpgradeConfig
+ }{
+ {
+ name: "default",
+ trafficFeature: nil,
+ expected: defaultUpgradeConfig,
+ },
+ {
+ name: "empty",
+ trafficFeature: &ir.TrafficFeatures{
+ HTTPUpgrade: nil,
+ },
+ expected: defaultUpgradeConfig,
+ },
+ {
+ name: "spdy",
+ trafficFeature: &ir.TrafficFeatures{
+ HTTPUpgrade: []string{"spdy/3.1"},
+ },
+ expected: []*routev3.RouteAction_UpgradeConfig{
+ {
+ UpgradeType: "spdy/3.1",
+ },
+ },
+ },
+ {
+ name: "spdy-websocket",
+ trafficFeature: &ir.TrafficFeatures{
+ HTTPUpgrade: []string{"spdy/3.1", "websocket"},
+ },
+ expected: []*routev3.RouteAction_UpgradeConfig{
+ {
+ UpgradeType: "spdy/3.1",
+ },
+ {
+ UpgradeType: "websocket",
+ },
+ },
+ },
+ {
+ name: "websocket-spdy",
+ trafficFeature: &ir.TrafficFeatures{
+ HTTPUpgrade: []string{"websocket", "spdy/3.1"},
+ },
+ expected: []*routev3.RouteAction_UpgradeConfig{
+ {
+ UpgradeType: "websocket",
+ },
+ {
+ UpgradeType: "spdy/3.1",
+ },
+ },
+ },
+ }
+
+ for _, tc := range cases {
+ t.Run(tc.name, func(t *testing.T) {
+ got := buildUpgradeConfig(tc.trafficFeature)
+ if !reflect.DeepEqual(got, tc.expected) {
+ t.Errorf("buildUpgradeConfig() got = %v, want %v", got, tc.expected)
+ }
+ })
+ }
+}
diff --git a/internal/xds/translator/testdata/in/xds-ir/http-upgrade-spdy.yaml b/internal/xds/translator/testdata/in/xds-ir/http-upgrade-spdy.yaml
new file mode 100644
index 0000000000..ce44231bd5
--- /dev/null
+++ b/internal/xds/translator/testdata/in/xds-ir/http-upgrade-spdy.yaml
@@ -0,0 +1,40 @@
+http:
+ - address: 0.0.0.0
+ hostnames:
+ - "*"
+ 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-1/rule/0
+ settings:
+ - addressType: IP
+ endpoints:
+ - host: 7.7.7.7
+ port: 8080
+ protocol: HTTP
+ weight: 1
+ name: httproute/default/httproute-1/rule/0/backend/0
+ 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: /
+ traffic:
+ httpUpgrade:
+ - "spdy/3.1"
diff --git a/internal/xds/translator/testdata/in/xds-ir/http-upgrade-websocket-spdy.yaml b/internal/xds/translator/testdata/in/xds-ir/http-upgrade-websocket-spdy.yaml
new file mode 100644
index 0000000000..8bac627452
--- /dev/null
+++ b/internal/xds/translator/testdata/in/xds-ir/http-upgrade-websocket-spdy.yaml
@@ -0,0 +1,41 @@
+http:
+ - address: 0.0.0.0
+ hostnames:
+ - "*"
+ 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-1/rule/0
+ settings:
+ - addressType: IP
+ endpoints:
+ - host: 7.7.7.7
+ port: 8080
+ protocol: HTTP
+ weight: 1
+ name: httproute/default/httproute-1/rule/0/backend/0
+ 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: /
+ traffic:
+ httpUpgrade:
+ - "websocket"
+ - "spdy/3.1"
diff --git a/internal/xds/translator/testdata/out/xds-ir/http-upgrade-spdy.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/http-upgrade-spdy.clusters.yaml
new file mode 100644
index 0000000000..c24d059eea
--- /dev/null
+++ b/internal/xds/translator/testdata/out/xds-ir/http-upgrade-spdy.clusters.yaml
@@ -0,0 +1,17 @@
+- circuitBreakers:
+ thresholds:
+ - maxRetries: 1024
+ commonLbConfig:
+ localityWeightedLbConfig: {}
+ connectTimeout: 10s
+ dnsLookupFamily: V4_PREFERRED
+ edsClusterConfig:
+ edsConfig:
+ ads: {}
+ resourceApiVersion: V3
+ serviceName: httproute/default/httproute-1/rule/0
+ ignoreHealthOnHostRemoval: true
+ lbPolicy: LEAST_REQUEST
+ name: httproute/default/httproute-1/rule/0
+ perConnectionBufferLimitBytes: 32768
+ type: EDS
diff --git a/internal/xds/translator/testdata/out/xds-ir/http-upgrade-spdy.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/http-upgrade-spdy.endpoints.yaml
new file mode 100644
index 0000000000..29bb6b4e44
--- /dev/null
+++ b/internal/xds/translator/testdata/out/xds-ir/http-upgrade-spdy.endpoints.yaml
@@ -0,0 +1,12 @@
+- clusterName: httproute/default/httproute-1/rule/0
+ endpoints:
+ - lbEndpoints:
+ - endpoint:
+ address:
+ socketAddress:
+ address: 7.7.7.7
+ portValue: 8080
+ loadBalancingWeight: 1
+ loadBalancingWeight: 1
+ locality:
+ region: httproute/default/httproute-1/rule/0/backend/0
diff --git a/internal/xds/translator/testdata/out/xds-ir/http-upgrade-spdy.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/http-upgrade-spdy.listeners.yaml
new file mode 100644
index 0000000000..09426a3177
--- /dev/null
+++ b/internal/xds/translator/testdata/out/xds-ir/http-upgrade-spdy.listeners.yaml
@@ -0,0 +1,34 @@
+- address:
+ socketAddress:
+ address: 0.0.0.0
+ portValue: 10080
+ defaultFilterChain:
+ filters:
+ - name: envoy.filters.network.http_connection_manager
+ typedConfig:
+ '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
+ commonHttpProtocolOptions:
+ headersWithUnderscoresAction: REJECT_REQUEST
+ http2ProtocolOptions:
+ initialConnectionWindowSize: 1048576
+ initialStreamWindowSize: 65536
+ maxConcurrentStreams: 100
+ httpFilters:
+ - name: envoy.filters.http.router
+ typedConfig:
+ '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
+ suppressEnvoyHeaders: true
+ mergeSlashes: true
+ normalizePath: true
+ pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT
+ rds:
+ configSource:
+ ads: {}
+ resourceApiVersion: V3
+ routeConfigName: envoy-gateway/gateway-1/http
+ serverHeaderTransformation: PASS_THROUGH
+ statPrefix: http-10080
+ useRemoteAddress: true
+ name: envoy-gateway/gateway-1/http
+ name: envoy-gateway/gateway-1/http
+ perConnectionBufferLimitBytes: 32768
diff --git a/internal/xds/translator/testdata/out/xds-ir/http-upgrade-spdy.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/http-upgrade-spdy.routes.yaml
new file mode 100644
index 0000000000..f447ef4e53
--- /dev/null
+++ b/internal/xds/translator/testdata/out/xds-ir/http-upgrade-spdy.routes.yaml
@@ -0,0 +1,29 @@
+- ignorePortInHostMatching: true
+ name: envoy-gateway/gateway-1/http
+ virtualHosts:
+ - domains:
+ - gateway.envoyproxy.io
+ metadata:
+ filterMetadata:
+ envoy-gateway:
+ resources:
+ - kind: Gateway
+ name: gateway-1
+ namespace: envoy-gateway
+ sectionName: http
+ name: envoy-gateway/gateway-1/http/gateway_envoyproxy_io
+ routes:
+ - match:
+ prefix: /
+ metadata:
+ filterMetadata:
+ envoy-gateway:
+ resources:
+ - kind: HTTPRoute
+ name: httproute-1
+ namespace: default
+ name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io
+ route:
+ cluster: httproute/default/httproute-1/rule/0
+ upgradeConfigs:
+ - upgradeType: spdy/3.1
diff --git a/internal/xds/translator/testdata/out/xds-ir/http-upgrade-websocket-spdy.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/http-upgrade-websocket-spdy.clusters.yaml
new file mode 100644
index 0000000000..c24d059eea
--- /dev/null
+++ b/internal/xds/translator/testdata/out/xds-ir/http-upgrade-websocket-spdy.clusters.yaml
@@ -0,0 +1,17 @@
+- circuitBreakers:
+ thresholds:
+ - maxRetries: 1024
+ commonLbConfig:
+ localityWeightedLbConfig: {}
+ connectTimeout: 10s
+ dnsLookupFamily: V4_PREFERRED
+ edsClusterConfig:
+ edsConfig:
+ ads: {}
+ resourceApiVersion: V3
+ serviceName: httproute/default/httproute-1/rule/0
+ ignoreHealthOnHostRemoval: true
+ lbPolicy: LEAST_REQUEST
+ name: httproute/default/httproute-1/rule/0
+ perConnectionBufferLimitBytes: 32768
+ type: EDS
diff --git a/internal/xds/translator/testdata/out/xds-ir/http-upgrade-websocket-spdy.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/http-upgrade-websocket-spdy.endpoints.yaml
new file mode 100644
index 0000000000..29bb6b4e44
--- /dev/null
+++ b/internal/xds/translator/testdata/out/xds-ir/http-upgrade-websocket-spdy.endpoints.yaml
@@ -0,0 +1,12 @@
+- clusterName: httproute/default/httproute-1/rule/0
+ endpoints:
+ - lbEndpoints:
+ - endpoint:
+ address:
+ socketAddress:
+ address: 7.7.7.7
+ portValue: 8080
+ loadBalancingWeight: 1
+ loadBalancingWeight: 1
+ locality:
+ region: httproute/default/httproute-1/rule/0/backend/0
diff --git a/internal/xds/translator/testdata/out/xds-ir/http-upgrade-websocket-spdy.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/http-upgrade-websocket-spdy.listeners.yaml
new file mode 100644
index 0000000000..09426a3177
--- /dev/null
+++ b/internal/xds/translator/testdata/out/xds-ir/http-upgrade-websocket-spdy.listeners.yaml
@@ -0,0 +1,34 @@
+- address:
+ socketAddress:
+ address: 0.0.0.0
+ portValue: 10080
+ defaultFilterChain:
+ filters:
+ - name: envoy.filters.network.http_connection_manager
+ typedConfig:
+ '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
+ commonHttpProtocolOptions:
+ headersWithUnderscoresAction: REJECT_REQUEST
+ http2ProtocolOptions:
+ initialConnectionWindowSize: 1048576
+ initialStreamWindowSize: 65536
+ maxConcurrentStreams: 100
+ httpFilters:
+ - name: envoy.filters.http.router
+ typedConfig:
+ '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
+ suppressEnvoyHeaders: true
+ mergeSlashes: true
+ normalizePath: true
+ pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT
+ rds:
+ configSource:
+ ads: {}
+ resourceApiVersion: V3
+ routeConfigName: envoy-gateway/gateway-1/http
+ serverHeaderTransformation: PASS_THROUGH
+ statPrefix: http-10080
+ useRemoteAddress: true
+ name: envoy-gateway/gateway-1/http
+ name: envoy-gateway/gateway-1/http
+ perConnectionBufferLimitBytes: 32768
diff --git a/internal/xds/translator/testdata/out/xds-ir/http-upgrade-websocket-spdy.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/http-upgrade-websocket-spdy.routes.yaml
new file mode 100644
index 0000000000..20c2467f51
--- /dev/null
+++ b/internal/xds/translator/testdata/out/xds-ir/http-upgrade-websocket-spdy.routes.yaml
@@ -0,0 +1,30 @@
+- ignorePortInHostMatching: true
+ name: envoy-gateway/gateway-1/http
+ virtualHosts:
+ - domains:
+ - gateway.envoyproxy.io
+ metadata:
+ filterMetadata:
+ envoy-gateway:
+ resources:
+ - kind: Gateway
+ name: gateway-1
+ namespace: envoy-gateway
+ sectionName: http
+ name: envoy-gateway/gateway-1/http/gateway_envoyproxy_io
+ routes:
+ - match:
+ prefix: /
+ metadata:
+ filterMetadata:
+ envoy-gateway:
+ resources:
+ - kind: HTTPRoute
+ name: httproute-1
+ namespace: default
+ name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io
+ route:
+ cluster: httproute/default/httproute-1/rule/0
+ upgradeConfigs:
+ - upgradeType: websocket
+ - upgradeType: spdy/3.1
diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md
index e5889126e2..81cb93e69b 100644
--- a/site/content/en/latest/api/extension_types.md
+++ b/site/content/en/latest/api/extension_types.md
@@ -487,6 +487,7 @@ _Appears in:_
| `useClientProtocol` | _boolean_ | false | | UseClientProtocol configures Envoy to prefer sending requests to backends using
the same HTTP protocol that the incoming request used. Defaults to false, which means
that Envoy will use the protocol indicated by the attached BackendRef. |
| `compression` | _[Compression](#compression) array_ | false | | The compression config for the http streams. |
| `responseOverride` | _[ResponseOverride](#responseoverride) array_ | false | | ResponseOverride defines the configuration to override specific responses with a custom one.
If multiple configurations are specified, the first one to match wins. |
+| `httpUpgrade` | _[ProtocolUpgradeConfig](#protocolupgradeconfig) array_ | false | | HTTPUpgrade defines the configuration for HTTP protocol upgrades.
If not specified, the default upgrade configuration(websocket) will be used. |
#### BasicAuth
@@ -3221,7 +3222,6 @@ _Appears in:_
| Field | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `type` | _string_ | true | | Type is the case-insensitive type of protocol upgrade.
e.g. `websocket`, `CONNECT`, `spdy/3.1` etc. |
-| `disabled` | _boolean_ | false | | Disabled indicates whether the upgrade is disabled. |
#### ProviderType