mirror of
https://github.com/caddyserver/caddy.git
synced 2026-05-21 22:36:32 -04:00
headers: Support placeholders in replacement search patterns (#7117)
* fix: resolve http.request placeholders in header directive find operation - Skip regex compilation during provision when placeholders are detected - Compile regex at runtime after placeholder replacement - Preserves performance for static regexes while enabling dynamic placeholders - Fixes #7109 * test: add tests for placeholder detection in header replacements - Test containsPlaceholders function edge cases - Test provision skips compilation for dynamic regexes - Test end-to-end placeholder replacement functionality
This commit is contained in:
@@ -141,6 +141,14 @@ func (ops *HeaderOps) Provision(_ caddy.Context) error {
|
||||
if r.SearchRegexp == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if it contains placeholders
|
||||
if containsPlaceholders(r.SearchRegexp) {
|
||||
// Contains placeholders, skips precompilation, and recompiles at runtime
|
||||
continue
|
||||
}
|
||||
|
||||
// Does not contain placeholders, safe to precompile
|
||||
re, err := regexp.Compile(r.SearchRegexp)
|
||||
if err != nil {
|
||||
return fmt.Errorf("replacement %d for header field '%s': %v", i, fieldName, err)
|
||||
@@ -151,6 +159,20 @@ func (ops *HeaderOps) Provision(_ caddy.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// containsCaddyPlaceholders checks if the string contains Caddy placeholder syntax {key}
|
||||
func containsPlaceholders(s string) bool {
|
||||
openIdx := strings.Index(s, "{")
|
||||
if openIdx == -1 {
|
||||
return false
|
||||
}
|
||||
closeIdx := strings.Index(s[openIdx+1:], "}")
|
||||
if closeIdx == -1 {
|
||||
return false
|
||||
}
|
||||
// Make sure there is content between the brackets
|
||||
return closeIdx > 0
|
||||
}
|
||||
|
||||
func (ops HeaderOps) validate() error {
|
||||
for fieldName, replacements := range ops.Replace {
|
||||
for _, r := range replacements {
|
||||
@@ -269,7 +291,15 @@ func (ops HeaderOps) ApplyTo(hdr http.Header, repl *caddy.Replacer) {
|
||||
for fieldName, vals := range hdr {
|
||||
for i := range vals {
|
||||
if r.re != nil {
|
||||
// Use precompiled regular expressions
|
||||
hdr[fieldName][i] = r.re.ReplaceAllString(hdr[fieldName][i], replace)
|
||||
} else if r.SearchRegexp != "" {
|
||||
// Runtime compilation of regular expressions
|
||||
searchRegexp := repl.ReplaceKnown(r.SearchRegexp, "")
|
||||
if re, err := regexp.Compile(searchRegexp); err == nil {
|
||||
hdr[fieldName][i] = re.ReplaceAllString(hdr[fieldName][i], replace)
|
||||
}
|
||||
// If compilation fails, skip this replacement
|
||||
} else {
|
||||
hdr[fieldName][i] = strings.ReplaceAll(hdr[fieldName][i], search, replace)
|
||||
}
|
||||
@@ -291,6 +321,11 @@ func (ops HeaderOps) ApplyTo(hdr http.Header, repl *caddy.Replacer) {
|
||||
for i := range vals {
|
||||
if r.re != nil {
|
||||
hdr[hdrFieldName][i] = r.re.ReplaceAllString(hdr[hdrFieldName][i], replace)
|
||||
} else if r.SearchRegexp != "" {
|
||||
searchRegexp := repl.ReplaceKnown(r.SearchRegexp, "")
|
||||
if re, err := regexp.Compile(searchRegexp); err == nil {
|
||||
hdr[hdrFieldName][i] = re.ReplaceAllString(hdr[hdrFieldName][i], replace)
|
||||
}
|
||||
} else {
|
||||
hdr[hdrFieldName][i] = strings.ReplaceAll(hdr[hdrFieldName][i], search, replace)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user