阶段性提交

This commit is contained in:
刘祥超
2020-09-13 19:27:47 +08:00
commit dd504b39c8
120 changed files with 19900 additions and 0 deletions

View File

@@ -0,0 +1,5 @@
package shared
// HTTP Header中Expire设置
type HTTPExpireHeaderConfig struct {
}

View 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
}

View 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))
}

View 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
}

View 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)
}
}

View 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
}

View 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()
}

View 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.-]+}") // 命名变量
)

View 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"))
}

View 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)
}
}

View 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))
}

File diff suppressed because it is too large Load Diff

View 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": "判断参数值解析后的文件是否不存在",
},
}
}

View 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
}
}

View 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
}
}