Repository: envoyproxy/gateway
Pull request #4887 (2024-12-10 20:41:27)

ci: fix license scan
**What this PR does / why we need it**:
Fix license scan by overriding unidentified license of `github.com/golang/groupcache`. Also opened an issue in [deps.dev](https://github.com/google/deps.dev/issues/119).

Release Notes: No


shahar-h: Still fails with:
`Failed to run code analysis (govulncheck) on '/github/workspace/go.mod' because govulncheck: loading packages: err: exit status 1: stderr: go: go.mod requires go >= 1.23.3 (running go 1.22.7; GOTOOLCHAIN=local)`

I'm looking into it.
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/envoyproxy/gateway/pull/4887?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=envoyproxy) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 66.29%. Comparing base [(`0898544`)](https://app.codecov.io/gh/envoyproxy/gateway/commit/0898544d9777934b5afdcd27f65dd6f73d78a90e?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=envoyproxy) to head [(`95f9e3b`)](https://app.codecov.io/gh/envoyproxy/gateway/commit/95f9e3b88e36a727f629c66fd191152cbfa09d6f?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=envoyproxy).

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main    #4887      +/-   ##
==========================================
- Coverage   66.31%   66.29%   -0.02%     
==========================================
  Files         209      209              
  Lines       31955    31955              
==========================================
- Hits        21190    21186       -4     
- Misses       9518     9521       +3     
- Partials     1247     1248       +1     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/envoyproxy/gateway/pull/4887?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=envoyproxy).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=envoyproxy).

zhaohuabing: @shahar-h Could you please help me understand why osv scanner report unknown licensece for github.com/golang/groupcache 0.0.0-20241129210726-2c02b8208cf8 ? This version is on this page: https://pkg.go.dev/github.com/golang/groupcache?tab=versions 

Patch:
diff --git a/internal/gatewayapi/backendtrafficpolicy.go b/internal/gatewayapi/backendtrafficpolicy.go
index 7a7cd1369..093462942 100644
--- a/internal/gatewayapi/backendtrafficpolicy.go
+++ b/internal/gatewayapi/backendtrafficpolicy.go
@@ -335,7 +335,7 @@ func (t *Translator) translateBackendTrafficPolicyForRoute(
 	if policy.Spec.Retry != nil {
 		rt = buildRetry(policy.Spec.Retry)
 	}
-	if to, err = buildClusterSettingsTimeout(policy.Spec.ClusterSettings, nil); err != nil {
+	if to, err = buildClusterSettingsTimeout(policy.Spec.ClusterSettings); err != nil {
 		err = perr.WithMessage(err, "Timeout")
 		errs = errors.Join(errs, err)
 	}
@@ -399,8 +399,7 @@ func (t *Translator) translateBackendTrafficPolicyForRoute(
 						continue
 					}
 
-					// Some timeout setting originate from the route.
-					if localTo, err := buildClusterSettingsTimeout(policy.Spec.ClusterSettings, r.Traffic); err == nil {
+					if localTo, err := buildClusterSettingsTimeout(policy.Spec.ClusterSettings); err == nil {
 						to = localTo
 					}
 
@@ -484,7 +483,7 @@ func (t *Translator) translateBackendTrafficPolicyForGateway(
 	if policy.Spec.Retry != nil {
 		rt = buildRetry(policy.Spec.Retry)
 	}
-	if ct, err = buildClusterSettingsTimeout(policy.Spec.ClusterSettings, nil); err != nil {
+	if ct, err = buildClusterSettingsTimeout(policy.Spec.ClusterSettings); err != nil {
 		err = perr.WithMessage(err, "Timeout")
 		errs = errors.Join(errs, err)
 	}
@@ -585,7 +584,7 @@ func (t *Translator) translateBackendTrafficPolicyForGateway(
 			// Update the Host field in HealthCheck, now that we have access to the Route Hostname.
 			r.Traffic.HealthCheck.SetHTTPHostIfAbsent(r.Hostname)
 
-			if ct, err = buildClusterSettingsTimeout(policy.Spec.ClusterSettings, r.Traffic); err == nil {
+			if ct, err = buildClusterSettingsTimeout(policy.Spec.ClusterSettings); err == nil {
 				r.Traffic.Timeout = ct
 			}
 
diff --git a/internal/gatewayapi/clustersettings.go b/internal/gatewayapi/clustersettings.go
index 260c1db84..40266553b 100644
--- a/internal/gatewayapi/clustersettings.go
+++ b/internal/gatewayapi/clustersettings.go
@@ -29,7 +29,7 @@ func translateTrafficFeatures(policy *egv1a1.ClusterSettings) (*ir.TrafficFeatur
 	}
 	ret := &ir.TrafficFeatures{}
 
-	if timeout, err := buildClusterSettingsTimeout(*policy, nil); err != nil {
+	if timeout, err := buildClusterSettingsTimeout(*policy); err != nil {
 		return nil, err
 	} else {
 		ret.Timeout = timeout
@@ -83,14 +83,11 @@ func translateTrafficFeatures(policy *egv1a1.ClusterSettings) (*ir.TrafficFeatur
 	return ret, nil
 }
 
-func buildClusterSettingsTimeout(policy egv1a1.ClusterSettings, routeTrafficFeatures *ir.TrafficFeatures) (*ir.Timeout, error) {
+func buildClusterSettingsTimeout(policy egv1a1.ClusterSettings) (*ir.Timeout, error) {
 	if policy.Timeout == nil {
-		if routeTrafficFeatures != nil {
-			// Don't lose any existing timeout definitions.
-			return mergeTimeoutSettings(nil, routeTrafficFeatures.Timeout), nil
-		}
 		return nil, nil
 	}
+
 	var (
 		errs error
 		to   = &ir.Timeout{}
@@ -146,40 +143,9 @@ func buildClusterSettingsTimeout(policy egv1a1.ClusterSettings, routeTrafficFeat
 			RequestTimeout:        rt,
 		}
 	}
-
-	// The timeout from route's TrafficFeatures takes precedence over the timeout in BTP
-	if routeTrafficFeatures != nil {
-		to = mergeTimeoutSettings(routeTrafficFeatures.Timeout, to)
-	}
-
 	return to, errs
 }
 
-// merge secondary into main if both are not nil, otherwise return the
-// one that is not nil. If both are nil, returns nil
-func mergeTimeoutSettings(main, secondary *ir.Timeout) *ir.Timeout {
-	switch {
-	case main == nil && secondary == nil:
-		return nil
-	case main == nil:
-		return secondary.DeepCopy()
-	case secondary == nil:
-		return main
-	default: // Neither main nor secondary are nil here
-		if secondary.HTTP != nil {
-			setIfNil(&main.HTTP, &ir.HTTPTimeout{})
-			setIfNil(&main.HTTP.RequestTimeout, secondary.HTTP.RequestTimeout)
-			setIfNil(&main.HTTP.ConnectionIdleTimeout, secondary.HTTP.ConnectionIdleTimeout)
-			setIfNil(&main.HTTP.MaxConnectionDuration, secondary.HTTP.MaxConnectionDuration)
-		}
-		if secondary.TCP != nil {
-			setIfNil(&main.TCP, &ir.TCPTimeout{})
-			setIfNil(&main.TCP.ConnectTimeout, secondary.TCP.ConnectTimeout)
-		}
-		return main
-	}
-}
-
 func buildBackendConnection(policy egv1a1.ClusterSettings) (*ir.BackendConnection, error) {
 	if policy.Connection == nil {
 		return nil, nil
diff --git a/internal/gatewayapi/route.go b/internal/gatewayapi/route.go
index 544bb85d6..ba51d964e 100644
--- a/internal/gatewayapi/route.go
+++ b/internal/gatewayapi/route.go
@@ -266,14 +266,12 @@ func (t *Translator) processHTTPRouteRules(httpRoute *HTTPRouteContext, parentRe
 
 func processRouteTimeout(irRoute *ir.HTTPRoute, rule gwapiv1.HTTPRouteRule) {
 	if rule.Timeouts != nil {
-		rto := &ir.Timeout{}
-
 		if rule.Timeouts.Request != nil {
 			d, err := time.ParseDuration(string(*rule.Timeouts.Request))
 			if err != nil {
 				d, _ = time.ParseDuration(HTTPRequestTimeout)
 			}
-			setRequestTimeout(rto, metav1.Duration{Duration: d})
+			irRoute.Timeout = ptr.To(metav1.Duration{Duration: d})
 		}
 
 		// Also set the IR Route Timeout to the backend request timeout
@@ -283,23 +281,8 @@ func processRouteTimeout(irRoute *ir.HTTPRoute, rule gwapiv1.HTTPRouteRule) {
 			if err != nil {
 				d, _ = time.ParseDuration(HTTPRequestTimeout)
 			}
-			setRequestTimeout(rto, metav1.Duration{Duration: d})
-		}
-
-		irRoute.Traffic = &ir.TrafficFeatures{
-			Timeout: rto,
-		}
-	}
-}
-
-func setRequestTimeout(irTimeout *ir.Timeout, d metav1.Duration) {
-	switch {
-	case irTimeout.HTTP == nil:
-		irTimeout.HTTP = &ir.HTTPTimeout{
-			RequestTimeout: ptr.To(d),
+			irRoute.Timeout = ptr.To(metav1.Duration{Duration: d})
 		}
-	default:
-		irTimeout.HTTP.RequestTimeout = ptr.To(d)
 	}
 }
 
@@ -760,11 +743,11 @@ func (t *Translator) processHTTPRouteParentRefListener(route RouteContext, route
 					ExtensionRefs:         routeRoute.ExtensionRefs,
 					IsHTTP2:               routeRoute.IsHTTP2,
 					SessionPersistence:    routeRoute.SessionPersistence,
+					Timeout:               routeRoute.Timeout,
 				}
 				if routeRoute.Traffic != nil {
 					hostRoute.Traffic = &ir.TrafficFeatures{
-						Timeout: routeRoute.Traffic.Timeout,
-						Retry:   routeRoute.Traffic.Retry,
+						Retry: routeRoute.Traffic.Retry,
 					}
 				}
 				perHostRoutes = append(perHostRoutes, hostRoute)
diff --git a/internal/ir/xds.go b/internal/ir/xds.go
index 00924ed9f..4db7b8445 100644
--- a/internal/ir/xds.go
+++ b/internal/ir/xds.go
@@ -691,6 +691,8 @@ type HTTPRoute struct {
 	Metadata *ResourceMetadata `json:"metadata,omitempty" yaml:"metadata,omitempty"`
 	// SessionPersistence holds the configuration for session persistence.
 	SessionPersistence *SessionPersistence `json:"sessionPersistence,omitempty" yaml:"sessionPersistence,omitempty"`
+	// Timeout is the time until which entire response is received from the upstream.
+	Timeout *metav1.Duration `json:"timeout,omitempty" yaml:"timeout,omitempty"`
 }
 
 // DNS contains configuration options for DNS resolution.
diff --git a/internal/ir/zz_generated.deepcopy.go b/internal/ir/zz_generated.deepcopy.go
index fbdb444e4..0c734dbec 100644
--- a/internal/ir/zz_generated.deepcopy.go
+++ b/internal/ir/zz_generated.deepcopy.go
@@ -1569,6 +1569,11 @@ func (in *HTTPRoute) DeepCopyInto(out *HTTPRoute) {
 		*out = new(SessionPersistence)
 		(*in).DeepCopyInto(*out)
 	}
+	if in.Timeout != nil {
+		in, out := &in.Timeout, &out.Timeout
+		*out = new(v1.Duration)
+		**out = **in
+	}
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPRoute.
diff --git a/internal/xds/translator/route.go b/internal/xds/translator/route.go
index 3750194fb..330484d41 100644
--- a/internal/xds/translator/route.go
+++ b/internal/xds/translator/route.go
@@ -16,6 +16,7 @@ import (
 	matcherv3 "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3"
 	"google.golang.org/protobuf/types/known/durationpb"
 	"google.golang.org/protobuf/types/known/wrapperspb"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 
 	"github.com/envoyproxy/gateway/internal/ir"
 	"github.com/envoyproxy/gateway/internal/utils/protocov"
@@ -96,12 +97,11 @@ func buildXdsRoute(httpRoute *ir.HTTPRoute) (*routev3.Route, error) {
 	}
 
 	// Timeouts
-	if router.GetRoute() != nil &&
-		httpRoute.Traffic != nil &&
-		httpRoute.Traffic.Timeout != nil &&
-		httpRoute.Traffic.Timeout.HTTP != nil &&
-		httpRoute.Traffic.Timeout.HTTP.RequestTimeout != nil {
-		router.GetRoute().Timeout = durationpb.New(httpRoute.Traffic.Timeout.HTTP.RequestTimeout.Duration)
+	if router.GetRoute() != nil {
+		rt := getEffectiveRequestTimeout(httpRoute)
+		if rt != nil {
+			router.GetRoute().Timeout = durationpb.New(rt.Duration)
+		}
 	}
 
 	// Retries
@@ -292,14 +292,26 @@ func buildXdsWeightedRouteAction(backendWeights *ir.BackendWeights, settings []*
 	}
 }
 
-func idleTimeout(httpRoute *ir.HTTPRoute) *durationpb.Duration {
+func getEffectiveRequestTimeout(httpRoute *ir.HTTPRoute) *metav1.Duration {
+	// gateway-api timeout takes precedence
+	if httpRoute.Timeout != nil {
+		return httpRoute.Timeout
+	}
+
 	if httpRoute.Traffic != nil &&
 		httpRoute.Traffic.Timeout != nil &&
 		httpRoute.Traffic.Timeout.HTTP != nil &&
 		httpRoute.Traffic.Timeout.HTTP.RequestTimeout != nil {
-		rt := httpRoute.Traffic.Timeout.HTTP.RequestTimeout
-		timeout := time.Hour // Default to 1 hour
+		return httpRoute.Traffic.Timeout.HTTP.RequestTimeout
+	}
 
+	return nil
+}
+
+func idleTimeout(httpRoute *ir.HTTPRoute) *durationpb.Duration {
+	rt := getEffectiveRequestTimeout(httpRoute)
+	timeout := time.Hour // Default to 1 hour
+	if rt != nil {
 		// Ensure is not less than the request timeout
 		if timeout < rt.Duration {
 			timeout = rt.Duration
diff --git a/osv-scanner.toml b/osv-scanner.toml
index bed9a0c7a..9f37555bc 100644
--- a/osv-scanner.toml
+++ b/osv-scanner.toml
@@ -68,6 +68,13 @@ ecosystem = "Go"
 license.ignore = true
 reason = "This package has MPL-2.0 which is not approved in CNCF Allowlist, but it has an exception. See https://github.com/cncf/foundation/blob/main/license-exceptions/cncf-exceptions-2023-08-31.spdx"
 
+[[PackageOverrides]]
+name = "github.com/golang/groupcache"
+version = "0.0.0-20241129210726-2c02b8208cf8"
+ecosystem = "Go"
+license.override = ["Apache-2.0 "]
+reason = "Unidentified license, remove once https://github.com/google/deps.dev/issues/119 is resolved"
+
 [[PackageOverrides]]
 name = "stdlib"
 ecosystem = "Go"
diff --git a/release-notes/current.yaml b/release-notes/current.yaml
index 66c24cba4..123759f7a 100644
--- a/release-notes/current.yaml
+++ b/release-notes/current.yaml
@@ -21,6 +21,7 @@ new features: |
 bug fixes: |
   Fixed BackendTLSPolicy didn't support using port name as the sectionName in the targetRefs
   Fixed reference grant from EnvoyExtensionPolicy to referenced ext-proc backend not respected
+  Fixed BackendTrafficPolicy not applying to Gateway Route when Route has a Request Timeout defined
 
 # Enhancements that improve performance.
 performance improvements: |


Test patch:
diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-httproute-timeout.in.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-httproute-timeout.in.yaml
index e26f10c35..33ae5c948 100644
--- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-httproute-timeout.in.yaml
+++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-httproute-timeout.in.yaml
@@ -35,6 +35,27 @@ httpRoutes:
         port: 8080
       timeouts:
         request: 130s
+- 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: "/"
+      backendRefs:
+      - name: service-1
+        port: 8080
+      timeouts:
+        request: 130s
 backendTrafficPolicies:
 - apiVersion: gateway.envoyproxy.io/v1alpha1
   kind: BackendTrafficPolicy
@@ -47,4 +68,20 @@ backendTrafficPolicies:
       kind: HTTPRoute
       name: httproute-1
     useClientProtocol: true
-
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+  kind: BackendTrafficPolicy
+  metadata:
+    namespace: envoy-gateway
+    name: policy-for-gateway
+  spec:
+    targetRef:
+      group: gateway.networking.k8s.io
+      kind: Gateway
+      name: gateway-1
+    useClientProtocol: true
+    loadBalancer:
+      type: ConsistentHash
+      consistentHash:
+        type: Cookie
+        cookie:
+          name: "test"
diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-httproute-timeout.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-httproute-timeout.out.yaml
index 245739ca2..c60ae5b23 100644
--- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-httproute-timeout.out.yaml
+++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-httproute-timeout.out.yaml
@@ -26,6 +26,44 @@ backendTrafficPolicies:
         status: "True"
         type: Accepted
       controllerName: gateway.envoyproxy.io/gatewayclass-controller
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+  kind: BackendTrafficPolicy
+  metadata:
+    creationTimestamp: null
+    name: policy-for-gateway
+    namespace: envoy-gateway
+  spec:
+    loadBalancer:
+      consistentHash:
+        cookie:
+          name: test
+        type: Cookie
+      type: ConsistentHash
+    targetRef:
+      group: gateway.networking.k8s.io
+      kind: Gateway
+      name: gateway-1
+    useClientProtocol: true
+  status:
+    ancestors:
+    - ancestorRef:
+        group: gateway.networking.k8s.io
+        kind: Gateway
+        name: gateway-1
+        namespace: envoy-gateway
+      conditions:
+      - lastTransitionTime: null
+        message: Policy has been accepted.
+        reason: Accepted
+        status: "True"
+        type: Accepted
+      - lastTransitionTime: null
+        message: 'This policy is being overridden by other backendTrafficPolicies
+          for these routes: [default/httproute-1]'
+        reason: Overridden
+        status: "True"
+        type: Overridden
+      controllerName: gateway.envoyproxy.io/gatewayclass-controller
 gateways:
 - apiVersion: gateway.networking.k8s.io/v1
   kind: Gateway
@@ -44,7 +82,7 @@ gateways:
       protocol: HTTP
   status:
     listeners:
-    - attachedRoutes: 1
+    - attachedRoutes: 2
       conditions:
       - lastTransitionTime: null
         message: Sending translated listener configuration to the data plane
@@ -108,6 +146,46 @@ httpRoutes:
         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
+      matches:
+      - path:
+          value: /
+      timeouts:
+        request: 130s
+  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:
@@ -165,8 +243,33 @@ xdsIR:
           distinct: false
           name: ""
           prefix: /
+        timeout: 2m10s
+        traffic: {}
+        useClientProtocol: true
+      - 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: /
+        timeout: 2m10s
         traffic:
-          timeout:
-            http:
-              requestTimeout: 2m10s
+          loadBalancer:
+            consistentHash:
+              cookie:
+                name: test
         useClientProtocol: true
diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-timeout.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-timeout.out.yaml
index 0fad514c5..7177bcae0 100644
--- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-timeout.out.yaml
+++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-timeout.out.yaml
@@ -336,11 +336,12 @@ xdsIR:
           distinct: false
           name: ""
           prefix: /
+        timeout: 1s
         traffic:
           timeout:
             http:
               connectionIdleTimeout: 21s
               maxConnectionDuration: 22s
-              requestTimeout: 1s
+              requestTimeout: 23s
             tcp:
               connectTimeout: 20s
diff --git a/internal/gatewayapi/testdata/httproute-and-backendtrafficpolicy-with-timeout-error.out.yaml b/internal/gatewayapi/testdata/httproute-and-backendtrafficpolicy-with-timeout-error.out.yaml
index bda6f990d..6cbe82eca 100644
--- a/internal/gatewayapi/testdata/httproute-and-backendtrafficpolicy-with-timeout-error.out.yaml
+++ b/internal/gatewayapi/testdata/httproute-and-backendtrafficpolicy-with-timeout-error.out.yaml
@@ -171,7 +171,4 @@ xdsIR:
           distinct: false
           name: ""
           prefix: /
-        traffic:
-          timeout:
-            http:
-              requestTimeout: 1s
+        timeout: 1s
diff --git a/internal/gatewayapi/testdata/httproute-and-backendtrafficpolicy-with-timeout.out.yaml b/internal/gatewayapi/testdata/httproute-and-backendtrafficpolicy-with-timeout.out.yaml
index 04843eba9..60c001200 100644
--- a/internal/gatewayapi/testdata/httproute-and-backendtrafficpolicy-with-timeout.out.yaml
+++ b/internal/gatewayapi/testdata/httproute-and-backendtrafficpolicy-with-timeout.out.yaml
@@ -332,10 +332,10 @@ xdsIR:
           distinct: false
           name: ""
           prefix: /
+        timeout: 1s
         traffic:
           timeout:
             http:
               maxConnectionDuration: 22s
-              requestTimeout: 1s
             tcp:
               connectTimeout: 20s
diff --git a/internal/gatewayapi/testdata/httproute-backend-request-timeout.out.yaml b/internal/gatewayapi/testdata/httproute-backend-request-timeout.out.yaml
index c49d551e8..189c96889 100644
--- a/internal/gatewayapi/testdata/httproute-backend-request-timeout.out.yaml
+++ b/internal/gatewayapi/testdata/httproute-backend-request-timeout.out.yaml
@@ -135,7 +135,4 @@ xdsIR:
           distinct: false
           name: ""
           prefix: /
-        traffic:
-          timeout:
-            http:
-              requestTimeout: 1s
+        timeout: 1s
diff --git a/internal/gatewayapi/testdata/httproute-request-timeout.out.yaml b/internal/gatewayapi/testdata/httproute-request-timeout.out.yaml
index dc1c9cb95..88b1dc893 100644
--- a/internal/gatewayapi/testdata/httproute-request-timeout.out.yaml
+++ b/internal/gatewayapi/testdata/httproute-request-timeout.out.yaml
@@ -135,7 +135,4 @@ xdsIR:
           distinct: false
           name: ""
           prefix: /
-        traffic:
-          timeout:
-            http:
-              requestTimeout: 5s
+        timeout: 5s
diff --git a/internal/xds/translator/testdata/in/xds-ir/http-route-timeout.yaml b/internal/xds/translator/testdata/in/xds-ir/http-route-timeout.yaml
index b3ab173f7..f14367f48 100644
--- a/internal/xds/translator/testdata/in/xds-ir/http-route-timeout.yaml
+++ b/internal/xds/translator/testdata/in/xds-ir/http-route-timeout.yaml
@@ -48,3 +48,47 @@ http:
       - endpoints:
         - host: "1.2.3.4"
           port: 50002
+  - name: "forth-route"
+    hostname: "*"
+    timeout: 10s
+    traffic:
+      timeout:
+        http:
+          requestTimeout: 5s
+    destination:
+      name: "fourth-route-dest"
+      settings:
+      - endpoints:
+        - host: "1.2.3.4"
+          port: 50002
+  - name: "fifth-route"
+    hostname: "*"
+    timeout: 10s
+    destination:
+      name: "fifth-route-dest"
+      settings:
+      - endpoints:
+        - host: "1.2.3.4"
+          port: 50002
+  - name: "sixth-route"
+    hostname: "*"
+    timeout: 0s
+    destination:
+      name: "sixth-route-dest"
+      settings:
+      - endpoints:
+        - host: "1.2.3.4"
+          port: 50002
+  - name: "seventh-route"
+    hostname: "*"
+    timeout: 0s
+    traffic:
+      timeout:
+        http:
+          requestTimeout: 5s
+    destination:
+      name: "seventh-route-dest"
+      settings:
+      - endpoints:
+        - host: "1.2.3.4"
+          port: 50002
diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-timeout.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-timeout.clusters.yaml
index 121c8aad8..a52b95bae 100644
--- a/internal/xds/translator/testdata/out/xds-ir/http-route-timeout.clusters.yaml
+++ b/internal/xds/translator/testdata/out/xds-ir/http-route-timeout.clusters.yaml
@@ -49,3 +49,75 @@
   name: third-route-dest
   perConnectionBufferLimitBytes: 32768
   type: EDS
+- circuitBreakers:
+    thresholds:
+    - maxRetries: 1024
+  commonLbConfig:
+    localityWeightedLbConfig: {}
+  connectTimeout: 10s
+  dnsLookupFamily: V4_PREFERRED
+  edsClusterConfig:
+    edsConfig:
+      ads: {}
+      resourceApiVersion: V3
+    serviceName: fourth-route-dest
+  ignoreHealthOnHostRemoval: true
+  lbPolicy: LEAST_REQUEST
+  name: fourth-route-dest
+  outlierDetection: {}
+  perConnectionBufferLimitBytes: 32768
+  type: EDS
+- circuitBreakers:
+    thresholds:
+    - maxRetries: 1024
+  commonLbConfig:
+    localityWeightedLbConfig: {}
+  connectTimeout: 10s
+  dnsLookupFamily: V4_PREFERRED
+  edsClusterConfig:
+    edsConfig:
+      ads: {}
+      resourceApiVersion: V3
+    serviceName: fifth-route-dest
+  ignoreHealthOnHostRemoval: true
+  lbPolicy: LEAST_REQUEST
+  name: fifth-route-dest
+  outlierDetection: {}
+  perConnectionBufferLimitBytes: 32768
+  type: EDS
+- circuitBreakers:
+    thresholds:
+    - maxRetries: 1024
+  commonLbConfig:
+    localityWeightedLbConfig: {}
+  connectTimeout: 10s
+  dnsLookupFamily: V4_PREFERRED
+  edsClusterConfig:
+    edsConfig:
+      ads: {}
+      resourceApiVersion: V3
+    serviceName: sixth-route-dest
+  ignoreHealthOnHostRemoval: true
+  lbPolicy: LEAST_REQUEST
+  name: sixth-route-dest
+  outlierDetection: {}
+  perConnectionBufferLimitBytes: 32768
+  type: EDS
+- circuitBreakers:
+    thresholds:
+    - maxRetries: 1024
+  commonLbConfig:
+    localityWeightedLbConfig: {}
+  connectTimeout: 10s
+  dnsLookupFamily: V4_PREFERRED
+  edsClusterConfig:
+    edsConfig:
+      ads: {}
+      resourceApiVersion: V3
+    serviceName: seventh-route-dest
+  ignoreHealthOnHostRemoval: true
+  lbPolicy: LEAST_REQUEST
+  name: seventh-route-dest
+  outlierDetection: {}
+  perConnectionBufferLimitBytes: 32768
+  type: EDS
diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-timeout.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-timeout.endpoints.yaml
index 42a346c40..59669a901 100644
--- a/internal/xds/translator/testdata/out/xds-ir/http-route-timeout.endpoints.yaml
+++ b/internal/xds/translator/testdata/out/xds-ir/http-route-timeout.endpoints.yaml
@@ -34,3 +34,51 @@
     loadBalancingWeight: 1
     locality:
       region: third-route-dest/backend/0
+- clusterName: fourth-route-dest
+  endpoints:
+  - lbEndpoints:
+    - endpoint:
+        address:
+          socketAddress:
+            address: 1.2.3.4
+            portValue: 50002
+      loadBalancingWeight: 1
+    loadBalancingWeight: 1
+    locality:
+      region: fourth-route-dest/backend/0
+- clusterName: fifth-route-dest
+  endpoints:
+  - lbEndpoints:
+    - endpoint:
+        address:
+          socketAddress:
+            address: 1.2.3.4
+            portValue: 50002
+      loadBalancingWeight: 1
+    loadBalancingWeight: 1
+    locality:
+      region: fifth-route-dest/backend/0
+- clusterName: sixth-route-dest
+  endpoints:
+  - lbEndpoints:
+    - endpoint:
+        address:
+          socketAddress:
+            address: 1.2.3.4
+            portValue: 50002
+      loadBalancingWeight: 1
+    loadBalancingWeight: 1
+    locality:
+      region: sixth-route-dest/backend/0
+- clusterName: seventh-route-dest
+  endpoints:
+  - lbEndpoints:
+    - endpoint:
+        address:
+          socketAddress:
+            address: 1.2.3.4
+            portValue: 50002
+      loadBalancingWeight: 1
+    loadBalancingWeight: 1
+    locality:
+      region: seventh-route-dest/backend/0
diff --git a/internal/xds/translator/testdata/out/xds-ir/http-route-timeout.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/http-route-timeout.routes.yaml
index 23d2bf4b7..1c335ad62 100644
--- a/internal/xds/translator/testdata/out/xds-ir/http-route-timeout.routes.yaml
+++ b/internal/xds/translator/testdata/out/xds-ir/http-route-timeout.routes.yaml
@@ -36,3 +36,39 @@
         timeout: 0s
         upgradeConfigs:
         - upgradeType: websocket
+    - match:
+        prefix: /
+      name: forth-route
+      route:
+        cluster: fourth-route-dest
+        idleTimeout: 3600s
+        timeout: 10s
+        upgradeConfigs:
+        - upgradeType: websocket
+    - match:
+        prefix: /
+      name: fifth-route
+      route:
+        cluster: fifth-route-dest
+        idleTimeout: 3600s
+        timeout: 10s
+        upgradeConfigs:
+        - upgradeType: websocket
+    - match:
+        prefix: /
+      name: sixth-route
+      route:
+        cluster: sixth-route-dest
+        idleTimeout: 0s
+        timeout: 0s
+        upgradeConfigs:
+        - upgradeType: websocket
+    - match:
+        prefix: /
+      name: seventh-route
+      route:
+        cluster: seventh-route-dest
+        idleTimeout: 0s
+        timeout: 0s
+        upgradeConfigs:
+        - upgradeType: websocket