Compare commits

...

34 Commits

Author SHA1 Message Date
刘祥超
99d24afbcd 验证码验证不区分访问路径 2023-11-19 15:34:22 +08:00
刘祥超
ba19a9f4c4 减少一些不必要的访问统计 2023-11-19 09:10:37 +08:00
刘祥超
7fea67a2b5 区域封禁支持观察者模式 2023-11-18 15:02:58 +08:00
刘祥超
ecd2e6955e 当SNI无法读取到ServerName时,尝试使用节点IP搜索网站 2023-11-18 12:08:51 +08:00
刘祥超
09d60a3047 优化内存缓存最大值算法 2023-11-17 19:12:24 +08:00
刘祥超
e24f390412 优化人机识别样式 2023-11-16 08:57:20 +08:00
刘祥超
eeacec1a4e 人机识别增加UA记录 2023-11-16 08:44:07 +08:00
刘祥超
30cd6373c5 修复WAF相关单元测试 2023-11-16 08:43:31 +08:00
刘祥超
87a6ab0559 源站支持404内容自动重试其他源站 2023-11-15 19:06:15 +08:00
刘祥超
59f27215d3 使用泛型优化计数器内存 2023-11-15 15:57:41 +08:00
刘祥超
768384dcf0 优化计数器 2023-11-15 15:17:03 +08:00
刘祥超
3b52ac0fd2 WAF人机识别实现点击验证和滑动解锁验证/单个网站可以设置默认的人机识别方式 2023-11-15 15:10:25 +08:00
刘祥超
41343b2264 版本号修改为1.3.0 2023-11-14 14:47:11 +08:00
刘祥超
d084059f04 缓存索引数据库取消最后访问时间,以提升某些查询速度 2023-11-13 21:43:25 +08:00
刘祥超
9253c44ba5 使用utils.CutPrefix代替strings.CutPrefix 2023-11-13 18:17:32 +08:00
刘祥超
ddec0bf2e0 限制请求域名长度不超过253 2023-11-13 17:20:46 +08:00
刘祥超
aeba1805af 限制统计数据中域名长度 2023-11-13 17:07:55 +08:00
刘祥超
ecff37e080 优化计数器代码 2023-11-13 15:11:11 +08:00
刘祥超
d31dac75be 自定义页面增加例外URL和限制URL设置 2023-11-13 10:46:26 +08:00
刘祥超
4571c84102 自定义页面增加“跳转URL”功能 2023-11-10 16:36:35 +08:00
刘祥超
6a9f59bee0 修复访问节点自定义内容可能无法生效的问题 2023-11-10 11:41:45 +08:00
刘祥超
f1951869f1 URL跳转中增加例外域名和仅限域名 2023-11-10 11:06:24 +08:00
刘祥超
cfd4195c0f 读取缓存时可以使用源站的ETag 2023-11-09 18:20:32 +08:00
刘祥超
d793472b42 调整缓存索引数据库缓存尺寸 2023-11-06 22:10:34 +08:00
刘祥超
1e56247b9c 调整缓存索引数据库缓存尺寸 2023-11-06 20:26:57 +08:00
刘祥超
c34a38857a 增加测试用例 2023-11-06 18:36:11 +08:00
刘祥超
57fa7036dc 修复磁盘占用统计计算错误 2023-11-03 11:51:53 +08:00
刘祥超
b8a3ac750f 上传域名统计时,限制域名长度不能超过64位 2023-11-02 17:23:39 +08:00
刘祥超
9d6692db0c 进一步缩短缓存Key临时缓存时间 2023-11-02 14:14:28 +08:00
刘祥超
ad94327226 实现网络数据包相关统计(商业版本) 2023-10-26 17:18:42 +08:00
刘祥超
aee1ff9609 更新库 2023-10-26 09:53:23 +08:00
刘祥超
822e967874 优化文件句柄缓存容量判断 2023-10-17 09:59:04 +08:00
刘祥超
2acf890b8e 限制内存缓存最大容量为系统内存的三分之一 2023-10-16 14:28:07 +08:00
刘祥超
3909695b44 优化代码 2023-10-16 11:48:38 +08:00
46 changed files with 1004 additions and 333 deletions

View File

@@ -6,6 +6,7 @@ function build() {
VERSION=$(lookup-version "$ROOT"/../internal/const/const.go) VERSION=$(lookup-version "$ROOT"/../internal/const/const.go)
DIST=$ROOT/"../dist/${NAME}" DIST=$ROOT/"../dist/${NAME}"
MUSL_DIR="/usr/local/opt/musl-cross/bin" MUSL_DIR="/usr/local/opt/musl-cross/bin"
SRCDIR=$(realpath "$ROOT/..")
# for macOS users: precompiled gcc can be downloaded from https://github.com/messense/homebrew-macos-cross-toolchains # for macOS users: precompiled gcc can be downloaded from https://github.com/messense/homebrew-macos-cross-toolchains
GCC_X86_64_DIR="/usr/local/gcc/x86_64-unknown-linux-gnu/bin" GCC_X86_64_DIR="/usr/local/gcc/x86_64-unknown-linux-gnu/bin"
@@ -70,6 +71,8 @@ function build() {
CC_PATH="" CC_PATH=""
CXX_PATH="" CXX_PATH=""
CGO_LDFLAGS=""
CGO_CFLAGS=""
BUILD_TAG=$TAG BUILD_TAG=$TAG
if [[ `uname -a` == *"Darwin"* && "${OS}" == "linux" ]]; then if [[ `uname -a` == *"Darwin"* && "${OS}" == "linux" ]]; then
if [ "${ARCH}" == "amd64" ]; then if [ "${ARCH}" == "amd64" ]; then
@@ -79,7 +82,7 @@ function build() {
CC_PATH="x86_64-unknown-linux-gnu-gcc" CC_PATH="x86_64-unknown-linux-gnu-gcc"
CXX_PATH="x86_64-unknown-linux-gnu-g++" CXX_PATH="x86_64-unknown-linux-gnu-g++"
if [ "$TAG" = "plus" ]; then if [ "$TAG" = "plus" ]; then
BUILD_TAG="plus,script" BUILD_TAG="plus,script,packet"
fi fi
else else
CC_PATH="x86_64-linux-musl-gcc" CC_PATH="x86_64-linux-musl-gcc"
@@ -97,7 +100,7 @@ function build() {
CC_PATH="aarch64-unknown-linux-gnu-gcc" CC_PATH="aarch64-unknown-linux-gnu-gcc"
CXX_PATH="aarch64-unknown-linux-gnu-g++" CXX_PATH="aarch64-unknown-linux-gnu-g++"
if [ "$TAG" = "plus" ]; then if [ "$TAG" = "plus" ]; then
BUILD_TAG="plus,script" BUILD_TAG="plus,script,packet"
fi fi
else else
CC_PATH="aarch64-linux-musl-gcc" CC_PATH="aarch64-linux-musl-gcc"
@@ -117,13 +120,26 @@ function build() {
CXX_PATH="mips64el-linux-musl-g++" CXX_PATH="mips64el-linux-musl-g++"
fi fi
fi fi
# libpcap
if [ "$OS" == "linux" ] && [[ "$ARCH" == "amd64" || "$ARCH" == "arm64" ]] && [ "$TAG" == "plus" ]; then
CGO_LDFLAGS="-L${SRCDIR}/libs/libpcap/${ARCH} -lpcap"
CGO_CFLAGS="-I${SRCDIR}/libs/libpcap/src/libpcap -I${SRCDIR}/libs/libpcap/src/libpcap/pcap"
fi
if [ ! -z $CC_PATH ]; then if [ ! -z $CC_PATH ]; then
env CC=$MUSL_DIR/$CC_PATH CXX=$MUSL_DIR/$CXX_PATH GOOS="${OS}" GOARCH="${ARCH}" CGO_ENABLED=1 go build -trimpath -tags $BUILD_TAG -o "$DIST"/bin/${NAME} -ldflags "-linkmode external -extldflags -static -s -w" "$ROOT"/../cmd/edge-node/main.go env CC=$MUSL_DIR/$CC_PATH \
CXX=$MUSL_DIR/$CXX_PATH GOOS="${OS}" \
GOARCH="${ARCH}" CGO_ENABLED=1 \
CGO_LDFLAGS="${CGO_LDFLAGS}" \
CGO_CFLAGS="${CGO_CFLAGS}" \
go build -trimpath -tags $BUILD_TAG -o "$DIST"/bin/${NAME} -ldflags "-linkmode external -extldflags -static -s -w" "$ROOT"/../cmd/edge-node/main.go
else else
if [[ `uname` == *"Linux"* ]] && [ "$OS" == "linux" ] && [[ "$ARCH" == "amd64" || "$ARCH" == "arm64" ]] && [ "$TAG" == "plus" ]; then if [[ `uname` == *"Linux"* ]] && [ "$OS" == "linux" ] && [[ "$ARCH" == "amd64" || "$ARCH" == "arm64" ]] && [ "$TAG" == "plus" ]; then
BUILD_TAG="plus,script" BUILD_TAG="plus,script,packet"
fi fi
env GOOS="${OS}" GOARCH="${ARCH}" CGO_ENABLED=1 go build -trimpath -tags $BUILD_TAG -o "$DIST"/bin/${NAME} -ldflags="-s -w" "$ROOT"/../cmd/edge-node/main.go
env GOOS="${OS}" GOARCH="${ARCH}" CGO_ENABLED=1 CGO_LDFLAGS="${CGO_LDFLAGS}" CGO_CFLAGS="${CGO_CFLAGS}" go build -trimpath -tags $BUILD_TAG -o "$DIST"/bin/${NAME} -ldflags="-s -w" "$ROOT"/../cmd/edge-node/main.go
fi fi
if [ ! -f "${DIST}/bin/${NAME}" ]; then if [ ! -f "${DIST}/bin/${NAME}" ]; then

29
go.mod
View File

@@ -25,28 +25,27 @@ require (
github.com/iwind/TeaGo v0.0.0-20230630104525-161f0b32996d github.com/iwind/TeaGo v0.0.0-20230630104525-161f0b32996d
github.com/iwind/gofcgi v0.0.0-20210528023741-a92711d45f11 github.com/iwind/gofcgi v0.0.0-20210528023741-a92711d45f11
github.com/iwind/gosock v0.0.0-20211103081026-ee4652210ca4 github.com/iwind/gosock v0.0.0-20211103081026-ee4652210ca4
github.com/iwind/gowebp v0.0.0-20230927084601-21954d2e229f github.com/iwind/gowebp v0.0.0-20231026013903-1c22b0d78cc4
github.com/klauspost/compress v1.16.5 github.com/klauspost/compress v1.17.2
github.com/mattn/go-sqlite3 v1.14.17 github.com/mattn/go-sqlite3 v1.14.17
github.com/mdlayher/netlink v1.7.1 github.com/mdlayher/netlink v1.7.1
github.com/miekg/dns v1.1.43 github.com/miekg/dns v1.1.43
github.com/mssola/useragent v1.0.0 github.com/mssola/useragent v1.0.0
github.com/pires/go-proxyproto v0.6.1 github.com/pires/go-proxyproto v0.6.1
github.com/qiniu/go-sdk/v7 v7.16.0 github.com/qiniu/go-sdk/v7 v7.16.0
github.com/quic-go/quic-go v0.39.0 github.com/quic-go/quic-go v0.39.2
github.com/shirou/gopsutil/v3 v3.22.2 github.com/shirou/gopsutil/v3 v3.22.2
github.com/tencentyun/cos-go-sdk-v5 v0.7.41 github.com/tencentyun/cos-go-sdk-v5 v0.7.41
golang.org/x/image v0.7.0 golang.org/x/image v0.13.0
golang.org/x/net v0.17.0 golang.org/x/net v0.17.0
golang.org/x/sys v0.13.0 golang.org/x/sys v0.13.0
google.golang.org/grpc v1.55.0 google.golang.org/grpc v1.59.0
google.golang.org/protobuf v1.30.0 google.golang.org/protobuf v1.31.0
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
) )
require ( require (
github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/chai2010/webp v1.1.1 // indirect
github.com/clbanning/mxj v1.8.4 // indirect github.com/clbanning/mxj v1.8.4 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-ole/go-ole v1.2.6 // indirect
@@ -54,7 +53,7 @@ require (
github.com/golang/protobuf v1.5.3 // indirect github.com/golang/protobuf v1.5.3 // indirect
github.com/google/go-cmp v0.5.9 // indirect github.com/google/go-cmp v0.5.9 // indirect
github.com/google/go-querystring v1.0.0 // indirect github.com/google/go-querystring v1.0.0 // indirect
github.com/google/pprof v0.0.0-20230912144702-c363fe2c2ed8 // indirect github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/josharian/native v1.0.0 // indirect github.com/josharian/native v1.0.0 // indirect
github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e // indirect github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e // indirect
@@ -62,10 +61,10 @@ require (
github.com/mdlayher/socket v0.4.0 // indirect github.com/mdlayher/socket v0.4.0 // indirect
github.com/mitchellh/mapstructure v1.4.3 // indirect github.com/mitchellh/mapstructure v1.4.3 // indirect
github.com/mozillazg/go-httpheader v0.2.1 // indirect github.com/mozillazg/go-httpheader v0.2.1 // indirect
github.com/onsi/ginkgo/v2 v2.12.1 // indirect github.com/onsi/ginkgo/v2 v2.13.0 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/quic-go/qpack v0.4.0 // indirect github.com/quic-go/qpack v0.4.0 // indirect
github.com/quic-go/qtls-go1-20 v0.3.4 // indirect github.com/quic-go/qtls-go1-20 v0.4.1 // indirect
github.com/tdewolff/minify/v2 v2.12.7 // indirect github.com/tdewolff/minify/v2 v2.12.7 // indirect
github.com/tdewolff/parse/v2 v2.6.6 // indirect github.com/tdewolff/parse/v2 v2.6.6 // indirect
github.com/tklauser/go-sysconf v0.3.9 // indirect github.com/tklauser/go-sysconf v0.3.9 // indirect
@@ -73,11 +72,11 @@ require (
github.com/yusufpapurcu/wmi v1.2.2 // indirect github.com/yusufpapurcu/wmi v1.2.2 // indirect
go.uber.org/mock v0.3.0 // indirect go.uber.org/mock v0.3.0 // indirect
golang.org/x/crypto v0.14.0 // indirect golang.org/x/crypto v0.14.0 // indirect
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
golang.org/x/mod v0.12.0 // indirect golang.org/x/mod v0.13.0 // indirect
golang.org/x/sync v0.3.0 // indirect golang.org/x/sync v0.4.0 // indirect
golang.org/x/text v0.13.0 // indirect golang.org/x/text v0.13.0 // indirect
golang.org/x/time v0.3.0 // indirect golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.13.0 // indirect golang.org/x/tools v0.14.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b // indirect
) )

74
go.sum
View File

@@ -15,8 +15,6 @@ github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chai2010/webp v1.1.1 h1:jTRmEccAJ4MGrhFOrPMpNGIJ/eybIgwKpcACsrTEapk=
github.com/chai2010/webp v1.1.1/go.mod h1:0XVwvZWdjjdxpUEIf7b9g9VkHFnInUSYujwqTLEuldU=
github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgkuj+NQRlZcDbAbM1ORAbXjXX77sX7T289U= github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgkuj+NQRlZcDbAbM1ORAbXjXX77sX7T289U=
github.com/clbanning/mxj v1.8.4 h1:HuhwZtbyvyOw+3Z1AowPkU87JkJUSv751ELWaiTpj8I= github.com/clbanning/mxj v1.8.4 h1:HuhwZtbyvyOw+3Z1AowPkU87JkJUSv751ELWaiTpj8I=
github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng= github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng=
@@ -55,10 +53,10 @@ github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASu
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
github.com/google/pprof v0.0.0-20230912144702-c363fe2c2ed8 h1:gpptm606MZYGaMHMsB4Srmb6EbW/IVHnt04rcMXnkBQ= github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b h1:RMpPgZTSApbPf7xaVel+QkoGPRLFLrwFO89uDUHEGf0=
github.com/google/pprof v0.0.0-20230912144702-c363fe2c2ed8/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
github.com/huaweicloud/huaweicloud-sdk-go-obs v3.23.4+incompatible h1:XRAk4HBDLCYEdPLWtKf5iZhOi7lfx17aY0oSO9+mcg8= github.com/huaweicloud/huaweicloud-sdk-go-obs v3.23.4+incompatible h1:XRAk4HBDLCYEdPLWtKf5iZhOi7lfx17aY0oSO9+mcg8=
github.com/huaweicloud/huaweicloud-sdk-go-obs v3.23.4+incompatible/go.mod h1:l7VUhRbTKCzdOacdT4oWCwATKyvZqUOlOqr0Ous3k4s= github.com/huaweicloud/huaweicloud-sdk-go-obs v3.23.4+incompatible/go.mod h1:l7VUhRbTKCzdOacdT4oWCwATKyvZqUOlOqr0Ous3k4s=
github.com/iwind/TeaGo v0.0.0-20230630104525-161f0b32996d h1:XnTIj781NdSipts60fVqbgZorVAKVSRaA6nqVNfBQ1g= github.com/iwind/TeaGo v0.0.0-20230630104525-161f0b32996d h1:XnTIj781NdSipts60fVqbgZorVAKVSRaA6nqVNfBQ1g=
@@ -69,8 +67,8 @@ github.com/iwind/gofcgi v0.0.0-20210528023741-a92711d45f11 h1:DaQjoWZhLNxjhIXedV
github.com/iwind/gofcgi v0.0.0-20210528023741-a92711d45f11/go.mod h1:JtbX20untAjUVjZs1ZBtq80f5rJWvwtQNRL6EnuYRnY= github.com/iwind/gofcgi v0.0.0-20210528023741-a92711d45f11/go.mod h1:JtbX20untAjUVjZs1ZBtq80f5rJWvwtQNRL6EnuYRnY=
github.com/iwind/gosock v0.0.0-20211103081026-ee4652210ca4 h1:VWGsCqTzObdlbf7UUE3oceIpcEKi4C/YBUszQXk118A= github.com/iwind/gosock v0.0.0-20211103081026-ee4652210ca4 h1:VWGsCqTzObdlbf7UUE3oceIpcEKi4C/YBUszQXk118A=
github.com/iwind/gosock v0.0.0-20211103081026-ee4652210ca4/go.mod h1:H5Q7SXwbx3a97ecJkaS2sD77gspzE7HFUafBO0peEyA= github.com/iwind/gosock v0.0.0-20211103081026-ee4652210ca4/go.mod h1:H5Q7SXwbx3a97ecJkaS2sD77gspzE7HFUafBO0peEyA=
github.com/iwind/gowebp v0.0.0-20230927084601-21954d2e229f h1:DCUsOhpZbuKiROTZGc9V9z1uEfm+EbU5nhze+Tv5xo0= github.com/iwind/gowebp v0.0.0-20231026013903-1c22b0d78cc4 h1:eyymORsZg0tZ0niyolYF4nao4sdNUI+Ll40s96tKHBY=
github.com/iwind/gowebp v0.0.0-20230927084601-21954d2e229f/go.mod h1:Re7TEhwL+ygnxFg52fC0PWy01ULAIZp2QR0q5WwEOQA= github.com/iwind/gowebp v0.0.0-20231026013903-1c22b0d78cc4/go.mod h1:AYyXDhbbD7q9N6rJff2jrE7pGupaiyvtv3YeyIAQLXk=
github.com/iwind/nftables v0.0.0-20230419014751-9f023a644ad4 h1:RPAH9Sj9l/20zH5zU5/iJGszfwPq6eLjoiC/n/asulA= github.com/iwind/nftables v0.0.0-20230419014751-9f023a644ad4 h1:RPAH9Sj9l/20zH5zU5/iJGszfwPq6eLjoiC/n/asulA=
github.com/iwind/nftables v0.0.0-20230419014751-9f023a644ad4/go.mod h1:7OLL+86wZKfBnAJxNxmdcZ0ebbgdp/A28fcagx9oJqA= github.com/iwind/nftables v0.0.0-20230419014751-9f023a644ad4/go.mod h1:7OLL+86wZKfBnAJxNxmdcZ0ebbgdp/A28fcagx9oJqA=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
@@ -81,8 +79,8 @@ github.com/josharian/native v1.0.0 h1:Ts/E8zCSEsG17dUqv7joXJFybuMLjQfWE04tsBODTx
github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e h1:LvL4XsI70QxOGHed6yhQtAU34Kx3Qq2wwBzGFKY8zKk= github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e h1:LvL4XsI70QxOGHed6yhQtAU34Kx3Qq2wwBzGFKY8zKk=
github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e/go.mod h1:kLgvv7o6UM+0QSf0QjAse3wReFDsb9qbZJdfexWlrQw= github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e/go.mod h1:kLgvv7o6UM+0QSf0QjAse3wReFDsb9qbZJdfexWlrQw=
github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI= github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4=
github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
@@ -111,8 +109,8 @@ github.com/mssola/useragent v1.0.0 h1:WRlDpXyxHDNfvZaPEut5Biveq86Ze4o4EMffyMxmH5
github.com/mssola/useragent v1.0.0/go.mod h1:hz9Cqz4RXusgg1EdI4Al0INR62kP7aPSRNHnpU+b85Y= github.com/mssola/useragent v1.0.0/go.mod h1:hz9Cqz4RXusgg1EdI4Al0INR62kP7aPSRNHnpU+b85Y=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo/v2 v2.12.1 h1:uHNEO1RP2SpuZApSkel9nEh1/Mu+hmQe7Q+Pepg5OYA= github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4=
github.com/onsi/ginkgo/v2 v2.12.1/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o=
github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
github.com/pires/go-proxyproto v0.6.1 h1:EBupykFmo22SDjv4fQVQd2J9NOoLPmyZA/15ldOGkPw= github.com/pires/go-proxyproto v0.6.1 h1:EBupykFmo22SDjv4fQVQd2J9NOoLPmyZA/15ldOGkPw=
github.com/pires/go-proxyproto v0.6.1/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY= github.com/pires/go-proxyproto v0.6.1/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY=
@@ -128,10 +126,10 @@ github.com/qiniu/go-sdk/v7 v7.16.0/go.mod h1:nqoYCNo53ZlGA521RvRethvxUDvXKt4gtYX
github.com/qiniu/x v1.10.5/go.mod h1:03Ni9tj+N2h2aKnAz+6N0Xfl8FwMEDRC2PAlxekASDs= github.com/qiniu/x v1.10.5/go.mod h1:03Ni9tj+N2h2aKnAz+6N0Xfl8FwMEDRC2PAlxekASDs=
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
github.com/quic-go/qtls-go1-20 v0.3.4 h1:MfFAPULvst4yoMgY9QmtpYmfij/em7O8UUi+bNVm7Cg= github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs=
github.com/quic-go/qtls-go1-20 v0.3.4/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
github.com/quic-go/quic-go v0.39.0 h1:AgP40iThFMY0bj8jGxROhw3S0FMGa8ryqsmi9tBH3So= github.com/quic-go/quic-go v0.39.2 h1:hmwAf8zAHlvan0Y5PXxeeBFZEW17IW99sXLry8I2kjk=
github.com/quic-go/quic-go v0.39.0/go.mod h1:T09QsDQWjLiQ74ZmacDfqZmhY/NLnw5BC40MANNNZ1Q= github.com/quic-go/quic-go v0.39.2/go.mod h1:T09QsDQWjLiQ74ZmacDfqZmhY/NLnw5BC40MANNNZ1Q=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
@@ -171,36 +169,29 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
golang.org/x/image v0.7.0 h1:gzS29xtG1J5ybQlv0PuyfE3nmc6R4qB73m6LUUmvFuw= golang.org/x/image v0.13.0 h1:3cge/F/QTkNLauhf2QoE9zp+7sr+ZcL4HnoZmdwg9sg=
golang.org/x/image v0.7.0/go.mod h1:nd/q4ef1AKKYl/4kft7g+6UyGbdiqWqTP1ZAbRoV7Rg= golang.org/x/image v0.13.0/go.mod h1:6mmbMOeV28HuMTgA6OSRkdXKYw/t5W9Uwn2Yv1r3Yxk=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY=
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -215,24 +206,18 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
@@ -241,20 +226,19 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc=
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc h1:XSJ8Vk1SWuNr8S18z1NZSziL0CPIXLCCMDOEFtHBOFc= google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b h1:ZlWIi1wSK56/8hn4QcBp/j9M7Gt3U/3hZw3mC7vDICo=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:swOH3j0KzcDDgGUWr+SNpyTen5YrXjS3eyPzFYKc6lc=
google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag= google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk=
google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=

View File

@@ -566,7 +566,7 @@ func (this *FileList) UpgradeV3(oldDir string, brokenOnError bool) error {
} }
func (this *FileList) maxExpiresAtForMemoryCache(expiresAt int64) int64 { func (this *FileList) maxExpiresAtForMemoryCache(expiresAt int64) int64 {
var maxTimestamp = fasttime.Now().Unix() + 7200 var maxTimestamp = fasttime.Now().Unix() + 3600
if expiresAt > maxTimestamp { if expiresAt > maxTimestamp {
return maxTimestamp return maxTimestamp
} }

View File

@@ -12,7 +12,6 @@ import (
"github.com/TeaOSLab/EdgeNode/internal/utils/fasttime" "github.com/TeaOSLab/EdgeNode/internal/utils/fasttime"
"github.com/iwind/TeaGo/logs" "github.com/iwind/TeaGo/logs"
"github.com/iwind/TeaGo/types" "github.com/iwind/TeaGo/types"
timeutil "github.com/iwind/TeaGo/utils/time"
"net" "net"
"net/url" "net/url"
"os" "os"
@@ -48,11 +47,10 @@ type FileListDB struct {
deleteByHashStmt *dbs.Stmt // 根据hash删除数据 deleteByHashStmt *dbs.Stmt // 根据hash删除数据
deleteByHashSQL string deleteByHashSQL string
statStmt *dbs.Stmt // 统计 statStmt *dbs.Stmt // 统计
purgeStmt *dbs.Stmt // 清理 purgeStmt *dbs.Stmt // 清理
deleteAllStmt *dbs.Stmt // 删除所有数据 deleteAllStmt *dbs.Stmt // 删除所有数据
listOlderItemsStmt *dbs.Stmt // 读取较早存储的缓存 listOlderItemsStmt *dbs.Stmt // 读取较早存储的缓存
updateAccessWeekStmt *dbs.Stmt // 修改访问日期
} }
func NewFileListDB() *FileListDB { func NewFileListDB() *FileListDB {
@@ -65,10 +63,10 @@ func (this *FileListDB) Open(dbPath string) error {
this.dbPath = dbPath this.dbPath = dbPath
// 动态调整Cache值 // 动态调整Cache值
var cacheSize = 32000 var cacheSize = 512
var memoryGB = utils.SystemMemoryGB() var memoryGB = utils.SystemMemoryGB()
if memoryGB >= 8 { if memoryGB >= 1 {
cacheSize += 32000 * memoryGB / 8 cacheSize = 256 * memoryGB
} }
// write db // write db
@@ -136,7 +134,7 @@ func (this *FileListDB) Init() error {
return err return err
} }
this.insertSQL = `INSERT INTO "` + this.itemsTableName + `" ("hash", "key", "headerSize", "bodySize", "metaSize", "expiredAt", "staleAt", "host", "serverId", "createdAt", "accessWeek") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` this.insertSQL = `INSERT INTO "` + this.itemsTableName + `" ("hash", "key", "headerSize", "bodySize", "metaSize", "expiredAt", "staleAt", "host", "serverId", "createdAt") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
this.insertStmt, err = this.writeDB.Prepare(this.insertSQL) this.insertStmt, err = this.writeDB.Prepare(this.insertSQL)
if err != nil { if err != nil {
return err return err
@@ -173,12 +171,7 @@ func (this *FileListDB) Init() error {
return err return err
} }
this.updateAccessWeekStmt, err = this.writeDB.Prepare(`UPDATE "` + this.itemsTableName + `" SET "accessWeek"=? WHERE "hash"=?`) this.listOlderItemsStmt, err = this.readDB.Prepare(`SELECT "hash" FROM "` + this.itemsTableName + `" ORDER BY "id" ASC LIMIT ?`)
if err != nil {
return err
}
this.listOlderItemsStmt, err = this.readDB.Prepare(`SELECT "hash" FROM "` + this.itemsTableName + `" ORDER BY "accessWeek" ASC, "id" ASC LIMIT ?`)
if err != nil { if err != nil {
return err return err
} }
@@ -213,7 +206,7 @@ func (this *FileListDB) AddSync(hash string, item *Item) error {
item.StaleAt = item.ExpiredAt item.StaleAt = item.ExpiredAt
} }
_, err := this.insertStmt.Exec(hash, item.Key, item.HeaderSize, item.BodySize, item.MetaSize, item.ExpiredAt, item.StaleAt, item.Host, item.ServerId, fasttime.Now().Unix(), timeutil.Format("YW")) _, err := this.insertStmt.Exec(hash, item.Key, item.HeaderSize, item.BodySize, item.MetaSize, item.ExpiredAt, item.StaleAt, item.Host, item.ServerId, fasttime.Now().Unix())
if err != nil { if err != nil {
return this.WrapError(err) return this.WrapError(err)
} }
@@ -309,8 +302,8 @@ func (this *FileListDB) ListHashes(lastId int64) (hashList []string, maxId int64
} }
func (this *FileListDB) IncreaseHitAsync(hash string) error { func (this *FileListDB) IncreaseHitAsync(hash string) error {
_, err := this.updateAccessWeekStmt.Exec(timeutil.Format("YW"), hash) // do nothing
return err return nil
} }
func (this *FileListDB) CleanPrefix(prefix string) error { func (this *FileListDB) CleanPrefix(prefix string) error {
@@ -458,9 +451,6 @@ func (this *FileListDB) Close() error {
if this.deleteAllStmt != nil { if this.deleteAllStmt != nil {
_ = this.deleteAllStmt.Close() _ = this.deleteAllStmt.Close()
} }
if this.updateAccessWeekStmt != nil {
_ = this.updateAccessWeekStmt.Close()
}
if this.listOlderItemsStmt != nil { if this.listOlderItemsStmt != nil {
_ = this.listOlderItemsStmt.Close() _ = this.listOlderItemsStmt.Close()
} }
@@ -516,8 +506,7 @@ func (this *FileListDB) initTables(times int) error {
"staleAt" integer DEFAULT 0, "staleAt" integer DEFAULT 0,
"createdAt" integer DEFAULT 0, "createdAt" integer DEFAULT 0,
"host" varchar(128), "host" varchar(128),
"serverId" integer, "serverId" integer
"accessWeek" varchar(6)
); );
DROP INDEX IF EXISTS "createdAt"; DROP INDEX IF EXISTS "createdAt";
@@ -533,8 +522,6 @@ CREATE INDEX IF NOT EXISTS "hash"
ON "` + this.itemsTableName + `" ( ON "` + this.itemsTableName + `" (
"hash" ASC "hash" ASC
); );
ALTER TABLE "cacheItems" ADD "accessWeek" varchar(6);
`) `)
if err != nil { if err != nil {

View File

@@ -4,12 +4,20 @@ package caches_test
import ( import (
"github.com/TeaOSLab/EdgeNode/internal/caches" "github.com/TeaOSLab/EdgeNode/internal/caches"
"github.com/TeaOSLab/EdgeNode/internal/utils/testutils"
"github.com/iwind/TeaGo/Tea" "github.com/iwind/TeaGo/Tea"
_ "github.com/iwind/TeaGo/bootstrap" _ "github.com/iwind/TeaGo/bootstrap"
"runtime"
"runtime/debug"
"testing" "testing"
"time"
) )
func TestFileListDB_ListLFUItems(t *testing.T) { func TestFileListDB_ListLFUItems(t *testing.T) {
if !testutils.IsSingleTesting() {
return
}
var db = caches.NewFileListDB() var db = caches.NewFileListDB()
defer func() { defer func() {
@@ -34,6 +42,10 @@ func TestFileListDB_ListLFUItems(t *testing.T) {
} }
func TestFileListDB_CleanMatchKey(t *testing.T) { func TestFileListDB_CleanMatchKey(t *testing.T) {
if !testutils.IsSingleTesting() {
return
}
var db = caches.NewFileListDB() var db = caches.NewFileListDB()
defer func() { defer func() {
@@ -62,6 +74,10 @@ func TestFileListDB_CleanMatchKey(t *testing.T) {
} }
func TestFileListDB_CleanMatchPrefix(t *testing.T) { func TestFileListDB_CleanMatchPrefix(t *testing.T) {
if !testutils.IsSingleTesting() {
return
}
var db = caches.NewFileListDB() var db = caches.NewFileListDB()
defer func() { defer func() {
@@ -88,3 +104,67 @@ func TestFileListDB_CleanMatchPrefix(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestFileListDB_Memory(t *testing.T) {
if !testutils.IsSingleTesting() {
return
}
var db = caches.NewFileListDB()
defer func() {
_ = db.Close()
}()
err := db.Open(Tea.Root + "/data/cache-index/p1/db-0.db")
if err != nil {
t.Fatal(err)
}
err = db.Init()
if err != nil {
t.Fatal(err)
}
t.Log(db.Total())
// load hashes
var maxId int64
var hashList []string
var before = time.Now()
for i := 0; i < 1_000; i++ {
hashList, maxId, err = db.ListHashes(maxId)
if err != nil {
t.Fatal(err)
}
if len(hashList) == 0 {
t.Log("hashes loaded", time.Since(before).Seconds()*1000, "ms")
break
}
if i%100 == 0 {
t.Log(i)
}
}
runtime.GC()
debug.FreeOSMemory()
//time.Sleep(600 * time.Second)
for i := 0; i < 1_000; i++ {
_, err = db.ListLFUItems(5000)
if err != nil {
t.Fatal(err)
}
if i%100 == 0 {
t.Log(i)
}
}
t.Log("loaded")
runtime.GC()
debug.FreeOSMemory()
time.Sleep(600 * time.Second)
}

View File

@@ -9,11 +9,13 @@ import (
"sync" "sync"
) )
const HashMapSharding = 11 const HashMapSharding = 31
var bigIntPool = sync.Pool{New: func() any { var bigIntPool = sync.Pool{
return big.NewInt(0) New: func() any {
}} return big.NewInt(0)
},
}
// FileListHashMap 文件Hash列表 // FileListHashMap 文件Hash列表
type FileListHashMap struct { type FileListHashMap struct {
@@ -63,7 +65,7 @@ func (this *FileListHashMap) Load(db *FileListDB) error {
this.AddHashes(hashList) this.AddHashes(hashList)
lastId = maxId lastId = maxId
maxLoops -- maxLoops--
if maxLoops <= 0 { if maxLoops <= 0 {
break break
} }

View File

@@ -6,6 +6,7 @@ import (
"github.com/TeaOSLab/EdgeNode/internal/caches" "github.com/TeaOSLab/EdgeNode/internal/caches"
"github.com/TeaOSLab/EdgeNode/internal/zero" "github.com/TeaOSLab/EdgeNode/internal/zero"
"github.com/iwind/TeaGo/Tea" "github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/assert"
"github.com/iwind/TeaGo/rands" "github.com/iwind/TeaGo/rands"
"github.com/iwind/TeaGo/types" "github.com/iwind/TeaGo/types"
stringutil "github.com/iwind/TeaGo/utils/string" stringutil "github.com/iwind/TeaGo/utils/string"
@@ -112,6 +113,18 @@ func TestFileListHashMap_Load(t *testing.T) {
} }
} }
func TestFileListHashMap_Delete(t *testing.T) {
var a = assert.NewAssertion(t)
var m = caches.NewFileListHashMap()
m.SetIsReady(true)
m.SetIsAvailable(true)
m.Add("a")
a.IsTrue(m.Len() == 1)
m.Delete("a")
a.IsTrue(m.Len() == 0)
}
func Benchmark_BigInt(b *testing.B) { func Benchmark_BigInt(b *testing.B) {
var hash = stringutil.Md5("123456") var hash = stringutil.Md5("123456")
b.ResetTimer() b.ResetTimer()

View File

@@ -7,6 +7,7 @@ import (
teaconst "github.com/TeaOSLab/EdgeNode/internal/const" teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
"github.com/TeaOSLab/EdgeNode/internal/events" "github.com/TeaOSLab/EdgeNode/internal/events"
"github.com/TeaOSLab/EdgeNode/internal/remotelogs" "github.com/TeaOSLab/EdgeNode/internal/remotelogs"
"github.com/TeaOSLab/EdgeNode/internal/utils"
"github.com/iwind/TeaGo/lists" "github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/types" "github.com/iwind/TeaGo/types"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
@@ -35,6 +36,9 @@ type Manager struct {
SubDiskDirs []*serverconfigs.CacheDir SubDiskDirs []*serverconfigs.CacheDir
MaxMemoryCapacity *shared.SizeCapacity MaxMemoryCapacity *shared.SizeCapacity
CountFileStorages int
CountMemoryStorages int
policyMap map[int64]*serverconfigs.HTTPCachePolicy // policyId => []*Policy policyMap map[int64]*serverconfigs.HTTPCachePolicy // policyId => []*Policy
storageMap map[int64]StorageInterface // policyId => *Storage storageMap map[int64]StorageInterface // policyId => *Storage
locker sync.RWMutex locker sync.RWMutex
@@ -143,6 +147,16 @@ func (this *Manager) UpdatePolicies(newPolicies []*serverconfigs.HTTPCachePolicy
} }
} }
} }
this.CountFileStorages = 0
this.CountFileStorages = 0
for _, storage := range this.storageMap {
_, isFileStorage := storage.(*FileStorage)
this.CountMemoryStorages++
if isFileStorage {
this.CountFileStorages++
}
}
} }
// FindPolicy 获取Policy信息 // FindPolicy 获取Policy信息
@@ -172,6 +186,11 @@ func (this *Manager) NewStorageWithPolicy(policy *serverconfigs.HTTPCachePolicy)
return nil return nil
} }
// StorageMap 获取已有的存储对象
func (this *Manager) StorageMap() map[int64]StorageInterface {
return this.storageMap
}
// TotalDiskSize 消耗的磁盘尺寸 // TotalDiskSize 消耗的磁盘尺寸
func (this *Manager) TotalDiskSize() int64 { func (this *Manager) TotalDiskSize() int64 {
this.locker.RLock() this.locker.RLock()
@@ -272,3 +291,17 @@ func (this *Manager) ScanGarbageCaches(callback func(path string) error) error {
} }
return nil return nil
} }
// MaxSystemMemoryBytesPerStorage 计算单个策略能使用的系统最大内存
func (this *Manager) MaxSystemMemoryBytesPerStorage() int64 {
var count = this.CountMemoryStorages
if count < 1 {
count = 1
}
var resultBytes = int64(utils.SystemMemoryBytes()) / 3 / int64(count) // 1/3 of the system memory
if resultBytes < 1<<30 {
resultBytes = 1 << 30
}
return resultBytes
}

View File

@@ -1,8 +1,9 @@
package caches package caches_test
import ( import (
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs" "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared" "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
"github.com/TeaOSLab/EdgeNode/internal/caches"
"github.com/iwind/TeaGo/Tea" "github.com/iwind/TeaGo/Tea"
"testing" "testing"
) )
@@ -10,7 +11,7 @@ import (
func TestManager_UpdatePolicies(t *testing.T) { func TestManager_UpdatePolicies(t *testing.T) {
{ {
var policies = []*serverconfigs.HTTPCachePolicy{} var policies = []*serverconfigs.HTTPCachePolicy{}
SharedManager.UpdatePolicies(policies) caches.SharedManager.UpdatePolicies(policies)
printManager(t) printManager(t)
} }
@@ -38,7 +39,7 @@ func TestManager_UpdatePolicies(t *testing.T) {
}, },
}, },
} }
SharedManager.UpdatePolicies(policies) caches.SharedManager.UpdatePolicies(policies)
printManager(t) printManager(t)
} }
@@ -66,7 +67,7 @@ func TestManager_UpdatePolicies(t *testing.T) {
}, },
}, },
} }
SharedManager.UpdatePolicies(policies) caches.SharedManager.UpdatePolicies(policies)
printManager(t) printManager(t)
} }
} }
@@ -80,8 +81,8 @@ func TestManager_ChangePolicy_Memory(t *testing.T) {
Capacity: &shared.SizeCapacity{Count: 1, Unit: shared.SizeCapacityUnitGB}, Capacity: &shared.SizeCapacity{Count: 1, Unit: shared.SizeCapacityUnitGB},
}, },
} }
SharedManager.UpdatePolicies(policies) caches.SharedManager.UpdatePolicies(policies)
SharedManager.UpdatePolicies([]*serverconfigs.HTTPCachePolicy{ caches.SharedManager.UpdatePolicies([]*serverconfigs.HTTPCachePolicy{
{ {
Id: 1, Id: 1,
Type: serverconfigs.CachePolicyStorageMemory, Type: serverconfigs.CachePolicyStorageMemory,
@@ -102,8 +103,8 @@ func TestManager_ChangePolicy_File(t *testing.T) {
Capacity: &shared.SizeCapacity{Count: 1, Unit: shared.SizeCapacityUnitGB}, Capacity: &shared.SizeCapacity{Count: 1, Unit: shared.SizeCapacityUnitGB},
}, },
} }
SharedManager.UpdatePolicies(policies) caches.SharedManager.UpdatePolicies(policies)
SharedManager.UpdatePolicies([]*serverconfigs.HTTPCachePolicy{ caches.SharedManager.UpdatePolicies([]*serverconfigs.HTTPCachePolicy{
{ {
Id: 1, Id: 1,
Type: serverconfigs.CachePolicyStorageFile, Type: serverconfigs.CachePolicyStorageFile,
@@ -115,10 +116,17 @@ func TestManager_ChangePolicy_File(t *testing.T) {
}) })
} }
func TestManager_MaxSystemMemoryBytesPerStorage(t *testing.T) {
for i := 0; i < 100; i++ {
caches.SharedManager.CountMemoryStorages = i
t.Log(i, caches.SharedManager.MaxSystemMemoryBytesPerStorage()>>30, "GB")
}
}
func printManager(t *testing.T) { func printManager(t *testing.T) {
t.Log("===manager==") t.Log("===manager==")
t.Log("storage:") t.Log("storage:")
for _, storage := range SharedManager.storageMap { for _, storage := range caches.SharedManager.StorageMap() {
t.Log(" storage:", storage.Policy().Id) t.Log(" storage:", storage.Policy().Id)
} }
t.Log("===============") t.Log("===============")

View File

@@ -93,7 +93,7 @@ func (this *OpenFileCache) Put(filename string, file *OpenFile) {
defer this.locker.Unlock() defer this.locker.Unlock()
// 如果超过当前容量,则关闭最早的 // 如果超过当前容量,则关闭最早的
if this.count >= this.maxCount || this.usedSize >= this.capacitySize { if this.count >= this.maxCount || this.usedSize+file.size >= this.capacitySize {
this.consumeHead() this.consumeHead()
return return
} }

View File

@@ -584,13 +584,18 @@ func (this *MemoryStorage) flushItem(key string) {
} }
func (this *MemoryStorage) memoryCapacityBytes() int64 { func (this *MemoryStorage) memoryCapacityBytes() int64 {
var maxSystemBytes = SharedManager.MaxSystemMemoryBytesPerStorage()
if this.policy == nil { if this.policy == nil {
return 0 return maxSystemBytes
} }
if SharedManager.MaxMemoryCapacity != nil { if SharedManager.MaxMemoryCapacity != nil {
var capacityBytes = SharedManager.MaxMemoryCapacity.Bytes() var capacityBytes = SharedManager.MaxMemoryCapacity.Bytes()
if capacityBytes > 0 { if capacityBytes > 0 {
if capacityBytes > maxSystemBytes {
return maxSystemBytes
}
return capacityBytes return capacityBytes
} }
} }
@@ -599,17 +604,14 @@ func (this *MemoryStorage) memoryCapacityBytes() int64 {
if capacity != nil { if capacity != nil {
var capacityBytes = capacity.Bytes() var capacityBytes = capacity.Bytes()
if capacityBytes > 0 { if capacityBytes > 0 {
if capacityBytes > maxSystemBytes {
return maxSystemBytes
}
return capacityBytes return capacityBytes
} }
} }
// half of the system memory return maxSystemBytes
var memoryGB = utils.SystemMemoryGB()
if memoryGB < 1 {
memoryGB = 1
}
return int64(memoryGB) << 30 / 2
} }
func (this *MemoryStorage) deleteWithoutLocker(key string) error { func (this *MemoryStorage) deleteWithoutLocker(key string) error {

View File

@@ -1,7 +1,7 @@
package teaconst package teaconst
const ( const (
Version = "1.2.10" Version = "1.3.0"
ProductName = "Edge Node" ProductName = "Edge Node"
ProcessName = "edge-node" ProcessName = "edge-node"

View File

@@ -305,7 +305,7 @@ func (this *ClientConn) increaseSYNFlood(synFloodConfig *firewallconfigs.SYNFloo
// 非TLS设置为两倍防止误封 // 非TLS设置为两倍防止误封
minAttempts = 2 * minAttempts minAttempts = 2 * minAttempts
} }
if result >= types.Uint64(minAttempts) { if result >= types.Uint32(minAttempts) {
var timeout = synFloodConfig.TimeoutSeconds var timeout = synFloodConfig.TimeoutSeconds
if timeout <= 0 { if timeout <= 0 {
timeout = 600 timeout = 600

View File

@@ -274,7 +274,13 @@ func (this *HTTPRequest) doCacheRead(useStale bool) (shouldStop bool) {
} }
if err != nil { if err != nil {
if err == caches.ErrNotFound { if errors.Is(err, caches.ErrNotFound) {
// 移除请求中的 If-None-Match 和 If-Modified-Since防止源站返回304而无法缓存
if this.reverseProxy != nil {
this.RawReq.Header.Del("If-None-Match")
this.RawReq.Header.Del("If-Modified-Since")
}
// cache相关变量 // cache相关变量
this.varMapping["cache.status"] = "MISS" this.varMapping["cache.status"] = "MISS"
@@ -365,24 +371,24 @@ func (this *HTTPRequest) doCacheRead(useStale bool) (shouldStop bool) {
} }
// ETag // ETag
// 这里强制设置ETag如果先前源站设置了ETag将会被覆盖避免因为源站的ETag导致源站返回304 Not Modified
var respHeader = this.writer.Header() var respHeader = this.writer.Header()
var eTag = "" var eTag = respHeader.Get("ETag")
var lastModifiedAt = reader.LastModified() var lastModifiedAt = reader.LastModified()
if lastModifiedAt > 0 { if len(eTag) == 0 {
if len(tags) > 0 { if lastModifiedAt > 0 {
eTag = "\"" + strconv.FormatInt(lastModifiedAt, 10) + "_" + strings.Join(tags, "_") + "\"" if len(tags) > 0 {
} else { eTag = "\"" + strconv.FormatInt(lastModifiedAt, 10) + "_" + strings.Join(tags, "_") + "\""
eTag = "\"" + strconv.FormatInt(lastModifiedAt, 10) + "\"" } else {
} eTag = "\"" + strconv.FormatInt(lastModifiedAt, 10) + "\""
respHeader.Del("Etag") }
if !isPartialCache { respHeader.Del("Etag")
respHeader["ETag"] = []string{eTag} if !isPartialCache {
respHeader["ETag"] = []string{eTag}
}
} }
} }
// 支持 Last-Modified // 支持 Last-Modified
// 这里强制设置Last-Modified如果先前源站设置了Last-Modified将会被覆盖避免因为源站的Last-Modified导致源站返回304 Not Modified
var modifiedTime = "" var modifiedTime = ""
if lastModifiedAt > 0 { if lastModifiedAt > 0 {
modifiedTime = time.Unix(utils.GMTUnixTime(lastModifiedAt), 0).Format("Mon, 02 Jan 2006 15:04:05") + " GMT" modifiedTime = time.Unix(utils.GMTUnixTime(lastModifiedAt), 0).Format("Mon, 02 Jan 2006 15:04:05") + " GMT"
@@ -490,7 +496,7 @@ func (this *HTTPRequest) doCacheRead(useStale bool) (shouldStop bool) {
if err != nil { if err != nil {
this.varMapping["cache.status"] = "MISS" this.varMapping["cache.status"] = "MISS"
if err == caches.ErrInvalidRange { if errors.Is(err, caches.ErrInvalidRange) {
this.ProcessResponseHeaders(this.writer.Header(), http.StatusRequestedRangeNotSatisfiable) this.ProcessResponseHeaders(this.writer.Header(), http.StatusRequestedRangeNotSatisfiable)
this.writer.WriteHeader(http.StatusRequestedRangeNotSatisfiable) this.writer.WriteHeader(http.StatusRequestedRangeNotSatisfiable)
return true return true

View File

@@ -25,6 +25,13 @@ func (this *HTTPRequest) doHostRedirect() (blocked bool) {
continue continue
} }
if len(u.ExceptDomains) > 0 && configutils.MatchDomains(u.ExceptDomains, this.ReqHost) {
continue
}
if len(u.OnlyDomains) > 0 && !configutils.MatchDomains(u.OnlyDomains, this.ReqHost) {
continue
}
var status = u.Status var status = u.Status
if status <= 0 { if status <= 0 {
if searchEngineRegex.MatchString(this.RawReq.UserAgent()) { if searchEngineRegex.MatchString(this.RawReq.UserAgent()) {

View File

@@ -2,7 +2,6 @@ package nodes
import ( import (
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs" "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
"github.com/TeaOSLab/EdgeNode/internal/remotelogs" "github.com/TeaOSLab/EdgeNode/internal/remotelogs"
"github.com/TeaOSLab/EdgeNode/internal/utils" "github.com/TeaOSLab/EdgeNode/internal/utils"
"github.com/iwind/TeaGo/Tea" "github.com/iwind/TeaGo/Tea"
@@ -46,9 +45,13 @@ func (this *HTTPRequest) doPage(status int) (shouldStop bool) {
} }
func (this *HTTPRequest) doPageLookup(pages []*serverconfigs.HTTPPageConfig, status int) (shouldStop bool) { func (this *HTTPRequest) doPageLookup(pages []*serverconfigs.HTTPPageConfig, status int) (shouldStop bool) {
var url = this.URL()
for _, page := range pages { for _, page := range pages {
if !page.MatchURL(url) {
continue
}
if page.Match(status) { if page.Match(status) {
if len(page.BodyType) == 0 || page.BodyType == shared.BodyTypeURL { if len(page.BodyType) == 0 || page.BodyType == serverconfigs.HTTPPageBodyTypeURL {
if urlSchemeRegexp.MatchString(page.URL) { if urlSchemeRegexp.MatchString(page.URL) {
var newStatus = page.NewStatus var newStatus = page.NewStatus
if newStatus <= 0 { if newStatus <= 0 {
@@ -115,7 +118,7 @@ func (this *HTTPRequest) doPageLookup(pages []*serverconfigs.HTTPPageConfig, sta
} }
return true return true
} else if page.BodyType == shared.BodyTypeHTML { } else if page.BodyType == serverconfigs.HTTPPageBodyTypeHTML {
// 这里需要实现设置Status因为在Format()中可以获取${status}等变量 // 这里需要实现设置Status因为在Format()中可以获取${status}等变量
if page.NewStatus > 0 { if page.NewStatus > 0 {
this.writer.statusCode = page.NewStatus this.writer.statusCode = page.NewStatus
@@ -147,6 +150,18 @@ func (this *HTTPRequest) doPageLookup(pages []*serverconfigs.HTTPPageConfig, sta
this.writer.SetOk() this.writer.SetOk()
} }
return true return true
} else if page.BodyType == serverconfigs.HTTPPageBodyTypeRedirectURL {
var newURL = page.URL
if len(newURL) == 0 {
newURL = "/"
}
if page.NewStatus > 0 && httpStatusIsRedirect(page.NewStatus) {
httpRedirect(this.writer, this.RawReq, newURL, page.NewStatus)
} else {
httpRedirect(this.writer, this.RawReq, newURL, http.StatusTemporaryRedirect)
}
this.writer.SetOk()
return true
} }
} }
} }

View File

@@ -27,9 +27,10 @@ func (this *HTTPRequest) doReverseProxy() {
var failedOriginIds []int64 var failedOriginIds []int64
var failedLnNodeIds []int64 var failedLnNodeIds []int64
var failStatusCode int
for i := 0; i < retries; i++ { for i := 0; i < retries; i++ {
originId, lnNodeId, shouldRetry := this.doOriginRequest(failedOriginIds, failedLnNodeIds, i == 0, i == retries-1) originId, lnNodeId, shouldRetry := this.doOriginRequest(failedOriginIds, failedLnNodeIds, i == 0, i == retries-1, &failStatusCode)
if !shouldRetry { if !shouldRetry {
break break
} }
@@ -43,7 +44,7 @@ func (this *HTTPRequest) doReverseProxy() {
} }
// 请求源站 // 请求源站
func (this *HTTPRequest) doOriginRequest(failedOriginIds []int64, failedLnNodeIds []int64, isFirstTry bool, isLastRetry bool) (originId int64, lnNodeId int64, shouldRetry bool) { func (this *HTTPRequest) doOriginRequest(failedOriginIds []int64, failedLnNodeIds []int64, isFirstTry bool, isLastRetry bool, failStatusCode *int) (originId int64, lnNodeId int64, shouldRetry bool) {
// 对URL的处理 // 对URL的处理
var stripPrefix = this.reverseProxy.StripPrefix var stripPrefix = this.reverseProxy.StripPrefix
var requestURI = this.reverseProxy.RequestURI var requestURI = this.reverseProxy.RequestURI
@@ -91,6 +92,10 @@ func (this *HTTPRequest) doOriginRequest(failedOriginIds []int64, failedLnNodeId
} }
if origin == nil { if origin == nil {
origin = this.reverseProxy.NextOrigin(requestCall) origin = this.reverseProxy.NextOrigin(requestCall)
if origin != nil && origin.Id > 0 && (*failStatusCode >= 403 && *failStatusCode <= 404) && lists.ContainsInt64(failedOriginIds, origin.Id) {
this.writeCode(*failStatusCode, "", "")
return
}
} }
requestCall.CallResponseCallbacks(this.writer) requestCall.CallResponseCallbacks(this.writer)
if origin == nil { if origin == nil {
@@ -376,11 +381,11 @@ func (this *HTTPRequest) doOriginRequest(failedOriginIds []int64, failedLnNodeId
return return
} }
// 50x // 40x && 50x
*failStatusCode = resp.StatusCode
if resp != nil && if resp != nil &&
resp.StatusCode >= 500 && ((resp.StatusCode >= 500 && resp.StatusCode < 510 && this.reverseProxy.Retry50X) ||
resp.StatusCode < 510 && (resp.StatusCode >= 403 && resp.StatusCode <= 404 && this.reverseProxy.Retry40X)) &&
this.reverseProxy.Retry50X &&
(originId > 0 || (lnNodeId > 0 && hasMultipleLnNodes)) && (originId > 0 || (lnNodeId > 0 && hasMultipleLnNodes)) &&
!isLastRetry { !isLastRetry {
if resp.Body != nil { if resp.Body != nil {

View File

@@ -1,7 +1,7 @@
package nodes package nodes
import ( import (
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared" "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/TeaOSLab/EdgeNode/internal/remotelogs" "github.com/TeaOSLab/EdgeNode/internal/remotelogs"
"github.com/TeaOSLab/EdgeNode/internal/utils" "github.com/TeaOSLab/EdgeNode/internal/utils"
"github.com/iwind/TeaGo/Tea" "github.com/iwind/TeaGo/Tea"
@@ -18,7 +18,7 @@ func (this *HTTPRequest) doShutdown() {
return return
} }
if len(shutdown.BodyType) == 0 || shutdown.BodyType == shared.BodyTypeURL { if len(shutdown.BodyType) == 0 || shutdown.BodyType == serverconfigs.HTTPPageBodyTypeURL {
// URL // URL
if urlSchemeRegexp.MatchString(shutdown.URL) { if urlSchemeRegexp.MatchString(shutdown.URL) {
this.doURL(http.MethodGet, shutdown.URL, "", shutdown.Status, true) this.doURL(http.MethodGet, shutdown.URL, "", shutdown.Status, true)
@@ -80,7 +80,7 @@ func (this *HTTPRequest) doShutdown() {
} else { } else {
this.writer.SetOk() this.writer.SetOk()
} }
} else if shutdown.BodyType == shared.BodyTypeHTML { } else if shutdown.BodyType == serverconfigs.HTTPPageBodyTypeHTML {
// 自定义响应Headers // 自定义响应Headers
if shutdown.Status > 0 { if shutdown.Status > 0 {
this.ProcessResponseHeaders(this.writer.Header(), shutdown.Status) this.ProcessResponseHeaders(this.writer.Header(), shutdown.Status)
@@ -98,5 +98,17 @@ func (this *HTTPRequest) doShutdown() {
} else { } else {
this.writer.SetOk() this.writer.SetOk()
} }
} else if shutdown.BodyType == serverconfigs.HTTPPageBodyTypeRedirectURL {
var newURL = shutdown.URL
if len(newURL) == 0 {
newURL = "/"
}
if shutdown.Status > 0 && httpStatusIsRedirect(shutdown.Status) {
httpRedirect(this.writer, this.RawReq, newURL, shutdown.Status)
} else {
httpRedirect(this.writer, this.RawReq, newURL, http.StatusTemporaryRedirect)
}
this.writer.SetOk()
} }
} }

View File

@@ -96,6 +96,8 @@ func (this *HTTPRequest) checkWAFRequest(firewallPolicy *firewallconfigs.HTTPFir
return return
} }
var isDefendMode = firewallPolicy.Mode == firewallconfigs.FirewallModeDefend
// 检查IP白名单 // 检查IP白名单
var remoteAddrs []string var remoteAddrs []string
if len(this.remoteAddr) > 0 { if len(this.remoteAddr) > 0 {
@@ -122,7 +124,7 @@ func (this *HTTPRequest) checkWAFRequest(firewallPolicy *firewallconfigs.HTTPFir
} }
// 检查IP黑名单 // 检查IP黑名单
if firewallPolicy.Mode == firewallconfigs.FirewallModeDefend { if isDefendMode {
for _, ref := range inbound.AllDenyListRefs() { for _, ref := range inbound.AllDenyListRefs() {
if ref.IsOn && ref.ListId > 0 { if ref.IsOn && ref.ListId > 0 {
list := iplibrary.SharedIPListManager.FindList(ref.ListId) list := iplibrary.SharedIPListManager.FindList(ref.ListId)
@@ -161,19 +163,20 @@ func (this *HTTPRequest) checkWAFRequest(firewallPolicy *firewallconfigs.HTTPFir
} }
// 检查地区封禁 // 检查地区封禁
if firewallPolicy.Mode == firewallconfigs.FirewallModeDefend {
if firewallPolicy.Inbound.Region != nil && firewallPolicy.Inbound.Region.IsOn {
var regionConfig = firewallPolicy.Inbound.Region
if regionConfig.IsNotEmpty() {
for _, remoteAddr := range remoteAddrs {
var result = iplib.LookupIP(remoteAddr)
if result != nil && result.IsOk() {
var currentURL = this.URL()
if regionConfig.MatchCountryURL(currentURL) {
// 检查国家/地区级别封禁
if !regionConfig.IsAllowedCountry(result.CountryId(), result.ProvinceId()) {
this.firewallPolicyId = firewallPolicy.Id
if firewallPolicy.Inbound.Region != nil && firewallPolicy.Inbound.Region.IsOn {
var regionConfig = firewallPolicy.Inbound.Region
if regionConfig.IsNotEmpty() {
for _, remoteAddr := range remoteAddrs {
var result = iplib.LookupIP(remoteAddr)
if result != nil && result.IsOk() {
var currentURL = this.URL()
if regionConfig.MatchCountryURL(currentURL) {
// 检查国家/地区级别封禁
if !regionConfig.IsAllowedCountry(result.CountryId(), result.ProvinceId()) {
this.firewallPolicyId = firewallPolicy.Id
if isDefendMode {
var promptHTML string var promptHTML string
if len(regionConfig.CountryHTML) > 0 { if len(regionConfig.CountryHTML) > 0 {
promptHTML = regionConfig.CountryHTML promptHTML = regionConfig.CountryHTML
@@ -193,23 +196,27 @@ func (this *HTTPRequest) checkWAFRequest(firewallPolicy *firewallconfigs.HTTPFir
// 延时返回,避免攻击 // 延时返回,避免攻击
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)
}
// 停止日志 // 停止日志
if !logDenying { if !logDenying {
this.disableLog = true this.disableLog = true
} else { } else {
this.tags = append(this.tags, "denyCountry") this.tags = append(this.tags, "denyCountry")
} }
if isDefendMode {
return true, false return true, false
} }
} }
}
if regionConfig.MatchProvinceURL(currentURL) { if regionConfig.MatchProvinceURL(currentURL) {
// 检查省份封禁 // 检查省份封禁
if !regionConfig.IsAllowedProvince(result.CountryId(), result.ProvinceId()) { if !regionConfig.IsAllowedProvince(result.CountryId(), result.ProvinceId()) {
this.firewallPolicyId = firewallPolicy.Id this.firewallPolicyId = firewallPolicy.Id
if isDefendMode {
var promptHTML string var promptHTML string
if len(regionConfig.ProvinceHTML) > 0 { if len(regionConfig.ProvinceHTML) > 0 {
promptHTML = regionConfig.ProvinceHTML promptHTML = regionConfig.ProvinceHTML
@@ -229,14 +236,16 @@ func (this *HTTPRequest) checkWAFRequest(firewallPolicy *firewallconfigs.HTTPFir
// 延时返回,避免攻击 // 延时返回,避免攻击
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)
}
// 停止日志 // 停止日志
if !logDenying { if !logDenying {
this.disableLog = true this.disableLog = true
} else { } else {
this.tags = append(this.tags, "denyProvince") this.tags = append(this.tags, "denyProvince")
} }
if isDefendMode {
return true, false return true, false
} }
} }
@@ -257,7 +266,7 @@ func (this *HTTPRequest) checkWAFRequest(firewallPolicy *firewallconfigs.HTTPFir
return return
} }
goNext, hasRequestBody, ruleGroup, ruleSet, err := w.MatchRequest(this, this.writer) goNext, hasRequestBody, ruleGroup, ruleSet, err := w.MatchRequest(this, this.writer, this.web.FirewallRef.DefaultCaptchaType)
if forceLog && logRequestBody && hasRequestBody && ruleSet != nil && ruleSet.HasAttackActions() { if forceLog && logRequestBody && hasRequestBody && ruleSet != nil && ruleSet.HasAttackActions() {
this.wafHasRequestBody = true this.wafHasRequestBody = true
} }
@@ -307,7 +316,7 @@ func (this *HTTPRequest) doWAFResponse(resp *http.Response) (blocked bool) {
} }
if this.web.FirewallPolicy != nil && this.web.FirewallPolicy.IsOn { if this.web.FirewallPolicy != nil && this.web.FirewallPolicy.IsOn {
blocked := this.checkWAFResponse(this.web.FirewallPolicy, resp, forceLog, forceLogRequestBody, false) blocked = this.checkWAFResponse(this.web.FirewallPolicy, resp, forceLog, forceLogRequestBody, false)
if blocked { if blocked {
return true return true
} }
@@ -315,7 +324,7 @@ func (this *HTTPRequest) doWAFResponse(resp *http.Response) (blocked bool) {
// 公用的防火墙设置 // 公用的防火墙设置
if this.ReqServer.HTTPFirewallPolicy != nil && this.ReqServer.HTTPFirewallPolicy.IsOn { if this.ReqServer.HTTPFirewallPolicy != nil && this.ReqServer.HTTPFirewallPolicy.IsOn {
blocked := this.checkWAFResponse(this.ReqServer.HTTPFirewallPolicy, resp, forceLog, forceLogRequestBody, this.web.FirewallRef.IgnoreGlobalRules) blocked = this.checkWAFResponse(this.ReqServer.HTTPFirewallPolicy, resp, forceLog, forceLogRequestBody, this.web.FirewallRef.IgnoreGlobalRules)
if blocked { if blocked {
return true return true
} }
@@ -469,3 +478,10 @@ func (this *HTTPRequest) WAFMaxRequestSize() int64 {
func (this *HTTPRequest) DisableAccessLog() { func (this *HTTPRequest) DisableAccessLog() {
this.disableLog = true this.disableLog = true
} }
// DisableStat 停用统计
func (this *HTTPRequest) DisableStat() {
if this.web != nil {
this.web.StatRef = nil
}
}

View File

@@ -46,7 +46,7 @@ func (this *BaseListener) buildTLSConfig() *tls.Config {
} }
} }
tlsPolicy, _, err := this.matchSSL(this.helloServerName(clientInfo)) tlsPolicy, _, err := this.matchSSL(this.helloServerNames(clientInfo))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -69,7 +69,7 @@ func (this *BaseListener) buildTLSConfig() *tls.Config {
} }
} }
tlsPolicy, cert, err := this.matchSSL(this.helloServerName(clientInfo)) tlsPolicy, cert, err := this.matchSSL(this.helloServerNames(clientInfo))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -85,7 +85,7 @@ func (this *BaseListener) buildTLSConfig() *tls.Config {
} }
// 根据域名匹配证书 // 根据域名匹配证书
func (this *BaseListener) matchSSL(domain string) (*sslconfigs.SSLPolicy, *tls.Certificate, error) { func (this *BaseListener) matchSSL(domains []string) (*sslconfigs.SSLPolicy, *tls.Certificate, error) {
var group = this.Group var group = this.Group
if group == nil { if group == nil {
@@ -99,7 +99,7 @@ func (this *BaseListener) matchSSL(domain string) (*sslconfigs.SSLPolicy, *tls.C
// 如果域名为空,则取第一个 // 如果域名为空,则取第一个
// 通常域名为空是因为是直接通过IP访问的 // 通常域名为空是因为是直接通过IP访问的
if len(domain) == 0 { if len(domains) == 0 {
if group.IsHTTPS() && globalServerConfig != nil && globalServerConfig.HTTPAll.MatchDomainStrictly { if group.IsHTTPS() && globalServerConfig != nil && globalServerConfig.HTTPAll.MatchDomainStrictly {
return nil, nil, errors.New("no tls server name matched") return nil, nil, errors.New("no tls server name matched")
} }
@@ -116,9 +116,25 @@ func (this *BaseListener) matchSSL(domain string) (*sslconfigs.SSLPolicy, *tls.C
} }
return nil, nil, errors.New("no tls server name found") return nil, nil, errors.New("no tls server name found")
} }
var firstDomain = domains[0]
// 通过网站域名配置匹配 // 通过网站域名配置匹配
server, _ := this.findNamedServer(domain) var server *serverconfigs.ServerConfig
var matchedDomain string
for _, domain := range domains {
server, _ = this.findNamedServer(domain, true)
if server != nil {
matchedDomain = domain
break
}
}
if server == nil {
server, _ = this.findNamedServer(firstDomain, false)
if server != nil {
matchedDomain = firstDomain
}
}
if server == nil { if server == nil {
// 找不到或者此时的服务没有配置证书需要搜索所有的Server通过SSL证书内容中的DNSName匹配 // 找不到或者此时的服务没有配置证书需要搜索所有的Server通过SSL证书内容中的DNSName匹配
// 此功能仅为了兼容以往版本v1.0.4),不应该作为常态启用 // 此功能仅为了兼容以往版本v1.0.4),不应该作为常态启用
@@ -127,14 +143,14 @@ func (this *BaseListener) matchSSL(domain string) (*sslconfigs.SSLPolicy, *tls.C
if searchingServer.SSLPolicy() == nil || !searchingServer.SSLPolicy().IsOn { if searchingServer.SSLPolicy() == nil || !searchingServer.SSLPolicy().IsOn {
continue continue
} }
cert, ok := searchingServer.SSLPolicy().MatchDomain(domain) cert, ok := searchingServer.SSLPolicy().MatchDomain(firstDomain)
if ok { if ok {
return searchingServer.SSLPolicy(), cert, nil return searchingServer.SSLPolicy(), cert, nil
} }
} }
} }
return nil, nil, errors.New("no server found for '" + domain + "'") return nil, nil, errors.New("no server found for '" + firstDomain + "'")
} }
if server.SSLPolicy() == nil || !server.SSLPolicy().IsOn { if server.SSLPolicy() == nil || !server.SSLPolicy().IsOn {
// 找不到或者此时的服务没有配置证书需要搜索所有的Server通过SSL证书内容中的DNSName匹配 // 找不到或者此时的服务没有配置证书需要搜索所有的Server通过SSL证书内容中的DNSName匹配
@@ -144,32 +160,32 @@ func (this *BaseListener) matchSSL(domain string) (*sslconfigs.SSLPolicy, *tls.C
if searchingServer.SSLPolicy() == nil || !searchingServer.SSLPolicy().IsOn { if searchingServer.SSLPolicy() == nil || !searchingServer.SSLPolicy().IsOn {
continue continue
} }
cert, ok := searchingServer.SSLPolicy().MatchDomain(domain) cert, ok := searchingServer.SSLPolicy().MatchDomain(matchedDomain)
if ok { if ok {
return searchingServer.SSLPolicy(), cert, nil return searchingServer.SSLPolicy(), cert, nil
} }
} }
} }
return nil, nil, errors.New("no cert found for '" + domain + "'") return nil, nil, errors.New("no cert found for '" + matchedDomain + "'")
} }
// 证书是否匹配 // 证书是否匹配
var sslConfig = server.SSLPolicy() var sslConfig = server.SSLPolicy()
cert, ok := sslConfig.MatchDomain(domain) cert, ok := sslConfig.MatchDomain(matchedDomain)
if ok { if ok {
return sslConfig, cert, nil return sslConfig, cert, nil
} }
if len(sslConfig.Certs) == 0 { if len(sslConfig.Certs) == 0 {
remotelogs.ServerError(server.Id, "BASE_LISTENER", "no ssl certs found for '"+domain+"', server id: "+types.String(server.Id), "", nil) remotelogs.ServerError(server.Id, "BASE_LISTENER", "no ssl certs found for '"+matchedDomain+"', server id: "+types.String(server.Id), "", nil)
} }
return sslConfig, sslConfig.FirstCert(), nil return sslConfig, sslConfig.FirstCert(), nil
} }
// 根据域名来查找匹配的域名 // 根据域名来查找匹配的域名
func (this *BaseListener) findNamedServer(name string) (serverConfig *serverconfigs.ServerConfig, serverName string) { func (this *BaseListener) findNamedServer(name string, exactly bool) (serverConfig *serverconfigs.ServerConfig, serverName string) {
serverConfig, serverName = this.findNamedServerMatched(name) serverConfig, serverName = this.findNamedServerMatched(name)
if serverConfig != nil { if serverConfig != nil {
return return
@@ -194,18 +210,22 @@ func (this *BaseListener) findNamedServer(name string) (serverConfig *serverconf
} }
} }
if matchDomainStrictly && !configutils.MatchDomains(globalServerConfig.HTTPAll.AllowMismatchDomains, name) && (!globalServerConfig.HTTPAll.AllowNodeIP || !utils.IsWildIP(name)) { if matchDomainStrictly && !configutils.MatchDomains(globalServerConfig.HTTPAll.AllowMismatchDomains, name) && (!globalServerConfig.HTTPAll.AllowNodeIP || (!utils.IsWildIP(name) || globalServerConfig.HTTPAll.NodeIPShowPage)) {
return return
} }
// 如果没有找到,则匹配到第一个 if !exactly {
var group = this.Group // 如果没有找到,则匹配到第一个
var currentServers = group.Servers() var group = this.Group
var countServers = len(currentServers) var currentServers = group.Servers()
if countServers == 0 { var countServers = len(currentServers)
return nil, "" if countServers == 0 {
return nil, ""
}
return currentServers[0], name
} }
return currentServers[0], name
return
} }
// 严格查找域名 // 严格查找域名
@@ -234,16 +254,23 @@ func (this *BaseListener) findNamedServerMatched(name string) (serverConfig *ser
} }
// 从Hello信息中获取服务名称 // 从Hello信息中获取服务名称
func (this *BaseListener) helloServerName(clientInfo *tls.ClientHelloInfo) string { func (this *BaseListener) helloServerNames(clientInfo *tls.ClientHelloInfo) (serverNames []string) {
var serverName = clientInfo.ServerName if len(clientInfo.ServerName) != 0 {
if len(serverName) == 0 && clientInfo.Conn != nil { serverNames = append(serverNames, clientInfo.ServerName)
return
}
if clientInfo.Conn != nil {
var localAddr = clientInfo.Conn.LocalAddr() var localAddr = clientInfo.Conn.LocalAddr()
if localAddr != nil { if localAddr != nil {
tcpAddr, ok := localAddr.(*net.TCPAddr) tcpAddr, ok := localAddr.(*net.TCPAddr)
if ok { if ok {
serverName = tcpAddr.IP.String() serverNames = append(serverNames, tcpAddr.IP.String())
} }
} }
} }
return serverName
serverNames = append(serverNames, sharedNodeConfig.IPAddresses...)
return
} }

View File

@@ -107,15 +107,23 @@ func (this *HTTPListener) Reload(group *serverconfigs.ServerAddressGroup) {
// ServerHTTP 处理HTTP请求 // ServerHTTP 处理HTTP请求
func (this *HTTPListener) ServeHTTP(rawWriter http.ResponseWriter, rawReq *http.Request) { func (this *HTTPListener) ServeHTTP(rawWriter http.ResponseWriter, rawReq *http.Request) {
if len(rawReq.Host) > 253 {
http.Error(rawWriter, "Host too long.", http.StatusBadRequest)
time.Sleep(1 * time.Second) // make connection slow down
return
}
var globalServerConfig = sharedNodeConfig.GlobalServerConfig var globalServerConfig = sharedNodeConfig.GlobalServerConfig
if globalServerConfig != nil && !globalServerConfig.HTTPAll.SupportsLowVersionHTTP && (rawReq.ProtoMajor < 1 /** 0.x **/ || (rawReq.ProtoMajor == 1 && rawReq.ProtoMinor == 0 /** 1.0 **/)) { if globalServerConfig != nil && !globalServerConfig.HTTPAll.SupportsLowVersionHTTP && (rawReq.ProtoMajor < 1 /** 0.x **/ || (rawReq.ProtoMajor == 1 && rawReq.ProtoMinor == 0 /** 1.0 **/)) {
http.Error(rawWriter, rawReq.Proto+" request is not supported.", http.StatusBadRequest) http.Error(rawWriter, rawReq.Proto+" request is not supported.", http.StatusBadRequest)
time.Sleep(1 * time.Second) // make connection slow down
return return
} }
// 不支持Connect // 不支持Connect
if rawReq.Method == http.MethodConnect { if rawReq.Method == http.MethodConnect {
http.Error(rawWriter, "Method Not Allowed", http.StatusMethodNotAllowed) http.Error(rawWriter, "Method Not Allowed", http.StatusMethodNotAllowed)
time.Sleep(1 * time.Second) // make connection slow down
return return
} }
@@ -154,7 +162,7 @@ func (this *HTTPListener) ServeHTTP(rawWriter http.ResponseWriter, rawReq *http.
domain = reqHost domain = reqHost
} }
server, serverName := this.findNamedServer(domain) server, serverName := this.findNamedServer(domain, false)
if server == nil { if server == nil {
if server == nil { if server == nil {
// 增加默认的一个服务 // 增加默认的一个服务

View File

@@ -146,12 +146,12 @@ func (this *Node) Start() {
remotelogs.Println("NODE", "init config ...") remotelogs.Println("NODE", "init config ...")
err = this.syncConfig(0) err = this.syncConfig(0)
if err != nil { if err != nil {
_, err := nodeconfigs.SharedNodeConfig() _, err = nodeconfigs.SharedNodeConfig()
if err != nil { if err != nil {
// 无本地数据时,会尝试多次读取 // 无本地数据时,会尝试多次读取
tryTimes := 0 tryTimes := 0
for { for {
err := this.syncConfig(0) err = this.syncConfig(0)
if err != nil { if err != nil {
tryTimes++ tryTimes++

View File

@@ -223,6 +223,7 @@ func (this *NodeStatusExecutor) updateDisk(status *nodeconfigs.NodeStatus) {
// 当前TeaWeb所在的fs // 当前TeaWeb所在的fs
var rootFS = "" var rootFS = ""
var rootTotal = uint64(0) var rootTotal = uint64(0)
var totalUsed = uint64(0)
if lists.ContainsString([]string{"darwin", "linux", "freebsd"}, runtime.GOOS) { if lists.ContainsString([]string{"darwin", "linux", "freebsd"}, runtime.GOOS) {
for _, p := range partitions { for _, p := range partitions {
if p.Mountpoint == "/" { if p.Mountpoint == "/" {
@@ -230,6 +231,7 @@ func (this *NodeStatusExecutor) updateDisk(status *nodeconfigs.NodeStatus) {
usage, _ := disk.Usage(p.Mountpoint) usage, _ := disk.Usage(p.Mountpoint)
if usage != nil { if usage != nil {
rootTotal = usage.Total rootTotal = usage.Total
totalUsed = usage.Used
} }
break break
} }
@@ -237,7 +239,6 @@ func (this *NodeStatusExecutor) updateDisk(status *nodeconfigs.NodeStatus) {
} }
var total = rootTotal var total = rootTotal
var totalUsage = uint64(0)
var maxUsage = float64(0) var maxUsage = float64(0)
for _, partition := range partitions { for _, partition := range partitions {
if runtime.GOOS != "windows" && !strings.Contains(partition.Device, "/") && !strings.Contains(partition.Device, "\\") { if runtime.GOOS != "windows" && !strings.Contains(partition.Device, "/") && !strings.Contains(partition.Device, "\\") {
@@ -256,16 +257,16 @@ func (this *NodeStatusExecutor) updateDisk(status *nodeconfigs.NodeStatus) {
if partition.Mountpoint != "/" && (usage.Total != rootTotal || total == 0) { if partition.Mountpoint != "/" && (usage.Total != rootTotal || total == 0) {
total += usage.Total total += usage.Total
} totalUsed += usage.Used
totalUsage += usage.Used if usage.UsedPercent >= maxUsage {
if usage.UsedPercent >= maxUsage { maxUsage = usage.UsedPercent
maxUsage = usage.UsedPercent status.DiskMaxUsagePartition = partition.Mountpoint
status.DiskMaxUsagePartition = partition.Mountpoint }
} }
} }
status.DiskTotal = total status.DiskTotal = total
if total > 0 { if total > 0 {
status.DiskUsage = float64(totalUsage) / float64(total) status.DiskUsage = float64(totalUsed) / float64(total)
} }
status.DiskMaxUsage = maxUsage / 100 status.DiskMaxUsage = maxUsage / 100

View File

@@ -17,6 +17,7 @@ import (
"github.com/TeaOSLab/EdgeNode/internal/remotelogs" "github.com/TeaOSLab/EdgeNode/internal/remotelogs"
"github.com/TeaOSLab/EdgeNode/internal/rpc" "github.com/TeaOSLab/EdgeNode/internal/rpc"
"github.com/TeaOSLab/EdgeNode/internal/trackers" "github.com/TeaOSLab/EdgeNode/internal/trackers"
"github.com/TeaOSLab/EdgeNode/internal/utils"
"github.com/TeaOSLab/EdgeNode/internal/waf" "github.com/TeaOSLab/EdgeNode/internal/waf"
"github.com/iwind/TeaGo/Tea" "github.com/iwind/TeaGo/Tea"
"github.com/iwind/TeaGo/maps" "github.com/iwind/TeaGo/maps"
@@ -97,6 +98,8 @@ func (this *Node) execTask(rpcClient *rpc.RPCClient, task *pb.NodeTask) error {
err = this.notifyPlusChange() err = this.notifyPlusChange()
case "toaChanged": case "toaChanged":
err = this.execTOAChangedTask() err = this.execTOAChangedTask()
case "networkSecurityPolicyChanged":
err = this.execNetworkSecurityPolicyChangedTask(rpcClient)
default: default:
// 特殊任务 // 特殊任务
if strings.HasPrefix(task.Type, "ipListDeleted") { // 删除IP名单 if strings.HasPrefix(task.Type, "ipListDeleted") { // 删除IP名单
@@ -296,7 +299,7 @@ func (this *Node) execUpdatingServersTask(rpcClient *rpc.RPCClient) error {
// 删除IP名单 // 删除IP名单
func (this *Node) execDeleteIPList(taskType string) error { func (this *Node) execDeleteIPList(taskType string) error {
optionsString, ok := strings.CutPrefix(taskType, "ipListDeleted@") optionsString, ok := utils.CutPrefix(taskType, "ipListDeleted@")
if !ok { if !ok {
return errors.New("invalid task type '" + taskType + "'") return errors.New("invalid task type '" + taskType + "'")
} }

View File

@@ -29,3 +29,8 @@ func (this *Node) execHTTPPagesPolicyChangedTask(rpcClient *rpc.RPCClient) error
// stub // stub
return nil return nil
} }
func (this *Node) execNetworkSecurityPolicyChangedTask(rpcClient *rpc.RPCClient) error {
// stub
return nil
}

View File

@@ -142,24 +142,26 @@ func (this *TrafficStatManager) Add(userId int64, serverId int64, domain string,
item.PlanId = planId item.PlanId = planId
// 单个域名流量 // 单个域名流量
var domainKey = types.String(timestamp) + "@" + domain if len(domain) < 128 {
serverDomainMap, ok := this.domainsMap[serverId] var domainKey = types.String(timestamp) + "@" + domain
if !ok { serverDomainMap, ok := this.domainsMap[serverId]
serverDomainMap = map[string]*TrafficItem{} if !ok {
this.domainsMap[serverId] = serverDomainMap serverDomainMap = map[string]*TrafficItem{}
} this.domainsMap[serverId] = serverDomainMap
}
domainItem, ok := serverDomainMap[domainKey] domainItem, ok := serverDomainMap[domainKey]
if !ok { if !ok {
domainItem = &TrafficItem{} domainItem = &TrafficItem{}
serverDomainMap[domainKey] = domainItem serverDomainMap[domainKey] = domainItem
}
domainItem.Bytes += bytes
domainItem.CachedBytes += cachedBytes
domainItem.CountRequests += countRequests
domainItem.CountCachedRequests += countCachedRequests
domainItem.CountAttackRequests += countAttacks
domainItem.AttackBytes += attackBytes
} }
domainItem.Bytes += bytes
domainItem.CachedBytes += cachedBytes
domainItem.CountRequests += countRequests
domainItem.CountCachedRequests += countCachedRequests
domainItem.CountAttackRequests += countAttacks
domainItem.AttackBytes += attackBytes
this.locker.Unlock() this.locker.Unlock()
} }

View File

@@ -13,12 +13,16 @@ import (
const maxItemsPerGroup = 50_000 const maxItemsPerGroup = 50_000
var SharedCounter = NewCounter().WithGC() var SharedCounter = NewCounter[uint32]().WithGC()
type Counter struct { type SupportedUIntType interface {
uint32 | uint64
}
type Counter[T SupportedUIntType] struct {
countMaps uint64 countMaps uint64
locker *syncutils.RWMutex locker *syncutils.RWMutex
itemMaps []map[uint64]*Item itemMaps []map[uint64]*Item[T]
gcTicker *time.Ticker gcTicker *time.Ticker
gcIndex int gcIndex int
@@ -26,18 +30,18 @@ type Counter struct {
} }
// NewCounter create new counter // NewCounter create new counter
func NewCounter() *Counter { func NewCounter[T SupportedUIntType]() *Counter[T] {
var count = utils.SystemMemoryGB() * 8 var count = utils.SystemMemoryGB() * 8
if count < 8 { if count < 8 {
count = 8 count = 8
} }
var itemMaps = []map[uint64]*Item{} var itemMaps = []map[uint64]*Item[T]{}
for i := 0; i < count; i++ { for i := 0; i < count; i++ {
itemMaps = append(itemMaps, map[uint64]*Item{}) itemMaps = append(itemMaps, map[uint64]*Item[T]{})
} }
var counter = &Counter{ var counter = &Counter[T]{
countMaps: uint64(count), countMaps: uint64(count),
locker: syncutils.NewRWMutex(count), locker: syncutils.NewRWMutex(count),
itemMaps: itemMaps, itemMaps: itemMaps,
@@ -47,7 +51,7 @@ func NewCounter() *Counter {
} }
// WithGC start the counter with gc automatically // WithGC start the counter with gc automatically
func (this *Counter) WithGC() *Counter { func (this *Counter[T]) WithGC() *Counter[T] {
if this.gcTicker != nil { if this.gcTicker != nil {
return this return this
} }
@@ -62,23 +66,17 @@ func (this *Counter) WithGC() *Counter {
} }
// Increase key // Increase key
func (this *Counter) Increase(key uint64, lifeSeconds int) uint64 { func (this *Counter[T]) Increase(key uint64, lifeSeconds int) T {
var index = int(key % this.countMaps) var index = int(key % this.countMaps)
this.locker.RLock(index) this.locker.RLock(index)
var item = this.itemMaps[index][key] var item = this.itemMaps[index][key]
this.locker.RUnlock(index) this.locker.RUnlock(index)
if item == nil { // no need to care about duplication if item == nil {
item = NewItem(lifeSeconds) // no need to care about duplication
// always insert new item even when itemMap is full
item = NewItem[T](lifeSeconds)
this.locker.Lock(index) this.locker.Lock(index)
this.itemMaps[index][key] = item
// check again
oldItem, ok := this.itemMaps[index][key]
if !ok {
this.itemMaps[index][key] = item
} else {
item = oldItem
}
this.locker.Unlock(index) this.locker.Unlock(index)
} }
@@ -89,12 +87,12 @@ func (this *Counter) Increase(key uint64, lifeSeconds int) uint64 {
} }
// IncreaseKey increase string key // IncreaseKey increase string key
func (this *Counter) IncreaseKey(key string, lifeSeconds int) uint64 { func (this *Counter[T]) IncreaseKey(key string, lifeSeconds int) T {
return this.Increase(this.hash(key), lifeSeconds) return this.Increase(this.hash(key), lifeSeconds)
} }
// Get value of key // Get value of key
func (this *Counter) Get(key uint64) uint64 { func (this *Counter[T]) Get(key uint64) T {
var index = int(key % this.countMaps) var index = int(key % this.countMaps)
this.locker.RLock(index) this.locker.RLock(index)
defer this.locker.RUnlock(index) defer this.locker.RUnlock(index)
@@ -106,12 +104,12 @@ func (this *Counter) Get(key uint64) uint64 {
} }
// GetKey get value of string key // GetKey get value of string key
func (this *Counter) GetKey(key string) uint64 { func (this *Counter[T]) GetKey(key string) T {
return this.Get(this.hash(key)) return this.Get(this.hash(key))
} }
// Reset key // Reset key
func (this *Counter) Reset(key uint64) { func (this *Counter[T]) Reset(key uint64) {
var index = int(key % this.countMaps) var index = int(key % this.countMaps)
this.locker.RLock(index) this.locker.RLock(index)
var item = this.itemMaps[index][key] var item = this.itemMaps[index][key]
@@ -125,12 +123,12 @@ func (this *Counter) Reset(key uint64) {
} }
// ResetKey string key // ResetKey string key
func (this *Counter) ResetKey(key string) { func (this *Counter[T]) ResetKey(key string) {
this.Reset(this.hash(key)) this.Reset(this.hash(key))
} }
// TotalItems get items count // TotalItems get items count
func (this *Counter) TotalItems() int { func (this *Counter[T]) TotalItems() int {
var total = 0 var total = 0
for i := 0; i < int(this.countMaps); i++ { for i := 0; i < int(this.countMaps); i++ {
@@ -143,7 +141,7 @@ func (this *Counter) TotalItems() int {
} }
// GC garbage expired items // GC garbage expired items
func (this *Counter) GC() { func (this *Counter[T]) GC() {
this.gcLocker.Lock() this.gcLocker.Lock()
var gcIndex = this.gcIndex var gcIndex = this.gcIndex
@@ -192,11 +190,11 @@ func (this *Counter) GC() {
} }
} }
func (this *Counter) CountMaps() int { func (this *Counter[T]) CountMaps() int {
return int(this.countMaps) return int(this.countMaps)
} }
// calculate hash of the key // calculate hash of the key
func (this *Counter) hash(key string) uint64 { func (this *Counter[T]) hash(key string) uint64 {
return xxhash.Sum64String(key) return xxhash.Sum64String(key)
} }

View File

@@ -19,7 +19,7 @@ import (
func TestCounter_Increase(t *testing.T) { func TestCounter_Increase(t *testing.T) {
var a = assert.NewAssertion(t) var a = assert.NewAssertion(t)
var counter = counters.NewCounter() var counter = counters.NewCounter[uint32]()
a.IsTrue(counter.Increase(1, 10) == 1) a.IsTrue(counter.Increase(1, 10) == 1)
a.IsTrue(counter.Increase(1, 10) == 2) a.IsTrue(counter.Increase(1, 10) == 2)
a.IsTrue(counter.Increase(2, 10) == 1) a.IsTrue(counter.Increase(2, 10) == 1)
@@ -32,7 +32,7 @@ func TestCounter_Increase(t *testing.T) {
func TestCounter_IncreaseKey(t *testing.T) { func TestCounter_IncreaseKey(t *testing.T) {
var a = assert.NewAssertion(t) var a = assert.NewAssertion(t)
var counter = counters.NewCounter() var counter = counters.NewCounter[uint32]()
a.IsTrue(counter.IncreaseKey("1", 10) == 1) a.IsTrue(counter.IncreaseKey("1", 10) == 1)
a.IsTrue(counter.IncreaseKey("1", 10) == 2) a.IsTrue(counter.IncreaseKey("1", 10) == 2)
a.IsTrue(counter.IncreaseKey("2", 10) == 1) a.IsTrue(counter.IncreaseKey("2", 10) == 1)
@@ -47,7 +47,7 @@ func TestCounter_GC(t *testing.T) {
return return
} }
var counter = counters.NewCounter() var counter = counters.NewCounter[uint32]()
counter.Increase(1, 20) counter.Increase(1, 20)
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)
counter.Increase(1, 20) counter.Increase(1, 20)
@@ -61,7 +61,7 @@ func TestCounter_GC2(t *testing.T) {
return return
} }
var counter = counters.NewCounter().WithGC() var counter = counters.NewCounter[uint32]().WithGC()
for i := 0; i < 1e5; i++ { for i := 0; i < 1e5; i++ {
counter.Increase(uint64(i), rands.Int(10, 300)) counter.Increase(uint64(i), rands.Int(10, 300))
} }
@@ -79,7 +79,7 @@ func TestCounterMemory(t *testing.T) {
var stat = &runtime.MemStats{} var stat = &runtime.MemStats{}
runtime.ReadMemStats(stat) runtime.ReadMemStats(stat)
var counter = counters.NewCounter().WithGC() var counter = counters.NewCounter[uint32]()
for i := 0; i < 1_000_000; i++ { for i := 0; i < 1_000_000; i++ {
counter.Increase(uint64(i), rands.Int(10, 300)) counter.Increase(uint64(i), rands.Int(10, 300))
} }
@@ -98,7 +98,7 @@ func TestCounterMemory(t *testing.T) {
func BenchmarkCounter_Increase(b *testing.B) { func BenchmarkCounter_Increase(b *testing.B) {
runtime.GOMAXPROCS(4) runtime.GOMAXPROCS(4)
var counter = counters.NewCounter() var counter = counters.NewCounter[uint32]()
b.ResetTimer() b.ResetTimer()
var i uint64 var i uint64
@@ -114,7 +114,7 @@ func BenchmarkCounter_Increase(b *testing.B) {
func BenchmarkCounter_IncreaseKey(b *testing.B) { func BenchmarkCounter_IncreaseKey(b *testing.B) {
runtime.GOMAXPROCS(4) runtime.GOMAXPROCS(4)
var counter = counters.NewCounter() var counter = counters.NewCounter[uint32]()
go func() { go func() {
var ticker = time.NewTicker(100 * time.Millisecond) var ticker = time.NewTicker(100 * time.Millisecond)
@@ -138,7 +138,7 @@ func BenchmarkCounter_IncreaseKey(b *testing.B) {
func BenchmarkCounter_IncreaseKey2(b *testing.B) { func BenchmarkCounter_IncreaseKey2(b *testing.B) {
runtime.GOMAXPROCS(4) runtime.GOMAXPROCS(4)
var counter = counters.NewCounter() var counter = counters.NewCounter[uint32]()
go func() { go func() {
var ticker = time.NewTicker(1 * time.Millisecond) var ticker = time.NewTicker(1 * time.Millisecond)
@@ -162,7 +162,7 @@ func BenchmarkCounter_IncreaseKey2(b *testing.B) {
func BenchmarkCounter_GC(b *testing.B) { func BenchmarkCounter_GC(b *testing.B) {
runtime.GOMAXPROCS(4) runtime.GOMAXPROCS(4)
var counter = counters.NewCounter() var counter = counters.NewCounter[uint32]()
for i := uint64(0); i < 1e5; i++ { for i := uint64(0); i < 1e5; i++ {
counter.IncreaseKey(types.String(i), 20) counter.IncreaseKey(types.String(i), 20)

View File

@@ -6,16 +6,16 @@ import (
"github.com/TeaOSLab/EdgeNode/internal/utils/fasttime" "github.com/TeaOSLab/EdgeNode/internal/utils/fasttime"
) )
type Item struct { const spanMaxValue = 10_000_000
lifeSeconds int64
spanSeconds int64
spans []uint64
type Item[T SupportedUIntType] struct {
spans []T
lastUpdateTime int64 lastUpdateTime int64
lifeSeconds int64
spanSeconds int64
} }
func NewItem(lifeSeconds int) *Item { func NewItem[T SupportedUIntType](lifeSeconds int) *Item[T] {
if lifeSeconds <= 0 { if lifeSeconds <= 0 {
lifeSeconds = 60 lifeSeconds = 60
} }
@@ -25,21 +25,23 @@ func NewItem(lifeSeconds int) *Item {
} }
var countSpans = lifeSeconds/spanSeconds + 1 /** prevent index out of bounds **/ var countSpans = lifeSeconds/spanSeconds + 1 /** prevent index out of bounds **/
return &Item{ return &Item[T]{
lifeSeconds: int64(lifeSeconds), lifeSeconds: int64(lifeSeconds),
spanSeconds: int64(spanSeconds), spanSeconds: int64(spanSeconds),
spans: make([]uint64, countSpans), spans: make([]T, countSpans),
lastUpdateTime: fasttime.Now().Unix(), lastUpdateTime: fasttime.Now().Unix(),
} }
} }
func (this *Item) Increase() (result uint64) { func (this *Item[T]) Increase() (result T) {
var currentTime = fasttime.Now().Unix() var currentTime = fasttime.Now().Unix()
var currentSpanIndex = this.calculateSpanIndex(currentTime) var currentSpanIndex = this.calculateSpanIndex(currentTime)
// return quickly // return quickly
if this.lastUpdateTime == currentTime { if this.lastUpdateTime == currentTime {
this.spans[currentSpanIndex]++ if this.spans[currentSpanIndex] < spanMaxValue {
this.spans[currentSpanIndex]++
}
for _, count := range this.spans { for _, count := range this.spans {
result += count result += count
} }
@@ -69,7 +71,9 @@ func (this *Item) Increase() (result uint64) {
} }
} }
this.spans[currentSpanIndex]++ if this.spans[currentSpanIndex] < spanMaxValue {
this.spans[currentSpanIndex]++
}
this.lastUpdateTime = currentTime this.lastUpdateTime = currentTime
for _, count := range this.spans { for _, count := range this.spans {
@@ -79,7 +83,7 @@ func (this *Item) Increase() (result uint64) {
return return
} }
func (this *Item) Sum() (result uint64) { func (this *Item[T]) Sum() (result T) {
if this.lastUpdateTime == 0 { if this.lastUpdateTime == 0 {
return 0 return 0
} }
@@ -104,16 +108,16 @@ func (this *Item) Sum() (result uint64) {
return result return result
} }
func (this *Item) Reset() { func (this *Item[T]) Reset() {
for index := range this.spans { for index := range this.spans {
this.spans[index] = 0 this.spans[index] = 0
} }
} }
func (this *Item) IsExpired(currentTime int64) bool { func (this *Item[T]) IsExpired(currentTime int64) bool {
return this.lastUpdateTime < currentTime-this.lifeSeconds-this.spanSeconds return this.lastUpdateTime < currentTime-this.lifeSeconds-this.spanSeconds
} }
func (this *Item) calculateSpanIndex(timestamp int64) int { func (this *Item[T]) calculateSpanIndex(timestamp int64) int {
return int(timestamp % this.lifeSeconds / this.spanSeconds) return int(timestamp % this.lifeSeconds / this.spanSeconds)
} }

View File

@@ -17,7 +17,7 @@ func TestItem_Increase(t *testing.T) {
return return
} }
var item = counters.NewItem(10) var item = counters.NewItem[uint32](10)
t.Log(item.Increase(), item.Sum()) t.Log(item.Increase(), item.Sum())
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)
t.Log(item.Increase(), item.Sum()) t.Log(item.Increase(), item.Sum())
@@ -41,7 +41,7 @@ func TestItem_Increase2(t *testing.T) {
var a = assert.NewAssertion(t) var a = assert.NewAssertion(t)
var item = counters.NewItem(20) var item = counters.NewItem[uint32](20)
for i := 0; i < 100; i++ { for i := 0; i < 100; i++ {
t.Log(item.Increase(), item.Sum(), timeutil.Format("H:i:s")) t.Log(item.Increase(), item.Sum(), timeutil.Format("H:i:s"))
time.Sleep(2 * time.Second) time.Sleep(2 * time.Second)
@@ -58,7 +58,7 @@ func TestItem_IsExpired(t *testing.T) {
var currentTime = time.Now().Unix() var currentTime = time.Now().Unix()
var item = counters.NewItem(10) var item = counters.NewItem[uint32](10)
t.Log(item.IsExpired(currentTime)) t.Log(item.IsExpired(currentTime))
time.Sleep(10 * time.Second) time.Sleep(10 * time.Second)
t.Log(item.IsExpired(currentTime)) t.Log(item.IsExpired(currentTime))
@@ -73,7 +73,7 @@ func BenchmarkItem_Increase(b *testing.B) {
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
var item = counters.NewItem(60) var item = counters.NewItem[uint32](60)
item.Increase() item.Increase()
item.Sum() item.Sum()
} }

View File

@@ -56,3 +56,16 @@ func EqualStrings(s1 []string, s2 []string) bool {
} }
return true return true
} }
// CutPrefix returns s without the provided leading prefix string
// and reports whether it found the prefix.
// If s doesn't start with prefix, CutPrefix returns s, false.
// If prefix is the empty string, CutPrefix returns s, true.
//
// copy from go source
func CutPrefix(s, prefix string) (after string, found bool) {
if !strings.HasPrefix(s, prefix) {
return s, false
}
return s[len(prefix):], true
}

View File

@@ -8,6 +8,7 @@ import (
) )
var systemTotalMemory = -1 var systemTotalMemory = -1
var systemMemoryBytes uint64
func init() { func init() {
if !teaconst.IsMain { if !teaconst.IsMain {
@@ -29,7 +30,9 @@ func SystemMemoryGB() int {
return 1 return 1
} }
systemTotalMemory = int(stat.Total / (1<<30)) systemMemoryBytes = stat.Total
systemTotalMemory = int(stat.Total / (1 << 30))
if systemTotalMemory <= 0 { if systemTotalMemory <= 0 {
systemTotalMemory = 1 systemTotalMemory = 1
} }
@@ -38,3 +41,8 @@ func SystemMemoryGB() int {
return systemTotalMemory return systemTotalMemory
} }
// SystemMemoryBytes 系统内存总字节数
func SystemMemoryBytes() uint64 {
return systemMemoryBytes
}

View File

@@ -8,4 +8,7 @@ func TestSystemMemoryGB(t *testing.T) {
t.Log(SystemMemoryGB()) t.Log(SystemMemoryGB())
t.Log(SystemMemoryGB()) t.Log(SystemMemoryGB())
t.Log(SystemMemoryGB()) t.Log(SystemMemoryGB())
t.Log(SystemMemoryBytes())
t.Log(SystemMemoryBytes())
t.Log(SystemMemoryBytes()>>30, "GB")
} }

View File

@@ -1,11 +1,12 @@
package waf package waf
import ( import (
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
"github.com/TeaOSLab/EdgeNode/internal/remotelogs" "github.com/TeaOSLab/EdgeNode/internal/remotelogs"
"github.com/TeaOSLab/EdgeNode/internal/utils" "github.com/TeaOSLab/EdgeNode/internal/utils"
"github.com/TeaOSLab/EdgeNode/internal/waf/requests" "github.com/TeaOSLab/EdgeNode/internal/waf/requests"
wafutils "github.com/TeaOSLab/EdgeNode/internal/waf/utils"
"github.com/iwind/TeaGo/maps" "github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
"net/http" "net/http"
"net/url" "net/url"
"strings" "strings"
@@ -27,6 +28,8 @@ type CaptchaAction struct {
CountLetters int8 `yaml:"countLetters" json:"countLetters"` CountLetters int8 `yaml:"countLetters" json:"countLetters"`
CaptchaType firewallconfigs.CaptchaType `yaml:"captchaType" json:"captchaType"`
UIIsOn bool `yaml:"uiIsOn" json:"uiIsOn"` // 是否使用自定义UI UIIsOn bool `yaml:"uiIsOn" json:"uiIsOn"` // 是否使用自定义UI
UITitle string `yaml:"uiTitle" json:"uiTitle"` // 消息标题 UITitle string `yaml:"uiTitle" json:"uiTitle"` // 消息标题
UIPrompt string `yaml:"uiPrompt" json:"uiPrompt"` // 消息提示 UIPrompt string `yaml:"uiPrompt" json:"uiPrompt"` // 消息提示
@@ -36,6 +39,22 @@ type CaptchaAction struct {
UIFooter string `yaml:"uiFooter" json:"uiFooter"` // 页脚 UIFooter string `yaml:"uiFooter" json:"uiFooter"` // 页脚
UIBody string `yaml:"uiBody" json:"uiBody"` // 内容轮廓 UIBody string `yaml:"uiBody" json:"uiBody"` // 内容轮廓
OneClickUIIsOn bool `yaml:"oneClickUIIsOn" json:"oneClickUIIsOn"` // 是否使用自定义UI
OneClickUITitle string `yaml:"oneClickUITitle" json:"oneClickUITitle"` // 消息标题
OneClickUIPrompt string `yaml:"oneClickUIPrompt" json:"oneClickUIPrompt"` // 消息提示
OneClickUIShowRequestId bool `yaml:"oneClickUIShowRequestId" json:"oneClickUIShowRequestId"` // 是否显示请求ID
OneClickUICss string `yaml:"oneClickUICss" json:"oneClickUICss"` // CSS样式
OneClickUIFooter string `yaml:"oneClickUIFooter" json:"oneClickUIFooter"` // 页脚
OneClickUIBody string `yaml:"oneClickUIBody" json:"oneClickUIBody"` // 内容轮廓
SlideUIIsOn bool `yaml:"sliceUIIsOn" json:"sliceUIIsOn"` // 是否使用自定义UI
SlideUITitle string `yaml:"slideUITitle" json:"slideUITitle"` // 消息标题
SlideUIPrompt string `yaml:"slideUIPrompt" json:"slideUIPrompt"` // 消息提示
SlideUIShowRequestId bool `yaml:"SlideUIShowRequestId" json:"SlideUIShowRequestId"` // 是否显示请求ID
SlideUICss string `yaml:"slideUICss" json:"slideUICss"` // CSS样式
SlideUIFooter string `yaml:"slideUIFooter" json:"slideUIFooter"` // 页脚
SlideUIBody string `yaml:"slideUIBody" json:"slideUIBody"` // 内容轮廓
Lang string `yaml:"lang" json:"lang"` // 语言zh-CN, en-US ... Lang string `yaml:"lang" json:"lang"` // 语言zh-CN, en-US ...
AddToWhiteList bool `yaml:"addToWhiteList" json:"addToWhiteList"` // 是否加入到白名单 AddToWhiteList bool `yaml:"addToWhiteList" json:"addToWhiteList"` // 是否加入到白名单
Scope string `yaml:"scope" json:"scope"` Scope string `yaml:"scope" json:"scope"`
@@ -81,6 +100,10 @@ func (this *CaptchaAction) Init(waf *WAF) error {
if len(this.Lang) == 0 { if len(this.Lang) == 0 {
this.Lang = waf.DefaultCaptchaAction.Lang this.Lang = waf.DefaultCaptchaAction.Lang
} }
if len(this.CaptchaType) == 0 {
this.CaptchaType = waf.DefaultCaptchaAction.CaptchaType
}
} }
return nil return nil
@@ -100,7 +123,7 @@ func (this *CaptchaAction) WillChange() bool {
func (this *CaptchaAction) Perform(waf *WAF, group *RuleGroup, set *RuleSet, req requests.Request, writer http.ResponseWriter) (continueRequest bool, goNextSet bool) { func (this *CaptchaAction) Perform(waf *WAF, group *RuleGroup, set *RuleSet, req requests.Request, writer http.ResponseWriter) (continueRequest bool, goNextSet bool) {
// 是否在白名单中 // 是否在白名单中
if SharedIPWhiteList.Contains("set:"+types.String(set.Id), this.Scope, req.WAFServerId(), req.WAFRemoteIP()) { if SharedIPWhiteList.Contains(wafutils.ComposeIPType(set.Id, req), this.Scope, req.WAFServerId(), req.WAFRemoteIP()) {
return true, false return true, false
} }

View File

@@ -4,7 +4,6 @@ package waf
import ( import (
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs" "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
"github.com/TeaOSLab/EdgeNode/internal/utils"
"github.com/TeaOSLab/EdgeNode/internal/utils/counters" "github.com/TeaOSLab/EdgeNode/internal/utils/counters"
"github.com/TeaOSLab/EdgeNode/internal/waf/requests" "github.com/TeaOSLab/EdgeNode/internal/waf/requests"
"github.com/iwind/TeaGo/types" "github.com/iwind/TeaGo/types"
@@ -45,14 +44,5 @@ func CaptchaDeleteCacheKey(req requests.Request) {
// CaptchaCacheKey 获取Captcha缓存Key // CaptchaCacheKey 获取Captcha缓存Key
func CaptchaCacheKey(req requests.Request, pageCode CaptchaPageCode) string { func CaptchaCacheKey(req requests.Request, pageCode CaptchaPageCode) string {
var requestPath = req.WAFRaw().URL.Path return "WAF:CAPTCHA:FAILS:" + pageCode + ":" + req.WAFRemoteIP() + ":" + types.String(req.WAFServerId())
if req.WAFRaw().URL.Path == CaptchaPath {
m, err := utils.SimpleDecryptMap(req.WAFRaw().URL.Query().Get("info"))
if err == nil && m != nil {
requestPath = m.GetString("url")
}
}
return "WAF:CAPTCHA:FAILS:" + pageCode + ":" + req.WAFRemoteIP() + ":" + types.String(req.WAFServerId()) + ":" + requestPath
} }

View File

@@ -3,13 +3,18 @@ package waf
import ( import (
"bytes" "bytes"
"encoding/base64" "encoding/base64"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
"github.com/TeaOSLab/EdgeNode/internal/ttlcache"
"github.com/TeaOSLab/EdgeNode/internal/utils" "github.com/TeaOSLab/EdgeNode/internal/utils"
"github.com/TeaOSLab/EdgeNode/internal/utils/fasttime"
"github.com/TeaOSLab/EdgeNode/internal/waf/requests" "github.com/TeaOSLab/EdgeNode/internal/waf/requests"
wafutils "github.com/TeaOSLab/EdgeNode/internal/waf/utils"
"github.com/dchest/captcha" "github.com/dchest/captcha"
"github.com/iwind/TeaGo/logs" "github.com/iwind/TeaGo/logs"
"github.com/iwind/TeaGo/rands"
"github.com/iwind/TeaGo/types" "github.com/iwind/TeaGo/types"
stringutil "github.com/iwind/TeaGo/utils/string"
"net/http" "net/http"
"strconv"
"strings" "strings"
"time" "time"
) )
@@ -23,7 +28,7 @@ func NewCaptchaValidator() *CaptchaValidator {
return &CaptchaValidator{} return &CaptchaValidator{}
} }
func (this *CaptchaValidator) Run(req requests.Request, writer http.ResponseWriter) { func (this *CaptchaValidator) Run(req requests.Request, writer http.ResponseWriter, defaultCaptchaType firewallconfigs.ServerCaptchaType) {
var info = req.WAFRaw().URL.Query().Get("info") var info = req.WAFRaw().URL.Query().Get("info")
if len(info) == 0 { if len(info) == 0 {
req.ProcessResponseHeaders(writer.Header(), http.StatusBadRequest) req.ProcessResponseHeaders(writer.Header(), http.StatusBadRequest)
@@ -71,16 +76,39 @@ func (this *CaptchaValidator) Run(req requests.Request, writer http.ResponseWrit
return return
} }
var captchaType = captchaActionConfig.CaptchaType
if len(defaultCaptchaType) > 0 && defaultCaptchaType != firewallconfigs.ServerCaptchaTypeNone {
captchaType = defaultCaptchaType
}
if req.WAFRaw().Method == http.MethodPost && len(req.WAFRaw().FormValue("GOEDGE_WAF_CAPTCHA_ID")) > 0 { if req.WAFRaw().Method == http.MethodPost && len(req.WAFRaw().FormValue("GOEDGE_WAF_CAPTCHA_ID")) > 0 {
this.validate(captchaActionConfig, policyId, groupId, setId, originURL, req, writer) switch captchaType {
case firewallconfigs.CaptchaTypeOneClick:
this.validateOneClickForm(captchaActionConfig, policyId, groupId, setId, originURL, req, writer)
case firewallconfigs.CaptchaTypeSlide:
this.validateSlideForm(captchaActionConfig, policyId, groupId, setId, originURL, req, writer)
default:
this.validateVerifyCodeForm(captchaActionConfig, policyId, groupId, setId, originURL, req, writer)
}
} else { } else {
// 增加计数 // 增加计数
CaptchaIncreaseFails(req, captchaActionConfig, policyId, groupId, setId, CaptchaPageCodeShow) CaptchaIncreaseFails(req, captchaActionConfig, policyId, groupId, setId, CaptchaPageCodeShow)
this.show(captchaActionConfig, req, writer) this.show(captchaActionConfig, req, writer, captchaType)
} }
} }
func (this *CaptchaValidator) show(actionConfig *CaptchaAction, req requests.Request, writer http.ResponseWriter) { func (this *CaptchaValidator) show(actionConfig *CaptchaAction, req requests.Request, writer http.ResponseWriter, captchaType firewallconfigs.ServerCaptchaType) {
switch captchaType {
case firewallconfigs.CaptchaTypeOneClick:
this.showOneClickForm(actionConfig, req, writer)
case firewallconfigs.CaptchaTypeSlide:
this.showSlideForm(actionConfig, req, writer)
default:
this.showVerifyCodesForm(actionConfig, req, writer)
}
}
func (this *CaptchaValidator) showVerifyCodesForm(actionConfig *CaptchaAction, req requests.Request, writer http.ResponseWriter) {
// show captcha // show captcha
var countLetters = 6 var countLetters = 6
if actionConfig.CountLetters > 0 && actionConfig.CountLetters <= 10 { if actionConfig.CountLetters > 0 && actionConfig.CountLetters <= 10 {
@@ -168,7 +196,7 @@ func (this *CaptchaValidator) show(actionConfig *CaptchaAction, req requests.Req
<img src="data:image/png;base64, ` + base64.StdEncoding.EncodeToString(buf.Bytes()) + `"/>` + ` <img src="data:image/png;base64, ` + base64.StdEncoding.EncodeToString(buf.Bytes()) + `"/>` + `
</div> </div>
<div class="ui-input"> <div class="ui-input">
<p>` + msgPrompt + `</p> <p class="ui-prompt">` + msgPrompt + `</p>
<input type="text" name="GOEDGE_WAF_CAPTCHA_CODE" id="GOEDGE_WAF_CAPTCHA_CODE" size="` + types.String(countLetters*17/10) + `" maxlength="` + types.String(countLetters) + `" autocomplete="off" z-index="1" class="input"/> <input type="text" name="GOEDGE_WAF_CAPTCHA_CODE" id="GOEDGE_WAF_CAPTCHA_CODE" size="` + types.String(countLetters*17/10) + `" maxlength="` + types.String(countLetters) + `" autocomplete="off" z-index="1" class="input"/>
</div> </div>
<div class="ui-button"> <div class="ui-button">
@@ -176,7 +204,7 @@ func (this *CaptchaValidator) show(actionConfig *CaptchaAction, req requests.Req
</div> </div>
</form> </form>
` + requestIdBox + ` ` + requestIdBox + `
` + msgFooter + `` ` + msgFooter
// Body // Body
if actionConfig.UIIsOn { if actionConfig.UIIsOn {
@@ -204,7 +232,7 @@ func (this *CaptchaValidator) show(actionConfig *CaptchaAction, req requests.Req
} }
</script> </script>
<style type="text/css"> <style type="text/css">
form { width: 20em; margin: 0 auto; text-align: center; } form { max-width: 20em; margin: 0 auto; text-align: center; }
.input { font-size:16px;line-height:24px; letter-spacing:0.2em; min-width: 5em; text-align: center; } .input { font-size:16px;line-height:24px; letter-spacing:0.2em; min-width: 5em; text-align: center; }
address { margin-top: 1em; padding-top: 0.5em; border-top: 1px #ccc solid; text-align: center; } address { margin-top: 1em; padding-top: 0.5em; border-top: 1px #ccc solid; text-align: center; }
` + msgCss + ` ` + msgCss + `
@@ -221,8 +249,7 @@ func (this *CaptchaValidator) show(actionConfig *CaptchaAction, req requests.Req
_, _ = writer.Write([]byte(msgHTML)) _, _ = writer.Write([]byte(msgHTML))
} }
func (this *CaptchaValidator) validate(actionConfig *CaptchaAction, policyId int64, groupId int64, setId int64, originURL string, req requests.Request, writer http.ResponseWriter) (allow bool) { func (this *CaptchaValidator) validateVerifyCodeForm(actionConfig *CaptchaAction, policyId int64, groupId int64, setId int64, originURL string, req requests.Request, writer http.ResponseWriter) (allow bool) {
var captchaId = req.WAFRaw().FormValue("GOEDGE_WAF_CAPTCHA_ID") var captchaId = req.WAFRaw().FormValue("GOEDGE_WAF_CAPTCHA_ID")
if len(captchaId) > 0 { if len(captchaId) > 0 {
var captchaCode = req.WAFRaw().FormValue("GOEDGE_WAF_CAPTCHA_CODE") var captchaCode = req.WAFRaw().FormValue("GOEDGE_WAF_CAPTCHA_CODE")
@@ -236,7 +263,7 @@ func (this *CaptchaValidator) validate(actionConfig *CaptchaAction, policyId int
} }
// 加入到白名单 // 加入到白名单
SharedIPWhiteList.RecordIP("set:"+strconv.FormatInt(setId, 10), actionConfig.Scope, req.WAFServerId(), req.WAFRemoteIP(), time.Now().Unix()+int64(life), policyId, false, groupId, setId, "") SharedIPWhiteList.RecordIP(wafutils.ComposeIPType(setId, req), actionConfig.Scope, req.WAFServerId(), req.WAFRemoteIP(), time.Now().Unix()+int64(life), policyId, false, groupId, setId, "")
req.ProcessResponseHeaders(writer.Header(), http.StatusSeeOther) req.ProcessResponseHeaders(writer.Header(), http.StatusSeeOther)
http.Redirect(writer, req.WAFRaw(), originURL, http.StatusSeeOther) http.Redirect(writer, req.WAFRaw(), originURL, http.StatusSeeOther)
@@ -255,3 +282,356 @@ func (this *CaptchaValidator) validate(actionConfig *CaptchaAction, policyId int
return true return true
} }
func (this *CaptchaValidator) showOneClickForm(actionConfig *CaptchaAction, req requests.Request, writer http.ResponseWriter) {
var lang = actionConfig.Lang
if len(lang) == 0 {
var acceptLanguage = req.WAFRaw().Header.Get("Accept-Language")
if len(acceptLanguage) > 0 {
langIndex := strings.Index(acceptLanguage, ",")
if langIndex > 0 {
lang = acceptLanguage[:langIndex]
}
}
}
if len(lang) == 0 {
lang = "en-US"
}
var msgTitle string
var msgPrompt string
var msgRequestId string
switch lang {
case "zh-CN":
msgTitle = "身份验证"
msgPrompt = "我不是机器人"
msgRequestId = "请求ID"
case "zh-TW":
msgTitle = "身份驗證"
msgPrompt = "我不是機器人"
msgRequestId = "請求ID"
default:
msgTitle = "Verify Yourself"
msgPrompt = "I'm not a robot"
msgRequestId = "Request ID"
}
var msgCss = ""
var requestIdBox = `<address>` + msgRequestId + `: ` + req.Format("${requestId}") + `</address>`
var msgFooter = ""
// 默认设置
if actionConfig.OneClickUIIsOn {
if len(actionConfig.OneClickUIPrompt) > 0 {
msgPrompt = actionConfig.OneClickUIPrompt
}
if len(actionConfig.OneClickUITitle) > 0 {
msgTitle = actionConfig.OneClickUITitle
}
if len(actionConfig.OneClickUICss) > 0 {
msgCss = actionConfig.OneClickUICss
}
if !actionConfig.OneClickUIShowRequestId {
requestIdBox = ""
}
if len(actionConfig.OneClickUIFooter) > 0 {
msgFooter = actionConfig.OneClickUIFooter
}
}
var captchaId = stringutil.Md5(req.WAFRemoteIP() + "@" + stringutil.Rand(32))
var nonce = rands.Int64()
if !ttlcache.SharedInt64Cache.Write("WAF_CAPTCHA:"+captchaId, nonce, fasttime.Now().Unix()+600) {
return
}
var body = `<form method="POST" id="ui-form">
<input type="hidden" name="GOEDGE_WAF_CAPTCHA_ID" value="` + captchaId + `"/>
<div class="ui-input">
<div class="ui-checkbox" id="checkbox"></div>
<p class="ui-prompt">` + msgPrompt + `</p>
</div>
</form>
` + requestIdBox + `
` + msgFooter
// Body
if actionConfig.OneClickUIIsOn {
if len(actionConfig.OneClickUIBody) > 0 {
var index = strings.Index(actionConfig.OneClickUIBody, "${body}")
if index < 0 {
body = actionConfig.OneClickUIBody + body
} else {
body = actionConfig.OneClickUIBody[:index] + body + actionConfig.OneClickUIBody[index+7:] // 7是"${body}"的长度
}
}
}
var msgHTML = `<!DOCTYPE html>
<html>
<head>
<title>` + msgTitle + `</title>
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=0">
<meta charset="UTF-8"/>
<script type="text/javascript">
window.addEventListener("load",function(){var t=document.getElementById("checkbox"),n=!1;t.addEventListener("click",function(){var e;t.className="ui-checkbox checked",n||(n=!0,(e=document.createElement("input")).setAttribute("name","nonce"),e.setAttribute("type","hidden"),e.setAttribute("value","` + types.String(nonce) + `"),document.getElementById("ui-form").appendChild(e),document.getElementById("ui-form").submit())})});
</script>
<style type="text/css">
form { max-width: 20em; margin: 0 auto; text-align: center; }
.ui-input { position: relative; padding-top: 1em; height: 2.2em; background: #eee; }
.ui-checkbox { width: 16px; height: 16px; border: 1px #999 solid; float: left; margin-left: 1em; cursor: pointer; }
.ui-checkbox.checked { background: #276AC6; }
.ui-prompt { float: left; margin: 0; margin-left: 0.5em; padding: 0; line-height: 1.2; }
address { margin-top: 1em; padding-top: 0.5em; border-top: 1px #ccc solid; text-align: center; clear: both; }
` + msgCss + `
</style>
</head>
<body>` + body + `
</body>
</html>`
req.ProcessResponseHeaders(writer.Header(), http.StatusOK)
writer.Header().Set("Content-Type", "text/html; charset=utf-8")
writer.Header().Set("Content-Length", types.String(len(msgHTML)))
writer.WriteHeader(http.StatusOK)
_, _ = writer.Write([]byte(msgHTML))
}
func (this *CaptchaValidator) validateOneClickForm(actionConfig *CaptchaAction, policyId int64, groupId int64, setId int64, originURL string, req requests.Request, writer http.ResponseWriter) (allow bool) {
var captchaId = req.WAFRaw().FormValue("GOEDGE_WAF_CAPTCHA_ID")
var nonce = req.WAFRaw().FormValue("nonce")
if len(captchaId) > 0 {
var key = "WAF_CAPTCHA:" + captchaId
var cacheItem = ttlcache.SharedInt64Cache.Read(key)
ttlcache.SharedInt64Cache.Delete(key)
if cacheItem != nil {
// 清除计数
CaptchaDeleteCacheKey(req)
if cacheItem.Value == types.Int64(nonce) {
var life = CaptchaSeconds
if actionConfig.Life > 0 {
life = types.Int(actionConfig.Life)
}
// 加入到白名单
SharedIPWhiteList.RecordIP(wafutils.ComposeIPType(setId, req), actionConfig.Scope, req.WAFServerId(), req.WAFRemoteIP(), time.Now().Unix()+int64(life), policyId, false, groupId, setId, "")
req.ProcessResponseHeaders(writer.Header(), http.StatusSeeOther)
http.Redirect(writer, req.WAFRaw(), originURL, http.StatusSeeOther)
return false
}
} else {
// 增加计数
if !CaptchaIncreaseFails(req, actionConfig, policyId, groupId, setId, CaptchaPageCodeSubmit) {
return false
}
req.ProcessResponseHeaders(writer.Header(), http.StatusSeeOther)
http.Redirect(writer, req.WAFRaw(), req.WAFRaw().URL.String(), http.StatusSeeOther)
}
}
return true
}
func (this *CaptchaValidator) showSlideForm(actionConfig *CaptchaAction, req requests.Request, writer http.ResponseWriter) {
var lang = actionConfig.Lang
if len(lang) == 0 {
var acceptLanguage = req.WAFRaw().Header.Get("Accept-Language")
if len(acceptLanguage) > 0 {
langIndex := strings.Index(acceptLanguage, ",")
if langIndex > 0 {
lang = acceptLanguage[:langIndex]
}
}
}
if len(lang) == 0 {
lang = "en-US"
}
var msgTitle string
var msgPrompt string
var msgRequestId string
switch lang {
case "zh-CN":
msgTitle = "身份验证"
msgPrompt = "滑动上面方块到右侧解锁"
msgRequestId = "请求ID"
case "zh-TW":
msgTitle = "身份驗證"
msgPrompt = "滑動上面方塊到右側解鎖"
msgRequestId = "請求ID"
default:
msgTitle = "Verify Yourself"
msgPrompt = "Slide to Unlock"
msgRequestId = "Request ID"
}
var msgCss = ""
var requestIdBox = `<address>` + msgRequestId + `: ` + req.Format("${requestId}") + `</address>`
var msgFooter = ""
// 默认设置
if actionConfig.OneClickUIIsOn {
if len(actionConfig.OneClickUIPrompt) > 0 {
msgPrompt = actionConfig.OneClickUIPrompt
}
if len(actionConfig.OneClickUITitle) > 0 {
msgTitle = actionConfig.OneClickUITitle
}
if len(actionConfig.OneClickUICss) > 0 {
msgCss = actionConfig.OneClickUICss
}
if !actionConfig.OneClickUIShowRequestId {
requestIdBox = ""
}
if len(actionConfig.OneClickUIFooter) > 0 {
msgFooter = actionConfig.OneClickUIFooter
}
}
var captchaId = stringutil.Md5(req.WAFRemoteIP() + "@" + stringutil.Rand(32))
var nonce = rands.Int64()
if !ttlcache.SharedInt64Cache.Write("WAF_CAPTCHA:"+captchaId, nonce, fasttime.Now().Unix()+600) {
return
}
var body = `<form method="POST" id="ui-form">
<input type="hidden" name="GOEDGE_WAF_CAPTCHA_ID" value="` + captchaId + `"/>
<div class="ui-input" id="input">
<div class="ui-progress-bar" id="progress-bar"></div>
<div class="ui-handler" id="handler"></div>
<div class="ui-handler-placeholder" id="handler-placeholder"></div>
<img alt="" src="data:image/jpeg;charset=utf-8;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAACXBIWXMAAAsTAAALEwEAmpwYAAACHklEQVR4nO3cu4oUQRgF4EI3UhDXRNRQUFnBC97XwIcRfQf3ZfYdTAxMFMwMzUw18q6JCOpaxayBTItdDd3V9v99cOKt5pyZnWlmJiUAAAAAAAAAAAAAAAAAAAAAgOgO5pzLOdn6IEzvXs6bnL39PM+53PRETKaUv9eRLzm3G56LCWykPx/5XSPYbnY6Rncm/b383/mcc6vVARnXqfTvARjBwpUXfH1HcLPRGRlRebVf/tf3GcGnZASLVF7olUe4Z4LAakZQnglutDkmYzICjAAjINWP4HqbYzImI8AIqBvBx2QEi1Q7gmttjsmYjAAjoH4EV9sckzEZAUaAEZDqRvAh50qbYzKm5iMoH3F+kPNi/w/I9PmW+g2g5G3Ohc4mBziQ87jij8s8Ur6TcLqjz2r3Z3AxMixP1uus93AGFyLD8jPn6HqldR7N4EJk+AA21yutszODC5FhedbRZ7UjOS9ncDFSl/LO4WxHn4Mcy9nN+TqDC5N+5Y9yQ6j80sWmTJ47qe5GkFvCC3IxrW7s9CnfR8YWRvmBKT8w5Qem/MAuJeWHVcp/l5QfkvIDU35gyg+spnzfDF4Y5QdWfjtQ+UEpPzDlB1bKf5+UH5LyAzufVu/f+77P90mehSmfylV+UIdzfiRP+2GdSB754b1Oyg/tblJ+eGUEr9Kq+O85T3O2mp6IJo7nHGp9CAAAAAAAAAAAAAAAAAAAAADgf/ILsUB70laSdmQAAAAASUVORK5CYII="/>
</div>
<p class="ui-prompt">` + msgPrompt + `</p>
</form>
` + requestIdBox + `
` + msgFooter
// Body
if actionConfig.OneClickUIIsOn {
if len(actionConfig.OneClickUIBody) > 0 {
var index = strings.Index(actionConfig.OneClickUIBody, "${body}")
if index < 0 {
body = actionConfig.OneClickUIBody + body
} else {
body = actionConfig.OneClickUIBody[:index] + body + actionConfig.OneClickUIBody[index+7:] // 7是"${body}"的长度
}
}
}
var msgHTML = `<!DOCTYPE html>
<html>
<head>
<title>` + msgTitle + `</title>
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=0">
<meta charset="UTF-8"/>
<script type="text/javascript">
window.addEventListener("load",function(){var n=document.getElementById("input"),s=document.getElementById("handler"),o=document.getElementById("progress-bar"),d=!1,u=0,t=n.offsetLeft,c=s.offsetLeft,i=n.offsetWidth-s.offsetWidth-s.offsetLeft,f=!1;function e(e){e.preventDefault(),d=!0,u=null!=e.touches&&0<e.touches.length?e.touches[0].clientX-t:e.offsetX}function l(e){var t;d&&(t=e.x,null!=e.touches&&0<e.touches.length&&(t=e.touches[0].clientX),(t=t-n.offsetLeft-u)<c?t=c:i<t&&(t=i),s.style.cssText="margin-left: "+t+"px",0<t&&(o.style.cssText="width: "+(t+s.offsetWidth+4)+"px"))}function r(e){var t;d=d&&!1,s.offsetLeft<i-4?(s.style.cssText="margin-left: "+c+"px",n.style.cssText="background: #eee",o.style.cssText="width: 0px"):(s.style.cssText="margin-left: "+i+"px",n.style.cssText="background: #a5dc86",f||(f=!0,(t=document.createElement("input")).setAttribute("name","nonce"),t.setAttribute("type","hidden"),t.setAttribute("value","` + types.String(nonce) + `"),document.getElementById("ui-form").appendChild(t),document.getElementById("ui-form").submit()))}void 0!==document.ontouchstart?(s.addEventListener("touchstart",e),document.addEventListener("touchmove",l),document.addEventListener("touchend",r)):(s.addEventListener("mousedown",e),window.addEventListener("mousemove",l),window.addEventListener("mouseup",r))});
</script>
<style type="text/css">
form { max-width: 20em; margin: 5em auto; text-align: center; }
.ui-input {
height: 4em;
background: #eee;
border: 1px #ccc solid;
text-align: left;
position: relative;
}
.ui-input .ui-progress-bar {
background: #a5dc86;
position: absolute;
top: 0;
left: 0;
bottom: 0;
}
.ui-handler, .ui-handler-placeholder {
width: 3.6em;
height: 3.6em;
margin: 0.2em;
background: #276AC6;
border-radius: 0.6em;
display: inline-block;
cursor: pointer;
z-index: 10;
position: relative;
}
.ui-handler-placeholder {
background: none;
border: 1px #ccc dashed;
position: absolute;
right: -1px;
top: 0;
bottom: 0;
}
.ui-input img {
position: absolute;
top: 0;
bottom: 0;
height: 100%;
left: 8em;
opacity: 5%;
}
.ui-prompt { float: left; margin: 1em 0; padding: 0; line-height: 1.2; }
address { margin-top: 1em; padding-top: 0.5em; border-top: 1px #ccc solid; text-align: center; clear: both; }
` + msgCss + `
</style>
</head>
<body>` + body + `
</body>
</html>`
req.ProcessResponseHeaders(writer.Header(), http.StatusOK)
writer.Header().Set("Content-Type", "text/html; charset=utf-8")
writer.Header().Set("Content-Length", types.String(len(msgHTML)))
writer.WriteHeader(http.StatusOK)
_, _ = writer.Write([]byte(msgHTML))
}
func (this *CaptchaValidator) validateSlideForm(actionConfig *CaptchaAction, policyId int64, groupId int64, setId int64, originURL string, req requests.Request, writer http.ResponseWriter) (allow bool) {
var captchaId = req.WAFRaw().FormValue("GOEDGE_WAF_CAPTCHA_ID")
var nonce = req.WAFRaw().FormValue("nonce")
if len(captchaId) > 0 {
var key = "WAF_CAPTCHA:" + captchaId
var cacheItem = ttlcache.SharedInt64Cache.Read(key)
ttlcache.SharedInt64Cache.Delete(key)
if cacheItem != nil {
// 清除计数
CaptchaDeleteCacheKey(req)
if cacheItem.Value == types.Int64(nonce) {
var life = CaptchaSeconds
if actionConfig.Life > 0 {
life = types.Int(actionConfig.Life)
}
// 加入到白名单
SharedIPWhiteList.RecordIP(wafutils.ComposeIPType(setId, req), actionConfig.Scope, req.WAFServerId(), req.WAFRemoteIP(), time.Now().Unix()+int64(life), policyId, false, groupId, setId, "")
req.ProcessResponseHeaders(writer.Header(), http.StatusSeeOther)
http.Redirect(writer, req.WAFRaw(), originURL, http.StatusSeeOther)
return false
}
} else {
// 增加计数
if !CaptchaIncreaseFails(req, actionConfig, policyId, groupId, setId, CaptchaPageCodeSubmit) {
return false
}
req.ProcessResponseHeaders(writer.Header(), http.StatusSeeOther)
http.Redirect(writer, req.WAFRaw(), req.WAFRaw().URL.String(), http.StatusSeeOther)
}
}
return true
}

View File

@@ -76,7 +76,8 @@ func (this *CC2Checkpoint) RequestValue(req requests.Request, param string, opti
} }
var ccKey = "WAF-CC-" + types.String(ruleId) + "-" + strings.Join(keyValues, "@") var ccKey = "WAF-CC-" + types.String(ruleId) + "-" + strings.Join(keyValues, "@")
value = counters.SharedCounter.IncreaseKey(ccKey, period) var ccValue = counters.SharedCounter.IncreaseKey(ccKey, period)
value = ccValue
// 基于指纹统计 // 基于指纹统计
var enableFingerprint = true var enableFingerprint = true
@@ -96,7 +97,7 @@ func (this *CC2Checkpoint) RequestValue(req requests.Request, param string, opti
} }
var fpCCKey = "WAF-CC-" + types.String(ruleId) + "-" + strings.Join(fpKeyValues, "@") var fpCCKey = "WAF-CC-" + types.String(ruleId) + "-" + strings.Join(fpKeyValues, "@")
var fpValue = counters.SharedCounter.IncreaseKey(fpCCKey, period) var fpValue = counters.SharedCounter.IncreaseKey(fpCCKey, period)
if fpValue > value.(uint64) { if fpValue > ccValue {
value = fpValue value = fpValue
} }
} }

View File

@@ -46,4 +46,7 @@ type Request interface {
// DisableAccessLog 在当前请求中不使用访问日志 // DisableAccessLog 在当前请求中不使用访问日志
DisableAccessLog() DisableAccessLog()
// DisableStat 在当前请求中停用统计
DisableStat()
} }

View File

@@ -85,6 +85,10 @@ func (this *TestRequest) DisableAccessLog() {
} }
func (this *TestRequest) DisableStat() {
}
func (this *TestRequest) ProcessResponseHeaders(headers http.Header, status int) { func (this *TestRequest) ProcessResponseHeaders(headers http.Header, status int) {
} }

View File

@@ -74,7 +74,7 @@ func (this *RuleGroup) RemoveRuleSet(id int64) {
this.RuleSets = result this.RuleSets = result
} }
func (this *RuleGroup) MatchRequest(req requests.Request) (b bool, hasRequestBody bool, set *RuleSet, err error) { func (this *RuleGroup) MatchRequest(req requests.Request) (b bool, hasRequestBody bool, resultSet *RuleSet, err error) {
if !this.hasRuleSets { if !this.hasRuleSets {
return return
} }
@@ -93,7 +93,7 @@ func (this *RuleGroup) MatchRequest(req requests.Request) (b bool, hasRequestBod
return return
} }
func (this *RuleGroup) MatchResponse(req requests.Request, resp *requests.Response) (b bool, hasRequestBody bool, set *RuleSet, err error) { func (this *RuleGroup) MatchResponse(req requests.Request, resp *requests.Response) (b bool, hasRequestBody bool, resultSet *RuleSet, err error) {
if !this.hasRuleSets { if !this.hasRuleSets {
return return
} }

View File

@@ -2,6 +2,7 @@ package waf
import ( import (
"bytes" "bytes"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
"github.com/TeaOSLab/EdgeNode/internal/waf/requests" "github.com/TeaOSLab/EdgeNode/internal/waf/requests"
"github.com/iwind/TeaGo/assert" "github.com/iwind/TeaGo/assert"
"github.com/iwind/TeaGo/lists" "github.com/iwind/TeaGo/lists"
@@ -49,7 +50,7 @@ func Test_Template2(t *testing.T) {
} }
now := time.Now() now := time.Now()
goNext, _, _, set, err := waf.MatchRequest(requests.NewTestRequest(req), nil) goNext, _, _, set, err := waf.MatchRequest(requests.NewTestRequest(req), nil, firewallconfigs.ServerCaptchaTypeNone)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -77,7 +78,7 @@ func BenchmarkTemplate(b *testing.B) {
b.Fatal(err) b.Fatal(err)
} }
_, _, _, _, _ = waf.MatchRequest(requests.NewTestRequest(req), nil) _, _, _, _, _ = waf.MatchRequest(requests.NewTestRequest(req), nil, firewallconfigs.ServerCaptchaTypeNone)
} }
} }
@@ -86,7 +87,7 @@ func testTemplate1001(a *assert.Assertion, t *testing.T, template *WAF) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
_, _, _, result, err := template.MatchRequest(requests.NewTestRequest(req), nil) _, _, _, result, err := template.MatchRequest(requests.NewTestRequest(req), nil, firewallconfigs.ServerCaptchaTypeNone)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -101,7 +102,7 @@ func testTemplate1002(a *assert.Assertion, t *testing.T, template *WAF) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
_, _, _, result, err := template.MatchRequest(requests.NewTestRequest(req), nil) _, _, _, result, err := template.MatchRequest(requests.NewTestRequest(req), nil, firewallconfigs.ServerCaptchaTypeNone)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -116,7 +117,7 @@ func testTemplate1003(a *assert.Assertion, t *testing.T, template *WAF) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
_, _, _, result, err := template.MatchRequest(requests.NewTestRequest(req), nil) _, _, _, result, err := template.MatchRequest(requests.NewTestRequest(req), nil, firewallconfigs.ServerCaptchaTypeNone)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -182,7 +183,7 @@ func testTemplate2001(a *assert.Assertion, t *testing.T, template *WAF) {
req.Header.Add("Content-Type", writer.FormDataContentType()) req.Header.Add("Content-Type", writer.FormDataContentType())
_, _, _, result, err := template.MatchRequest(requests.NewTestRequest(req), nil) _, _, _, result, err := template.MatchRequest(requests.NewTestRequest(req), nil, firewallconfigs.ServerCaptchaTypeNone)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -197,7 +198,7 @@ func testTemplate3001(a *assert.Assertion, t *testing.T, template *WAF) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
_, _, _, result, err := template.MatchRequest(requests.NewTestRequest(req), nil) _, _, _, result, err := template.MatchRequest(requests.NewTestRequest(req), nil, firewallconfigs.ServerCaptchaTypeNone)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -212,7 +213,7 @@ func testTemplate4001(a *assert.Assertion, t *testing.T, template *WAF) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
_, _, _, result, err := template.MatchRequest(requests.NewTestRequest(req), nil) _, _, _, result, err := template.MatchRequest(requests.NewTestRequest(req), nil, firewallconfigs.ServerCaptchaTypeNone)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -228,7 +229,7 @@ func testTemplate5001(a *assert.Assertion, t *testing.T, template *WAF) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
_, _, _, result, err := template.MatchRequest(requests.NewTestRequest(req), nil) _, _, _, result, err := template.MatchRequest(requests.NewTestRequest(req), nil, firewallconfigs.ServerCaptchaTypeNone)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -243,7 +244,7 @@ func testTemplate5001(a *assert.Assertion, t *testing.T, template *WAF) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
_, _, _, result, err := template.MatchRequest(requests.NewTestRequest(req), nil) _, _, _, result, err := template.MatchRequest(requests.NewTestRequest(req), nil, firewallconfigs.ServerCaptchaTypeNone)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -260,7 +261,7 @@ func testTemplate6001(a *assert.Assertion, t *testing.T, template *WAF) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
_, _, _, result, err := template.MatchRequest(requests.NewTestRequest(req), nil) _, _, _, result, err := template.MatchRequest(requests.NewTestRequest(req), nil, firewallconfigs.ServerCaptchaTypeNone)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -275,7 +276,7 @@ func testTemplate6001(a *assert.Assertion, t *testing.T, template *WAF) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
_, _, _, result, err := template.MatchRequest(requests.NewTestRequest(req), nil) _, _, _, result, err := template.MatchRequest(requests.NewTestRequest(req), nil, firewallconfigs.ServerCaptchaTypeNone)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -298,7 +299,7 @@ func testTemplate7001(a *assert.Assertion, t *testing.T, template *WAF) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
_, _, _, result, err := template.MatchRequest(requests.NewTestRequest(req), nil) _, _, _, result, err := template.MatchRequest(requests.NewTestRequest(req), nil, firewallconfigs.ServerCaptchaTypeNone)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -335,7 +336,7 @@ func testTemplate20001(a *assert.Assertion, t *testing.T, template *WAF) {
t.Fatal(err) t.Fatal(err)
} }
req.Header.Set("User-Agent", bot) req.Header.Set("User-Agent", bot)
_, _, _, result, err := template.MatchRequest(requests.NewTestRequest(req), nil) _, _, _, result, err := template.MatchRequest(requests.NewTestRequest(req), nil, firewallconfigs.ServerCaptchaTypeNone)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@@ -6,7 +6,10 @@ import (
"github.com/TeaOSLab/EdgeNode/internal/ttlcache" "github.com/TeaOSLab/EdgeNode/internal/ttlcache"
"github.com/TeaOSLab/EdgeNode/internal/utils/cachehits" "github.com/TeaOSLab/EdgeNode/internal/utils/cachehits"
"github.com/TeaOSLab/EdgeNode/internal/utils/fasttime" "github.com/TeaOSLab/EdgeNode/internal/utils/fasttime"
"github.com/TeaOSLab/EdgeNode/internal/waf/requests"
"github.com/cespare/xxhash" "github.com/cespare/xxhash"
"github.com/iwind/TeaGo/types"
stringutil "github.com/iwind/TeaGo/utils/string"
"strconv" "strconv"
) )
@@ -95,3 +98,8 @@ func MatchBytesCache(regex *re.Regexp, byteSlice []byte, cacheLife CacheLife) bo
cacheHits.IncreaseCached(regIdString) cacheHits.IncreaseCached(regIdString)
return b return b
} }
// ComposeIPType 组合IP类型
func ComposeIPType(setId int64, req requests.Request) string {
return "set:" + types.String(setId) + "@" + stringutil.Md5(req.WAFRaw().UserAgent())
}

View File

@@ -242,7 +242,7 @@ func (this *WAF) MoveOutboundRuleGroup(fromIndex int, toIndex int) {
this.Outbound = result this.Outbound = result
} }
func (this *WAF) MatchRequest(req requests.Request, writer http.ResponseWriter) (goNext bool, hasRequestBody bool, group *RuleGroup, sets *RuleSet, err error) { func (this *WAF) MatchRequest(req requests.Request, writer http.ResponseWriter, defaultCaptchaType firewallconfigs.ServerCaptchaType) (goNext bool, hasRequestBody bool, resultGroup *RuleGroup, resultSet *RuleSet, err error) {
if !this.hasInboundRules { if !this.hasInboundRules {
return true, hasRequestBody, nil, nil, nil return true, hasRequestBody, nil, nil, nil
} }
@@ -251,13 +251,15 @@ func (this *WAF) MatchRequest(req requests.Request, writer http.ResponseWriter)
var rawPath = req.WAFRaw().URL.Path var rawPath = req.WAFRaw().URL.Path
if rawPath == CaptchaPath { if rawPath == CaptchaPath {
req.DisableAccessLog() req.DisableAccessLog()
captchaValidator.Run(req, writer) req.DisableStat()
captchaValidator.Run(req, writer, defaultCaptchaType)
return return
} }
// Get 302验证 // Get 302验证
if rawPath == Get302Path { if rawPath == Get302Path {
req.DisableAccessLog() req.DisableAccessLog()
req.DisableStat()
get302Validator.Run(req, writer) get302Validator.Run(req, writer)
return return
} }
@@ -284,7 +286,7 @@ func (this *WAF) MatchRequest(req requests.Request, writer http.ResponseWriter)
return true, hasRequestBody, nil, nil, nil return true, hasRequestBody, nil, nil, nil
} }
func (this *WAF) MatchResponse(req requests.Request, rawResp *http.Response, writer http.ResponseWriter) (goNext bool, hasRequestBody bool, group *RuleGroup, set *RuleSet, err error) { func (this *WAF) MatchResponse(req requests.Request, rawResp *http.Response, writer http.ResponseWriter) (goNext bool, hasRequestBody bool, resultGroup *RuleGroup, resultSet *RuleSet, err error) {
if !this.hasOutboundRules { if !this.hasOutboundRules {
return true, hasRequestBody, nil, nil, nil return true, hasRequestBody, nil, nil, nil
} }
@@ -310,7 +312,7 @@ func (this *WAF) MatchResponse(req requests.Request, rawResp *http.Response, wri
return true, hasRequestBody, nil, nil, nil return true, hasRequestBody, nil, nil, nil
} }
// Save save to file path // Save to file path
func (this *WAF) Save(path string) error { func (this *WAF) Save(path string) error {
if len(path) == 0 { if len(path) == 0 {
return errors.New("path should not be empty") return errors.New("path should not be empty")
@@ -385,7 +387,7 @@ func (this *WAF) FindCheckpointInstance(prefix string) checkpoints.CheckpointInt
return nil return nil
} }
// Start start // Start
func (this *WAF) Start() { func (this *WAF) Start() {
for _, checkpoint := range this.checkpointsMap { for _, checkpoint := range this.checkpointsMap {
checkpoint.Start() checkpoint.Start()

View File

@@ -192,6 +192,7 @@ func (this *WAFManager) ConvertWAF(policy *firewallconfigs.HTTPFirewallPolicy) (
FailBlockTimeout: policy.CaptchaOptions.FailBlockTimeout, FailBlockTimeout: policy.CaptchaOptions.FailBlockTimeout,
FailBlockScopeAll: policy.CaptchaOptions.FailBlockScopeAll, FailBlockScopeAll: policy.CaptchaOptions.FailBlockScopeAll,
CountLetters: policy.CaptchaOptions.CountLetters, CountLetters: policy.CaptchaOptions.CountLetters,
CaptchaType: policy.CaptchaOptions.CaptchaType,
UIIsOn: policy.CaptchaOptions.UIIsOn, UIIsOn: policy.CaptchaOptions.UIIsOn,
UITitle: policy.CaptchaOptions.UITitle, UITitle: policy.CaptchaOptions.UITitle,
UIPrompt: policy.CaptchaOptions.UIPrompt, UIPrompt: policy.CaptchaOptions.UIPrompt,

View File

@@ -1,6 +1,7 @@
package waf package waf
import ( import (
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
"github.com/TeaOSLab/EdgeNode/internal/waf/requests" "github.com/TeaOSLab/EdgeNode/internal/waf/requests"
"github.com/iwind/TeaGo/assert" "github.com/iwind/TeaGo/assert"
"net/http" "net/http"
@@ -42,7 +43,7 @@ func TestWAF_MatchRequest(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
goNext, _, _, set, err := waf.MatchRequest(requests.NewTestRequest(req), nil) goNext, _, _, set, err := waf.MatchRequest(requests.NewTestRequest(req), nil, firewallconfigs.ServerCaptchaTypeNone)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }