feat(matcher): 支持 internal location 标记

- MatchResult 新增 Internal 字段
- AddExact/AddPrefix/AddPrefixPriority/AddRadix/AddRegex 方法新增 internal 参数
- location 解析器支持 internal 指令

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
xfy 2026-04-20 18:08:23 +08:00
parent e05cf8a674
commit 65080cca66
7 changed files with 53 additions and 20 deletions

View File

@ -15,6 +15,9 @@ type ExactMatcher struct {
// handler 请求处理器 // handler 请求处理器
handler fasthttp.RequestHandler handler fasthttp.RequestHandler
// internal 是否为 internal location
internal bool
// path 精确匹配路径 // path 精确匹配路径
path string path string
@ -28,14 +31,16 @@ type ExactMatcher struct {
// - path: 精确匹配的路径 // - path: 精确匹配的路径
// - handler: 匹配成功后的请求处理器 // - handler: 匹配成功后的请求处理器
// - priority: 优先级(通常设为 1 // - priority: 优先级(通常设为 1
// - internal: 是否为 internal location
// //
// 返回值: // 返回值:
// - *ExactMatcher: 精确匹配器实例 // - *ExactMatcher: 精确匹配器实例
func NewExactMatcher(path string, handler fasthttp.RequestHandler, priority int) *ExactMatcher { func NewExactMatcher(path string, handler fasthttp.RequestHandler, priority int, internal bool) *ExactMatcher {
return &ExactMatcher{ return &ExactMatcher{
path: path, path: path,
handler: handler, handler: handler,
priority: priority, priority: priority,
internal: internal,
} }
} }
@ -60,5 +65,6 @@ func (m *ExactMatcher) Result() *MatchResult {
Path: m.path, Path: m.path,
Priority: m.priority, Priority: m.priority,
LocationType: LocationTypeExact, LocationType: LocationTypeExact,
Internal: m.internal,
} }
} }

View File

@ -74,10 +74,11 @@ func NewLocationEngine() *LocationEngine {
// 参数: // 参数:
// - path: 精确匹配路径 // - path: 精确匹配路径
// - handler: 请求处理器 // - handler: 请求处理器
// - internal: 是否为 internal location
// //
// 返回值: // 返回值:
// - error: 引擎已初始化或路径冲突时返回错误 // - error: 引擎已初始化或路径冲突时返回错误
func (e *LocationEngine) AddExact(path string, handler fasthttp.RequestHandler) error { func (e *LocationEngine) AddExact(path string, handler fasthttp.RequestHandler, internal bool) error {
if e.initialized { if e.initialized {
return errors.New("LocationEngine already initialized") return errors.New("LocationEngine already initialized")
} }
@ -86,7 +87,7 @@ func (e *LocationEngine) AddExact(path string, handler fasthttp.RequestHandler)
return err return err
} }
matcher := NewExactMatcher(path, handler, 1) matcher := NewExactMatcher(path, handler, 1, internal)
e.exactMatchers[path] = matcher e.exactMatchers[path] = matcher
return nil return nil
} }
@ -96,10 +97,11 @@ func (e *LocationEngine) AddExact(path string, handler fasthttp.RequestHandler)
// 参数: // 参数:
// - path: 前缀优先路径 // - path: 前缀优先路径
// - handler: 请求处理器 // - handler: 请求处理器
// - internal: 是否为 internal location
// //
// 返回值: // 返回值:
// - error: 引擎已初始化或路径冲突时返回错误 // - error: 引擎已初始化或路径冲突时返回错误
func (e *LocationEngine) AddPrefixPriority(path string, handler fasthttp.RequestHandler) error { func (e *LocationEngine) AddPrefixPriority(path string, handler fasthttp.RequestHandler, internal bool) error {
if e.initialized { if e.initialized {
return errors.New("LocationEngine already initialized") return errors.New("LocationEngine already initialized")
} }
@ -108,7 +110,7 @@ func (e *LocationEngine) AddPrefixPriority(path string, handler fasthttp.Request
return err return err
} }
return e.prefixPriorityTree.Insert(path, handler, 2, "prefix_priority") return e.prefixPriorityTree.Insert(path, handler, 2, "prefix_priority", internal)
} }
// AddRegex 添加正则匹配 location。 // AddRegex 添加正则匹配 location。
@ -117,15 +119,16 @@ func (e *LocationEngine) AddPrefixPriority(path string, handler fasthttp.Request
// - pattern: 正则表达式模式 // - pattern: 正则表达式模式
// - handler: 请求处理器 // - handler: 请求处理器
// - caseInsensitive: 是否大小写不敏感(~* 模式) // - caseInsensitive: 是否大小写不敏感(~* 模式)
// - internal: 是否为 internal location
// //
// 返回值: // 返回值:
// - error: 引擎已初始化或正则表达式无效时返回错误 // - error: 引擎已初始化或正则表达式无效时返回错误
func (e *LocationEngine) AddRegex(pattern string, handler fasthttp.RequestHandler, caseInsensitive bool) error { func (e *LocationEngine) AddRegex(pattern string, handler fasthttp.RequestHandler, caseInsensitive bool, internal bool) error {
if e.initialized { if e.initialized {
return errors.New("LocationEngine already initialized") return errors.New("LocationEngine already initialized")
} }
matcher, err := NewRegexMatcher(pattern, handler, 3, caseInsensitive) matcher, err := NewRegexMatcher(pattern, handler, 3, caseInsensitive, internal)
if err != nil { if err != nil {
return fmt.Errorf("invalid regex pattern: %w", err) return fmt.Errorf("invalid regex pattern: %w", err)
} }
@ -139,10 +142,11 @@ func (e *LocationEngine) AddRegex(pattern string, handler fasthttp.RequestHandle
// 参数: // 参数:
// - path: 前缀路径 // - path: 前缀路径
// - handler: 请求处理器 // - handler: 请求处理器
// - internal: 是否为 internal location
// //
// 返回值: // 返回值:
// - error: 引擎已初始化或路径冲突时返回错误 // - error: 引擎已初始化或路径冲突时返回错误
func (e *LocationEngine) AddPrefix(path string, handler fasthttp.RequestHandler) error { func (e *LocationEngine) AddPrefix(path string, handler fasthttp.RequestHandler, internal bool) error {
if e.initialized { if e.initialized {
return errors.New("LocationEngine already initialized") return errors.New("LocationEngine already initialized")
} }
@ -151,7 +155,7 @@ func (e *LocationEngine) AddPrefix(path string, handler fasthttp.RequestHandler)
return err return err
} }
return e.prefixTree.Insert(path, handler, 4, "prefix") return e.prefixTree.Insert(path, handler, 4, "prefix", internal)
} }
// AddNamed 添加命名 location。 // AddNamed 添加命名 location。

View File

@ -42,6 +42,9 @@ type MatchResult struct {
// Handler 匹配到的请求处理器 // Handler 匹配到的请求处理器
Handler fasthttp.RequestHandler Handler fasthttp.RequestHandler
// Internal 是否为 internal location
Internal bool
// LocationType 匹配类型exact/prefix/regex 等) // LocationType 匹配类型exact/prefix/regex 等)
LocationType string LocationType string

View File

@ -35,11 +35,12 @@ func NewPrefixMatcher() *PrefixMatcher {
// 参数: // 参数:
// - path: 前缀路径 // - path: 前缀路径
// - handler: 匹配成功后的请求处理器 // - handler: 匹配成功后的请求处理器
// - internal: 是否为 internal location
// //
// 返回值: // 返回值:
// - error: 路径重复或树已初始化时返回错误 // - error: 路径重复或树已初始化时返回错误
func (pm *PrefixMatcher) AddPath(path string, handler fasthttp.RequestHandler) error { func (pm *PrefixMatcher) AddPath(path string, handler fasthttp.RequestHandler, internal bool) error {
return pm.tree.Insert(path, handler, pm.priority, "prefix") return pm.tree.Insert(path, handler, pm.priority, "prefix", internal)
} }
// Match 前缀匹配,返回最长前缀匹配结果。 // Match 前缀匹配,返回最长前缀匹配结果。

View File

@ -35,11 +35,12 @@ func NewPrefixPriorityMatcher() *PrefixPriorityMatcher {
// 参数: // 参数:
// - path: 前缀优先路径 // - path: 前缀优先路径
// - handler: 匹配成功后的请求处理器 // - handler: 匹配成功后的请求处理器
// - internal: 是否为 internal location
// //
// 返回值: // 返回值:
// - error: 路径重复或树已初始化时返回错误 // - error: 路径重复或树已初始化时返回错误
func (ppm *PrefixPriorityMatcher) AddPath(path string, handler fasthttp.RequestHandler) error { func (ppm *PrefixPriorityMatcher) AddPath(path string, handler fasthttp.RequestHandler, internal bool) error {
return ppm.tree.Insert(path, handler, ppm.priority, "prefix_priority") return ppm.tree.Insert(path, handler, ppm.priority, "prefix_priority", internal)
} }
// Match 前缀优先匹配,返回最长前缀匹配结果。 // Match 前缀优先匹配,返回最长前缀匹配结果。

View File

@ -29,6 +29,9 @@ type RadixNode struct {
// priority 匹配优先级 // priority 匹配优先级
priority int priority int
// internal 是否为 internal location
internal bool
// isLeaf 是否为叶子节点(有 handler // isLeaf 是否为叶子节点(有 handler
isLeaf bool isLeaf bool
@ -70,14 +73,15 @@ func NewRadixTree() *RadixTree {
// - handler: 匹配成功后的请求处理器 // - handler: 匹配成功后的请求处理器
// - priority: 匹配优先级 // - priority: 匹配优先级
// - locationType: 位置类型标识 // - locationType: 位置类型标识
// - internal: 是否为 internal location
// //
// 返回值: // 返回值:
// - error: 树已初始化或路径已存在时返回错误 // - error: 树已初始化或路径已存在时返回错误
func (t *RadixTree) Insert(path string, handler fasthttp.RequestHandler, priority int, locationType string) error { func (t *RadixTree) Insert(path string, handler fasthttp.RequestHandler, priority int, locationType string, internal bool) error {
if t.initialized { if t.initialized {
return errors.New("RadixTree already initialized") return errors.New("RadixTree already initialized")
} }
return t.insertNode(nil, t.root, path, handler, priority, locationType) return t.insertNode(nil, t.root, path, handler, priority, locationType, internal)
} }
// insertNode 完整路径分割插入算法。 // insertNode 完整路径分割插入算法。
@ -95,10 +99,11 @@ func (t *RadixTree) Insert(path string, handler fasthttp.RequestHandler, priorit
// - handler: 请求处理器 // - handler: 请求处理器
// - priority: 优先级 // - priority: 优先级
// - locationType: 位置类型 // - locationType: 位置类型
// - internal: 是否为 internal location
// //
// 返回值: // 返回值:
// - error: 路径已存在时返回错误 // - error: 路径已存在时返回错误
func (t *RadixTree) insertNode(parent *RadixNode, node *RadixNode, path string, handler fasthttp.RequestHandler, priority int, locationType string) error { func (t *RadixTree) insertNode(parent *RadixNode, node *RadixNode, path string, handler fasthttp.RequestHandler, priority int, locationType string, internal bool) error {
// Case 1: 空节点(根节点),直接设置 // Case 1: 空节点(根节点),直接设置
if node.prefix == "" && len(node.children) == 0 && node.handler == nil { if node.prefix == "" && len(node.children) == 0 && node.handler == nil {
if path == "" { if path == "" {
@ -106,6 +111,7 @@ func (t *RadixTree) insertNode(parent *RadixNode, node *RadixNode, path string,
node.priority = priority node.priority = priority
node.isLeaf = true node.isLeaf = true
node.locationType = locationType node.locationType = locationType
node.internal = internal
return nil return nil
} }
// 创建新子节点 // 创建新子节点
@ -115,6 +121,7 @@ func (t *RadixTree) insertNode(parent *RadixNode, node *RadixNode, path string,
isLeaf: true, isLeaf: true,
priority: priority, priority: priority,
locationType: locationType, locationType: locationType,
internal: internal,
} }
node.children = append(node.children, newNode) node.children = append(node.children, newNode)
return nil return nil
@ -140,13 +147,14 @@ func (t *RadixTree) insertNode(parent *RadixNode, node *RadixNode, path string,
node.priority = priority node.priority = priority
node.isLeaf = true node.isLeaf = true
node.locationType = locationType node.locationType = locationType
node.internal = internal
return nil return nil
} }
// 搜索匹配剩余路径的子节点 // 搜索匹配剩余路径的子节点
for _, child := range node.children { for _, child := range node.children {
if strings.HasPrefix(remaining, child.prefix) { if strings.HasPrefix(remaining, child.prefix) {
return t.insertNode(node, child, remaining, handler, priority, locationType) return t.insertNode(node, child, remaining, handler, priority, locationType, internal)
} }
} }
@ -157,6 +165,7 @@ func (t *RadixTree) insertNode(parent *RadixNode, node *RadixNode, path string,
isLeaf: true, isLeaf: true,
priority: priority, priority: priority,
locationType: locationType, locationType: locationType,
internal: internal,
} }
node.children = append(node.children, newNode) node.children = append(node.children, newNode)
return nil return nil
@ -179,6 +188,7 @@ func (t *RadixTree) insertNode(parent *RadixNode, node *RadixNode, path string,
isLeaf: true, isLeaf: true,
priority: priority, priority: priority,
locationType: locationType, locationType: locationType,
internal: internal,
} }
// 将原节点和新节点作为 splitNode 的子节点 // 将原节点和新节点作为 splitNode 的子节点
@ -243,6 +253,7 @@ func (t *RadixTree) searchLongest(node *RadixNode, path string, bestMatch *Match
Path: node.prefix, Path: node.prefix,
Priority: node.priority, Priority: node.priority,
LocationType: node.locationType, LocationType: node.locationType,
Internal: node.internal,
} }
// nil-safe 优先级比较 + 长度比较 // nil-safe 优先级比较 + 长度比较

View File

@ -28,6 +28,9 @@ type RegexMatcher struct {
// priority 匹配优先级,正则匹配为 3 // priority 匹配优先级,正则匹配为 3
priority int priority int
// internal 是否为 internal location
internal bool
// caseInsensitive 是否大小写不敏感(~* 模式) // caseInsensitive 是否大小写不敏感(~* 模式)
caseInsensitive bool caseInsensitive bool
} }
@ -39,11 +42,12 @@ type RegexMatcher struct {
// - handler: 匹配成功后的请求处理器 // - handler: 匹配成功后的请求处理器
// - priority: 优先级(通常设为 3 // - priority: 优先级(通常设为 3
// - caseInsensitive: 是否大小写不敏感 // - caseInsensitive: 是否大小写不敏感
// - internal: 是否为 internal location
// //
// 返回值: // 返回值:
// - *RegexMatcher: 正则匹配器实例 // - *RegexMatcher: 正则匹配器实例
// - error: 正则表达式编译失败时返回错误 // - error: 正则表达式编译失败时返回错误
func NewRegexMatcher(pattern string, handler fasthttp.RequestHandler, priority int, caseInsensitive bool) (*RegexMatcher, error) { func NewRegexMatcher(pattern string, handler fasthttp.RequestHandler, priority int, caseInsensitive bool, internal bool) (*RegexMatcher, error) {
re, err := regexp.Compile(pattern) re, err := regexp.Compile(pattern)
if err != nil { if err != nil {
return nil, err return nil, err
@ -54,6 +58,7 @@ func NewRegexMatcher(pattern string, handler fasthttp.RequestHandler, priority i
handler: handler, handler: handler,
priority: priority, priority: priority,
caseInsensitive: caseInsensitive, caseInsensitive: caseInsensitive,
internal: internal,
captures: make(map[string]string), captures: make(map[string]string),
}, nil }, nil
} }
@ -67,11 +72,12 @@ func NewRegexMatcher(pattern string, handler fasthttp.RequestHandler, priority i
// - handler: 匹配成功后的请求处理器 // - handler: 匹配成功后的请求处理器
// - priority: 优先级 // - priority: 优先级
// - caseInsensitive: 是否大小写不敏感 // - caseInsensitive: 是否大小写不敏感
// - internal: 是否为 internal location
// //
// 返回值: // 返回值:
// - *RegexMatcher: 正则匹配器实例 // - *RegexMatcher: 正则匹配器实例
func MustRegexMatcher(pattern string, handler fasthttp.RequestHandler, priority int, caseInsensitive bool) *RegexMatcher { func MustRegexMatcher(pattern string, handler fasthttp.RequestHandler, priority int, caseInsensitive bool, internal bool) *RegexMatcher {
m, err := NewRegexMatcher(pattern, handler, priority, caseInsensitive) m, err := NewRegexMatcher(pattern, handler, priority, caseInsensitive, internal)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -104,6 +110,7 @@ func (m *RegexMatcher) Result() *MatchResult {
Priority: m.priority, Priority: m.priority,
LocationType: locType, LocationType: locType,
Captures: m.captures, Captures: m.captures,
Internal: m.internal,
} }
} }