Skip to content

Commit 895c15c

Browse files
committed
Add regex support for path matching
Problem: As regex path matching is missing support for NGF. Solution: Add support for regex path matching, only allow a full path rewrite/redirect after the regex path match. Testing: Add additional unit test case for regex path match.
1 parent 2f19ba8 commit 895c15c

File tree

14 files changed

+696
-125
lines changed

14 files changed

+696
-125
lines changed

internal/controller/nginx/config/servers.go

Lines changed: 45 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -414,7 +414,7 @@ func initializeExternalLocations(
414414
}
415415
if !exactPathExists {
416416
externalLocExact := http.Location{
417-
Path: exactPath(externalLocPath),
417+
Path: exactPath(rule.Path),
418418
Type: locType,
419419
}
420420
extLocations = append(extLocations, externalLocExact)
@@ -458,23 +458,22 @@ func updateLocation(
458458
mirrorPercentage *float64,
459459
) http.Location {
460460
filters := matchRule.Filters
461-
path := pathRule.Path
462461
grpc := pathRule.GRPC
463462

464463
if filters.InvalidFilter != nil {
465464
location.Return = &http.Return{Code: http.StatusInternalServerError}
466465
return location
467466
}
468467

469-
location = updateLocationMirrorRoute(location, path, grpc)
468+
location = updateLocationMirrorRoute(location, pathRule.Path, grpc)
470469
location.Includes = append(location.Includes, createIncludesFromLocationSnippetsFilters(filters.SnippetsFilters)...)
471470

472471
if filters.RequestRedirect != nil {
473-
return updateLocationRedirectFilter(location, filters.RequestRedirect, listenerPort, path)
472+
return updateLocationRedirectFilter(location, filters.RequestRedirect, listenerPort, pathRule)
474473
}
475474

476-
location = updateLocationRewriteFilter(location, filters.RequestURLRewrite, path)
477-
location = updateLocationMirrorFilters(location, filters.RequestMirrors, path, mirrorPercentage)
475+
location = updateLocationRewriteFilter(location, filters.RequestURLRewrite, pathRule)
476+
location = updateLocationMirrorFilters(location, filters.RequestMirrors, pathRule.Path, mirrorPercentage)
478477
location = updateLocationProxySettings(location, matchRule, grpc, keepAliveCheck)
479478

480479
return location
@@ -495,9 +494,9 @@ func updateLocationRedirectFilter(
495494
location http.Location,
496495
redirectFilter *dataplane.HTTPRequestRedirectFilter,
497496
listenerPort int32,
498-
path string,
497+
pathRule dataplane.PathRule,
499498
) http.Location {
500-
ret, rewrite := createReturnAndRewriteConfigForRedirectFilter(redirectFilter, listenerPort, path)
499+
ret, rewrite := createReturnAndRewriteConfigForRedirectFilter(redirectFilter, listenerPort, pathRule)
501500
if rewrite.MainRewrite != "" {
502501
location.Rewrites = append(location.Rewrites, rewrite.MainRewrite)
503502
}
@@ -509,9 +508,9 @@ func updateLocationRedirectFilter(
509508
func updateLocationRewriteFilter(
510509
location http.Location,
511510
rewriteFilter *dataplane.HTTPURLRewriteFilter,
512-
path string,
511+
pathRule dataplane.PathRule,
513512
) http.Location {
514-
rewrites := createRewritesValForRewriteFilter(rewriteFilter, path)
513+
rewrites := createRewritesValForRewriteFilter(rewriteFilter, pathRule)
515514
if rewrites != nil {
516515
if location.Type == http.InternalLocationType && rewrites.InternalRewrite != "" {
517516
location.Rewrites = append(location.Rewrites, rewrites.InternalRewrite)
@@ -658,7 +657,7 @@ func createProxySSLVerify(v *dataplane.VerifyTLS) *http.ProxySSLVerify {
658657
func createReturnAndRewriteConfigForRedirectFilter(
659658
filter *dataplane.HTTPRequestRedirectFilter,
660659
listenerPort int32,
661-
path string,
660+
pathRule dataplane.PathRule,
662661
) (*http.Return, *rewriteConfig) {
663662
if filter == nil {
664663
return nil, nil
@@ -702,7 +701,12 @@ func createReturnAndRewriteConfigForRedirectFilter(
702701

703702
rewrites := &rewriteConfig{}
704703
if filter.Path != nil {
705-
rewrites.MainRewrite = createMainRewriteForFilters(filter.Path, path)
704+
mainRewrite := createMainRewriteForFilters(filter.Path, pathRule)
705+
if mainRewrite == "" {
706+
// Invalid configuration for the rewrite filter
707+
return nil, nil
708+
}
709+
rewrites.MainRewrite = mainRewrite
706710
body = fmt.Sprintf("%s://%s$uri$is_args$args", scheme, hostnamePort)
707711
}
708712

@@ -712,19 +716,26 @@ func createReturnAndRewriteConfigForRedirectFilter(
712716
}, rewrites
713717
}
714718

715-
func createMainRewriteForFilters(pathModifier *dataplane.HTTPPathModifier, path string) string {
719+
func createMainRewriteForFilters(pathModifier *dataplane.HTTPPathModifier, pathRule dataplane.PathRule) string {
716720
var mainRewrite string
717721
switch pathModifier.Type {
718722
case dataplane.ReplaceFullPath:
719723
mainRewrite = fmt.Sprintf("^ %s", pathModifier.Replacement)
720724
case dataplane.ReplacePrefixMatch:
725+
// ReplacePrefixMatch is only compatible with a PathPrefix HTTPRouteMatch.
726+
// ReplaceFullPath is compatible with PathTypeExact/PathTypePrefix/PathTypeRegularExpression HTTPRouteMatch.
727+
// see https://gateway-api.sigs.k8s.io/reference/spec/?h=replaceprefixmatch#httppathmodifier
728+
if pathRule.PathType != dataplane.PathTypePrefix {
729+
return ""
730+
}
731+
721732
filterPrefix := pathModifier.Replacement
722733
if filterPrefix == "" {
723734
filterPrefix = "/"
724735
}
725736

726737
// capture everything following the configured prefix up to the first ?, if present.
727-
regex := fmt.Sprintf("^%s([^?]*)?", path)
738+
regex := fmt.Sprintf("^%s([^?]*)?", pathRule.Path)
728739
// replace the configured prefix with the filter prefix, append the captured segment,
729740
// and include the request arguments stored in nginx variable $args.
730741
// https://nginx.org/en/docs/http/ngx_http_core_module.html#var_args
@@ -733,13 +744,13 @@ func createMainRewriteForFilters(pathModifier *dataplane.HTTPPathModifier, path
733744
// if configured prefix does not end in /, but replacement prefix does end in /,
734745
// then make sure that we *require* but *don't capture* a trailing slash in the request,
735746
// otherwise we'll get duplicate slashes in the full replacement
736-
if strings.HasSuffix(filterPrefix, "/") && !strings.HasSuffix(path, "/") {
737-
regex = fmt.Sprintf("^%s(?:/([^?]*))?", path)
747+
if strings.HasSuffix(filterPrefix, "/") && !strings.HasSuffix(pathRule.Path, "/") {
748+
regex = fmt.Sprintf("^%s(?:/([^?]*))?", pathRule.Path)
738749
}
739750

740751
// if configured prefix ends in / we won't capture it for a request (since it's not in the regex),
741752
// so append it to the replacement prefix if the replacement prefix doesn't already end in /
742-
if strings.HasSuffix(path, "/") && !strings.HasSuffix(filterPrefix, "/") {
753+
if strings.HasSuffix(pathRule.Path, "/") && !strings.HasSuffix(filterPrefix, "/") {
743754
replacement = fmt.Sprintf("%s/$1?$args?", filterPrefix)
744755
}
745756

@@ -749,7 +760,10 @@ func createMainRewriteForFilters(pathModifier *dataplane.HTTPPathModifier, path
749760
return mainRewrite
750761
}
751762

752-
func createRewritesValForRewriteFilter(filter *dataplane.HTTPURLRewriteFilter, path string) *rewriteConfig {
763+
func createRewritesValForRewriteFilter(
764+
filter *dataplane.HTTPURLRewriteFilter,
765+
pathRule dataplane.PathRule,
766+
) *rewriteConfig {
753767
if filter == nil {
754768
return nil
755769
}
@@ -758,8 +772,13 @@ func createRewritesValForRewriteFilter(filter *dataplane.HTTPURLRewriteFilter, p
758772
if filter.Path != nil {
759773
rewrites.InternalRewrite = "^ $request_uri"
760774

761-
// for URLRewriteFilter, we add a break to the rewrite to prevent further processing of the request.
762-
rewrites.MainRewrite = fmt.Sprintf("%s break", createMainRewriteForFilters(filter.Path, path))
775+
mainRewrite := createMainRewriteForFilters(filter.Path, pathRule)
776+
if mainRewrite == "" {
777+
// Invalid configuration for the rewrite filter
778+
return nil
779+
}
780+
// For URLRewriteFilter, add "break" to prevent further processing of the request.
781+
rewrites.MainRewrite = fmt.Sprintf("%s break", mainRewrite)
763782
}
764783

765784
return rewrites
@@ -982,14 +1001,18 @@ func createPath(rule dataplane.PathRule) string {
9821001
switch rule.PathType {
9831002
case dataplane.PathTypeExact:
9841003
return exactPath(rule.Path)
1004+
case dataplane.PathTypePrefix:
1005+
return fmt.Sprintf("^~ %s", rule.Path)
1006+
case dataplane.PathTypeRegularExpression:
1007+
return fmt.Sprintf("~ %s", rule.Path)
9851008
default:
986-
return rule.Path
1009+
return "" // should never happen because path type is validated earlier
9871010
}
9881011
}
9891012

9901013
func createDefaultRootLocation() http.Location {
9911014
return http.Location{
992-
Path: "/",
1015+
Path: "= /",
9931016
Return: &http.Return{Code: http.StatusNotFound},
9941017
}
9951018
}

0 commit comments

Comments
 (0)