mirror of
https://github.com/TeaOSLab/EdgeCommon.git
synced 2026-04-02 03:45:20 +08:00
阶段性提交
This commit is contained in:
5
pkg/serverconfigs/shared/http_expire_header_config.go
Normal file
5
pkg/serverconfigs/shared/http_expire_header_config.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package shared
|
||||
|
||||
// HTTP Header中Expire设置
|
||||
type HTTPExpireHeaderConfig struct {
|
||||
}
|
||||
57
pkg/serverconfigs/shared/http_header_config.go
Normal file
57
pkg/serverconfigs/shared/http_header_config.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package shared
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
)
|
||||
|
||||
var regexpNamedVariable = regexp.MustCompile("\\${[\\w.-]+}")
|
||||
|
||||
// 头部信息定义
|
||||
type HTTPHeaderConfig struct {
|
||||
Id int `yaml:"id" json:"id"` // ID
|
||||
IsOn bool `yaml:"isOn" json:"isOn"` // 是否开启
|
||||
Name string `yaml:"name" json:"name"` // Name
|
||||
Value string `yaml:"value" json:"value"` // Value
|
||||
Status *HTTPStatusConfig `yaml:"status" json:"status"` // 支持的状态码 TODO
|
||||
|
||||
hasVariables bool
|
||||
}
|
||||
|
||||
// 获取新Header对象
|
||||
func NewHeaderConfig() *HTTPHeaderConfig {
|
||||
return &HTTPHeaderConfig{
|
||||
IsOn: true,
|
||||
}
|
||||
}
|
||||
|
||||
// 校验
|
||||
func (this *HTTPHeaderConfig) Init() error {
|
||||
this.hasVariables = regexpNamedVariable.MatchString(this.Value)
|
||||
|
||||
if this.Status != nil {
|
||||
err := this.Status.Init()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 判断是否匹配状态码
|
||||
func (this *HTTPHeaderConfig) Match(statusCode int) bool {
|
||||
if !this.IsOn {
|
||||
return false
|
||||
}
|
||||
|
||||
if this.Status == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return this.Status.Match(statusCode)
|
||||
}
|
||||
|
||||
// 是否有变量
|
||||
func (this *HTTPHeaderConfig) HasVariables() bool {
|
||||
return this.hasVariables
|
||||
}
|
||||
31
pkg/serverconfigs/shared/http_header_config_test.go
Normal file
31
pkg/serverconfigs/shared/http_header_config_test.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package shared
|
||||
|
||||
import (
|
||||
"github.com/iwind/TeaGo/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestHeaderConfig_Match(t *testing.T) {
|
||||
a := assert.NewAssertion(t)
|
||||
h := NewHeaderConfig()
|
||||
err := h.Init()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
a.IsFalse(h.Match(200))
|
||||
a.IsFalse(h.Match(400))
|
||||
|
||||
h.Status = &HTTPStatusConfig{
|
||||
Always: false,
|
||||
Codes: []int{200, 301, 302, 400},
|
||||
}
|
||||
err = h.Init()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
a.IsTrue(h.Match(400))
|
||||
a.IsFalse(h.Match(500))
|
||||
|
||||
h.Status.Always = true
|
||||
a.IsTrue(h.Match(500))
|
||||
}
|
||||
54
pkg/serverconfigs/shared/http_headers_config.go
Normal file
54
pkg/serverconfigs/shared/http_headers_config.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package shared
|
||||
|
||||
// HeaderList定义
|
||||
type HTTPHeadersConfig struct {
|
||||
AddHeaders []*HTTPHeaderConfig `yaml:"addHeaders" json:"addHeaders"` // TODO
|
||||
AddTrailers []*HTTPHeaderConfig `yaml:"addTrailers" json:"addTrailers"` // TODO
|
||||
SetHeaders []*HTTPHeaderConfig `yaml:"setHeaders" json:"setHeaders"` // TODO
|
||||
ReplaceHeaders []*HTTPHeaderConfig `yaml:"replaceHeaders" json:"replaceHeaders"` // TODO
|
||||
|
||||
Expires *HTTPExpireHeaderConfig `yaml:"expires" json:"expires"` // TODO
|
||||
}
|
||||
|
||||
// 获取新对象
|
||||
func NewHTTPHeaders() *HTTPHeadersConfig {
|
||||
return &HTTPHeadersConfig{}
|
||||
}
|
||||
|
||||
// 校验
|
||||
func (this *HTTPHeadersConfig) Init() error {
|
||||
for _, h := range this.AddHeaders {
|
||||
err := h.Init()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, h := range this.AddTrailers {
|
||||
err := h.Init()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, h := range this.SetHeaders {
|
||||
err := h.Init()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, h := range this.ReplaceHeaders {
|
||||
err := h.Init()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 判断是否为空
|
||||
func (this *HTTPHeadersConfig) IsEmpty() bool {
|
||||
return len(this.AddHeaders) == 0 && len(this.AddTrailers) == 0 && len(this.SetHeaders) == 0 && len(this.ReplaceHeaders) == 0 && this.Expires == nil
|
||||
}
|
||||
13
pkg/serverconfigs/shared/http_headers_config_test.go
Normal file
13
pkg/serverconfigs/shared/http_headers_config_test.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package shared
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestHeaderList_FormatHeaders(t *testing.T) {
|
||||
list := NewHTTPHeaders()
|
||||
err := list.Init()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
27
pkg/serverconfigs/shared/http_status_config.go
Normal file
27
pkg/serverconfigs/shared/http_status_config.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package shared
|
||||
|
||||
// 状态吗
|
||||
type HTTPStatusConfig struct {
|
||||
Always bool `yaml:"always" json:"always"`
|
||||
Codes []int `yaml:"codes" json:"codes"`
|
||||
}
|
||||
|
||||
func (this *HTTPStatusConfig) Init() error {
|
||||
// TODO
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *HTTPStatusConfig) Match(statusCode int) bool {
|
||||
if this.Always {
|
||||
return true
|
||||
}
|
||||
if len(this.Codes) == 0 {
|
||||
return false
|
||||
}
|
||||
for _, c := range this.Codes {
|
||||
if c == statusCode {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
21
pkg/serverconfigs/shared/locker.go
Normal file
21
pkg/serverconfigs/shared/locker.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package shared
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
var Locker = new(FileLocker)
|
||||
|
||||
// global file modify locker
|
||||
type FileLocker struct {
|
||||
locker sync.Mutex
|
||||
}
|
||||
|
||||
// lock
|
||||
func (this *FileLocker) Lock() {
|
||||
this.locker.Lock()
|
||||
}
|
||||
|
||||
func (this *FileLocker) Unlock() {
|
||||
this.locker.Unlock()
|
||||
}
|
||||
13
pkg/serverconfigs/shared/regexp.go
Normal file
13
pkg/serverconfigs/shared/regexp.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package shared
|
||||
|
||||
import "regexp"
|
||||
|
||||
// 常用的正则表达式
|
||||
var (
|
||||
RegexpDigitNumber = regexp.MustCompile(`^\d+$`) // 正整数
|
||||
RegexpFloatNumber = regexp.MustCompile(`^\d+(\.\d+)?$`) // 正浮点数,不支持e
|
||||
RegexpAllDigitNumber = regexp.MustCompile(`^[+-]?\d+$`) // 整数,支持正负数
|
||||
RegexpAllFloatNumber = regexp.MustCompile(`^[+-]?\d+(\.\d+)?$`) // 浮点数,支持正负数,不支持e
|
||||
RegexpExternalURL = regexp.MustCompile("(?i)^(http|https|ftp)://") // URL
|
||||
RegexpNamedVariable = regexp.MustCompile("\\${[\\w.-]+}") // 命名变量
|
||||
)
|
||||
17
pkg/serverconfigs/shared/regexp_test.go
Normal file
17
pkg/serverconfigs/shared/regexp_test.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package shared
|
||||
|
||||
import (
|
||||
"github.com/iwind/TeaGo/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRegexp(t *testing.T) {
|
||||
a := assert.NewAssertion(t)
|
||||
|
||||
a.IsTrue(RegexpFloatNumber.MatchString("123"))
|
||||
a.IsTrue(RegexpFloatNumber.MatchString("123.456"))
|
||||
a.IsFalse(RegexpFloatNumber.MatchString(".456"))
|
||||
a.IsFalse(RegexpFloatNumber.MatchString("abc"))
|
||||
a.IsFalse(RegexpFloatNumber.MatchString("123."))
|
||||
a.IsFalse(RegexpFloatNumber.MatchString("123.456e7"))
|
||||
}
|
||||
41
pkg/serverconfigs/shared/request_call.go
Normal file
41
pkg/serverconfigs/shared/request_call.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package shared
|
||||
|
||||
import (
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// 请求调用
|
||||
type RequestCall struct {
|
||||
Formatter func(source string) string
|
||||
Request *http.Request
|
||||
ResponseCallbacks []func(resp http.ResponseWriter)
|
||||
Options maps.Map
|
||||
}
|
||||
|
||||
// 获取新对象
|
||||
func NewRequestCall() *RequestCall {
|
||||
return &RequestCall{
|
||||
Options: maps.Map{},
|
||||
}
|
||||
}
|
||||
|
||||
// 重置
|
||||
func (this *RequestCall) Reset() {
|
||||
this.Formatter = nil
|
||||
this.Request = nil
|
||||
this.ResponseCallbacks = nil
|
||||
this.Options = maps.Map{}
|
||||
}
|
||||
|
||||
// 添加响应回调
|
||||
func (this *RequestCall) AddResponseCall(callback func(resp http.ResponseWriter)) {
|
||||
this.ResponseCallbacks = append(this.ResponseCallbacks, callback)
|
||||
}
|
||||
|
||||
// 执行响应回调
|
||||
func (this *RequestCall) CallResponseCallbacks(resp http.ResponseWriter) {
|
||||
for _, callback := range this.ResponseCallbacks {
|
||||
callback(resp)
|
||||
}
|
||||
}
|
||||
372
pkg/serverconfigs/shared/request_cond.go
Normal file
372
pkg/serverconfigs/shared/request_cond.go
Normal file
@@ -0,0 +1,372 @@
|
||||
package shared
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
"github.com/iwind/TeaGo/lists"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"github.com/iwind/TeaGo/utils/string"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 重写条件定义
|
||||
type RequestCond struct {
|
||||
Id string `yaml:"id" json:"id"` // ID
|
||||
|
||||
// 要测试的字符串
|
||||
// 其中可以使用跟请求相关的参数,比如:
|
||||
// ${arg.name}, ${requestPath}
|
||||
Param string `yaml:"param" json:"param"`
|
||||
|
||||
// 运算符
|
||||
Operator RequestCondOperator `yaml:"operator" json:"operator"`
|
||||
|
||||
// 对比
|
||||
Value string `yaml:"value" json:"value"`
|
||||
|
||||
isInt bool
|
||||
isFloat bool
|
||||
isIP bool
|
||||
|
||||
regValue *regexp.Regexp
|
||||
floatValue float64
|
||||
ipValue net.IP
|
||||
arrayValue []string
|
||||
}
|
||||
|
||||
// 取得新对象
|
||||
func NewRequestCond() *RequestCond {
|
||||
return &RequestCond{
|
||||
Id: stringutil.Rand(16),
|
||||
}
|
||||
}
|
||||
|
||||
// 校验配置
|
||||
func (this *RequestCond) Validate() error {
|
||||
this.isInt = RegexpDigitNumber.MatchString(this.Value)
|
||||
this.isFloat = RegexpFloatNumber.MatchString(this.Value)
|
||||
|
||||
if lists.ContainsString([]string{
|
||||
RequestCondOperatorRegexp,
|
||||
RequestCondOperatorNotRegexp,
|
||||
}, this.Operator) {
|
||||
reg, err := regexp.Compile(this.Value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
this.regValue = reg
|
||||
} else if lists.ContainsString([]string{
|
||||
RequestCondOperatorEqFloat,
|
||||
RequestCondOperatorGtFloat,
|
||||
RequestCondOperatorGteFloat,
|
||||
RequestCondOperatorLtFloat,
|
||||
RequestCondOperatorLteFloat,
|
||||
}, this.Operator) {
|
||||
this.floatValue = types.Float64(this.Value)
|
||||
} else if lists.ContainsString([]string{
|
||||
RequestCondOperatorEqIP,
|
||||
RequestCondOperatorGtIP,
|
||||
RequestCondOperatorGteIP,
|
||||
RequestCondOperatorLtIP,
|
||||
RequestCondOperatorLteIP,
|
||||
}, this.Operator) {
|
||||
this.ipValue = net.ParseIP(this.Value)
|
||||
this.isIP = this.ipValue != nil
|
||||
|
||||
if !this.isIP {
|
||||
return errors.New("value should be a valid ip")
|
||||
}
|
||||
} else if lists.ContainsString([]string{
|
||||
RequestCondOperatorIPRange,
|
||||
}, this.Operator) {
|
||||
if strings.Contains(this.Value, ",") {
|
||||
ipList := strings.SplitN(this.Value, ",", 2)
|
||||
ipString1 := strings.TrimSpace(ipList[0])
|
||||
ipString2 := strings.TrimSpace(ipList[1])
|
||||
|
||||
if len(ipString1) > 0 {
|
||||
ip1 := net.ParseIP(ipString1)
|
||||
if ip1 == nil {
|
||||
return errors.New("start ip is invalid")
|
||||
}
|
||||
}
|
||||
|
||||
if len(ipString2) > 0 {
|
||||
ip2 := net.ParseIP(ipString2)
|
||||
if ip2 == nil {
|
||||
return errors.New("end ip is invalid")
|
||||
}
|
||||
}
|
||||
} else if strings.Contains(this.Value, "/") {
|
||||
_, _, err := net.ParseCIDR(this.Value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return errors.New("invalid ip range")
|
||||
}
|
||||
} else if lists.ContainsString([]string{
|
||||
RequestCondOperatorIn,
|
||||
RequestCondOperatorNotIn,
|
||||
RequestCondOperatorFileExt,
|
||||
}, this.Operator) {
|
||||
stringsValue := []string{}
|
||||
err := json.Unmarshal([]byte(this.Value), &stringsValue)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
this.arrayValue = stringsValue
|
||||
} else if lists.ContainsString([]string{
|
||||
RequestCondOperatorFileMimeType,
|
||||
}, this.Operator) {
|
||||
stringsValue := []string{}
|
||||
err := json.Unmarshal([]byte(this.Value), &stringsValue)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for k, v := range stringsValue {
|
||||
if strings.Contains(v, "*") {
|
||||
v = regexp.QuoteMeta(v)
|
||||
v = strings.Replace(v, `\*`, ".*", -1)
|
||||
stringsValue[k] = v
|
||||
}
|
||||
}
|
||||
this.arrayValue = stringsValue
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 将此条件应用于请求,检查是否匹配
|
||||
func (this *RequestCond) Match(formatter func(source string) string) bool {
|
||||
paramValue := formatter(this.Param)
|
||||
switch this.Operator {
|
||||
case RequestCondOperatorRegexp:
|
||||
if this.regValue == nil {
|
||||
return false
|
||||
}
|
||||
return this.regValue.MatchString(paramValue)
|
||||
case RequestCondOperatorNotRegexp:
|
||||
if this.regValue == nil {
|
||||
return false
|
||||
}
|
||||
return !this.regValue.MatchString(paramValue)
|
||||
case RequestCondOperatorEqInt:
|
||||
return this.isInt && paramValue == this.Value
|
||||
case RequestCondOperatorEqFloat:
|
||||
return this.isFloat && types.Float64(paramValue) == this.floatValue
|
||||
case RequestCondOperatorGtFloat:
|
||||
return this.isFloat && types.Float64(paramValue) > this.floatValue
|
||||
case RequestCondOperatorGteFloat:
|
||||
return this.isFloat && types.Float64(paramValue) >= this.floatValue
|
||||
case RequestCondOperatorLtFloat:
|
||||
return this.isFloat && types.Float64(paramValue) < this.floatValue
|
||||
case RequestCondOperatorLteFloat:
|
||||
return this.isFloat && types.Float64(paramValue) <= this.floatValue
|
||||
case RequestCondOperatorMod:
|
||||
pieces := strings.SplitN(this.Value, ",", 2)
|
||||
if len(pieces) == 1 {
|
||||
rem := types.Int64(pieces[0])
|
||||
return types.Int64(paramValue)%10 == rem
|
||||
}
|
||||
div := types.Int64(pieces[0])
|
||||
if div == 0 {
|
||||
return false
|
||||
}
|
||||
rem := types.Int64(pieces[1])
|
||||
return types.Int64(paramValue)%div == rem
|
||||
case RequestCondOperatorMod10:
|
||||
return types.Int64(paramValue)%10 == types.Int64(this.Value)
|
||||
case RequestCondOperatorMod100:
|
||||
return types.Int64(paramValue)%100 == types.Int64(this.Value)
|
||||
case RequestCondOperatorEqString:
|
||||
return paramValue == this.Value
|
||||
case RequestCondOperatorNeqString:
|
||||
return paramValue != this.Value
|
||||
case RequestCondOperatorHasPrefix:
|
||||
return strings.HasPrefix(paramValue, this.Value)
|
||||
case RequestCondOperatorHasSuffix:
|
||||
return strings.HasSuffix(paramValue, this.Value)
|
||||
case RequestCondOperatorContainsString:
|
||||
return strings.Contains(paramValue, this.Value)
|
||||
case RequestCondOperatorNotContainsString:
|
||||
return !strings.Contains(paramValue, this.Value)
|
||||
case RequestCondOperatorEqIP:
|
||||
ip := net.ParseIP(paramValue)
|
||||
if ip == nil {
|
||||
return false
|
||||
}
|
||||
return this.isIP && bytes.Compare(this.ipValue, ip) == 0
|
||||
case RequestCondOperatorGtIP:
|
||||
ip := net.ParseIP(paramValue)
|
||||
if ip == nil {
|
||||
return false
|
||||
}
|
||||
return this.isIP && bytes.Compare(ip, this.ipValue) > 0
|
||||
case RequestCondOperatorGteIP:
|
||||
ip := net.ParseIP(paramValue)
|
||||
if ip == nil {
|
||||
return false
|
||||
}
|
||||
return this.isIP && bytes.Compare(ip, this.ipValue) >= 0
|
||||
case RequestCondOperatorLtIP:
|
||||
ip := net.ParseIP(paramValue)
|
||||
if ip == nil {
|
||||
return false
|
||||
}
|
||||
return this.isIP && bytes.Compare(ip, this.ipValue) < 0
|
||||
case RequestCondOperatorLteIP:
|
||||
ip := net.ParseIP(paramValue)
|
||||
if ip == nil {
|
||||
return false
|
||||
}
|
||||
return this.isIP && bytes.Compare(ip, this.ipValue) <= 0
|
||||
case RequestCondOperatorIPRange:
|
||||
ip := net.ParseIP(paramValue)
|
||||
if ip == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// 检查IP范围格式
|
||||
if strings.Contains(this.Value, ",") {
|
||||
ipList := strings.SplitN(this.Value, ",", 2)
|
||||
ipString1 := strings.TrimSpace(ipList[0])
|
||||
ipString2 := strings.TrimSpace(ipList[1])
|
||||
|
||||
if len(ipString1) > 0 {
|
||||
ip1 := net.ParseIP(ipString1)
|
||||
if ip1 == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if bytes.Compare(ip, ip1) < 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if len(ipString2) > 0 {
|
||||
ip2 := net.ParseIP(ipString2)
|
||||
if ip2 == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if bytes.Compare(ip, ip2) > 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
} else if strings.Contains(this.Value, "/") {
|
||||
_, ipNet, err := net.ParseCIDR(this.Value)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return ipNet.Contains(ip)
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case RequestCondOperatorIn:
|
||||
return lists.ContainsString(this.arrayValue, paramValue)
|
||||
case RequestCondOperatorNotIn:
|
||||
return !lists.ContainsString(this.arrayValue, paramValue)
|
||||
case RequestCondOperatorFileExt:
|
||||
ext := filepath.Ext(paramValue)
|
||||
if len(ext) > 0 {
|
||||
ext = ext[1:] // remove dot
|
||||
}
|
||||
return lists.ContainsString(this.arrayValue, strings.ToLower(ext))
|
||||
case RequestCondOperatorFileMimeType:
|
||||
index := strings.Index(paramValue, ";")
|
||||
if index >= 0 {
|
||||
paramValue = strings.TrimSpace(paramValue[:index])
|
||||
}
|
||||
if len(this.arrayValue) == 0 {
|
||||
return false
|
||||
}
|
||||
for _, v := range this.arrayValue {
|
||||
if strings.Contains(v, "*") {
|
||||
reg, err := stringutil.RegexpCompile("^" + v + "$")
|
||||
if err == nil && reg.MatchString(paramValue) {
|
||||
return true
|
||||
}
|
||||
} else if paramValue == v {
|
||||
return true
|
||||
}
|
||||
}
|
||||
case RequestCondOperatorVersionRange:
|
||||
if strings.Contains(this.Value, ",") {
|
||||
versions := strings.SplitN(this.Value, ",", 2)
|
||||
version1 := strings.TrimSpace(versions[0])
|
||||
version2 := strings.TrimSpace(versions[1])
|
||||
if len(version1) > 0 && stringutil.VersionCompare(paramValue, version1) < 0 {
|
||||
return false
|
||||
}
|
||||
if len(version2) > 0 && stringutil.VersionCompare(paramValue, version2) > 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
return stringutil.VersionCompare(paramValue, this.Value) >= 0
|
||||
}
|
||||
case RequestCondOperatorIPMod:
|
||||
pieces := strings.SplitN(this.Value, ",", 2)
|
||||
if len(pieces) == 1 {
|
||||
rem := types.Int64(pieces[0])
|
||||
return this.ipToInt64(net.ParseIP(paramValue))%10 == rem
|
||||
}
|
||||
div := types.Int64(pieces[0])
|
||||
if div == 0 {
|
||||
return false
|
||||
}
|
||||
rem := types.Int64(pieces[1])
|
||||
return this.ipToInt64(net.ParseIP(paramValue))%div == rem
|
||||
case RequestCondOperatorIPMod10:
|
||||
return this.ipToInt64(net.ParseIP(paramValue))%10 == types.Int64(this.Value)
|
||||
case RequestCondOperatorIPMod100:
|
||||
return this.ipToInt64(net.ParseIP(paramValue))%100 == types.Int64(this.Value)
|
||||
case RequestCondOperatorFileExist:
|
||||
index := strings.Index(paramValue, "?")
|
||||
if index > -1 {
|
||||
paramValue = paramValue[:index]
|
||||
}
|
||||
if len(paramValue) == 0 {
|
||||
return false
|
||||
}
|
||||
if !filepath.IsAbs(paramValue) {
|
||||
paramValue = Tea.Root + Tea.DS + paramValue
|
||||
}
|
||||
stat, err := os.Stat(paramValue)
|
||||
return err == nil && !stat.IsDir()
|
||||
case RequestCondOperatorFileNotExist:
|
||||
index := strings.Index(paramValue, "?")
|
||||
if index > -1 {
|
||||
paramValue = paramValue[:index]
|
||||
}
|
||||
if len(paramValue) == 0 {
|
||||
return true
|
||||
}
|
||||
if !filepath.IsAbs(paramValue) {
|
||||
paramValue = Tea.Root + Tea.DS + paramValue
|
||||
}
|
||||
stat, err := os.Stat(paramValue)
|
||||
return err != nil || stat.IsDir()
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (this *RequestCond) ipToInt64(ip net.IP) int64 {
|
||||
if len(ip) == 0 {
|
||||
return 0
|
||||
}
|
||||
if len(ip) == 16 {
|
||||
return int64(binary.BigEndian.Uint32(ip[12:16]))
|
||||
}
|
||||
return int64(binary.BigEndian.Uint32(ip))
|
||||
}
|
||||
1000
pkg/serverconfigs/shared/request_cond_test.go
Normal file
1000
pkg/serverconfigs/shared/request_cond_test.go
Normal file
File diff suppressed because it is too large
Load Diff
226
pkg/serverconfigs/shared/request_operators.go
Normal file
226
pkg/serverconfigs/shared/request_operators.go
Normal file
@@ -0,0 +1,226 @@
|
||||
package shared
|
||||
|
||||
import "github.com/iwind/TeaGo/maps"
|
||||
|
||||
// 运算符定义
|
||||
type RequestCondOperator = string
|
||||
|
||||
const (
|
||||
// 正则
|
||||
RequestCondOperatorRegexp RequestCondOperator = "regexp"
|
||||
RequestCondOperatorNotRegexp RequestCondOperator = "not regexp"
|
||||
|
||||
// 数字相关
|
||||
RequestCondOperatorEqInt RequestCondOperator = "eq int" // 整数等于
|
||||
RequestCondOperatorEqFloat RequestCondOperator = "eq float" // 浮点数等于
|
||||
RequestCondOperatorGtFloat RequestCondOperator = "gt"
|
||||
RequestCondOperatorGteFloat RequestCondOperator = "gte"
|
||||
RequestCondOperatorLtFloat RequestCondOperator = "lt"
|
||||
RequestCondOperatorLteFloat RequestCondOperator = "lte"
|
||||
|
||||
// 取模
|
||||
RequestCondOperatorMod10 RequestCondOperator = "mod 10"
|
||||
RequestCondOperatorMod100 RequestCondOperator = "mod 100"
|
||||
RequestCondOperatorMod RequestCondOperator = "mod"
|
||||
|
||||
// 字符串相关
|
||||
RequestCondOperatorEqString RequestCondOperator = "eq"
|
||||
RequestCondOperatorNeqString RequestCondOperator = "not"
|
||||
RequestCondOperatorHasPrefix RequestCondOperator = "prefix"
|
||||
RequestCondOperatorHasSuffix RequestCondOperator = "suffix"
|
||||
RequestCondOperatorContainsString RequestCondOperator = "contains"
|
||||
RequestCondOperatorNotContainsString RequestCondOperator = "not contains"
|
||||
RequestCondOperatorIn RequestCondOperator = "in"
|
||||
RequestCondOperatorNotIn RequestCondOperator = "not in"
|
||||
RequestCondOperatorFileExt RequestCondOperator = "file ext"
|
||||
RequestCondOperatorFileMimeType RequestCondOperator = "mime type"
|
||||
RequestCondOperatorVersionRange RequestCondOperator = "version range"
|
||||
|
||||
// IP相关
|
||||
RequestCondOperatorEqIP RequestCondOperator = "eq ip"
|
||||
RequestCondOperatorGtIP RequestCondOperator = "gt ip"
|
||||
RequestCondOperatorGteIP RequestCondOperator = "gte ip"
|
||||
RequestCondOperatorLtIP RequestCondOperator = "lt ip"
|
||||
RequestCondOperatorLteIP RequestCondOperator = "lte ip"
|
||||
RequestCondOperatorIPRange RequestCondOperator = "ip range"
|
||||
RequestCondOperatorIPMod10 RequestCondOperator = "ip mod 10"
|
||||
RequestCondOperatorIPMod100 RequestCondOperator = "ip mod 100"
|
||||
RequestCondOperatorIPMod RequestCondOperator = "ip mod"
|
||||
|
||||
// 文件相关
|
||||
RequestCondOperatorFileExist RequestCondOperator = "file exist"
|
||||
RequestCondOperatorFileNotExist RequestCondOperator = "file not exist"
|
||||
)
|
||||
|
||||
// 所有的运算符
|
||||
func AllRequestOperators() []maps.Map {
|
||||
return []maps.Map{
|
||||
{
|
||||
"name": "正则表达式匹配",
|
||||
"op": RequestCondOperatorRegexp,
|
||||
"description": "判断是否正则表达式匹配",
|
||||
},
|
||||
{
|
||||
"name": "正则表达式不匹配",
|
||||
"op": RequestCondOperatorNotRegexp,
|
||||
"description": "判断是否正则表达式不匹配",
|
||||
},
|
||||
{
|
||||
"name": "字符串等于",
|
||||
"op": RequestCondOperatorEqString,
|
||||
"description": "使用字符串对比参数值是否相等于某个值",
|
||||
},
|
||||
{
|
||||
"name": "字符串前缀",
|
||||
"op": RequestCondOperatorHasPrefix,
|
||||
"description": "参数值包含某个前缀",
|
||||
},
|
||||
{
|
||||
"name": "字符串后缀",
|
||||
"op": RequestCondOperatorHasSuffix,
|
||||
"description": "参数值包含某个后缀",
|
||||
},
|
||||
{
|
||||
"name": "字符串包含",
|
||||
"op": RequestCondOperatorContainsString,
|
||||
"description": "参数值包含另外一个字符串",
|
||||
},
|
||||
{
|
||||
"name": "字符串不包含",
|
||||
"op": RequestCondOperatorNotContainsString,
|
||||
"description": "参数值不包含另外一个字符串",
|
||||
},
|
||||
{
|
||||
"name": "字符串不等于",
|
||||
"op": RequestCondOperatorNeqString,
|
||||
"description": "使用字符串对比参数值是否不相等于某个值",
|
||||
},
|
||||
{
|
||||
"name": "在列表中",
|
||||
"op": RequestCondOperatorIn,
|
||||
"description": "判断参数值在某个列表中",
|
||||
},
|
||||
{
|
||||
"name": "不在列表中",
|
||||
"op": RequestCondOperatorNotIn,
|
||||
"description": "判断参数值不在某个列表中",
|
||||
},
|
||||
{
|
||||
"name": "扩展名",
|
||||
"op": RequestCondOperatorFileExt,
|
||||
"description": "判断小写的扩展名(不带点)在某个列表中",
|
||||
},
|
||||
{
|
||||
"name": "MimeType",
|
||||
"op": RequestCondOperatorFileMimeType,
|
||||
"description": "判断MimeType在某个列表中,支持类似于image/*的语法",
|
||||
},
|
||||
{
|
||||
"name": "版本号范围",
|
||||
"op": RequestCondOperatorVersionRange,
|
||||
"description": "判断版本号在某个范围内,格式为version1,version2",
|
||||
},
|
||||
{
|
||||
"name": "整数等于",
|
||||
"op": RequestCondOperatorEqInt,
|
||||
"description": "将参数转换为整数数字后进行对比",
|
||||
},
|
||||
{
|
||||
"name": "浮点数等于",
|
||||
"op": RequestCondOperatorEqFloat,
|
||||
"description": "将参数转换为可以有小数的浮点数字进行对比",
|
||||
},
|
||||
{
|
||||
"name": "数字大于",
|
||||
"op": RequestCondOperatorGtFloat,
|
||||
"description": "将参数转换为数字进行对比",
|
||||
},
|
||||
{
|
||||
"name": "数字大于等于",
|
||||
"op": RequestCondOperatorGteFloat,
|
||||
"description": "将参数转换为数字进行对比",
|
||||
},
|
||||
{
|
||||
"name": "数字小于",
|
||||
"op": RequestCondOperatorLtFloat,
|
||||
"description": "将参数转换为数字进行对比",
|
||||
},
|
||||
{
|
||||
"name": "数字小于等于",
|
||||
"op": RequestCondOperatorLteFloat,
|
||||
"description": "将参数转换为数字进行对比",
|
||||
},
|
||||
{
|
||||
"name": "整数取模10",
|
||||
"op": RequestCondOperatorMod10,
|
||||
"description": "对整数参数值取模,除数为10,对比值为余数",
|
||||
},
|
||||
{
|
||||
"name": "整数取模100",
|
||||
"op": RequestCondOperatorMod100,
|
||||
"description": "对整数参数值取模,除数为100,对比值为余数",
|
||||
},
|
||||
{
|
||||
"name": "整数取模",
|
||||
"op": RequestCondOperatorMod,
|
||||
"description": "对整数参数值取模,对比值格式为:除数,余数,比如10,1",
|
||||
},
|
||||
{
|
||||
"name": "IP等于",
|
||||
"op": RequestCondOperatorEqIP,
|
||||
"description": "将参数转换为IP进行对比",
|
||||
},
|
||||
{
|
||||
"name": "IP大于",
|
||||
"op": RequestCondOperatorGtIP,
|
||||
"description": "将参数转换为IP进行对比",
|
||||
},
|
||||
{
|
||||
"name": "IP大于等于",
|
||||
"op": RequestCondOperatorGteIP,
|
||||
"description": "将参数转换为IP进行对比",
|
||||
},
|
||||
{
|
||||
"name": "IP小于",
|
||||
"op": RequestCondOperatorLtIP,
|
||||
"description": "将参数转换为IP进行对比",
|
||||
},
|
||||
{
|
||||
"name": "IP小于等于",
|
||||
"op": RequestCondOperatorLteIP,
|
||||
"description": "将参数转换为IP进行对比",
|
||||
},
|
||||
{
|
||||
"name": "IP范围",
|
||||
"op": RequestCondOperatorIPRange,
|
||||
"description": "IP在某个范围之内,范围格式可以是英文逗号分隔的ip1,ip2,或者CIDR格式的ip/bits",
|
||||
},
|
||||
{
|
||||
"name": "IP取模10",
|
||||
"op": RequestCondOperatorIPMod10,
|
||||
"description": "对IP参数值取模,除数为10,对比值为余数",
|
||||
},
|
||||
{
|
||||
"name": "IP取模100",
|
||||
"op": RequestCondOperatorIPMod100,
|
||||
"description": "对IP参数值取模,除数为100,对比值为余数",
|
||||
},
|
||||
{
|
||||
"name": "IP取模",
|
||||
"op": RequestCondOperatorIPMod,
|
||||
"description": "对IP参数值取模,对比值格式为:除数,余数,比如10,1",
|
||||
},
|
||||
|
||||
{
|
||||
"name": "文件存在",
|
||||
"op": RequestCondOperatorFileExist,
|
||||
"description": "判断参数值解析后的文件是否存在",
|
||||
},
|
||||
|
||||
{
|
||||
"name": "文件不存在",
|
||||
"op": RequestCondOperatorFileNotExist,
|
||||
"description": "判断参数值解析后的文件是否不存在",
|
||||
},
|
||||
}
|
||||
}
|
||||
30
pkg/serverconfigs/shared/size_capacity.go
Normal file
30
pkg/serverconfigs/shared/size_capacity.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package shared
|
||||
|
||||
type SizeCapacityUnit = string
|
||||
|
||||
const (
|
||||
SizeCapacityUnitByte SizeCapacityUnit = "byte"
|
||||
SizeCapacityUnitKB SizeCapacityUnit = "kb"
|
||||
SizeCapacityUnitMB SizeCapacityUnit = "mb"
|
||||
SizeCapacityUnitGB SizeCapacityUnit = "gb"
|
||||
)
|
||||
|
||||
type SizeCapacity struct {
|
||||
Count int64 `json:"count" yaml:"count"`
|
||||
Unit SizeCapacityUnit `json:"unit" yaml:"unit"`
|
||||
}
|
||||
|
||||
func (this *SizeCapacity) Bytes() int64 {
|
||||
switch this.Unit {
|
||||
case SizeCapacityUnitByte:
|
||||
return this.Count
|
||||
case SizeCapacityUnitKB:
|
||||
return this.Count * 1024
|
||||
case SizeCapacityUnitMB:
|
||||
return this.Count * 1024 * 1024
|
||||
case SizeCapacityUnitGB:
|
||||
return this.Count * 1024 * 1024 * 1024
|
||||
default:
|
||||
return this.Count
|
||||
}
|
||||
}
|
||||
36
pkg/serverconfigs/shared/time_duration.go
Normal file
36
pkg/serverconfigs/shared/time_duration.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package shared
|
||||
|
||||
import "time"
|
||||
|
||||
type TimeDurationUnit = string
|
||||
|
||||
const (
|
||||
TimeDurationUnitMS TimeDurationUnit = "ms"
|
||||
TimeDurationUnitSecond TimeDurationUnit = "second"
|
||||
TimeDurationUnitMinute TimeDurationUnit = "minute"
|
||||
TimeDurationUnitHour TimeDurationUnit = "hour"
|
||||
TimeDurationUnitDay TimeDurationUnit = "day"
|
||||
)
|
||||
|
||||
// 时间间隔
|
||||
type TimeDuration struct {
|
||||
Count int64 `yaml:"count" json:"count"` // 数量
|
||||
Unit TimeDurationUnit `yaml:"unit" json:"unit"` // 单位
|
||||
}
|
||||
|
||||
func (this *TimeDuration) Duration() time.Duration {
|
||||
switch this.Unit {
|
||||
case TimeDurationUnitMS:
|
||||
return time.Duration(this.Count) * time.Millisecond
|
||||
case TimeDurationUnitSecond:
|
||||
return time.Duration(this.Count) * time.Second
|
||||
case TimeDurationUnitMinute:
|
||||
return time.Duration(this.Count) * time.Minute
|
||||
case TimeDurationUnitHour:
|
||||
return time.Duration(this.Count) * time.Hour
|
||||
case TimeDurationUnitDay:
|
||||
return time.Duration(this.Count) * 24 * time.Hour
|
||||
default:
|
||||
return time.Duration(this.Count) * time.Second
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user