Skip to content

Commit 9071f14

Browse files
davidwin93RoseWrightdev
authored andcommitted
Add 307 / 308 Redirect Status Code Support (kubernetes-sigs#3823)
* add support for 303 status and split 307/308 into seperate features * add tests for 303 redirect * split 307 and 308 redirect features * update documentation to cover 303,307,308 status codes * add redirect status codes to httproute crd enum * update conformance tests to focus only on status code validation
1 parent 4ee390f commit 9071f14

20 files changed

Lines changed: 724 additions & 2 deletions

apis/v1/httproute_types.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1270,7 +1270,7 @@ type HTTPRequestRedirectFilter struct {
12701270
//
12711271
// +optional
12721272
// +kubebuilder:default=302
1273-
// +kubebuilder:validation:Enum=301;302
1273+
// +kubebuilder:validation:Enum=301;302;303;307;308
12741274
StatusCode *int `json:"statusCode,omitempty"`
12751275
}
12761276

config/crd/experimental/gateway.networking.k8s.io_httproutes.yaml

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/crd/standard/gateway.networking.k8s.io_httproutes.yaml

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
Copyright 2025 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package tests
18+
19+
import (
20+
"testing"
21+
22+
"k8s.io/apimachinery/pkg/types"
23+
24+
"sigs.k8s.io/gateway-api/conformance/utils/http"
25+
"sigs.k8s.io/gateway-api/conformance/utils/kubernetes"
26+
"sigs.k8s.io/gateway-api/conformance/utils/roundtripper"
27+
"sigs.k8s.io/gateway-api/conformance/utils/suite"
28+
"sigs.k8s.io/gateway-api/pkg/features"
29+
)
30+
31+
func init() {
32+
ConformanceTests = append(ConformanceTests, HTTPRoute303Redirect)
33+
}
34+
35+
var HTTPRoute303Redirect = suite.ConformanceTest{
36+
ShortName: "HTTPRoute303Redirect",
37+
Description: "An HTTPRoute with a 303 redirect filter",
38+
Manifests: []string{"tests/httproute-303-redirect.yaml"},
39+
Provisional: true,
40+
Features: []features.FeatureName{
41+
features.SupportGateway,
42+
features.SupportHTTPRoute,
43+
features.SupportHTTPRoute303RedirectStatusCode,
44+
},
45+
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
46+
ns := "gateway-conformance-infra"
47+
routeNN := types.NamespacedName{Name: "redirect-path", Namespace: ns}
48+
gwNN := types.NamespacedName{Name: "same-namespace", Namespace: ns}
49+
gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN)
50+
kubernetes.HTTPRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN)
51+
52+
testCases := []http.ExpectedResponse{
53+
{
54+
Request: http.Request{
55+
Path: "/see-other",
56+
UnfollowRedirect: true,
57+
Method: "POST",
58+
},
59+
Response: http.Response{
60+
StatusCode: 303,
61+
},
62+
RedirectRequest: &roundtripper.RedirectRequest{
63+
Path: "/see-other",
64+
},
65+
Namespace: ns,
66+
},
67+
}
68+
for i := range testCases {
69+
// Declare tc here to avoid loop variable
70+
// reuse issues across parallel tests.
71+
tc := testCases[i]
72+
t.Run(tc.GetTestCaseName(i), func(t *testing.T) {
73+
t.Parallel()
74+
http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, tc)
75+
})
76+
}
77+
},
78+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
apiVersion: gateway.networking.k8s.io/v1
2+
kind: HTTPRoute
3+
metadata:
4+
name: 303-redirect
5+
namespace: gateway-conformance-infra
6+
spec:
7+
parentRefs:
8+
- name: same-namespace
9+
rules:
10+
- matches:
11+
- path:
12+
type: PathPrefix
13+
value: /see-other
14+
filters:
15+
- type: RequestRedirect
16+
requestRedirect:
17+
statusCode: 303
18+
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
Copyright 2025 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package tests
18+
19+
import (
20+
"testing"
21+
22+
"k8s.io/apimachinery/pkg/types"
23+
24+
"sigs.k8s.io/gateway-api/conformance/utils/http"
25+
"sigs.k8s.io/gateway-api/conformance/utils/kubernetes"
26+
"sigs.k8s.io/gateway-api/conformance/utils/roundtripper"
27+
"sigs.k8s.io/gateway-api/conformance/utils/suite"
28+
"sigs.k8s.io/gateway-api/pkg/features"
29+
)
30+
31+
func init() {
32+
ConformanceTests = append(ConformanceTests, HTTPRoute307Redirect)
33+
}
34+
35+
var HTTPRoute307Redirect = suite.ConformanceTest{
36+
ShortName: "HTTPRoute307Redirect",
37+
Description: "An HTTPRoute with a 307 path redirect filter",
38+
Manifests: []string{"tests/httproute-307-redirect.yaml"},
39+
Provisional: true,
40+
Features: []features.FeatureName{
41+
features.SupportGateway,
42+
features.SupportHTTPRoute,
43+
features.SupportHTTPRoute307RedirectStatusCode,
44+
},
45+
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
46+
ns := "gateway-conformance-infra"
47+
routeNN := types.NamespacedName{Name: "redirect-path", Namespace: ns}
48+
gwNN := types.NamespacedName{Name: "same-namespace", Namespace: ns}
49+
gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN)
50+
kubernetes.HTTPRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN)
51+
52+
testCases := []http.ExpectedResponse{
53+
{
54+
Request: http.Request{
55+
Path: "/temporary",
56+
UnfollowRedirect: true,
57+
},
58+
Response: http.Response{
59+
StatusCode: 307,
60+
},
61+
RedirectRequest: &roundtripper.RedirectRequest{
62+
Path: "/temporary",
63+
},
64+
Namespace: ns,
65+
},
66+
}
67+
for i := range testCases {
68+
// Declare tc here to avoid loop variable
69+
// reuse issues across parallel tests.
70+
tc := testCases[i]
71+
t.Run(tc.GetTestCaseName(i), func(t *testing.T) {
72+
t.Parallel()
73+
http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, tc)
74+
})
75+
}
76+
},
77+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
apiVersion: gateway.networking.k8s.io/v1
2+
kind: HTTPRoute
3+
metadata:
4+
name: 307-redirect
5+
namespace: gateway-conformance-infra
6+
spec:
7+
parentRefs:
8+
- name: same-namespace
9+
rules:
10+
- matches:
11+
- path:
12+
type: PathPrefix
13+
value: /temporary
14+
filters:
15+
- type: RequestRedirect
16+
requestRedirect:
17+
statusCode: 307
18+
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
Copyright 2025 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package tests
18+
19+
import (
20+
"testing"
21+
22+
"k8s.io/apimachinery/pkg/types"
23+
24+
"sigs.k8s.io/gateway-api/conformance/utils/http"
25+
"sigs.k8s.io/gateway-api/conformance/utils/kubernetes"
26+
"sigs.k8s.io/gateway-api/conformance/utils/roundtripper"
27+
"sigs.k8s.io/gateway-api/conformance/utils/suite"
28+
"sigs.k8s.io/gateway-api/pkg/features"
29+
)
30+
31+
func init() {
32+
ConformanceTests = append(ConformanceTests, HTTPRoute308Redirect)
33+
}
34+
35+
var HTTPRoute308Redirect = suite.ConformanceTest{
36+
ShortName: "HTTPRoute308Redirect",
37+
Description: "An HTTPRoute with a 308 path redirect filter",
38+
Manifests: []string{"tests/httproute-308-redirect.yaml"},
39+
Provisional: true,
40+
Features: []features.FeatureName{
41+
features.SupportGateway,
42+
features.SupportHTTPRoute,
43+
features.SupportHTTPRoute308RedirectStatusCode,
44+
},
45+
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
46+
ns := "gateway-conformance-infra"
47+
routeNN := types.NamespacedName{Name: "redirect-path", Namespace: ns}
48+
gwNN := types.NamespacedName{Name: "same-namespace", Namespace: ns}
49+
gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN)
50+
kubernetes.HTTPRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN)
51+
52+
testCases := []http.ExpectedResponse{
53+
{
54+
Request: http.Request{
55+
Path: "/permanent",
56+
UnfollowRedirect: true,
57+
},
58+
Response: http.Response{
59+
StatusCode: 308,
60+
},
61+
RedirectRequest: &roundtripper.RedirectRequest{
62+
Path: "/permanent",
63+
},
64+
Namespace: ns,
65+
},
66+
}
67+
for i := range testCases {
68+
// Declare tc here to avoid loop variable
69+
// reuse issues across parallel tests.
70+
tc := testCases[i]
71+
t.Run(tc.GetTestCaseName(i), func(t *testing.T) {
72+
t.Parallel()
73+
http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, tc)
74+
})
75+
}
76+
},
77+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
apiVersion: gateway.networking.k8s.io/v1
2+
kind: HTTPRoute
3+
metadata:
4+
name: 308-redirect
5+
namespace: gateway-conformance-infra
6+
spec:
7+
parentRefs:
8+
- name: same-namespace
9+
rules:
10+
- matches:
11+
- path:
12+
type: PathPrefix
13+
value: /permanent
14+
filters:
15+
- type: RequestRedirect
16+
requestRedirect:
17+
statusCode: 308
18+

0 commit comments

Comments
 (0)