mirror of
				https://github.com/TeaOSLab/EdgeCommon.git
				synced 2025-11-04 05:00:24 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			131 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			131 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package serverconfigs
 | 
						||
 | 
						||
import (
 | 
						||
	"fmt"
 | 
						||
	"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
 | 
						||
	"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
 | 
						||
	"regexp"
 | 
						||
)
 | 
						||
 | 
						||
type HTTPRewriteMode = string
 | 
						||
 | 
						||
const (
 | 
						||
	HTTPRewriteTargetProxy = 1
 | 
						||
	HTTPRewriteTargetURL   = 2
 | 
						||
)
 | 
						||
 | 
						||
const (
 | 
						||
	HTTPRewriteModeRedirect HTTPRewriteMode = "redirect" // 跳转
 | 
						||
	HTTPRewriteModeProxy    HTTPRewriteMode = "proxy"    // 代理
 | 
						||
)
 | 
						||
 | 
						||
// HTTPRewriteRule 重写规则定义
 | 
						||
// TODO 实现对其他代理服务的引用
 | 
						||
type HTTPRewriteRule struct {
 | 
						||
	Id   int64 `yaml:"id" json:"id"`     // ID
 | 
						||
	IsOn bool  `yaml:"isOn" json:"isOn"` // 是否开启
 | 
						||
 | 
						||
	// 开启的条件
 | 
						||
	// 语法为:cond param operator value 比如:
 | 
						||
	// - cond ${status} gte 200
 | 
						||
	// - cond ${arg.name} eq lily
 | 
						||
	// - cond ${requestPath} regexp .*\.png
 | 
						||
	Conds *shared.HTTPRequestCondsConfig `yaml:"conds" json:"conds"` // 匹配条件
 | 
						||
 | 
						||
	// 规则
 | 
						||
	// 语法为:pattern regexp 比如:
 | 
						||
	// - pattern ^/article/(\d+).html
 | 
						||
	Pattern string `yaml:"pattern" json:"pattern"`
 | 
						||
 | 
						||
	// 模式
 | 
						||
	Mode           HTTPRewriteMode `yaml:"mode" json:"mode"`
 | 
						||
	RedirectStatus int             `yaml:"redirectStatus" json:"redirectStatus"` // 跳转的状态码
 | 
						||
	ProxyHost      string          `yaml:"proxyHost" json:"proxyHost"`           // 代理模式下的Host
 | 
						||
 | 
						||
	// 要替换成的URL
 | 
						||
	// 支持反向引用:${0}, ${1}, ...,也支持?P<NAME>语法
 | 
						||
	// - 如果以 proxy:// 开头,表示目标为代理,首先会尝试作为代理ID请求,如果找不到,会尝试作为代理Host请求
 | 
						||
	Replace string `yaml:"replace" json:"replace"`
 | 
						||
 | 
						||
	// 其他选项
 | 
						||
	IsBreak   bool `yaml:"isBreak" json:"isBreak"`     // 终止向下解析
 | 
						||
	WithQuery bool `yaml:"withQuery" json:"withQuery"` // 是否保留QueryString
 | 
						||
 | 
						||
	reg                   *regexp.Regexp
 | 
						||
	replaceHasVariables   bool
 | 
						||
	proxyHostHasVariables bool
 | 
						||
}
 | 
						||
 | 
						||
// Init 校验
 | 
						||
func (this *HTTPRewriteRule) Init() error {
 | 
						||
	this.replaceHasVariables = configutils.HasVariables(this.Replace)
 | 
						||
	this.proxyHostHasVariables = configutils.HasVariables(this.ProxyHost)
 | 
						||
 | 
						||
	reg, err := regexp.Compile(this.Pattern)
 | 
						||
	if err != nil {
 | 
						||
		return err
 | 
						||
	}
 | 
						||
	this.reg = reg
 | 
						||
 | 
						||
	// 校验条件
 | 
						||
	if this.Conds != nil {
 | 
						||
		err := this.Conds.Init()
 | 
						||
		if err != nil {
 | 
						||
			return err
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	return nil
 | 
						||
}
 | 
						||
 | 
						||
// MatchRequest 对某个请求执行规则
 | 
						||
func (this *HTTPRewriteRule) MatchRequest(requestPath string, formatter func(source string) string) (replace string, varMapping map[string]string, matched bool) {
 | 
						||
	if this.reg == nil {
 | 
						||
		return "", nil, false
 | 
						||
	}
 | 
						||
 | 
						||
	matches := this.reg.FindStringSubmatch(requestPath)
 | 
						||
	if len(matches) == 0 {
 | 
						||
		return "", nil, false
 | 
						||
	}
 | 
						||
 | 
						||
	// 判断条件
 | 
						||
	if this.Conds != nil && this.Conds.HasRequestConds() && !this.Conds.MatchRequest(formatter) {
 | 
						||
		return "", nil, false
 | 
						||
	}
 | 
						||
 | 
						||
	varMapping = map[string]string{}
 | 
						||
	subNames := this.reg.SubexpNames()
 | 
						||
	for index, match := range matches {
 | 
						||
		varMapping[fmt.Sprintf("%d", index)] = match
 | 
						||
		subName := subNames[index]
 | 
						||
		if len(subName) > 0 {
 | 
						||
			varMapping[subName] = match
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	replace = configutils.ParseVariables(this.Replace, func(varName string) string {
 | 
						||
		v, ok := varMapping[varName]
 | 
						||
		if ok {
 | 
						||
			return v
 | 
						||
		}
 | 
						||
		return "${" + varName + "}"
 | 
						||
	})
 | 
						||
 | 
						||
	if this.replaceHasVariables {
 | 
						||
		replace = formatter(replace)
 | 
						||
	}
 | 
						||
 | 
						||
	return replace, varMapping, true
 | 
						||
}
 | 
						||
 | 
						||
// IsExternalURL 判断是否是外部URL
 | 
						||
func (this *HTTPRewriteRule) IsExternalURL(url string) bool {
 | 
						||
	return shared.RegexpExternalURL.MatchString(url)
 | 
						||
}
 | 
						||
 | 
						||
// ProxyHostHasVariables 判断ProxyHost是否有变量
 | 
						||
func (this *HTTPRewriteRule) ProxyHostHasVariables() bool {
 | 
						||
	return this.proxyHostHasVariables
 | 
						||
}
 |