in internal/redirects/matching.go [72:117]
func matchesRuleWithPlaceholderOrSplats(requestPath string, fromPath string, toPath string, status int) (bool, string) {
// Any logic beyond this point handles placeholders and splats.
// If the FF_ENABLE_PLACEHOLDERS feature flag isn't enabled, exit now.
if !feature.RedirectsPlaceholders.Enabled() {
return false, ""
}
regexSegments := convertToRegexSegments(fromPath)
if len(regexSegments) == 0 {
return false, ""
}
fromRegexString := `(?i)^` + strings.Join(regexSegments, "") + `/*$`
fromRegex, err := regexp.Compile(fromRegexString)
if err != nil {
log.WithFields(log.Fields{
"fromRegexString": fromRegexString,
"rule.From": fromPath,
"rule.To": toPath,
"rule.Status": status,
"path": requestPath,
}).WithError(err).Warnf("matchesRule generated an invalid regex: %q", fromRegexString)
return false, ""
}
template := regexPlaceholderReplacement.ReplaceAllString(toPath, `${$placeholder}`)
subMatchIndex := fromRegex.FindStringSubmatchIndex(requestPath)
if subMatchIndex == nil {
return false, ""
}
var templatedToPath []byte
templatedToPath = fromRegex.ExpandString(templatedToPath, template, requestPath, subMatchIndex)
// Some replacements result in subsequent slashes. For example, a rule with a "to"
// like `foo/:splat/bar` will result in a path like `foo//bar` if the splat
// character matches nothing. To avoid this, replace all instances
// of multiple subsequent forward slashes with a single forward slash.
// The regex captures any character except a colon ([^:]) before the double slashes
// and includes it in the replacement.
templatedToPath = regexMultipleSlashes.ReplaceAll(templatedToPath, []byte("$1/"))
return true, string(templatedToPath)
}