fix(matcher): 修正 ParseRegexPattern nginx 风格正则解析

- ~ 现为大小写敏感正则(之前错误为不敏感)
- ~* 为大小写不敏感正则
- ^~ 为前缀优先匹配(非正则,之前错误标记为正则)
- 更新测试用例匹配正确行为

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
xfy 2026-04-20 16:04:18 +08:00
parent 5354dceaf7
commit 82658359cb
2 changed files with 26 additions and 25 deletions

View File

@ -256,35 +256,36 @@ func (e *LocationEngine) checkConflict(path, locationType string) error {
// ParseRegexPattern 解析 nginx 风格的正则模式。 // ParseRegexPattern 解析 nginx 风格的正则模式。
// //
// 支持以下前缀: // 支持以下前缀:
// - ~: 大小写敏感正则 // - ~: 大小写敏感正则case-sensitive regex
// - ~*: 大小写不敏感正则(同上 // - ~*: 大小写不敏感正则(case-insensitive regex
// - ^~: 前缀优先(非正则,但在此解析 // - ^~: 前缀优先匹配(非正则)
// //
// 参数: // 该函数用于配置验证层,检测用户配置的模式格式是否正确。
// - pattern: 原始模式字符串 // 运行时匹配器直接使用 LocationType 枚举进行匹配。
//
// 返回值:
// - cleanPattern: 去除前缀后的正则模式
// - caseInsensitive: 是否大小写不敏感
// - isRegex: 是否为正则模式
func ParseRegexPattern(pattern string) (cleanPattern string, caseInsensitive bool, isRegex bool) { func ParseRegexPattern(pattern string) (cleanPattern string, caseInsensitive bool, isRegex bool) {
if len(pattern) == 0 { if len(pattern) == 0 {
return pattern, false, false return pattern, false, false
} }
switch pattern[0] { // Handle ~* (case-insensitive regex) - must check first (2-char prefix)
case '~': if len(pattern) >= 2 && pattern[0] == '~' && pattern[1] == '*' {
cleanPattern = pattern[1:] cleanPattern = pattern[2:]
caseInsensitive = true return cleanPattern, true, true // case-insensitive, is regex
return cleanPattern, caseInsensitive, true
case '^':
if len(pattern) > 1 && pattern[1] == '~' {
cleanPattern = pattern[2:]
caseInsensitive = false
return cleanPattern, caseInsensitive, true
}
} }
// Handle ~ (case-sensitive regex)
if pattern[0] == '~' {
cleanPattern = pattern[1:]
return cleanPattern, false, true // case-sensitive, is regex
}
// Handle ^~ (prefix priority, NOT regex)
if len(pattern) >= 2 && pattern[0] == '^' && pattern[1] == '~' {
cleanPattern = pattern[2:]
return cleanPattern, false, false // NOT a regex
}
// Default: exact/prefix match
return pattern, false, false return pattern, false, false
} }

View File

@ -347,10 +347,10 @@ func TestParseRegexPattern(t *testing.T) {
}{ }{
{"", "", false, false}, {"", "", false, false},
{"/api", "/api", false, false}, {"/api", "/api", false, false},
{"~\\.php$", "\\.php$", true, true}, {"~\\.php$", "\\.php$", false, true}, // ~ is case-sensitive regex
{"^~", "", false, true}, {"^~", "", false, false}, // ^~ is NOT regex (prefix priority)
{"^~/static", "/static", false, true}, {"^~/static", "/static", false, false}, // ^~ is NOT regex
{"~*.php$", "*.php$", true, true}, {"~*.php$", ".php$", true, true}, // ~* is case-insensitive regex
} }
for _, tt := range tests { for _, tt := range tests {