Files
EdgeCommon/pkg/serverconfigs/http_location_config.go
2023-08-08 15:12:28 +08:00

346 lines
9.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package serverconfigs
import (
"context"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
"regexp"
"strconv"
"strings"
)
type HTTPLocationConfig struct {
Id int64 `yaml:"id" json:"id"` // ID
IsOn bool `yaml:"isOn" json:"isOn"` // 是否启用
Pattern string `yaml:"pattern" json:"pattern"` // 匹配规则 TODO 未来支持更多样的匹配规则
Name string `yaml:"name" json:"name"` // 名称
Web *HTTPWebConfig `yaml:"web" json:"web"` // Web配置
URLPrefix string `yaml:"urlPrefix" json:"urlPrefix"` // 实际的URL前缀TODO 未来支持变量
Description string `yaml:"description" json:"description"` // 描述
ReverseProxyRef *ReverseProxyRef `yaml:"reverseProxyRef" json:"reverseProxyRef"` // 反向代理引用
ReverseProxy *ReverseProxyConfig `yaml:"reverseProxy" json:"reverseProxy"` // 反向代理设置
IsBreak bool `yaml:"isBreak" json:"isBreak"` // 终止向下解析
Children []*HTTPLocationConfig `yaml:"children" json:"children"` // 子规则
Conds *shared.HTTPRequestCondsConfig `yaml:"conds" json:"conds"` // 匹配条件
Domains []string `yaml:"domains" json:"domains"` // 所属域名
patternType HTTPLocationPatternType // 规则类型LocationPattern*
prefix string // 前缀
suffix string // 后缀
path string // 精确的路径
reg *regexp.Regexp // 匹配规则
caseInsensitive bool // 大小写不敏感
reverse bool // 是否翻转规则,比如非前缀,非路径
}
func (this *HTTPLocationConfig) Init(ctx context.Context) error {
err := this.ExtractPattern()
if err != nil {
return err
}
if this.Web != nil {
err := this.Web.Init(ctx)
if err != nil {
return err
}
}
if this.ReverseProxyRef != nil {
err := this.ReverseProxyRef.Init()
if err != nil {
return err
}
}
if this.ReverseProxy != nil {
err := this.ReverseProxy.Init(ctx)
if err != nil {
return err
}
}
// Children
for _, child := range this.Children {
err := child.Init(ctx)
if err != nil {
return err
}
}
// conds
if this.Conds != nil {
err := this.Conds.Init()
if err != nil {
return err
}
}
return nil
}
// SetPattern 组合参数为一个字符串
func (this *HTTPLocationConfig) SetPattern(pattern string, patternType int, caseInsensitive bool, reverse bool) {
op := ""
if patternType == HTTPLocationPatternTypePrefix {
if caseInsensitive {
op = "*"
if reverse {
op = "!*"
}
} else {
if reverse {
op = "!"
}
}
} else if patternType == HTTPLocationPatternTypeSuffix {
op = "suffix"
if caseInsensitive {
op += "*"
}
if reverse {
op = "!" + op
}
} else if patternType == HTTPLocationPatternTypeExact {
op = "="
if caseInsensitive {
op += "*"
}
if reverse {
op = "!" + op
}
} else if patternType == HTTPLocationPatternTypeRegexp {
op = "~"
if caseInsensitive {
op += "*"
}
if reverse {
op = "!" + op
}
}
if len(op) > 0 {
pattern = op + " " + pattern
}
this.Pattern = pattern
}
// PatternType 模式类型
func (this *HTTPLocationConfig) PatternType() int {
return this.patternType
}
// PatternString 模式字符串
// 去掉了模式字符
func (this *HTTPLocationConfig) PatternString() string {
if this.patternType == HTTPLocationPatternTypePrefix {
return this.prefix
}
if this.patternType == HTTPLocationPatternTypeSuffix {
return this.suffix
}
return this.path
}
// IsReverse 是否翻转
func (this *HTTPLocationConfig) IsReverse() bool {
return this.reverse
}
// IsCaseInsensitive 是否大小写非敏感
func (this *HTTPLocationConfig) IsCaseInsensitive() bool {
return this.caseInsensitive
}
// ExtractPattern 分析匹配条件
func (this *HTTPLocationConfig) ExtractPattern() error {
// 分析pattern
this.reverse = false
this.caseInsensitive = false
if len(this.Pattern) > 0 {
spaceIndex := strings.Index(this.Pattern, " ")
if spaceIndex < 0 {
this.patternType = HTTPLocationPatternTypePrefix
this.prefix = this.Pattern
} else {
cmd := this.Pattern[:spaceIndex]
pattern := strings.TrimSpace(this.Pattern[spaceIndex+1:])
if cmd == "*" { // 大小写非敏感
this.patternType = HTTPLocationPatternTypePrefix
this.prefix = pattern
this.caseInsensitive = true
} else if cmd == "!*" { // 大小写非敏感,翻转
this.patternType = HTTPLocationPatternTypePrefix
this.prefix = pattern
this.caseInsensitive = true
this.reverse = true
} else if cmd == "!" {
this.patternType = HTTPLocationPatternTypePrefix
this.prefix = pattern
this.reverse = true
} else if cmd == "=" {
this.patternType = HTTPLocationPatternTypeExact
this.path = pattern
} else if cmd == "=*" {
this.patternType = HTTPLocationPatternTypeExact
this.path = pattern
this.caseInsensitive = true
} else if cmd == "!=" {
this.patternType = HTTPLocationPatternTypeExact
this.path = pattern
this.reverse = true
} else if cmd == "!=*" {
this.patternType = HTTPLocationPatternTypeExact
this.path = pattern
this.reverse = true
this.caseInsensitive = true
} else if cmd == "~" { // 正则
this.patternType = HTTPLocationPatternTypeRegexp
reg, err := regexp.Compile(pattern)
if err != nil {
return err
}
this.reg = reg
this.path = pattern
} else if cmd == "!~" {
this.patternType = HTTPLocationPatternTypeRegexp
reg, err := regexp.Compile(pattern)
if err != nil {
return err
}
this.reg = reg
this.reverse = true
this.path = pattern
} else if cmd == "~*" { // 大小写非敏感小写
this.patternType = HTTPLocationPatternTypeRegexp
reg, err := regexp.Compile("(?i)" + pattern)
if err != nil {
return err
}
this.reg = reg
this.caseInsensitive = true
this.path = pattern
} else if cmd == "!~*" {
this.patternType = HTTPLocationPatternTypeRegexp
reg, err := regexp.Compile("(?i)" + pattern)
if err != nil {
return err
}
this.reg = reg
this.reverse = true
this.caseInsensitive = true
this.path = pattern
} else if cmd == "suffix" {
this.patternType = HTTPLocationPatternTypeSuffix
this.suffix = pattern
} else if cmd == "suffix*" {
this.patternType = HTTPLocationPatternTypeSuffix
this.caseInsensitive = true
this.suffix = pattern
} else if cmd == "!suffix" {
this.patternType = HTTPLocationPatternTypeSuffix
this.reverse = true
this.suffix = pattern
} else if cmd == "!suffix*" {
this.patternType = HTTPLocationPatternTypeSuffix
this.caseInsensitive = true
this.reverse = true
this.suffix = pattern
} else {
this.patternType = HTTPLocationPatternTypePrefix
this.prefix = pattern
}
}
} else {
this.patternType = HTTPLocationPatternTypePrefix
this.prefix = this.Pattern
}
return nil
}
// Match 判断是否匹配路径
// TODO 支持子Location
func (this *HTTPLocationConfig) Match(path string, formatter func(source string) string) (vars map[string]string, isMatched bool) {
// 判断条件
if this.Conds != nil && this.Conds.HasRequestConds() && !this.Conds.MatchRequest(formatter) {
return
}
if this.patternType == HTTPLocationPatternTypePrefix {
if this.reverse {
if this.caseInsensitive {
return nil, !strings.HasPrefix(strings.ToLower(path), strings.ToLower(this.prefix))
} else {
return nil, !strings.HasPrefix(path, this.prefix)
}
} else {
if this.caseInsensitive {
return nil, strings.HasPrefix(strings.ToLower(path), strings.ToLower(this.prefix))
} else {
return nil, strings.HasPrefix(path, this.prefix)
}
}
}
if this.patternType == HTTPLocationPatternTypeSuffix {
if this.reverse {
if this.caseInsensitive {
return nil, !strings.HasSuffix(strings.ToLower(path), strings.ToLower(this.suffix))
} else {
return nil, !strings.HasSuffix(path, this.suffix)
}
} else {
if this.caseInsensitive {
return nil, strings.HasSuffix(strings.ToLower(path), strings.ToLower(this.suffix))
} else {
return nil, strings.HasSuffix(path, this.suffix)
}
}
}
if this.patternType == HTTPLocationPatternTypeExact {
if this.reverse {
if this.caseInsensitive {
return nil, !strings.EqualFold(path, this.path)
} else {
return nil, path != this.path
}
} else {
if this.caseInsensitive {
return nil, strings.EqualFold(path, this.path)
} else {
return nil, path == this.path
}
}
}
// TODO 正则表达式匹配会让请求延迟0.01-0.02ms,可以使用缓存加速正则匹配,因为大部分路径都是不变的
if this.patternType == HTTPLocationPatternTypeRegexp {
if this.reg != nil {
if this.reverse {
return nil, !this.reg.MatchString(path)
} else {
b := this.reg.MatchString(path)
if b {
result := map[string]string{}
matches := this.reg.FindStringSubmatch(path)
subNames := this.reg.SubexpNames()
for index, value := range matches {
result[strconv.Itoa(index)] = value
subName := subNames[index]
if len(subName) > 0 {
result[subName] = value
}
}
return result, true
}
return nil, b
}
}
return nil, this.reverse
}
return nil, false
}