Skip to content

Commit

Permalink
gateway-api: Add HTTP method condition in sortable routes
Browse files Browse the repository at this point in the history
[ upstream commit a3510fe ]

As per the below, method match should be considered before the largest
of header/query param matches. This commit is to consider method attr
into sorting rule.

Relates: https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.HTTPRouteRule
Signed-off-by: Tam Mach <[email protected]>
Signed-off-by: Nicolas Busseneau <[email protected]>
  • Loading branch information
sayboras authored and nbusseneau committed Aug 6, 2024
1 parent f3f4a38 commit d88772b
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 6 deletions.
24 changes: 24 additions & 0 deletions operator/pkg/model/translation/envoy_virtual_host.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ type VirtualHostMutator func(*envoy_config_route_v3.VirtualHost) *envoy_config_r
// - Exact Match length
// - Regex Match length
// - Prefix match length
// - Method match
// - Number of header matches
// - Number of query parameter matches
//
Expand Down Expand Up @@ -81,6 +82,20 @@ func (s SortableRoute) Less(i, j int) bool {
return prefixMatch1 > prefixMatch2
}

// Next up, sort by method based on :method header
// Give higher priority for the route having method specified
method1 := getMethod(s[i].Match.GetHeaders())
method2 := getMethod(s[j].Match.GetHeaders())
if method1 == nil && method2 != nil {
return false
}
if method1 != nil && method2 == nil {
return true
}
if method1 != nil && *method1 != *method2 {
return *method1 < *method2
}

// If that's the same, then sort by header length
if headerMatch1 != headerMatch2 {
return headerMatch1 > headerMatch2
Expand All @@ -90,6 +105,15 @@ func (s SortableRoute) Less(i, j int) bool {
return queryMatch1 > queryMatch2
}

func getMethod(headers []*envoy_config_route_v3.HeaderMatcher) *string {
for _, h := range headers {
if h.Name == ":method" {
return model.AddressOf(h.GetStringMatch().GetExact())
}
}
return nil
}

func (s SortableRoute) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
Expand Down
90 changes: 88 additions & 2 deletions operator/pkg/model/translation/envoy_virtual_host_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,6 @@ func TestSortableRoute(t *testing.T) {
},
},
},

{
Name: "regex match with two headers",
Match: &envoy_config_route_v3.RouteMatch{
Expand Down Expand Up @@ -124,7 +123,6 @@ func TestSortableRoute(t *testing.T) {
},
},
},

{
Name: "exact match short",
Match: &envoy_config_route_v3.RouteMatch{
Expand All @@ -141,6 +139,46 @@ func TestSortableRoute(t *testing.T) {
},
},
},
{
Name: "exact match long with POST method",
Match: &envoy_config_route_v3.RouteMatch{
PathSpecifier: &envoy_config_route_v3.RouteMatch_Path{
Path: "/exact/match/longest",
},
Headers: []*envoy_config_route_v3.HeaderMatcher{
{
Name: ":method",
HeaderMatchSpecifier: &envoy_config_route_v3.HeaderMatcher_StringMatch{
StringMatch: &envoy_type_matcher_v3.StringMatcher{
MatchPattern: &envoy_type_matcher_v3.StringMatcher_Exact{
Exact: "POST",
},
},
},
},
},
},
},
{
Name: "exact match long with GET method",
Match: &envoy_config_route_v3.RouteMatch{
PathSpecifier: &envoy_config_route_v3.RouteMatch_Path{
Path: "/exact/match/longest",
},
Headers: []*envoy_config_route_v3.HeaderMatcher{
{
Name: ":method",
HeaderMatchSpecifier: &envoy_config_route_v3.HeaderMatcher_StringMatch{
StringMatch: &envoy_type_matcher_v3.StringMatcher{
MatchPattern: &envoy_type_matcher_v3.StringMatcher_Exact{
Exact: "GET",
},
},
},
},
},
},
},
{
Name: "exact match with one header",
Match: &envoy_config_route_v3.RouteMatch{
Expand Down Expand Up @@ -227,6 +265,46 @@ func TestSortableRoute(t *testing.T) {
},
},
},
{
Name: "prefix match short with HEAD method",
Match: &envoy_config_route_v3.RouteMatch{
PathSpecifier: &envoy_config_route_v3.RouteMatch_PathSeparatedPrefix{
PathSeparatedPrefix: "/prefix/match",
},
Headers: []*envoy_config_route_v3.HeaderMatcher{
{
Name: ":method",
HeaderMatchSpecifier: &envoy_config_route_v3.HeaderMatcher_StringMatch{
StringMatch: &envoy_type_matcher_v3.StringMatcher{
MatchPattern: &envoy_type_matcher_v3.StringMatcher_Exact{
Exact: "HEAD",
},
},
},
},
},
},
},
{
Name: "prefix match short with GET method",
Match: &envoy_config_route_v3.RouteMatch{
PathSpecifier: &envoy_config_route_v3.RouteMatch_PathSeparatedPrefix{
PathSeparatedPrefix: "/prefix/match",
},
Headers: []*envoy_config_route_v3.HeaderMatcher{
{
Name: ":method",
HeaderMatchSpecifier: &envoy_config_route_v3.HeaderMatcher_StringMatch{
StringMatch: &envoy_type_matcher_v3.StringMatcher{
MatchPattern: &envoy_type_matcher_v3.StringMatcher_Exact{
Exact: "GET",
},
},
},
},
},
},
},
{
Name: "prefix match long",
Match: &envoy_config_route_v3.RouteMatch{
Expand Down Expand Up @@ -328,10 +406,14 @@ func TestSortableRoute(t *testing.T) {
"regex match with two headers",
"exact match short",
"exact match long",
"exact match long with POST method",
"exact match long with GET method",
"exact match with one header",
"exact match with one header and one query",
"exact match with two headers",
"prefix match short",
"prefix match short with HEAD method",
"prefix match short with GET method",
"prefix match long",
"prefix match with one header",
"prefix match with one header and one query",
Expand All @@ -342,6 +424,8 @@ func TestSortableRoute(t *testing.T) {

namesAfterSort := buildNameSlice(arr)
assert.Equal(t, []string{
"exact match long with GET method",
"exact match long with POST method",
"exact match long",
"exact match with two headers",
"exact match with one header and one query",
Expand All @@ -353,6 +437,8 @@ func TestSortableRoute(t *testing.T) {
"regex match with one header",
"regex match short",
"prefix match long",
"prefix match short with GET method",
"prefix match short with HEAD method",
"prefix match short",
"prefix match with two headers",
"prefix match with one header and one query",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2066,14 +2066,14 @@ var methodMatchingHTTPListenersHTTPListenersCiliumEnvoyConfig = &ciliumv2.Cilium
HeaderMatchSpecifier: &envoy_config_route_v3.HeaderMatcher_StringMatch{
StringMatch: &envoy_type_matcher_v3.StringMatcher{
MatchPattern: &envoy_type_matcher_v3.StringMatcher_Exact{
Exact: "POST",
Exact: "GET",
},
},
},
},
},
},
Action: routeActionBackendV1,
Action: routeActionBackendV2,
},
{
Match: &envoy_config_route_v3.RouteMatch{
Expand All @@ -2086,14 +2086,14 @@ var methodMatchingHTTPListenersHTTPListenersCiliumEnvoyConfig = &ciliumv2.Cilium
HeaderMatchSpecifier: &envoy_config_route_v3.HeaderMatcher_StringMatch{
StringMatch: &envoy_type_matcher_v3.StringMatcher{
MatchPattern: &envoy_type_matcher_v3.StringMatcher_Exact{
Exact: "GET",
Exact: "POST",
},
},
},
},
},
},
Action: routeActionBackendV2,
Action: routeActionBackendV1,
},
},
},
Expand Down

0 comments on commit d88772b

Please sign in to comment.