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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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