Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Add regex for path matching #3874

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
fabian4 wants to merge 1 commit into nginx:main
base: main
Choose a base branch
Loading
from fabian4:add_regex_for_path_matching
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 45 additions & 22 deletions internal/controller/nginx/config/servers.go
View file Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,7 @@ func initializeExternalLocations(
}
if !exactPathExists {
externalLocExact := http.Location{
Path: exactPath(externalLocPath),
Path: exactPath(rule.Path),
Type: locType,
}
extLocations = append(extLocations, externalLocExact)
Expand Down Expand Up @@ -458,23 +458,22 @@ func updateLocation(
mirrorPercentage *float64,
) http.Location {
filters := matchRule.Filters
path := pathRule.Path
grpc := pathRule.GRPC

if filters.InvalidFilter != nil {
location.Return = &http.Return{Code: http.StatusInternalServerError}
return location
}

location = updateLocationMirrorRoute(location, path, grpc)
location = updateLocationMirrorRoute(location, pathRule.Path, grpc)
location.Includes = append(location.Includes, createIncludesFromLocationSnippetsFilters(filters.SnippetsFilters)...)

if filters.RequestRedirect != nil {
return updateLocationRedirectFilter(location, filters.RequestRedirect, listenerPort, path)
return updateLocationRedirectFilter(location, filters.RequestRedirect, listenerPort, pathRule)
}

location = updateLocationRewriteFilter(location, filters.RequestURLRewrite, path)
location = updateLocationMirrorFilters(location, filters.RequestMirrors, path, mirrorPercentage)
location = updateLocationRewriteFilter(location, filters.RequestURLRewrite, pathRule)
location = updateLocationMirrorFilters(location, filters.RequestMirrors, pathRule.Path, mirrorPercentage)
location = updateLocationProxySettings(location, matchRule, grpc, keepAliveCheck)

return location
Expand All @@ -495,9 +494,9 @@ func updateLocationRedirectFilter(
location http.Location,
redirectFilter *dataplane.HTTPRequestRedirectFilter,
listenerPort int32,
path string,
pathRule dataplane.PathRule,
) http.Location {
ret, rewrite := createReturnAndRewriteConfigForRedirectFilter(redirectFilter, listenerPort, path)
ret, rewrite := createReturnAndRewriteConfigForRedirectFilter(redirectFilter, listenerPort, pathRule)
if rewrite.MainRewrite != "" {
location.Rewrites = append(location.Rewrites, rewrite.MainRewrite)
}
Expand All @@ -509,9 +508,9 @@ func updateLocationRedirectFilter(
func updateLocationRewriteFilter(
location http.Location,
rewriteFilter *dataplane.HTTPURLRewriteFilter,
path string,
pathRule dataplane.PathRule,
) http.Location {
rewrites := createRewritesValForRewriteFilter(rewriteFilter, path)
rewrites := createRewritesValForRewriteFilter(rewriteFilter, pathRule)
if rewrites != nil {
if location.Type == http.InternalLocationType && rewrites.InternalRewrite != "" {
location.Rewrites = append(location.Rewrites, rewrites.InternalRewrite)
Expand Down Expand Up @@ -658,7 +657,7 @@ func createProxySSLVerify(v *dataplane.VerifyTLS) *http.ProxySSLVerify {
func createReturnAndRewriteConfigForRedirectFilter(
filter *dataplane.HTTPRequestRedirectFilter,
listenerPort int32,
path string,
pathRule dataplane.PathRule,
) (*http.Return, *rewriteConfig) {
if filter == nil {
return nil, nil
Expand Down Expand Up @@ -702,7 +701,12 @@ func createReturnAndRewriteConfigForRedirectFilter(

rewrites := &rewriteConfig{}
if filter.Path != nil {
rewrites.MainRewrite = createMainRewriteForFilters(filter.Path, path)
mainRewrite := createMainRewriteForFilters(filter.Path, pathRule)
if mainRewrite == "" {
// Invalid configuration for the rewrite filter
return nil, nil
}
rewrites.MainRewrite = mainRewrite
body = fmt.Sprintf("%s://%s$uri$is_args$args", scheme, hostnamePort)
}

Expand All @@ -712,19 +716,26 @@ func createReturnAndRewriteConfigForRedirectFilter(
}, rewrites
}

func createMainRewriteForFilters(pathModifier *dataplane.HTTPPathModifier, path string) string {
func createMainRewriteForFilters(pathModifier *dataplane.HTTPPathModifier, pathRule dataplane.PathRule) string {
var mainRewrite string
switch pathModifier.Type {
case dataplane.ReplaceFullPath:
mainRewrite = fmt.Sprintf("^ %s", pathModifier.Replacement)
case dataplane.ReplacePrefixMatch:
// ReplacePrefixMatch is only compatible with a PathPrefix HTTPRouteMatch.
// ReplaceFullPath is compatible with PathTypeExact/PathTypePrefix/PathTypeRegularExpression HTTPRouteMatch.
// see https://gateway-api.sigs.k8s.io/reference/spec/?h=replaceprefixmatch#httppathmodifier
if pathRule.PathType != dataplane.PathTypePrefix {
return ""
}

filterPrefix := pathModifier.Replacement
if filterPrefix == "" {
filterPrefix = "/"
}

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

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

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

func createRewritesValForRewriteFilter(filter *dataplane.HTTPURLRewriteFilter, path string) *rewriteConfig {
func createRewritesValForRewriteFilter(
filter *dataplane.HTTPURLRewriteFilter,
pathRule dataplane.PathRule,
) *rewriteConfig {
if filter == nil {
return nil
}
Expand All @@ -758,8 +772,13 @@ func createRewritesValForRewriteFilter(filter *dataplane.HTTPURLRewriteFilter, p
if filter.Path != nil {
rewrites.InternalRewrite = "^ $request_uri"

// for URLRewriteFilter, we add a break to the rewrite to prevent further processing of the request.
rewrites.MainRewrite = fmt.Sprintf("%s break", createMainRewriteForFilters(filter.Path, path))
mainRewrite := createMainRewriteForFilters(filter.Path, pathRule)
if mainRewrite == "" {
// Invalid configuration for the rewrite filter
return nil
}
// For URLRewriteFilter, add "break" to prevent further processing of the request.
rewrites.MainRewrite = fmt.Sprintf("%s break", mainRewrite)
}

return rewrites
Expand Down Expand Up @@ -982,14 +1001,18 @@ func createPath(rule dataplane.PathRule) string {
switch rule.PathType {
case dataplane.PathTypeExact:
return exactPath(rule.Path)
case dataplane.PathTypePrefix:
return fmt.Sprintf("^~ %s", rule.Path)
case dataplane.PathTypeRegularExpression:
return fmt.Sprintf("~ %s", rule.Path)
default:
return rule.Path
return "" // should never happen because path type is validated earlier
Copy link
Contributor

@salonichf5 salonichf5 Oct 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we panic here like we do for other cases @ciarams87 ?

Copy link
Contributor

@salonichf5 salonichf5 Oct 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i guess its fine, this is how we have handled it in the past so should be good

Copy link
Contributor

@ciarams87 ciarams87 Oct 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think your first instinct was correct actually - this should be a panic. Now this function silently returns an invalid location path instead of at least returning the original path as a fallback. This is a programmer error (all path types should be handled), not a runtime error from user input (which is validated earlier), so panic() is appropriate and consistent.

salonichf5 and fabian4 reacted with thumbs up emoji
}
}

func createDefaultRootLocation() http.Location {
return http.Location{
Path: "/",
Path: "= /",
Return: &http.Return{Code: http.StatusNotFound},
}
}
Expand Down
Loading
Loading

AltStyle によって変換されたページ (->オリジナル) /