Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
23a4b64e6d | ||
|
|
46a82f5988 | ||
|
|
1f37816a3a | ||
|
|
2301e74b1c | ||
|
|
a47d7d275c | ||
|
|
706519ac2e | ||
|
|
b8e193bc60 | ||
|
|
e44f9cc2fb | ||
|
|
c6823ae3a8 | ||
|
|
a18ebc0060 | ||
|
|
7cf41ace47 | ||
|
|
987350f0b4 | ||
|
|
ce7dda8cf5 | ||
|
|
af87cc9f16 | ||
|
|
d5f6acf690 | ||
|
|
92f541b0aa | ||
|
|
fb6fee8c60 | ||
|
|
75fe0bd8c6 | ||
|
|
8f1f5f5bb4 | ||
|
|
9fe6bc2dcc | ||
|
|
b254cfc1a7 | ||
|
|
f8e155887f | ||
|
|
c5a635d796 | ||
|
|
607fa58ece | ||
|
|
0d5540295f | ||
|
|
9ce516caeb |
@@ -3,7 +3,7 @@
|
||||
function build() {
|
||||
ROOT=$(dirname $0)
|
||||
NAME="edge-node"
|
||||
VERSION=$(lookup-version $ROOT/../internal/const/const.go)
|
||||
VERSION=$(lookup-version "$ROOT"/../internal/const/const.go)
|
||||
DIST=$ROOT/"../dist/${NAME}"
|
||||
MUSL_DIR="/usr/local/opt/musl-cross/bin"
|
||||
GCC_X86_64_DIR="/usr/local/Cellar/x86_64-unknown-linux-gnu/10.3.0/bin"
|
||||
@@ -13,21 +13,21 @@ function build() {
|
||||
ARCH=${2}
|
||||
TAG=${3}
|
||||
|
||||
if [ -z $OS ]; then
|
||||
if [ -z "$OS" ]; then
|
||||
echo "usage: build.sh OS ARCH"
|
||||
exit
|
||||
fi
|
||||
if [ -z $ARCH ]; then
|
||||
if [ -z "$ARCH" ]; then
|
||||
echo "usage: build.sh OS ARCH"
|
||||
exit
|
||||
fi
|
||||
if [ -z $TAG ]; then
|
||||
if [ -z "$TAG" ]; then
|
||||
TAG="community"
|
||||
fi
|
||||
|
||||
echo "checking ..."
|
||||
ZIP_PATH=$(which zip)
|
||||
if [ -z $ZIP_PATH ]; then
|
||||
if [ -z "$ZIP_PATH" ]; then
|
||||
echo "we need 'zip' command to compress files"
|
||||
exit
|
||||
fi
|
||||
@@ -36,28 +36,28 @@ function build() {
|
||||
ZIP="${NAME}-${OS}-${ARCH}-${TAG}-v${VERSION}.zip"
|
||||
|
||||
echo "copying ..."
|
||||
if [ ! -d $DIST ]; then
|
||||
mkdir $DIST
|
||||
mkdir $DIST/bin
|
||||
mkdir $DIST/configs
|
||||
mkdir $DIST/logs
|
||||
mkdir $DIST/data
|
||||
if [ ! -d "$DIST" ]; then
|
||||
mkdir "$DIST"
|
||||
mkdir "$DIST"/bin
|
||||
mkdir "$DIST"/configs
|
||||
mkdir "$DIST"/logs
|
||||
mkdir "$DIST"/data
|
||||
|
||||
if [ "$TAG" = "plus" ]; then
|
||||
mkdir $DIST/scripts
|
||||
mkdir $DIST/scripts/js
|
||||
mkdir "$DIST"/scripts
|
||||
mkdir "$DIST"/scripts/js
|
||||
fi
|
||||
fi
|
||||
|
||||
cp $ROOT/configs/api.template.yaml $DIST/configs
|
||||
cp -R $ROOT/www $DIST/
|
||||
cp -R $ROOT/pages $DIST/
|
||||
cp -R $ROOT/resources $DIST/
|
||||
cp "$ROOT"/configs/api.template.yaml "$DIST"/configs
|
||||
cp -R "$ROOT"/www "$DIST"/
|
||||
cp -R "$ROOT"/pages "$DIST"/
|
||||
cp -R "$ROOT"/resources "$DIST"/
|
||||
|
||||
# we support TOA on linux/amd64 only
|
||||
if [ $OS == "linux" -a $ARCH == "amd64" ]
|
||||
if [ "$OS" == "linux" -a "$ARCH" == "amd64" ]
|
||||
then
|
||||
cp -R $ROOT/edge-toa $DIST
|
||||
cp -R "$ROOT"/edge-toa "$DIST"
|
||||
fi
|
||||
|
||||
echo "building ..."
|
||||
@@ -112,14 +112,14 @@ function build() {
|
||||
fi
|
||||
fi
|
||||
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 -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 go build -trimpath -tags $BUILD_TAG -o "$DIST"/bin/${NAME} -ldflags "-linkmode external -extldflags -static -s -w" "$ROOT"/../cmd/edge-node/main.go
|
||||
else
|
||||
env GOOS=${OS} GOARCH=${ARCH} CGO_ENABLED=1 go build -tags $TAG -o $DIST/bin/${NAME} -ldflags="-s -w" $ROOT/../cmd/edge-node/main.go
|
||||
env GOOS="${OS}" GOARCH="${ARCH}" CGO_ENABLED=1 go build -trimpath -tags $TAG -o "$DIST"/bin/${NAME} -ldflags="-s -w" "$ROOT"/../cmd/edge-node/main.go
|
||||
fi
|
||||
|
||||
# delete hidden files
|
||||
find $DIST -name ".DS_Store" -delete
|
||||
find $DIST -name ".gitignore" -delete
|
||||
find "$DIST" -name ".DS_Store" -delete
|
||||
find "$DIST" -name ".gitignore" -delete
|
||||
|
||||
echo "zip files"
|
||||
cd "${DIST}/../" || exit
|
||||
@@ -135,15 +135,15 @@ function build() {
|
||||
|
||||
function lookup-version() {
|
||||
FILE=$1
|
||||
VERSION_DATA=$(cat $FILE)
|
||||
VERSION_DATA=$(cat "$FILE")
|
||||
re="Version[ ]+=[ ]+\"([0-9.]+)\""
|
||||
if [[ $VERSION_DATA =~ $re ]]; then
|
||||
VERSION=${BASH_REMATCH[1]}
|
||||
echo $VERSION
|
||||
echo "$VERSION"
|
||||
else
|
||||
echo "could not match version"
|
||||
exit
|
||||
fi
|
||||
}
|
||||
|
||||
build $1 $2 $3
|
||||
build "$1" "$2" "$3"
|
||||
|
||||
3
dist/.gitignore
vendored
3
dist/.gitignore
vendored
@@ -1 +1,2 @@
|
||||
*.zip
|
||||
*.zip
|
||||
edge-node
|
||||
34
go.mod
34
go.mod
@@ -1,6 +1,6 @@
|
||||
module github.com/TeaOSLab/EdgeNode
|
||||
|
||||
go 1.15
|
||||
go 1.18
|
||||
|
||||
replace (
|
||||
github.com/TeaOSLab/EdgeCommon => ../EdgeCommon
|
||||
@@ -11,7 +11,6 @@ require (
|
||||
github.com/andybalholm/brotli v1.0.4
|
||||
github.com/biessek/golang-ico v0.0.0-20180326222316-d348d9ea4670
|
||||
github.com/cespare/xxhash v1.1.0
|
||||
github.com/chai2010/webp v1.1.0 // indirect
|
||||
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f
|
||||
github.com/fsnotify/fsnotify v1.5.1
|
||||
github.com/go-redis/redis/v8 v8.11.5
|
||||
@@ -21,10 +20,9 @@ require (
|
||||
github.com/iwind/gofcgi v0.0.0-20210528023741-a92711d45f11
|
||||
github.com/iwind/gosock v0.0.0-20211103081026-ee4652210ca4
|
||||
github.com/iwind/gowebp v0.0.0-20211029040624-7331ecc78ed8
|
||||
github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e // indirect
|
||||
github.com/klauspost/compress v1.15.2 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/klauspost/compress v1.15.8
|
||||
github.com/mattn/go-sqlite3 v1.14.9
|
||||
github.com/mdlayher/netlink v1.4.2
|
||||
github.com/miekg/dns v1.1.43
|
||||
github.com/mssola/user_agent v0.5.3
|
||||
github.com/pires/go-proxyproto v0.6.1
|
||||
@@ -34,6 +32,30 @@ require (
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad
|
||||
golang.org/x/text v0.3.7
|
||||
google.golang.org/grpc v1.45.0
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v0.4.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/chai2010/webp v1.1.0 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/google/go-cmp v0.5.7 // indirect
|
||||
github.com/josharian/native v0.0.0-20200817173448-b6b71def0850 // indirect
|
||||
github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||
github.com/mdlayher/socket v0.0.0-20211102153432-57e3fa563ecb // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.9 // indirect
|
||||
github.com/tklauser/numcpus v0.3.0 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||
golang.org/x/mod v0.5.1 // indirect
|
||||
golang.org/x/tools v0.1.8 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
google.golang.org/genproto v0.0.0-20220317150908-0efb43f6373e // indirect
|
||||
google.golang.org/protobuf v1.27.1 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
honnef.co/go/tools v0.2.2 // indirect
|
||||
)
|
||||
|
||||
40
go.sum
40
go.sum
@@ -22,11 +22,7 @@ github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cb
|
||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chai2010/webp v1.1.0 h1:4Ei0/BRroMF9FaXDG2e4OxwFcuW2vcXd+A6tyqTJUQQ=
|
||||
github.com/chai2010/webp v1.1.0/go.mod h1:LP12PG5IFmLGHUU26tBiCBKnghxx3toZFwDjOYvd3Ow=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/cilium/ebpf v0.5.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=
|
||||
github.com/cilium/ebpf v0.7.0 h1:1k/q3ATgxSXRdrmPfH8d7YK0GfqVsEKZAX9dQZvs56k=
|
||||
github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
@@ -50,12 +46,7 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/frankban/quicktest v1.11.3 h1:8sXhOn0uLys67V8EsXLc6eszDs8VXWxL3iRvebPhedY=
|
||||
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
|
||||
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||
@@ -65,7 +56,6 @@ github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC
|
||||
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
|
||||
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
@@ -96,17 +86,13 @@ github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/nftables v0.0.0-20220407195405-950e408d48c6 h1:btadZscaRmsi/+fOhkyUguRpSnrf6dykNEWxDeUCj9I=
|
||||
github.com/google/nftables v0.0.0-20220407195405-950e408d48c6/go.mod h1:0F8on3JWMkm+xahTHItkiu/E1SPqMd0TOxNweQv8ptE=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/iwind/TeaGo v0.0.0-20220304043459-0dd944a5b475 h1:EseyfFaQOjWanGiby9KMw7PjDBMg/95tLDgIw/ns0Cw=
|
||||
github.com/iwind/TeaGo v0.0.0-20220304043459-0dd944a5b475/go.mod h1:HRHK0zoC/og3c9/hKosD9yYVMTnnzm3PgXUdhRYHaLc=
|
||||
github.com/iwind/gofcgi v0.0.0-20210528023741-a92711d45f11 h1:DaQjoWZhLNxjhIXedVg4/vFEtHkZhK4IjIwsWdyzBLg=
|
||||
github.com/iwind/gofcgi v0.0.0-20210528023741-a92711d45f11/go.mod h1:JtbX20untAjUVjZs1ZBtq80f5rJWvwtQNRL6EnuYRnY=
|
||||
github.com/iwind/gosock v0.0.0-20210722083328-12b2d66abec3 h1:aBSonas7vFcgTj9u96/bWGILGv1ZbUSTLiOzcI1ZT6c=
|
||||
github.com/iwind/gosock v0.0.0-20210722083328-12b2d66abec3/go.mod h1:H5Q7SXwbx3a97ecJkaS2sD77gspzE7HFUafBO0peEyA=
|
||||
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/gowebp v0.0.0-20211029040624-7331ecc78ed8 h1:AojsHz9Es9B3He2MQQxeRq3TyD//o9huxUo7r1wh44g=
|
||||
@@ -126,8 +112,10 @@ github.com/jsimonetti/rtnetlink v0.0.0-20211022192332-93da33804786/go.mod h1:v4h
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
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/klauspost/compress v1.15.2 h1:3WH+AG7s2+T8o3nrM/8u2rdqUEcQhmga7smjrT41nAw=
|
||||
github.com/klauspost/compress v1.15.2/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
|
||||
github.com/klauspost/compress v1.15.6 h1:6D9PcO8QWu0JyaQ2zUMmu16T1T+zjjEpP91guRsvDfY=
|
||||
github.com/klauspost/compress v1.15.6/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
|
||||
github.com/klauspost/compress v1.15.8 h1:JahtItbkWjf2jzm/T+qgMxkP9EMHsqEUA6vCMGmXvhA=
|
||||
github.com/klauspost/compress v1.15.8/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
@@ -169,22 +157,15 @@ github.com/mssola/user_agent v0.5.3/go.mod h1:TTPno8LPY3wAIEKRpAtkdMT0f8SE24pLRG
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
||||
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||
github.com/onsi/ginkgo/v2 v2.0.0 h1:CcuG/HvWNkkaqCUpJifQY8z7qEMBJya6aLPx6ftGyjQ=
|
||||
github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
|
||||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
||||
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
|
||||
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
|
||||
github.com/opentracing/opentracing-go v1.1.1-0.20190913142402-a7454ce5950e/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/pires/go-proxyproto v0.6.1 h1:EBupykFmo22SDjv4fQVQd2J9NOoLPmyZA/15ldOGkPw=
|
||||
github.com/pires/go-proxyproto v0.6.1/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY=
|
||||
@@ -259,7 +240,6 @@ golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v
|
||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
@@ -288,11 +268,9 @@ golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -303,7 +281,6 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201218084310-7d0127a74742/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210110051926-789bb1bd4061/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210123111255-9b0068b26619/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -314,8 +291,6 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@@ -324,14 +299,10 @@ golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8 h1:OH54vjqzRWmbJ62fjuhxy7AxFFgoHN0/DPc/UrL8cAs=
|
||||
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0=
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/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-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
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.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
@@ -346,7 +317,6 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
||||
golang.org/x/tools v0.1.8 h1:P1HhGGuLW4aAclzjtmJdf0mJOjVUZUzOTqkAkWL+l6w=
|
||||
@@ -402,7 +372,6 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
@@ -411,4 +380,3 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
|
||||
honnef.co/go/tools v0.2.1/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY=
|
||||
honnef.co/go/tools v0.2.2 h1:MNh1AVMyVX23VUHE2O27jm6lNj3vjO5DexS4A1xvnzk=
|
||||
honnef.co/go/tools v0.2.2/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY=
|
||||
rogchap.com/v8go v0.7.0/go.mod h1:MxgP3pL2MW4dpme/72QRs8sgNMmM0pRc8DPhcuLWPAs=
|
||||
|
||||
@@ -3,44 +3,24 @@
|
||||
package caches
|
||||
|
||||
import (
|
||||
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
minOpenFilesValue int32 = 4
|
||||
maxOpenFilesValue int32 = 65535
|
||||
|
||||
modeSlow int32 = 1
|
||||
modeFast int32 = 2
|
||||
)
|
||||
|
||||
// MaxOpenFiles max open files manager
|
||||
type MaxOpenFiles struct {
|
||||
step int32
|
||||
maxOpenFiles int32
|
||||
ptr *int32
|
||||
ticker *time.Ticker
|
||||
mode int32
|
||||
|
||||
lastOpens int32
|
||||
currentOpens int32
|
||||
ticker *time.Ticker
|
||||
mode int32
|
||||
}
|
||||
|
||||
func NewMaxOpenFiles(step int32) *MaxOpenFiles {
|
||||
if step <= 0 {
|
||||
step = 2
|
||||
}
|
||||
var f = &MaxOpenFiles{
|
||||
step: step,
|
||||
maxOpenFiles: minOpenFilesValue,
|
||||
}
|
||||
if teaconst.DiskIsFast {
|
||||
f.maxOpenFiles = 32
|
||||
}
|
||||
f.ptr = &f.maxOpenFiles
|
||||
func NewMaxOpenFiles() *MaxOpenFiles {
|
||||
var f = &MaxOpenFiles{}
|
||||
f.ticker = time.NewTicker(1 * time.Second)
|
||||
f.init()
|
||||
return f
|
||||
@@ -49,50 +29,24 @@ func NewMaxOpenFiles(step int32) *MaxOpenFiles {
|
||||
func (this *MaxOpenFiles) init() {
|
||||
goman.New(func() {
|
||||
for range this.ticker.C {
|
||||
var mod = atomic.LoadInt32(&this.mode)
|
||||
switch mod {
|
||||
case modeSlow:
|
||||
// we decrease more quickly, with more steps
|
||||
if atomic.AddInt32(this.ptr, -this.step*2) <= 0 {
|
||||
atomic.StoreInt32(this.ptr, minOpenFilesValue)
|
||||
}
|
||||
case modeFast:
|
||||
// we increase only when file opens increases
|
||||
var currentOpens = atomic.LoadInt32(&this.currentOpens)
|
||||
if currentOpens > this.lastOpens {
|
||||
if atomic.AddInt32(this.ptr, this.step) >= maxOpenFilesValue {
|
||||
atomic.StoreInt32(this.ptr, maxOpenFilesValue)
|
||||
}
|
||||
}
|
||||
this.lastOpens = currentOpens
|
||||
atomic.StoreInt32(&this.currentOpens, 0)
|
||||
}
|
||||
|
||||
// reset mode
|
||||
atomic.StoreInt32(&this.mode, 0)
|
||||
atomic.StoreInt32(&this.mode, modeFast)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (this *MaxOpenFiles) Fast() {
|
||||
if atomic.LoadInt32(&this.mode) == 0 {
|
||||
this.mode = modeFast
|
||||
}
|
||||
atomic.AddInt32(&this.currentOpens, 1)
|
||||
atomic.AddInt32(&this.mode, modeFast)
|
||||
}
|
||||
|
||||
func (this *MaxOpenFiles) FinishAll() {
|
||||
this.Fast()
|
||||
}
|
||||
|
||||
func (this *MaxOpenFiles) Slow() {
|
||||
atomic.StoreInt32(&this.mode, modeSlow)
|
||||
}
|
||||
|
||||
func (this *MaxOpenFiles) Max() int32 {
|
||||
if atomic.LoadInt32(&this.mode) == modeSlow {
|
||||
return 0
|
||||
}
|
||||
|
||||
var v = atomic.LoadInt32(this.ptr)
|
||||
if v <= minOpenFilesValue {
|
||||
return minOpenFilesValue
|
||||
}
|
||||
return v
|
||||
func (this *MaxOpenFiles) Next() bool {
|
||||
return atomic.LoadInt32(&this.mode) != modeSlow
|
||||
}
|
||||
|
||||
@@ -9,20 +9,27 @@ import (
|
||||
)
|
||||
|
||||
func TestNewMaxOpenFiles(t *testing.T) {
|
||||
var maxOpenFiles = caches.NewMaxOpenFiles(2)
|
||||
var maxOpenFiles = caches.NewMaxOpenFiles()
|
||||
maxOpenFiles.Fast()
|
||||
t.Log(maxOpenFiles.Max())
|
||||
t.Log("fast:", maxOpenFiles.Next())
|
||||
|
||||
maxOpenFiles.Slow()
|
||||
t.Log("slow:", maxOpenFiles.Next())
|
||||
time.Sleep(1*time.Second + 1*time.Millisecond)
|
||||
t.Log("slow 1 second:", maxOpenFiles.Next())
|
||||
|
||||
maxOpenFiles.Slow()
|
||||
t.Log("slow:", maxOpenFiles.Next())
|
||||
|
||||
maxOpenFiles.Slow()
|
||||
t.Log("slow:", maxOpenFiles.Next())
|
||||
|
||||
maxOpenFiles.Fast()
|
||||
time.Sleep(1 * time.Second)
|
||||
t.Log(maxOpenFiles.Max())
|
||||
t.Log("slow 1 second:", maxOpenFiles.Next())
|
||||
|
||||
maxOpenFiles.Slow()
|
||||
t.Log(maxOpenFiles.Max())
|
||||
t.Log("slow:", maxOpenFiles.Next())
|
||||
|
||||
maxOpenFiles.Slow()
|
||||
t.Log(maxOpenFiles.Max())
|
||||
|
||||
maxOpenFiles.Slow()
|
||||
t.Log(maxOpenFiles.Max())
|
||||
maxOpenFiles.Fast()
|
||||
t.Log("fast:", maxOpenFiles.Next())
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ const (
|
||||
var sharedWritingFileKeyMap = map[string]zero.Zero{} // key => bool
|
||||
var sharedWritingFileKeyLocker = sync.Mutex{}
|
||||
|
||||
var maxOpenFiles = NewMaxOpenFiles(2)
|
||||
var maxOpenFiles = NewMaxOpenFiles()
|
||||
|
||||
const maxOpenFilesSlowCost = 500 * time.Microsecond // 0.5ms
|
||||
|
||||
@@ -428,7 +428,7 @@ func (this *FileStorage) openWriter(key string, expiredAt int64, status int, siz
|
||||
return nil, ErrFileIsWriting
|
||||
}
|
||||
|
||||
if !isFlushing && len(sharedWritingFileKeyMap) >= int(maxOpenFiles.Max()) {
|
||||
if !isFlushing && !maxOpenFiles.Next() {
|
||||
sharedWritingFileKeyLocker.Unlock()
|
||||
return nil, ErrTooManyOpenFiles
|
||||
}
|
||||
@@ -439,6 +439,9 @@ func (this *FileStorage) openWriter(key string, expiredAt int64, status int, siz
|
||||
if !isOk {
|
||||
sharedWritingFileKeyLocker.Lock()
|
||||
delete(sharedWritingFileKeyMap, key)
|
||||
if len(sharedWritingFileKeyMap) == 0 {
|
||||
maxOpenFiles.FinishAll()
|
||||
}
|
||||
sharedWritingFileKeyLocker.Unlock()
|
||||
}
|
||||
}()
|
||||
@@ -608,12 +611,18 @@ func (this *FileStorage) openWriter(key string, expiredAt int64, status int, siz
|
||||
return NewPartialFileWriter(writer, key, expiredAt, isNewCreated, isPartial, partialBodyOffset, ranges, func() {
|
||||
sharedWritingFileKeyLocker.Lock()
|
||||
delete(sharedWritingFileKeyMap, key)
|
||||
if len(sharedWritingFileKeyMap) == 0 {
|
||||
maxOpenFiles.FinishAll()
|
||||
}
|
||||
sharedWritingFileKeyLocker.Unlock()
|
||||
}), nil
|
||||
} else {
|
||||
return NewFileWriter(this, writer, key, expiredAt, -1, func() {
|
||||
sharedWritingFileKeyLocker.Lock()
|
||||
delete(sharedWritingFileKeyMap, key)
|
||||
if len(sharedWritingFileKeyMap) == 0 {
|
||||
maxOpenFiles.FinishAll()
|
||||
}
|
||||
sharedWritingFileKeyLocker.Unlock()
|
||||
}), nil
|
||||
}
|
||||
|
||||
20
internal/compressions/reader_pool_zstd.go
Normal file
20
internal/compressions/reader_pool_zstd.go
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package compressions
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||
"io"
|
||||
)
|
||||
|
||||
var sharedZSTDReaderPool *ReaderPool
|
||||
|
||||
func init() {
|
||||
var maxSize = utils.SystemMemoryGB() * 256
|
||||
if maxSize == 0 {
|
||||
maxSize = 256
|
||||
}
|
||||
sharedZSTDReaderPool = NewReaderPool(maxSize, func(reader io.Reader) (Reader, error) {
|
||||
return newZSTDReader(reader)
|
||||
})
|
||||
}
|
||||
45
internal/compressions/reader_zstd.go
Normal file
45
internal/compressions/reader_zstd.go
Normal file
@@ -0,0 +1,45 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package compressions
|
||||
|
||||
import (
|
||||
"github.com/klauspost/compress/zstd"
|
||||
"io"
|
||||
)
|
||||
|
||||
type ZSTDReader struct {
|
||||
BaseReader
|
||||
|
||||
reader *zstd.Decoder
|
||||
}
|
||||
|
||||
func NewZSTDReader(reader io.Reader) (Reader, error) {
|
||||
return sharedZSTDReaderPool.Get(reader)
|
||||
}
|
||||
|
||||
func newZSTDReader(reader io.Reader) (Reader, error) {
|
||||
r, err := zstd.NewReader(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ZSTDReader{
|
||||
reader: r,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (this *ZSTDReader) Read(p []byte) (n int, err error) {
|
||||
return this.reader.Read(p)
|
||||
}
|
||||
|
||||
func (this *ZSTDReader) Reset(reader io.Reader) error {
|
||||
return this.reader.Reset(reader)
|
||||
}
|
||||
|
||||
func (this *ZSTDReader) RawClose() error {
|
||||
this.reader.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *ZSTDReader) Close() error {
|
||||
return this.Finish(this)
|
||||
}
|
||||
106
internal/compressions/reader_zstd_test.go
Normal file
106
internal/compressions/reader_zstd_test.go
Normal file
@@ -0,0 +1,106 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package compressions_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/compressions"
|
||||
"github.com/iwind/TeaGo/rands"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"io"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestZSTDReader(t *testing.T) {
|
||||
for _, testString := range []string{"Hello", "World", "Ni", "Hao"} {
|
||||
t.Log("===", testString, "===")
|
||||
var buf = &bytes.Buffer{}
|
||||
writer, err := compressions.NewZSTDWriter(buf, 5)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = writer.Write([]byte(testString))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = writer.Close()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
reader, err := compressions.NewZSTDReader(buf)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var data = make([]byte, 4096)
|
||||
for {
|
||||
n, err := reader.Read(data)
|
||||
if n > 0 {
|
||||
t.Log(string(data[:n]))
|
||||
}
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
err = reader.Close()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkZSTDReader(b *testing.B) {
|
||||
var randomData = func() []byte {
|
||||
var b = strings.Builder{}
|
||||
for i := 0; i < 1024; i++ {
|
||||
b.WriteString(types.String(rands.Int64() % 10))
|
||||
}
|
||||
return []byte(b.String())
|
||||
}
|
||||
|
||||
var buf = &bytes.Buffer{}
|
||||
writer, err := compressions.NewZSTDWriter(buf, 5)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
_, err = writer.Write(randomData())
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
err = writer.Close()
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
var newBytes = make([]byte, buf.Len())
|
||||
copy(newBytes, buf.Bytes())
|
||||
reader, err := compressions.NewZSTDReader(bytes.NewReader(newBytes))
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
var data = make([]byte, 4096)
|
||||
for {
|
||||
n, err := reader.Read(data)
|
||||
if n > 0 {
|
||||
_ = data[:n]
|
||||
}
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
err = reader.Close()
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,13 +14,19 @@ const (
|
||||
ContentEncodingBr ContentEncoding = "br"
|
||||
ContentEncodingGzip ContentEncoding = "gzip"
|
||||
ContentEncodingDeflate ContentEncoding = "deflate"
|
||||
ContentEncodingZSTD ContentEncoding = "zstd"
|
||||
)
|
||||
|
||||
var ErrNotSupportedContentEncoding = errors.New("not supported content encoding")
|
||||
|
||||
// AllEncodings 当前支持的所有编码
|
||||
func AllEncodings() []ContentEncoding {
|
||||
return []ContentEncoding{ContentEncodingBr, ContentEncodingGzip, ContentEncodingDeflate}
|
||||
return []ContentEncoding{
|
||||
ContentEncodingBr,
|
||||
ContentEncodingGzip,
|
||||
ContentEncodingZSTD,
|
||||
ContentEncodingDeflate,
|
||||
}
|
||||
}
|
||||
|
||||
// NewReader 获取Reader
|
||||
@@ -32,6 +38,8 @@ func NewReader(reader io.Reader, contentEncoding ContentEncoding) (Reader, error
|
||||
return NewGzipReader(reader)
|
||||
case ContentEncodingDeflate:
|
||||
return NewDeflateReader(reader)
|
||||
case ContentEncodingZSTD:
|
||||
return NewZSTDReader(reader)
|
||||
}
|
||||
return nil, ErrNotSupportedContentEncoding
|
||||
}
|
||||
@@ -45,6 +53,8 @@ func NewWriter(writer io.Writer, compressType serverconfigs.HTTPCompressionType,
|
||||
return NewDeflateWriter(writer, level)
|
||||
case serverconfigs.HTTPCompressionTypeBrotli:
|
||||
return NewBrotliWriter(writer, level)
|
||||
case serverconfigs.HTTPCompressionTypeZSTD:
|
||||
return NewZSTDWriter(writer, level)
|
||||
}
|
||||
return nil, errors.New("invalid compression type '" + compressType + "'")
|
||||
}
|
||||
|
||||
21
internal/compressions/writer_pool_zstd.go
Normal file
21
internal/compressions/writer_pool_zstd.go
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package compressions
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||
"github.com/klauspost/compress/zstd"
|
||||
"io"
|
||||
)
|
||||
|
||||
var sharedZSTDWriterPool *WriterPool
|
||||
|
||||
func init() {
|
||||
var maxSize = utils.SystemMemoryGB() * 256
|
||||
if maxSize == 0 {
|
||||
maxSize = 256
|
||||
}
|
||||
sharedZSTDWriterPool = NewWriterPool(maxSize, int(zstd.SpeedBestCompression), func(writer io.Writer, level int) (Writer, error) {
|
||||
return newZSTDWriter(writer, level)
|
||||
})
|
||||
}
|
||||
57
internal/compressions/writer_zstd.go
Normal file
57
internal/compressions/writer_zstd.go
Normal file
@@ -0,0 +1,57 @@
|
||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package compressions
|
||||
|
||||
import (
|
||||
"github.com/klauspost/compress/zstd"
|
||||
"io"
|
||||
)
|
||||
|
||||
type ZSTDWriter struct {
|
||||
BaseWriter
|
||||
|
||||
writer *zstd.Encoder
|
||||
level int
|
||||
}
|
||||
|
||||
func NewZSTDWriter(writer io.Writer, level int) (Writer, error) {
|
||||
return sharedZSTDWriterPool.Get(writer, level)
|
||||
}
|
||||
|
||||
func newZSTDWriter(writer io.Writer, level int) (Writer, error) {
|
||||
var zstdLevel = zstd.EncoderLevelFromZstd(level)
|
||||
|
||||
zstdWriter, err := zstd.NewWriter(writer, zstd.WithEncoderLevel(zstdLevel))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &ZSTDWriter{
|
||||
writer: zstdWriter,
|
||||
level: level,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (this *ZSTDWriter) Write(p []byte) (int, error) {
|
||||
return this.writer.Write(p)
|
||||
}
|
||||
|
||||
func (this *ZSTDWriter) Flush() error {
|
||||
return this.writer.Flush()
|
||||
}
|
||||
|
||||
func (this *ZSTDWriter) Reset(writer io.Writer) {
|
||||
this.writer.Reset(writer)
|
||||
}
|
||||
|
||||
func (this *ZSTDWriter) RawClose() error {
|
||||
return this.writer.Close()
|
||||
}
|
||||
|
||||
func (this *ZSTDWriter) Close() error {
|
||||
return this.Finish(this)
|
||||
}
|
||||
|
||||
func (this *ZSTDWriter) Level() int {
|
||||
return this.level
|
||||
}
|
||||
82
internal/compressions/writer_zstd_test.go
Normal file
82
internal/compressions/writer_zstd_test.go
Normal file
@@ -0,0 +1,82 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||
|
||||
package compressions_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/compressions"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewZSTDWriter(t *testing.T) {
|
||||
var buf = &bytes.Buffer{}
|
||||
writer, err := compressions.NewZSTDWriter(buf, 10)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var originData = []byte(strings.Repeat("Hello", 1024))
|
||||
_, err = writer.Write(originData)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = writer.Close()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log("origin data:", len(originData), "result:", buf.Len())
|
||||
}
|
||||
|
||||
func BenchmarkZSTDWriter_Write(b *testing.B) {
|
||||
var data = []byte(strings.Repeat("A", 1024))
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
var buf = &bytes.Buffer{}
|
||||
writer, err := compressions.NewZSTDWriter(buf, 5)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
for j := 0; j < 100; j++ {
|
||||
_, err = writer.Write(data)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
/**err = writer.Flush()
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}**/
|
||||
}
|
||||
|
||||
_ = writer.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkZSTDWriter_Write_Parallel(b *testing.B) {
|
||||
var data = []byte(strings.Repeat("A", 1024))
|
||||
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
var buf = &bytes.Buffer{}
|
||||
writer, err := compressions.NewZSTDWriter(buf, 5)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
for j := 0; j < 100; j++ {
|
||||
_, err = writer.Write(data)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
/**err = writer.Flush()
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}**/
|
||||
}
|
||||
|
||||
_ = writer.Close()
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package teaconst
|
||||
|
||||
const (
|
||||
Version = "0.4.8.1"
|
||||
Version = "0.4.9"
|
||||
|
||||
ProductName = "Edge Node"
|
||||
ProcessName = "edge-node"
|
||||
|
||||
@@ -33,7 +33,7 @@ func init() {
|
||||
|
||||
nodeConfig, _ := nodeconfigs.SharedNodeConfig()
|
||||
if nodeConfig != nil {
|
||||
err := SharedDDoSProtectionManager.Apply(nodeConfig.DDOSProtection)
|
||||
err := SharedDDoSProtectionManager.Apply(nodeConfig.DDoSProtection)
|
||||
if err != nil {
|
||||
remotelogs.Error("FIREWALL", "apply DDoS protection failed: "+err.Error())
|
||||
}
|
||||
@@ -43,7 +43,7 @@ func init() {
|
||||
events.On(events.EventNFTablesReady, func() {
|
||||
nodeConfig, _ := nodeconfigs.SharedNodeConfig()
|
||||
if nodeConfig != nil {
|
||||
err := SharedDDoSProtectionManager.Apply(nodeConfig.DDOSProtection)
|
||||
err := SharedDDoSProtectionManager.Apply(nodeConfig.DDoSProtection)
|
||||
if err != nil {
|
||||
remotelogs.Error("FIREWALL", "apply DDoS protection failed: "+err.Error())
|
||||
}
|
||||
|
||||
@@ -5,14 +5,23 @@ package iplibrary
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||
"github.com/iwind/TeaGo/Tea"
|
||||
)
|
||||
|
||||
// AllowIP 检查IP是否被允许访问
|
||||
// 如果一个IP不在任何名单中,则允许访问
|
||||
func AllowIP(ip string, serverId int64) (canGoNext bool, inAllowList bool) {
|
||||
// 放行lo
|
||||
if ip == "127.0.0.1" {
|
||||
return true, true
|
||||
if !Tea.IsTesting() { // 如果在测试环境,我们不加入一些白名单,以便于可以在本地和局域网正常测试
|
||||
// 放行lo
|
||||
if ip == "127.0.0.1" || ip == "::1" {
|
||||
return true, true
|
||||
}
|
||||
|
||||
// check node
|
||||
nodeConfig, err := nodeconfigs.SharedNodeConfig()
|
||||
if err == nil && nodeConfig.IPIsAutoAllowed(ip) {
|
||||
return true, true
|
||||
}
|
||||
}
|
||||
|
||||
var ipLong = utils.IP2Long(ip)
|
||||
@@ -20,12 +29,6 @@ func AllowIP(ip string, serverId int64) (canGoNext bool, inAllowList bool) {
|
||||
return false, false
|
||||
}
|
||||
|
||||
// check node
|
||||
nodeConfig, err := nodeconfigs.SharedNodeConfig()
|
||||
if err == nil && nodeConfig.IPIsAutoAllowed(ip) {
|
||||
return true, true
|
||||
}
|
||||
|
||||
// check white lists
|
||||
if GlobalWhiteIPList.Contains(ipLong) {
|
||||
return true, true
|
||||
|
||||
@@ -210,7 +210,7 @@ func (this *Task) Start() error {
|
||||
var tr = trackers.Begin("[METRIC]UPLOAD_STATS")
|
||||
err := this.Upload(1 * time.Second)
|
||||
tr.End()
|
||||
if err != nil {
|
||||
if err != nil && !rpc.IsConnError(err) {
|
||||
remotelogs.Error("METRIC", "upload stats failed: "+err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -406,7 +406,7 @@ func (this *APIStream) handleCheckLocalFirewall(message *pb.NodeStreamMessage) e
|
||||
"version": version,
|
||||
}
|
||||
|
||||
var protectionConfig = sharedNodeConfig.DDOSProtection
|
||||
var protectionConfig = sharedNodeConfig.DDoSProtection
|
||||
err = firewalls.SharedDDoSProtectionManager.Apply(protectionConfig)
|
||||
if err != nil {
|
||||
this.replyFail(message.RequestId, dataMessage.Name+"was installed, but apply DDoS protection config failed: "+err.Error())
|
||||
|
||||
@@ -7,12 +7,14 @@ import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
||||
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/iplibrary"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/stats"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/ttlcache"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/waf"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
@@ -26,6 +28,8 @@ type ClientConn struct {
|
||||
hasDeadline bool
|
||||
hasRead bool
|
||||
|
||||
isLO bool // 是否为环路
|
||||
|
||||
hasResetSYNFlood bool
|
||||
|
||||
BaseClientConn
|
||||
@@ -41,10 +45,28 @@ func NewClientConn(conn net.Conn, isTLS bool, quickClose bool) net.Conn {
|
||||
}
|
||||
}
|
||||
|
||||
return &ClientConn{BaseClientConn: BaseClientConn{rawConn: conn}, isTLS: isTLS}
|
||||
// 是否为环路
|
||||
var remoteAddr = conn.RemoteAddr().String()
|
||||
var isLO = strings.HasPrefix(remoteAddr, "127.0.0.1:") || strings.HasPrefix(remoteAddr, "[::1]:")
|
||||
|
||||
return &ClientConn{
|
||||
BaseClientConn: BaseClientConn{rawConn: conn},
|
||||
isTLS: isTLS,
|
||||
isLO: isLO,
|
||||
}
|
||||
}
|
||||
|
||||
func (this *ClientConn) Read(b []byte) (n int, err error) {
|
||||
// 环路直接读取
|
||||
if this.isLO {
|
||||
n, err = this.rawConn.Read(b)
|
||||
if n > 0 {
|
||||
atomic.AddUint64(&teaconst.InTrafficBytes, uint64(n))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// TLS
|
||||
if this.isTLS {
|
||||
if !this.hasDeadline {
|
||||
_ = this.rawConn.SetReadDeadline(time.Now().Add(time.Duration(nodeconfigs.DefaultTLSHandshakeTimeout) * time.Second)) // TODO 握手超时时间可以设置
|
||||
@@ -55,6 +77,7 @@ func (this *ClientConn) Read(b []byte) (n int, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
// 开始读取
|
||||
n, err = this.rawConn.Read(b)
|
||||
if n > 0 {
|
||||
atomic.AddUint64(&teaconst.InTrafficBytes, uint64(n))
|
||||
@@ -63,18 +86,21 @@ func (this *ClientConn) Read(b []byte) (n int, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
// SYN Flood检测
|
||||
var isHandshakeError = err != nil && os.IsTimeout(err) && !this.hasRead
|
||||
if isHandshakeError {
|
||||
_ = this.SetLinger(0)
|
||||
}
|
||||
var synFloodConfig = sharedNodeConfig.SYNFloodConfig()
|
||||
if synFloodConfig != nil && synFloodConfig.IsOn {
|
||||
if isHandshakeError {
|
||||
this.increaseSYNFlood(synFloodConfig)
|
||||
} else if err == nil && !this.hasResetSYNFlood {
|
||||
this.hasResetSYNFlood = true
|
||||
this.resetSYNFlood()
|
||||
|
||||
// SYN Flood检测
|
||||
if this.serverId == 0 || !this.hasResetSYNFlood {
|
||||
var synFloodConfig = sharedNodeConfig.SYNFloodConfig()
|
||||
if synFloodConfig != nil && synFloodConfig.IsOn {
|
||||
if isHandshakeError {
|
||||
this.increaseSYNFlood(synFloodConfig)
|
||||
} else if err == nil && !this.hasResetSYNFlood {
|
||||
this.hasResetSYNFlood = true
|
||||
this.resetSYNFlood()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,7 +111,15 @@ func (this *ClientConn) Write(b []byte) (n int, err error) {
|
||||
n, err = this.rawConn.Write(b)
|
||||
if n > 0 {
|
||||
atomic.AddUint64(&teaconst.OutTrafficBytes, uint64(n))
|
||||
|
||||
// 统计当前服务带宽
|
||||
if this.serverId > 0 {
|
||||
if !this.isLO { // 环路不统计带宽,避免缓存预热等行为产生带宽
|
||||
stats.SharedBandwidthStatManager.Add(this.userId, this.serverId, int64(n))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -95,7 +129,9 @@ func (this *ClientConn) Close() error {
|
||||
err := this.rawConn.Close()
|
||||
|
||||
// 单个服务并发数限制
|
||||
sharedClientConnLimiter.Remove(this.rawConn.RemoteAddr().String())
|
||||
if this.hasLimit {
|
||||
sharedClientConnLimiter.Remove(this.rawConn.RemoteAddr().String())
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -8,8 +8,10 @@ type BaseClientConn struct {
|
||||
rawConn net.Conn
|
||||
|
||||
isBound bool
|
||||
userId int64
|
||||
serverId int64
|
||||
remoteAddr string
|
||||
hasLimit bool
|
||||
|
||||
isClosed bool
|
||||
}
|
||||
@@ -31,11 +33,32 @@ func (this *BaseClientConn) Bind(serverId int64, remoteAddr string, maxConnsPerS
|
||||
this.isBound = true
|
||||
this.serverId = serverId
|
||||
this.remoteAddr = remoteAddr
|
||||
this.hasLimit = true
|
||||
|
||||
// 检查是否可以连接
|
||||
return sharedClientConnLimiter.Add(this.rawConn.RemoteAddr().String(), serverId, remoteAddr, maxConnsPerServer, maxConnsPerIP)
|
||||
}
|
||||
|
||||
// SetServerId 设置服务ID
|
||||
func (this *BaseClientConn) SetServerId(serverId int64) {
|
||||
this.serverId = serverId
|
||||
}
|
||||
|
||||
// ServerId 读取当前连接绑定的服务ID
|
||||
func (this *BaseClientConn) ServerId() int64 {
|
||||
return this.serverId
|
||||
}
|
||||
|
||||
// SetUserId 设置所属服务的用户ID
|
||||
func (this *BaseClientConn) SetUserId(userId int64) {
|
||||
this.userId = userId
|
||||
}
|
||||
|
||||
// UserId 获取当前连接所属服务的用户ID
|
||||
func (this *BaseClientConn) UserId() int64 {
|
||||
return this.userId
|
||||
}
|
||||
|
||||
// RawIP 原本IP
|
||||
func (this *BaseClientConn) RawIP() string {
|
||||
ip, _, _ := net.SplitHostPort(this.rawConn.RemoteAddr().String())
|
||||
|
||||
@@ -11,4 +11,16 @@ type ClientConnInterface interface {
|
||||
|
||||
// Bind 绑定服务
|
||||
Bind(serverId int64, remoteAddr string, maxConnsPerServer int, maxConnsPerIP int) bool
|
||||
|
||||
// ServerId 获取服务ID
|
||||
ServerId() int64
|
||||
|
||||
// SetServerId 设置服务ID
|
||||
SetServerId(serverId int64)
|
||||
|
||||
// SetUserId 设置所属服务的用户ID
|
||||
SetUserId(userId int64)
|
||||
|
||||
// UserId 获取当前连接所属服务的用户ID
|
||||
UserId() int64
|
||||
}
|
||||
|
||||
@@ -22,16 +22,18 @@ var SharedHTTPClientPool = NewHTTPClientPool()
|
||||
|
||||
// HTTPClientPool 客户端池
|
||||
type HTTPClientPool struct {
|
||||
clientExpiredDuration time.Duration
|
||||
clientsMap map[string]*HTTPClient // backend key => client
|
||||
locker sync.Mutex
|
||||
clientsMap map[string]*HTTPClient // backend key => client
|
||||
|
||||
cleanTicker *time.Ticker
|
||||
|
||||
locker sync.RWMutex
|
||||
}
|
||||
|
||||
// NewHTTPClientPool 获取新对象
|
||||
func NewHTTPClientPool() *HTTPClientPool {
|
||||
var pool = &HTTPClientPool{
|
||||
clientExpiredDuration: 3600 * time.Second,
|
||||
clientsMap: map[string]*HTTPClient{},
|
||||
cleanTicker: time.NewTicker(1 * time.Hour),
|
||||
clientsMap: map[string]*HTTPClient{},
|
||||
}
|
||||
|
||||
goman.New(func() {
|
||||
@@ -53,10 +55,20 @@ func (this *HTTPClientPool) Client(req *HTTPRequest,
|
||||
|
||||
var key = origin.UniqueKey() + "@" + originAddr
|
||||
|
||||
this.locker.RLock()
|
||||
client, found := this.clientsMap[key]
|
||||
this.locker.RUnlock()
|
||||
if found {
|
||||
client.UpdateAccessTime()
|
||||
return client.RawClient(), nil
|
||||
}
|
||||
|
||||
// 这里不能使用RLock,避免因为并发生成多个同样的client实例
|
||||
this.locker.Lock()
|
||||
defer this.locker.Unlock()
|
||||
|
||||
client, found := this.clientsMap[key]
|
||||
// 再次查找
|
||||
client, found = this.clientsMap[key]
|
||||
if found {
|
||||
client.UpdateAccessTime()
|
||||
return client.RawClient(), nil
|
||||
@@ -135,7 +147,7 @@ func (this *HTTPClientPool) Client(req *HTTPRequest,
|
||||
MaxConnsPerHost: maxConnections,
|
||||
IdleConnTimeout: idleTimeout,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
TLSHandshakeTimeout: 3 * time.Second,
|
||||
TLSHandshakeTimeout: 5 * time.Second,
|
||||
TLSClientConfig: tlsConfig,
|
||||
Proxy: nil,
|
||||
},
|
||||
@@ -170,13 +182,12 @@ func (this *HTTPClientPool) Client(req *HTTPRequest,
|
||||
|
||||
// 清理不使用的Client
|
||||
func (this *HTTPClientPool) cleanClients() {
|
||||
var ticker = time.NewTicker(this.clientExpiredDuration)
|
||||
for range ticker.C {
|
||||
currentAt := time.Now().Unix()
|
||||
for range this.cleanTicker.C {
|
||||
var nowTime = time.Now().Unix()
|
||||
|
||||
this.locker.Lock()
|
||||
for k, client := range this.clientsMap {
|
||||
if client.AccessTime() < currentAt+86400 { // 超过 N 秒没有调用就关闭
|
||||
if client.AccessTime() < nowTime+86400 { // 超过 N 秒没有调用就关闭
|
||||
delete(this.clientsMap, k)
|
||||
client.Close()
|
||||
}
|
||||
|
||||
@@ -89,7 +89,9 @@ type HTTPRequest struct {
|
||||
firewallRuleSetId int64
|
||||
firewallRuleId int64
|
||||
firewallActions []string
|
||||
tags []string
|
||||
wafHasRequestBody bool
|
||||
|
||||
tags []string
|
||||
|
||||
logAttrs map[string]string
|
||||
|
||||
@@ -164,6 +166,16 @@ func (this *HTTPRequest) Do() {
|
||||
return
|
||||
}
|
||||
|
||||
// 处理健康检查
|
||||
var isHealthCheck = false
|
||||
var healthCheckKey = this.RawReq.Header.Get(serverconfigs.HealthCheckHeaderName)
|
||||
if len(healthCheckKey) > 0 {
|
||||
if this.doHealthCheck(healthCheckKey, &isHealthCheck) {
|
||||
this.doEnd()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if !this.isLnRequest {
|
||||
// 特殊URL处理
|
||||
if len(this.rawURI) > 1 && this.rawURI[1] == '.' {
|
||||
@@ -191,6 +203,24 @@ func (this *HTTPRequest) Do() {
|
||||
return
|
||||
}
|
||||
|
||||
// UAM
|
||||
if !isHealthCheck {
|
||||
if this.web.UAM != nil {
|
||||
if this.web.UAM.IsOn {
|
||||
if this.doUAM() {
|
||||
this.doEnd()
|
||||
return
|
||||
}
|
||||
}
|
||||
} else if this.ReqServer.UAM != nil && this.ReqServer.UAM.IsOn {
|
||||
this.web.UAM = this.ReqServer.UAM
|
||||
if this.doUAM() {
|
||||
this.doEnd()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WAF
|
||||
if this.web.FirewallRef != nil && this.web.FirewallRef.IsOn {
|
||||
if this.doWAFRequest() {
|
||||
@@ -233,6 +263,12 @@ func (this *HTTPRequest) Do() {
|
||||
|
||||
// 开始调用
|
||||
func (this *HTTPRequest) doBegin() {
|
||||
// 是否找不到域名匹配
|
||||
if this.ReqServer.Id == 0 {
|
||||
this.doMismatch()
|
||||
return
|
||||
}
|
||||
|
||||
if !this.isLnRequest {
|
||||
// 处理request limit
|
||||
if this.web.RequestLimit != nil &&
|
||||
@@ -256,32 +292,6 @@ func (this *HTTPRequest) doBegin() {
|
||||
this.RawReq.Body = ioutil.NopCloser(io.MultiReader(bytes.NewBuffer(this.requestBodyData), this.RawReq.Body))
|
||||
}
|
||||
|
||||
// 处理健康检查
|
||||
var isHealthCheck = false
|
||||
var healthCheckKey = this.RawReq.Header.Get(serverconfigs.HealthCheckHeaderName)
|
||||
if len(healthCheckKey) > 0 {
|
||||
if this.doHealthCheck(healthCheckKey, &isHealthCheck) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// UAM
|
||||
if !isHealthCheck {
|
||||
if this.web.UAM != nil {
|
||||
if this.web.UAM.IsOn {
|
||||
if this.doUAM() {
|
||||
this.doEnd()
|
||||
return
|
||||
}
|
||||
}
|
||||
} else if this.ReqServer.UAM != nil && this.ReqServer.UAM.IsOn {
|
||||
if this.doUAM() {
|
||||
this.doEnd()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 跳转
|
||||
if len(this.web.HostRedirects) > 0 {
|
||||
if this.doHostRedirect() {
|
||||
@@ -349,7 +359,7 @@ func (this *HTTPRequest) doEnd() {
|
||||
|
||||
// 流量统计
|
||||
// TODO 增加是否开启开关
|
||||
if this.ReqServer != nil {
|
||||
if this.ReqServer != nil && this.ReqServer.Id > 0 {
|
||||
var countCached int64 = 0
|
||||
var cachedBytes int64 = 0
|
||||
|
||||
@@ -366,17 +376,17 @@ func (this *HTTPRequest) doEnd() {
|
||||
}
|
||||
|
||||
stats.SharedTrafficStatManager.Add(this.ReqServer.Id, this.ReqHost, this.writer.SentBodyBytes()+this.writer.SentHeaderBytes(), cachedBytes, 1, countCached, countAttacks, attackBytes, this.ReqServer.ShouldCheckTrafficLimit(), this.ReqServer.PlanId())
|
||||
}
|
||||
|
||||
// 指标
|
||||
if metrics.SharedManager.HasHTTPMetrics() {
|
||||
this.doMetricsResponse()
|
||||
}
|
||||
// 指标
|
||||
if metrics.SharedManager.HasHTTPMetrics() {
|
||||
this.doMetricsResponse()
|
||||
}
|
||||
|
||||
// 统计
|
||||
if this.web.StatRef != nil && this.web.StatRef.IsOn {
|
||||
// 放到最后执行
|
||||
this.doStat()
|
||||
// 统计
|
||||
if this.web.StatRef != nil && this.web.StatRef.IsOn {
|
||||
// 放到最后执行
|
||||
this.doStat()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1104,7 +1114,7 @@ func (this *HTTPRequest) requestRemoteAddr(supportVar bool) string {
|
||||
// 获取请求的客户端地址列表
|
||||
func (this *HTTPRequest) requestRemoteAddrs() (result []string) {
|
||||
// X-Forwarded-For
|
||||
forwardedFor := this.RawReq.Header.Get("X-Forwarded-For")
|
||||
var forwardedFor = this.RawReq.Header.Get("X-Forwarded-For")
|
||||
if len(forwardedFor) > 0 {
|
||||
commaIndex := strings.Index(forwardedFor, ",")
|
||||
if commaIndex > 0 {
|
||||
@@ -1129,13 +1139,16 @@ func (this *HTTPRequest) requestRemoteAddrs() (result []string) {
|
||||
}
|
||||
|
||||
// Remote-Addr
|
||||
remoteAddr := this.RawReq.RemoteAddr
|
||||
host, _, err := net.SplitHostPort(remoteAddr)
|
||||
if err == nil {
|
||||
result = append(result, host)
|
||||
} else {
|
||||
result = append(result, remoteAddr)
|
||||
{
|
||||
var remoteAddr = this.RawReq.RemoteAddr
|
||||
host, _, err := net.SplitHostPort(remoteAddr)
|
||||
if err == nil {
|
||||
result = append(result, host)
|
||||
} else {
|
||||
result = append(result, remoteAddr)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -375,6 +375,7 @@ func (this *HTTPRequest) doCacheRead(useStale bool) (shouldStop bool) {
|
||||
if !this.isLnRequest && !isPartialCache && len(eTag) > 0 && this.requestHeader("If-None-Match") == eTag {
|
||||
// 自定义Header
|
||||
this.processResponseHeaders(http.StatusNotModified)
|
||||
this.addExpiresHeader(reader.ExpiresAt())
|
||||
this.writer.WriteHeader(http.StatusNotModified)
|
||||
this.isCached = true
|
||||
this.cacheRef = nil
|
||||
@@ -386,6 +387,7 @@ func (this *HTTPRequest) doCacheRead(useStale bool) (shouldStop bool) {
|
||||
if !this.isLnRequest && !isPartialCache && len(modifiedTime) > 0 && this.requestHeader("If-Modified-Since") == modifiedTime {
|
||||
// 自定义Header
|
||||
this.processResponseHeaders(http.StatusNotModified)
|
||||
this.addExpiresHeader(reader.ExpiresAt())
|
||||
this.writer.WriteHeader(http.StatusNotModified)
|
||||
this.isCached = true
|
||||
this.cacheRef = nil
|
||||
@@ -575,10 +577,12 @@ func (this *HTTPRequest) addExpiresHeader(expiresAt int64) {
|
||||
if this.cacheRef.ExpiresTime.Overwrite || len(this.writer.Header().Get("Expires")) == 0 {
|
||||
if this.cacheRef.ExpiresTime.AutoCalculate {
|
||||
this.writer.Header().Set("Expires", time.Unix(utils.GMTUnixTime(expiresAt), 0).Format("Mon, 2 Jan 2006 15:04:05")+" GMT")
|
||||
this.writer.Header().Del("Cache-Control")
|
||||
} else if this.cacheRef.ExpiresTime.Duration != nil {
|
||||
var duration = this.cacheRef.ExpiresTime.Duration.Duration()
|
||||
if duration > 0 {
|
||||
this.writer.Header().Set("Expires", utils.GMTTime(time.Now().Add(duration)).Format("Mon, 2 Jan 2006 15:04:05")+" GMT")
|
||||
this.writer.Header().Del("Cache-Control")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ func (this *HTTPRequest) doRequestLimit() (shouldStop bool) {
|
||||
|
||||
// 设置连接相关参数
|
||||
if this.web.RequestLimit.MaxConns > 0 || this.web.RequestLimit.MaxConnsPerIP > 0 {
|
||||
requestConn := this.RawReq.Context().Value(HTTPConnContextKey)
|
||||
var requestConn = this.RawReq.Context().Value(HTTPConnContextKey)
|
||||
if requestConn != nil {
|
||||
clientConn, ok := requestConn.(ClientConnInterface)
|
||||
if ok && !clientConn.IsBound() {
|
||||
|
||||
@@ -149,8 +149,7 @@ func (this *HTTPRequest) log() {
|
||||
}
|
||||
|
||||
// 请求Body
|
||||
// TODO 考虑在被攻击时记录攻击的requestBody(如果requestBody匹配规则的话),但要考虑请求尺寸、数据库容量,避免因为日志而导致服务不稳定
|
||||
if ref != nil && ref.ContainsField(serverconfigs.HTTPAccessLogFieldRequestBody) {
|
||||
if (ref != nil && ref.ContainsField(serverconfigs.HTTPAccessLogFieldRequestBody)) || this.wafHasRequestBody {
|
||||
accessLog.RequestBody = this.requestBodyData
|
||||
|
||||
if len(accessLog.RequestBody) > AccessLogMaxRequestBodySize {
|
||||
|
||||
68
internal/nodes/http_request_mismatch.go
Normal file
68
internal/nodes/http_request_mismatch.go
Normal file
@@ -0,0 +1,68 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package nodes
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/ttlcache"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/waf"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 域名无匹配情况处理
|
||||
func (this *HTTPRequest) doMismatch() {
|
||||
// 是否为健康检查
|
||||
var healthCheckKey = this.RawReq.Header.Get(serverconfigs.HealthCheckHeaderName)
|
||||
if len(healthCheckKey) > 0 {
|
||||
_, err := nodeutils.Base64DecodeMap(healthCheckKey)
|
||||
if err == nil {
|
||||
this.writer.WriteHeader(http.StatusOK)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 是否已经在黑名单
|
||||
var remoteIP = this.RemoteAddr()
|
||||
if waf.SharedIPBlackList.Contains(waf.IPTypeAll, firewallconfigs.FirewallScopeGlobal, 0, remoteIP) {
|
||||
this.Close()
|
||||
return
|
||||
}
|
||||
|
||||
// 根据配置进行相应的处理
|
||||
if sharedNodeConfig.GlobalConfig != nil && sharedNodeConfig.GlobalConfig.HTTPAll.MatchDomainStrictly {
|
||||
// 检查cc
|
||||
// TODO 可以在管理端配置是否开启以及最多尝试次数
|
||||
if len(remoteIP) > 0 {
|
||||
const maxAttempts = 100
|
||||
if ttlcache.SharedCache.IncreaseInt64("MISMATCH_DOMAIN:"+remoteIP, int64(1), time.Now().Unix()+60, false) > maxAttempts {
|
||||
// 在加入之前再次检查黑名单
|
||||
if !waf.SharedIPBlackList.Contains(waf.IPTypeAll, firewallconfigs.FirewallScopeGlobal, 0, remoteIP) {
|
||||
waf.SharedIPBlackList.RecordIP(waf.IPTypeAll, firewallconfigs.FirewallScopeGlobal, 0, remoteIP, time.Now().Unix()+int64(3600), 0, true, 0, 0, "access mismatch domain '"+this.RawReq.Host+"' too frequently")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理当前连接
|
||||
var httpAllConfig = sharedNodeConfig.GlobalConfig.HTTPAll
|
||||
var mismatchAction = httpAllConfig.DomainMismatchAction
|
||||
if mismatchAction != nil && mismatchAction.Code == "page" {
|
||||
if mismatchAction.Options != nil {
|
||||
this.writer.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
this.writer.WriteHeader(mismatchAction.Options.GetInt("statusCode"))
|
||||
_, _ = this.writer.Write([]byte(mismatchAction.Options.GetString("contentHTML")))
|
||||
} else {
|
||||
http.Error(this.writer, "404 page not found: '"+this.URL()+"'", http.StatusNotFound)
|
||||
}
|
||||
return
|
||||
} else {
|
||||
http.Error(this.writer, "404 page not found: '"+this.URL()+"'", http.StatusNotFound)
|
||||
this.Close()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
http.Error(this.writer, "404 page not found: '"+this.URL()+"'", http.StatusNotFound)
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@@ -77,7 +78,7 @@ func (this *HTTPRequest) doReverseProxy() {
|
||||
|
||||
// 处理Scheme
|
||||
if origin.Addr == nil {
|
||||
err := errors.New(this.URL() + ": origin '" + strconv.FormatInt(origin.Id, 10) + "' does not has a address")
|
||||
err := errors.New(this.URL() + ": Origin '" + strconv.FormatInt(origin.Id, 10) + "' does not has a address")
|
||||
remotelogs.Error("HTTP_REQUEST_REVERSE_PROXY", err.Error())
|
||||
this.write50x(err, http.StatusBadGateway, true)
|
||||
return
|
||||
@@ -124,6 +125,18 @@ func (this *HTTPRequest) doReverseProxy() {
|
||||
if origin.Addr.HostHasVariables() {
|
||||
originAddr = this.Format(originAddr)
|
||||
}
|
||||
|
||||
// 端口跟随
|
||||
if origin.FollowPort {
|
||||
var originHostIndex = strings.Index(originAddr, ":")
|
||||
if originHostIndex < 0 {
|
||||
var originErr = errors.New(this.URL() + ": Invalid origin address '" + originAddr + "', lacking port")
|
||||
remotelogs.Error("HTTP_REQUEST_REVERSE_PROXY", originErr.Error())
|
||||
this.write50x(originErr, http.StatusBadGateway, true)
|
||||
return
|
||||
}
|
||||
originAddr = originAddr[:originHostIndex+1] + types.String(this.requestServerPort())
|
||||
}
|
||||
this.originAddr = originAddr
|
||||
|
||||
// RequestHost
|
||||
@@ -133,6 +146,12 @@ func (this *HTTPRequest) doReverseProxy() {
|
||||
} else {
|
||||
this.RawReq.Host = requestHost
|
||||
}
|
||||
|
||||
// 是否移除端口
|
||||
if this.reverseProxy.RequestHostExcludingPort {
|
||||
this.RawReq.Host = utils.ParseAddrHost(this.RawReq.Host)
|
||||
}
|
||||
|
||||
this.RawReq.URL.Host = this.RawReq.Host
|
||||
} else if this.reverseProxy.RequestHostType == serverconfigs.RequestHostTypeOrigin {
|
||||
// 源站主机名
|
||||
@@ -144,9 +163,21 @@ func (this *HTTPRequest) doReverseProxy() {
|
||||
}
|
||||
|
||||
this.RawReq.Host = hostname
|
||||
|
||||
// 是否移除端口
|
||||
if this.reverseProxy.RequestHostExcludingPort {
|
||||
this.RawReq.Host = utils.ParseAddrHost(this.RawReq.Host)
|
||||
}
|
||||
|
||||
this.RawReq.URL.Host = this.RawReq.Host
|
||||
} else {
|
||||
this.RawReq.URL.Host = this.ReqHost
|
||||
|
||||
// 是否移除端口
|
||||
if this.reverseProxy.RequestHostExcludingPort {
|
||||
this.RawReq.Host = utils.ParseAddrHost(this.RawReq.Host)
|
||||
this.RawReq.URL.Host = utils.ParseAddrHost(this.RawReq.URL.Host)
|
||||
}
|
||||
}
|
||||
|
||||
// 重组请求URL
|
||||
@@ -172,14 +203,14 @@ func (this *HTTPRequest) doReverseProxy() {
|
||||
|
||||
// 判断是否为Websocket请求
|
||||
if this.RawReq.Header.Get("Upgrade") == "websocket" {
|
||||
this.doWebsocket()
|
||||
this.doWebsocket(requestHost)
|
||||
return
|
||||
}
|
||||
|
||||
// 获取请求客户端
|
||||
client, err := SharedHTTPClientPool.Client(this, origin, originAddr, this.reverseProxy.ProxyProtocol, this.reverseProxy.FollowRedirects)
|
||||
if err != nil {
|
||||
remotelogs.Error("HTTP_REQUEST_REVERSE_PROXY", this.URL()+": "+err.Error())
|
||||
remotelogs.Error("HTTP_REQUEST_REVERSE_PROXY", this.URL()+": Create client failed: "+err.Error())
|
||||
this.write50x(err, http.StatusBadGateway, true)
|
||||
return
|
||||
}
|
||||
@@ -196,13 +227,13 @@ func (this *HTTPRequest) doReverseProxy() {
|
||||
// 客户端取消请求,则不提示
|
||||
httpErr, ok := err.(*url.Error)
|
||||
if !ok {
|
||||
SharedOriginStateManager.Fail(origin, this.reverseProxy, func() {
|
||||
SharedOriginStateManager.Fail(origin, requestHost, this.reverseProxy, func() {
|
||||
this.reverseProxy.ResetScheduling()
|
||||
})
|
||||
this.write50x(err, http.StatusBadGateway, true)
|
||||
remotelogs.Warn("HTTP_REQUEST_REVERSE_PROXY", this.RawReq.URL.String()+"': "+err.Error())
|
||||
remotelogs.Warn("HTTP_REQUEST_REVERSE_PROXY", this.RawReq.URL.String()+": Request origin server failed: "+err.Error())
|
||||
} else if httpErr.Err != context.Canceled {
|
||||
SharedOriginStateManager.Fail(origin, this.reverseProxy, func() {
|
||||
SharedOriginStateManager.Fail(origin, requestHost, this.reverseProxy, func() {
|
||||
this.reverseProxy.ResetScheduling()
|
||||
})
|
||||
if httpErr.Timeout() {
|
||||
@@ -213,7 +244,7 @@ func (this *HTTPRequest) doReverseProxy() {
|
||||
this.write50x(err, http.StatusBadGateway, true)
|
||||
}
|
||||
if httpErr.Err != io.EOF {
|
||||
remotelogs.Warn("HTTP_REQUEST_REVERSE_PROXY", this.URL()+": "+err.Error())
|
||||
remotelogs.Warn("HTTP_REQUEST_REVERSE_PROXY", this.URL()+": Request origin server failed: "+err.Error())
|
||||
}
|
||||
} else {
|
||||
// 是否为客户端方面的错误
|
||||
@@ -252,7 +283,7 @@ func (this *HTTPRequest) doReverseProxy() {
|
||||
if this.doWAFResponse(resp) {
|
||||
err = resp.Body.Close()
|
||||
if err != nil {
|
||||
remotelogs.Warn("HTTP_REQUEST_REVERSE_PROXY", this.URL()+": "+err.Error())
|
||||
remotelogs.Warn("HTTP_REQUEST_REVERSE_PROXY", this.URL()+": Closing Error (WAF): "+err.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -262,7 +293,7 @@ func (this *HTTPRequest) doReverseProxy() {
|
||||
if len(this.web.Pages) > 0 && this.doPage(resp.StatusCode) {
|
||||
err = resp.Body.Close()
|
||||
if err != nil {
|
||||
remotelogs.Warn("HTTP_REQUEST_REVERSE_PROXY", this.URL()+": "+err.Error())
|
||||
remotelogs.Warn("HTTP_REQUEST_REVERSE_PROXY", this.URL()+": Closing error (Page): "+err.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -364,13 +395,13 @@ func (this *HTTPRequest) doReverseProxy() {
|
||||
var closeErr = resp.Body.Close()
|
||||
if closeErr != nil {
|
||||
if !this.canIgnore(closeErr) {
|
||||
remotelogs.Warn("HTTP_REQUEST_REVERSE_PROXY", this.URL()+": "+closeErr.Error())
|
||||
remotelogs.Warn("HTTP_REQUEST_REVERSE_PROXY", this.URL()+": Closing error: "+closeErr.Error())
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil && err != io.EOF {
|
||||
if !this.canIgnore(err) {
|
||||
remotelogs.Warn("HTTP_REQUEST_REVERSE_PROXY", this.URL()+": "+err.Error())
|
||||
remotelogs.Warn("HTTP_REQUEST_REVERSE_PROXY", this.URL()+": Writing error: "+err.Error())
|
||||
this.addError(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,20 +208,3 @@ func httpAcceptEncoding(acceptEncodings string, encoding string) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 分隔编码
|
||||
func httpAcceptEncodings(acceptEncodings string) (encodings []string) {
|
||||
if len(acceptEncodings) == 0 {
|
||||
return
|
||||
}
|
||||
var pieces = strings.Split(acceptEncodings, ",")
|
||||
for _, piece := range pieces {
|
||||
var qualityIndex = strings.Index(piece, ";")
|
||||
if qualityIndex >= 0 {
|
||||
piece = piece[:qualityIndex]
|
||||
}
|
||||
|
||||
encodings = append(encodings, strings.TrimSpace(piece))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -53,11 +53,21 @@ func (this *HTTPRequest) doWAFRequest() (blocked bool) {
|
||||
return true
|
||||
}
|
||||
|
||||
var forceLog = this.ReqServer.HTTPFirewallPolicy != nil && this.ReqServer.HTTPFirewallPolicy.IsOn && this.ReqServer.HTTPFirewallPolicy.Log != nil && this.ReqServer.HTTPFirewallPolicy.Log.IsOn
|
||||
var forceLog = false
|
||||
var forceLogRequestBody = false
|
||||
var forceLogRegionDenying = false
|
||||
if this.ReqServer.HTTPFirewallPolicy != nil &&
|
||||
this.ReqServer.HTTPFirewallPolicy.IsOn &&
|
||||
this.ReqServer.HTTPFirewallPolicy.Log != nil &&
|
||||
this.ReqServer.HTTPFirewallPolicy.Log.IsOn {
|
||||
forceLog = true
|
||||
forceLogRequestBody = this.ReqServer.HTTPFirewallPolicy.Log.RequestBody
|
||||
forceLogRegionDenying = this.ReqServer.HTTPFirewallPolicy.Log.RegionDenying
|
||||
}
|
||||
|
||||
// 当前服务的独立设置
|
||||
if this.web.FirewallPolicy != nil && this.web.FirewallPolicy.IsOn {
|
||||
blocked, breakChecking := this.checkWAFRequest(this.web.FirewallPolicy, forceLog)
|
||||
blocked, breakChecking := this.checkWAFRequest(this.web.FirewallPolicy, forceLog, forceLogRequestBody, forceLogRegionDenying)
|
||||
if blocked {
|
||||
return true
|
||||
}
|
||||
@@ -68,7 +78,7 @@ func (this *HTTPRequest) doWAFRequest() (blocked bool) {
|
||||
|
||||
// 公用的防火墙设置
|
||||
if this.ReqServer.HTTPFirewallPolicy != nil && this.ReqServer.HTTPFirewallPolicy.IsOn {
|
||||
blocked, breakChecking := this.checkWAFRequest(this.ReqServer.HTTPFirewallPolicy, forceLog)
|
||||
blocked, breakChecking := this.checkWAFRequest(this.ReqServer.HTTPFirewallPolicy, forceLog, forceLogRequestBody, forceLogRegionDenying)
|
||||
if blocked {
|
||||
return true
|
||||
}
|
||||
@@ -80,15 +90,21 @@ func (this *HTTPRequest) doWAFRequest() (blocked bool) {
|
||||
return
|
||||
}
|
||||
|
||||
func (this *HTTPRequest) checkWAFRequest(firewallPolicy *firewallconfigs.HTTPFirewallPolicy, forceLog bool) (blocked bool, breakChecking bool) {
|
||||
func (this *HTTPRequest) checkWAFRequest(firewallPolicy *firewallconfigs.HTTPFirewallPolicy, forceLog bool, logRequestBody bool, logDenying bool) (blocked bool, breakChecking bool) {
|
||||
// 检查配置是否为空
|
||||
if firewallPolicy == nil || !firewallPolicy.IsOn || firewallPolicy.Inbound == nil || !firewallPolicy.Inbound.IsOn || firewallPolicy.Mode == firewallconfigs.FirewallModeBypass {
|
||||
return
|
||||
}
|
||||
|
||||
// 检查IP白名单
|
||||
remoteAddrs := this.requestRemoteAddrs()
|
||||
inbound := firewallPolicy.Inbound
|
||||
var remoteAddrs []string
|
||||
if len(this.remoteAddr) > 0 {
|
||||
remoteAddrs = []string{this.remoteAddr}
|
||||
} else {
|
||||
remoteAddrs = this.requestRemoteAddrs()
|
||||
}
|
||||
|
||||
var inbound = firewallPolicy.Inbound
|
||||
if inbound == nil {
|
||||
return
|
||||
}
|
||||
@@ -159,13 +175,17 @@ func (this *HTTPRequest) checkWAFRequest(firewallPolicy *firewallconfigs.HTTPFir
|
||||
if len(regionConfig.DenyCountryIds) > 0 && len(result.Country) > 0 {
|
||||
countryId := iplibrary.SharedCountryManager.Lookup(result.Country)
|
||||
if countryId > 0 && lists.ContainsInt64(regionConfig.DenyCountryIds, countryId) {
|
||||
// TODO 可以配置对封禁的处理方式等
|
||||
// TODO 需要记录日志信息
|
||||
this.firewallPolicyId = firewallPolicy.Id
|
||||
|
||||
this.writer.WriteHeader(http.StatusForbidden)
|
||||
this.writer.Close()
|
||||
|
||||
// 停止日志
|
||||
this.disableLog = true
|
||||
if !logDenying {
|
||||
this.disableLog = true
|
||||
} else {
|
||||
this.tags = append(this.tags, "denyCountry")
|
||||
}
|
||||
|
||||
return true, false
|
||||
}
|
||||
@@ -173,15 +193,19 @@ func (this *HTTPRequest) checkWAFRequest(firewallPolicy *firewallconfigs.HTTPFir
|
||||
|
||||
// 检查省份封禁
|
||||
if len(regionConfig.DenyProvinceIds) > 0 && len(result.Province) > 0 {
|
||||
provinceId := iplibrary.SharedProvinceManager.Lookup(result.Province)
|
||||
var provinceId = iplibrary.SharedProvinceManager.Lookup(result.Province)
|
||||
if provinceId > 0 && lists.ContainsInt64(regionConfig.DenyProvinceIds, provinceId) {
|
||||
// TODO 可以配置对封禁的处理方式等
|
||||
// TODO 需要记录日志信息
|
||||
this.firewallPolicyId = firewallPolicy.Id
|
||||
|
||||
this.writer.WriteHeader(http.StatusForbidden)
|
||||
this.writer.Close()
|
||||
|
||||
// 停止日志
|
||||
this.disableLog = true
|
||||
if !logDenying {
|
||||
this.disableLog = true
|
||||
} else {
|
||||
this.tags = append(this.tags, "denyProvince")
|
||||
}
|
||||
|
||||
return true, false
|
||||
}
|
||||
@@ -199,7 +223,10 @@ func (this *HTTPRequest) checkWAFRequest(firewallPolicy *firewallconfigs.HTTPFir
|
||||
return
|
||||
}
|
||||
|
||||
goNext, ruleGroup, ruleSet, err := w.MatchRequest(this, this.writer)
|
||||
goNext, hasRequestBody, ruleGroup, ruleSet, err := w.MatchRequest(this, this.writer)
|
||||
if forceLog && logRequestBody && hasRequestBody {
|
||||
this.wafHasRequestBody = true
|
||||
}
|
||||
if err != nil {
|
||||
if !this.canIgnore(err) {
|
||||
remotelogs.Error("HTTP_REQUEST_WAF", this.rawURI+": "+err.Error())
|
||||
@@ -238,9 +265,15 @@ func (this *HTTPRequest) doWAFResponse(resp *http.Response) (blocked bool) {
|
||||
}
|
||||
|
||||
// 当前服务的独立设置
|
||||
var forceLog = this.ReqServer.HTTPFirewallPolicy != nil && this.ReqServer.HTTPFirewallPolicy.IsOn && this.ReqServer.HTTPFirewallPolicy.Log != nil && this.ReqServer.HTTPFirewallPolicy.Log.IsOn
|
||||
var forceLog = false
|
||||
var forceLogRequestBody = false
|
||||
if this.ReqServer.HTTPFirewallPolicy != nil && this.ReqServer.HTTPFirewallPolicy.IsOn && this.ReqServer.HTTPFirewallPolicy.Log != nil && this.ReqServer.HTTPFirewallPolicy.Log.IsOn {
|
||||
forceLog = true
|
||||
forceLogRequestBody = this.ReqServer.HTTPFirewallPolicy.Log.RequestBody
|
||||
}
|
||||
|
||||
if this.web.FirewallPolicy != nil && this.web.FirewallPolicy.IsOn {
|
||||
blocked := this.checkWAFResponse(this.web.FirewallPolicy, resp, forceLog)
|
||||
blocked := this.checkWAFResponse(this.web.FirewallPolicy, resp, forceLog, forceLogRequestBody)
|
||||
if blocked {
|
||||
return true
|
||||
}
|
||||
@@ -248,7 +281,7 @@ func (this *HTTPRequest) doWAFResponse(resp *http.Response) (blocked bool) {
|
||||
|
||||
// 公用的防火墙设置
|
||||
if this.ReqServer.HTTPFirewallPolicy != nil && this.ReqServer.HTTPFirewallPolicy.IsOn {
|
||||
blocked := this.checkWAFResponse(this.ReqServer.HTTPFirewallPolicy, resp, forceLog)
|
||||
blocked := this.checkWAFResponse(this.ReqServer.HTTPFirewallPolicy, resp, forceLog, forceLogRequestBody)
|
||||
if blocked {
|
||||
return true
|
||||
}
|
||||
@@ -256,7 +289,7 @@ func (this *HTTPRequest) doWAFResponse(resp *http.Response) (blocked bool) {
|
||||
return
|
||||
}
|
||||
|
||||
func (this *HTTPRequest) checkWAFResponse(firewallPolicy *firewallconfigs.HTTPFirewallPolicy, resp *http.Response, forceLog bool) (blocked bool) {
|
||||
func (this *HTTPRequest) checkWAFResponse(firewallPolicy *firewallconfigs.HTTPFirewallPolicy, resp *http.Response, forceLog bool, logRequestBody bool) (blocked bool) {
|
||||
if firewallPolicy == nil || !firewallPolicy.IsOn || !firewallPolicy.Outbound.IsOn || firewallPolicy.Mode == firewallconfigs.FirewallModeBypass {
|
||||
return
|
||||
}
|
||||
@@ -266,7 +299,10 @@ func (this *HTTPRequest) checkWAFResponse(firewallPolicy *firewallconfigs.HTTPFi
|
||||
return
|
||||
}
|
||||
|
||||
goNext, ruleGroup, ruleSet, err := w.MatchResponse(this, resp, this.writer)
|
||||
goNext, hasRequestBody, ruleGroup, ruleSet, err := w.MatchResponse(this, resp, this.writer)
|
||||
if forceLog && logRequestBody && hasRequestBody {
|
||||
this.wafHasRequestBody = true
|
||||
}
|
||||
if err != nil {
|
||||
if !this.canIgnore(err) {
|
||||
remotelogs.Error("HTTP_REQUEST_WAF", this.rawURI+": "+err.Error())
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
)
|
||||
|
||||
// 处理Websocket请求
|
||||
func (this *HTTPRequest) doWebsocket() {
|
||||
func (this *HTTPRequest) doWebsocket(requestHost string) {
|
||||
if this.web.WebsocketRef == nil || !this.web.WebsocketRef.IsOn || this.web.Websocket == nil || !this.web.Websocket.IsOn {
|
||||
this.writer.WriteHeader(http.StatusForbidden)
|
||||
this.addError(errors.New("websocket have not been enabled yet"))
|
||||
@@ -41,12 +41,12 @@ func (this *HTTPRequest) doWebsocket() {
|
||||
}
|
||||
|
||||
// TODO 增加N次错误重试,重试的时候需要尝试不同的源站
|
||||
originConn, err := OriginConnect(this.origin, this.RawReq.RemoteAddr)
|
||||
originConn, _, err := OriginConnect(this.origin, this.requestServerPort(), this.RawReq.RemoteAddr, requestHost)
|
||||
if err != nil {
|
||||
this.write50x(err, http.StatusBadGateway, false)
|
||||
|
||||
// 增加失败次数
|
||||
SharedOriginStateManager.Fail(this.origin, this.reverseProxy, func() {
|
||||
SharedOriginStateManager.Fail(this.origin, requestHost, this.reverseProxy, func() {
|
||||
this.reverseProxy.ResetScheduling()
|
||||
})
|
||||
|
||||
|
||||
@@ -509,7 +509,7 @@ func (this *HTTPWriter) PrepareWebP(resp *http.Response, size int64) {
|
||||
|
||||
var contentEncoding = this.GetHeader("Content-Encoding")
|
||||
switch contentEncoding {
|
||||
case "gzip", "deflate", "br":
|
||||
case "gzip", "deflate", "br", "zstd":
|
||||
reader, err := compressions.NewReader(resp.Body, contentEncoding)
|
||||
if err != nil {
|
||||
return
|
||||
@@ -547,7 +547,7 @@ func (this *HTTPWriter) PrepareCompression(resp *http.Response, size int64) {
|
||||
var contentEncoding = this.GetHeader("Content-Encoding")
|
||||
|
||||
if this.compressionConfig == nil || !this.compressionConfig.IsOn {
|
||||
if lists.ContainsString([]string{"gzip", "deflate", "br"}, contentEncoding) && !httpAcceptEncoding(acceptEncodings, contentEncoding) {
|
||||
if lists.ContainsString([]string{"gzip", "deflate", "br", "zstd"}, contentEncoding) && !httpAcceptEncoding(acceptEncodings, contentEncoding) {
|
||||
reader, err := compressions.NewReader(resp.Body, contentEncoding)
|
||||
if err != nil {
|
||||
return
|
||||
@@ -564,7 +564,7 @@ func (this *HTTPWriter) PrepareCompression(resp *http.Response, size int64) {
|
||||
}
|
||||
|
||||
// 如果已经有编码则不处理
|
||||
if len(contentEncoding) > 0 && (!this.compressionConfig.DecompressData || !lists.ContainsString([]string{"gzip", "deflate", "br"}, contentEncoding)) {
|
||||
if len(contentEncoding) > 0 && (!this.compressionConfig.DecompressData || !lists.ContainsString([]string{"gzip", "deflate", "br", "zstd"}, contentEncoding)) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ func (this *Listener) Listen() error {
|
||||
if this.group == nil {
|
||||
return nil
|
||||
}
|
||||
protocol := this.group.Protocol()
|
||||
var protocol = this.group.Protocol()
|
||||
if protocol.IsUDPFamily() {
|
||||
return this.listenUDP()
|
||||
}
|
||||
@@ -54,7 +54,7 @@ func (this *Listener) listenTCP() error {
|
||||
if this.group == nil {
|
||||
return nil
|
||||
}
|
||||
protocol := this.group.Protocol()
|
||||
var protocol = this.group.Protocol()
|
||||
|
||||
tcpListener, err := this.createTCPListener()
|
||||
if err != nil {
|
||||
@@ -153,7 +153,7 @@ func (this *Listener) Close() error {
|
||||
|
||||
// 创建TCP监听器
|
||||
func (this *Listener) createTCPListener() (net.Listener, error) {
|
||||
listenConfig := net.ListenConfig{
|
||||
var listenConfig = net.ListenConfig{
|
||||
Control: nil,
|
||||
KeepAlive: 0,
|
||||
}
|
||||
|
||||
@@ -40,6 +40,10 @@ func (this *BaseListener) buildTLSConfig() *tls.Config {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if tlsPolicy == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
tlsPolicy.CheckOCSP()
|
||||
|
||||
return tlsPolicy.TLSConfig(), nil
|
||||
@@ -62,7 +66,7 @@ func (this *BaseListener) buildTLSConfig() *tls.Config {
|
||||
|
||||
// 根据域名匹配证书
|
||||
func (this *BaseListener) matchSSL(domain string) (*sslconfigs.SSLPolicy, *tls.Certificate, error) {
|
||||
group := this.Group
|
||||
var group = this.Group
|
||||
|
||||
if group == nil {
|
||||
return nil, nil, errors.New("no configure found")
|
||||
@@ -107,7 +111,7 @@ func (this *BaseListener) matchSSL(domain string) (*sslconfigs.SSLPolicy, *tls.C
|
||||
}
|
||||
|
||||
// 证书是否匹配
|
||||
sslConfig := server.SSLPolicy()
|
||||
var sslConfig = server.SSLPolicy()
|
||||
cert, ok := sslConfig.MatchDomain(domain)
|
||||
if ok {
|
||||
return sslConfig, cert, nil
|
||||
@@ -127,7 +131,7 @@ func (this *BaseListener) findNamedServer(name string) (serverConfig *serverconf
|
||||
return
|
||||
}
|
||||
|
||||
matchDomainStrictly := sharedNodeConfig.GlobalConfig != nil && sharedNodeConfig.GlobalConfig.HTTPAll.MatchDomainStrictly
|
||||
var matchDomainStrictly = sharedNodeConfig.GlobalConfig != nil && sharedNodeConfig.GlobalConfig.HTTPAll.MatchDomainStrictly
|
||||
|
||||
if sharedNodeConfig.GlobalConfig != nil &&
|
||||
len(sharedNodeConfig.GlobalConfig.HTTPAll.DefaultDomain) > 0 &&
|
||||
@@ -144,9 +148,9 @@ func (this *BaseListener) findNamedServer(name string) (serverConfig *serverconf
|
||||
}
|
||||
|
||||
// 如果没有找到,则匹配到第一个
|
||||
group := this.Group
|
||||
currentServers := group.Servers()
|
||||
countServers := len(currentServers)
|
||||
var group = this.Group
|
||||
var currentServers = group.Servers()
|
||||
var countServers = len(currentServers)
|
||||
if countServers == 0 {
|
||||
return nil, ""
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package nodes
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/zero"
|
||||
@@ -140,10 +139,10 @@ func (this *HTTPListener) ServeHTTP(rawWriter http.ResponseWriter, rawReq *http.
|
||||
// TLS域名
|
||||
if this.isIP(reqHost) {
|
||||
if rawReq.TLS != nil {
|
||||
serverName := rawReq.TLS.ServerName
|
||||
var serverName = rawReq.TLS.ServerName
|
||||
if len(serverName) > 0 {
|
||||
// 端口
|
||||
index := strings.LastIndex(reqHost, ":")
|
||||
var index = strings.LastIndex(reqHost, ":")
|
||||
if index >= 0 {
|
||||
reqHost = serverName + reqHost[index:]
|
||||
} else {
|
||||
@@ -155,7 +154,7 @@ func (this *HTTPListener) ServeHTTP(rawWriter http.ResponseWriter, rawReq *http.
|
||||
|
||||
// 防止空Host
|
||||
if len(reqHost) == 0 {
|
||||
ctx := rawReq.Context()
|
||||
var ctx = rawReq.Context()
|
||||
if ctx != nil {
|
||||
addr := ctx.Value(http.LocalAddrContextKey)
|
||||
if addr != nil {
|
||||
@@ -172,11 +171,25 @@ func (this *HTTPListener) ServeHTTP(rawWriter http.ResponseWriter, rawReq *http.
|
||||
server, serverName := this.findNamedServer(domain)
|
||||
if server == nil {
|
||||
if server == nil {
|
||||
this.handleMismatch(rawReq, rawWriter)
|
||||
return
|
||||
// 增加默认的一个服务
|
||||
server = this.emptyServer()
|
||||
} else {
|
||||
serverName = domain
|
||||
}
|
||||
} else if !server.CNameAsDomain && server.CNameDomain == domain {
|
||||
server = this.emptyServer()
|
||||
}
|
||||
|
||||
// 绑定连接
|
||||
if server != nil && server.Id > 0 {
|
||||
var requestConn = rawReq.Context().Value(HTTPConnContextKey)
|
||||
if requestConn != nil {
|
||||
clientConn, ok := requestConn.(ClientConnInterface)
|
||||
if ok {
|
||||
clientConn.SetServerId(server.Id)
|
||||
clientConn.SetUserId(server.UserId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 包装新请求对象
|
||||
@@ -195,48 +208,7 @@ func (this *HTTPListener) ServeHTTP(rawWriter http.ResponseWriter, rawReq *http.
|
||||
req.Do()
|
||||
}
|
||||
|
||||
// 处理域名不匹配的情况
|
||||
func (this *HTTPListener) handleMismatch(rawReq *http.Request, rawWriter http.ResponseWriter) {
|
||||
// TODO 需要记录访问记录和防止CC
|
||||
|
||||
// 是否为健康检查
|
||||
var healthCheckKey = rawReq.Header.Get(serverconfigs.HealthCheckHeaderName)
|
||||
if len(healthCheckKey) > 0 {
|
||||
_, err := nodeutils.Base64DecodeMap(healthCheckKey)
|
||||
if err == nil {
|
||||
rawWriter.WriteHeader(http.StatusOK)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 严格匹配域名模式下,我们拒绝用户访问
|
||||
if sharedNodeConfig.GlobalConfig != nil && sharedNodeConfig.GlobalConfig.HTTPAll.MatchDomainStrictly {
|
||||
var httpAllConfig = sharedNodeConfig.GlobalConfig.HTTPAll
|
||||
var mismatchAction = httpAllConfig.DomainMismatchAction
|
||||
if mismatchAction != nil && mismatchAction.Code == "page" {
|
||||
if mismatchAction.Options != nil {
|
||||
rawWriter.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
rawWriter.WriteHeader(mismatchAction.Options.GetInt("statusCode"))
|
||||
_, _ = rawWriter.Write([]byte(mismatchAction.Options.GetString("contentHTML")))
|
||||
} else {
|
||||
http.Error(rawWriter, "404 page not found: '"+rawReq.URL.String()+"'", http.StatusNotFound)
|
||||
}
|
||||
return
|
||||
} else {
|
||||
hijacker, ok := rawWriter.(http.Hijacker)
|
||||
if ok {
|
||||
conn, _, _ := hijacker.Hijack()
|
||||
if conn != nil {
|
||||
_ = conn.Close()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
http.Error(rawWriter, "404 page not found: '"+rawReq.URL.String()+"'", http.StatusNotFound)
|
||||
}
|
||||
|
||||
// 检查host是否为IP
|
||||
func (this *HTTPListener) isIP(host string) bool {
|
||||
// IPv6
|
||||
if strings.Index(host, "[") > -1 {
|
||||
@@ -251,3 +223,21 @@ func (this *HTTPListener) isIP(host string) bool {
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// 默认的访问日志
|
||||
func (this *HTTPListener) emptyServer() *serverconfigs.ServerConfig {
|
||||
var server = &serverconfigs.ServerConfig{
|
||||
Type: serverconfigs.ServerTypeHTTPProxy,
|
||||
}
|
||||
|
||||
var accessLogRef = serverconfigs.NewHTTPAccessLogRef()
|
||||
// TODO 需要配置是否记录日志
|
||||
accessLogRef.IsOn = true
|
||||
accessLogRef.Fields = append([]int{}, serverconfigs.HTTPAccessLogDefaultFieldsCodes...)
|
||||
server.Web = &serverconfigs.HTTPWebConfig{
|
||||
IsOn: true,
|
||||
AccessLogRef: accessLogRef,
|
||||
}
|
||||
|
||||
return server
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/stats"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"github.com/pires/go-proxyproto"
|
||||
"net"
|
||||
"strings"
|
||||
@@ -18,6 +19,8 @@ type TCPListener struct {
|
||||
BaseListener
|
||||
|
||||
Listener net.Listener
|
||||
|
||||
port int
|
||||
}
|
||||
|
||||
func (this *TCPListener) Serve() error {
|
||||
@@ -26,6 +29,14 @@ func (this *TCPListener) Serve() error {
|
||||
listener = tls.NewListener(listener, this.buildTLSConfig())
|
||||
}
|
||||
|
||||
// 获取分组端口
|
||||
var groupAddr = this.Group.Addr()
|
||||
var portIndex = strings.LastIndex(groupAddr, ":")
|
||||
if portIndex >= 0 {
|
||||
var port = groupAddr[portIndex+1:]
|
||||
this.port = types.Int(port)
|
||||
}
|
||||
|
||||
for {
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
@@ -60,6 +71,25 @@ func (this *TCPListener) handleConn(conn net.Conn) error {
|
||||
return errors.New("no ReverseProxy configured for the server")
|
||||
}
|
||||
|
||||
// 绑定连接和服务
|
||||
clientConn, ok := conn.(ClientConnInterface)
|
||||
if ok {
|
||||
clientConn.SetServerId(server.Id)
|
||||
clientConn.SetUserId(server.UserId)
|
||||
} else {
|
||||
tlsConn, ok := conn.(*tls.Conn)
|
||||
if ok {
|
||||
var internalConn = tlsConn.NetConn()
|
||||
if internalConn != nil {
|
||||
clientConn, ok = internalConn.(ClientConnInterface)
|
||||
if ok {
|
||||
clientConn.SetServerId(server.Id)
|
||||
clientConn.SetUserId(server.UserId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 是否已达到流量限制
|
||||
if this.reachedTrafficLimit() {
|
||||
// 关闭连接
|
||||
@@ -187,26 +217,38 @@ func (this *TCPListener) Close() error {
|
||||
return this.Listener.Close()
|
||||
}
|
||||
|
||||
// 连接源站
|
||||
func (this *TCPListener) connectOrigin(serverId int64, reverseProxy *serverconfigs.ReverseProxyConfig, remoteAddr string) (conn net.Conn, err error) {
|
||||
if reverseProxy == nil {
|
||||
return nil, errors.New("no reverse proxy config")
|
||||
}
|
||||
|
||||
retries := 3
|
||||
var retries = 3
|
||||
var addr string
|
||||
for i := 0; i < retries; i++ {
|
||||
origin := reverseProxy.NextOrigin(nil)
|
||||
var origin = reverseProxy.NextOrigin(nil)
|
||||
if origin == nil {
|
||||
continue
|
||||
}
|
||||
conn, err = OriginConnect(origin, remoteAddr)
|
||||
|
||||
// 回源主机名
|
||||
var requestHost = ""
|
||||
if len(reverseProxy.RequestHost) > 0 {
|
||||
requestHost = reverseProxy.RequestHost
|
||||
}
|
||||
if len(origin.RequestHost) > 0 {
|
||||
requestHost = origin.RequestHost
|
||||
}
|
||||
|
||||
conn, addr, err = OriginConnect(origin, this.port, remoteAddr, requestHost)
|
||||
if err != nil {
|
||||
remotelogs.ServerError(serverId, "TCP_LISTENER", "unable to connect origin: "+origin.Addr.Host+":"+origin.Addr.PortRange+": "+err.Error(), "", nil)
|
||||
remotelogs.ServerError(serverId, "TCP_LISTENER", "unable to connect origin server: "+addr+": "+err.Error(), "", nil)
|
||||
continue
|
||||
} else {
|
||||
return
|
||||
}
|
||||
}
|
||||
err = errors.New("no origin can be used")
|
||||
err = errors.New("server '" + types.String(serverId) + "': no available origin server can be used")
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/stats"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"github.com/pires/go-proxyproto"
|
||||
"net"
|
||||
"strings"
|
||||
@@ -25,11 +26,21 @@ type UDPListener struct {
|
||||
|
||||
reverseProxy *serverconfigs.ReverseProxyConfig
|
||||
|
||||
port int
|
||||
|
||||
isClosed bool
|
||||
}
|
||||
|
||||
func (this *UDPListener) Serve() error {
|
||||
firstServer := this.Group.FirstServer()
|
||||
// 获取分组端口
|
||||
var groupAddr = this.Group.Addr()
|
||||
var portIndex = strings.LastIndex(groupAddr, ":")
|
||||
if portIndex >= 0 {
|
||||
var port = groupAddr[portIndex+1:]
|
||||
this.port = types.Int(port)
|
||||
}
|
||||
|
||||
var firstServer = this.Group.FirstServer()
|
||||
if firstServer == nil {
|
||||
return errors.New("no server available")
|
||||
}
|
||||
@@ -110,7 +121,7 @@ func (this *UDPListener) Reload(group *serverconfigs.ServerAddressGroup) {
|
||||
this.Reset()
|
||||
|
||||
// 重置配置
|
||||
firstServer := this.Group.FirstServer()
|
||||
var firstServer = this.Group.FirstServer()
|
||||
if firstServer == nil {
|
||||
return
|
||||
}
|
||||
@@ -122,15 +133,16 @@ func (this *UDPListener) connectOrigin(serverId int64, reverseProxy *serverconfi
|
||||
return nil, errors.New("no reverse proxy config")
|
||||
}
|
||||
|
||||
retries := 3
|
||||
var retries = 3
|
||||
var addr string
|
||||
for i := 0; i < retries; i++ {
|
||||
origin := reverseProxy.NextOrigin(nil)
|
||||
var origin = reverseProxy.NextOrigin(nil)
|
||||
if origin == nil {
|
||||
continue
|
||||
}
|
||||
conn, err = OriginConnect(origin, remoteAddr.String())
|
||||
conn, addr, err = OriginConnect(origin, this.port, remoteAddr.String(), "")
|
||||
if err != nil {
|
||||
remotelogs.ServerError(serverId, "UDP_LISTENER", "unable to connect origin: "+origin.Addr.Host+":"+origin.Addr.PortRange+": "+err.Error(), "", nil)
|
||||
remotelogs.ServerError(serverId, "UDP_LISTENER", "unable to connect origin server: "+addr+": "+err.Error(), "", nil)
|
||||
continue
|
||||
} else {
|
||||
// PROXY Protocol
|
||||
@@ -159,7 +171,7 @@ func (this *UDPListener) connectOrigin(serverId int64, reverseProxy *serverconfi
|
||||
return
|
||||
}
|
||||
}
|
||||
err = errors.New("no origin can be used")
|
||||
err = errors.New("server '" + types.String(serverId) + "': no available origin server can be used")
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -386,7 +386,7 @@ func (this *Node) loop() error {
|
||||
}
|
||||
if len(resp.DdosProtectionJSON) == 0 {
|
||||
if sharedNodeConfig != nil {
|
||||
sharedNodeConfig.DDOSProtection = nil
|
||||
sharedNodeConfig.DDoSProtection = nil
|
||||
}
|
||||
} else {
|
||||
var ddosProtectionConfig = &ddosconfigs.ProtectionConfig{}
|
||||
@@ -395,6 +395,10 @@ func (this *Node) loop() error {
|
||||
return errors.New("decode DDoS protection config failed: " + err.Error())
|
||||
}
|
||||
|
||||
if sharedNodeConfig != nil {
|
||||
sharedNodeConfig.DDoSProtection = ddosProtectionConfig
|
||||
}
|
||||
|
||||
err = firewalls.SharedDDoSProtectionManager.Apply(ddosProtectionConfig)
|
||||
if err != nil {
|
||||
// 不阻塞
|
||||
|
||||
@@ -8,5 +8,7 @@ type OriginState struct {
|
||||
CountFails int64
|
||||
UpdatedAt int64
|
||||
Config *serverconfigs.OriginConfig
|
||||
Addr string
|
||||
TLSHost string
|
||||
ReverseProxy *serverconfigs.ReverseProxyConfig
|
||||
}
|
||||
|
||||
@@ -26,6 +26,10 @@ func init() {
|
||||
})
|
||||
}
|
||||
|
||||
const (
|
||||
maxOriginStates = 512 // 最多可以监控的源站状态数量
|
||||
)
|
||||
|
||||
// OriginStateManager 源站状态管理
|
||||
type OriginStateManager struct {
|
||||
stateMap map[int64]*OriginState // originId => *OriginState
|
||||
@@ -44,14 +48,6 @@ func NewOriginStateManager() *OriginStateManager {
|
||||
|
||||
// Start 启动
|
||||
func (this *OriginStateManager) Start() {
|
||||
events.OnKey(events.EventReload, this, func() {
|
||||
// TODO 检查源站是否有变化
|
||||
|
||||
this.locker.Lock()
|
||||
this.stateMap = map[int64]*OriginState{}
|
||||
this.locker.Unlock()
|
||||
})
|
||||
|
||||
if Tea.IsTesting() {
|
||||
this.ticker = time.NewTicker(10 * time.Second)
|
||||
}
|
||||
@@ -71,7 +67,8 @@ func (this *OriginStateManager) Stop() {
|
||||
|
||||
// Loop 单次循环检查
|
||||
func (this *OriginStateManager) Loop() error {
|
||||
if sharedNodeConfig == nil {
|
||||
var nodeConfig = sharedNodeConfig // 复制
|
||||
if nodeConfig == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -82,12 +79,12 @@ func (this *OriginStateManager) Loop() error {
|
||||
this.locker.Lock()
|
||||
for originId, state := range this.stateMap {
|
||||
// 检查Origin是否正在使用
|
||||
config := sharedNodeConfig.FindOrigin(originId)
|
||||
if config == nil || !config.IsOn {
|
||||
var originConfig = nodeConfig.FindOrigin(originId)
|
||||
if originConfig == nil || !originConfig.IsOn {
|
||||
delete(this.stateMap, originId)
|
||||
continue
|
||||
}
|
||||
state.Config = config
|
||||
state.Config = originConfig
|
||||
currentStates = append(currentStates, state)
|
||||
}
|
||||
this.locker.Unlock()
|
||||
@@ -97,12 +94,12 @@ func (this *OriginStateManager) Loop() error {
|
||||
}
|
||||
|
||||
var count = len(currentStates)
|
||||
wg := &sync.WaitGroup{}
|
||||
var wg = &sync.WaitGroup{}
|
||||
wg.Add(count)
|
||||
for _, state := range currentStates {
|
||||
go func(state *OriginState) {
|
||||
defer wg.Done()
|
||||
conn, err := OriginConnect(state.Config, "")
|
||||
conn, _, err := OriginConnect(state.Config, 0, "", state.TLSHost)
|
||||
if err == nil {
|
||||
_ = conn.Close()
|
||||
|
||||
@@ -125,7 +122,7 @@ func (this *OriginStateManager) Loop() error {
|
||||
}
|
||||
|
||||
// Fail 添加失败的源站
|
||||
func (this *OriginStateManager) Fail(origin *serverconfigs.OriginConfig, reverseProxy *serverconfigs.ReverseProxyConfig, callback func()) {
|
||||
func (this *OriginStateManager) Fail(origin *serverconfigs.OriginConfig, tlsHost string, reverseProxy *serverconfigs.ReverseProxyConfig, callback func()) {
|
||||
if origin == nil || origin.Id <= 0 {
|
||||
return
|
||||
}
|
||||
@@ -139,6 +136,7 @@ func (this *OriginStateManager) Fail(origin *serverconfigs.OriginConfig, reverse
|
||||
state.Config.IsOk = true
|
||||
}
|
||||
|
||||
state.TLSHost = tlsHost
|
||||
state.CountFails++
|
||||
state.Config = origin
|
||||
state.ReverseProxy = reverseProxy
|
||||
@@ -154,12 +152,17 @@ func (this *OriginStateManager) Fail(origin *serverconfigs.OriginConfig, reverse
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.stateMap[origin.Id] = &OriginState{
|
||||
CountFails: 1,
|
||||
Config: origin,
|
||||
ReverseProxy: reverseProxy,
|
||||
UpdatedAt: timestamp,
|
||||
// 同时最多监控 N 个源站地址
|
||||
if len(this.stateMap) < maxOriginStates {
|
||||
this.stateMap[origin.Id] = &OriginState{
|
||||
CountFails: 1,
|
||||
Config: origin,
|
||||
TLSHost: tlsHost,
|
||||
ReverseProxy: reverseProxy,
|
||||
UpdatedAt: timestamp,
|
||||
}
|
||||
}
|
||||
|
||||
origin.IsOk = true
|
||||
}
|
||||
this.locker.Unlock()
|
||||
|
||||
@@ -3,47 +3,54 @@ package nodes
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"net"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// OriginConnect 连接源站
|
||||
func OriginConnect(origin *serverconfigs.OriginConfig, remoteAddr string) (net.Conn, error) {
|
||||
func OriginConnect(origin *serverconfigs.OriginConfig, serverPort int, remoteAddr string, tlsHost string) (originConn net.Conn, originAddr string, err error) {
|
||||
if origin.Addr == nil {
|
||||
return nil, errors.New("origin server address should not be empty")
|
||||
return nil, "", errors.New("origin server address should not be empty")
|
||||
}
|
||||
|
||||
// 支持TOA的连接
|
||||
// 这个条件很重要,如果没有传递remoteAddr,表示不使用TOA
|
||||
if len(remoteAddr) > 0 {
|
||||
toaConfig := sharedTOAManager.Config()
|
||||
var toaConfig = sharedTOAManager.Config()
|
||||
if toaConfig != nil && toaConfig.IsOn {
|
||||
retries := 3
|
||||
var retries = 3
|
||||
for i := 1; i <= retries; i++ {
|
||||
port := int(toaConfig.RandLocalPort())
|
||||
err := sharedTOAManager.SendMsg("add:" + strconv.Itoa(port) + ":" + remoteAddr)
|
||||
var port = int(toaConfig.RandLocalPort())
|
||||
err = sharedTOAManager.SendMsg("add:" + strconv.Itoa(port) + ":" + remoteAddr)
|
||||
if err != nil {
|
||||
remotelogs.Error("TOA", "add failed: "+err.Error())
|
||||
} else {
|
||||
dialer := net.Dialer{
|
||||
var dialer = net.Dialer{
|
||||
Timeout: origin.ConnTimeoutDuration(),
|
||||
LocalAddr: &net.TCPAddr{
|
||||
Port: port,
|
||||
},
|
||||
}
|
||||
originAddr = origin.Addr.PickAddress()
|
||||
|
||||
// 端口跟随
|
||||
if origin.FollowPort && serverPort > 0 {
|
||||
originAddr = configutils.QuoteIP(origin.Addr.Host) + ":" + types.String(serverPort)
|
||||
}
|
||||
|
||||
var conn net.Conn
|
||||
switch origin.Addr.Protocol {
|
||||
case "", serverconfigs.ProtocolTCP, serverconfigs.ProtocolHTTP:
|
||||
// TODO 支持TCP4/TCP6
|
||||
// TODO 支持指定特定网卡
|
||||
// TODO Addr支持端口范围,如果有多个端口时,随机一个端口使用
|
||||
conn, err = dialer.Dial("tcp", origin.Addr.Host+":"+origin.Addr.PortRange)
|
||||
conn, err = dialer.Dial("tcp", originAddr)
|
||||
case serverconfigs.ProtocolTLS, serverconfigs.ProtocolHTTPS:
|
||||
// TODO 支持TCP4/TCP6
|
||||
// TODO 支持指定特定网卡
|
||||
// TODO Addr支持端口范围,如果有多个端口时,随机一个端口使用
|
||||
|
||||
var tlsConfig = &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
@@ -58,29 +65,38 @@ func OriginConnect(origin *serverconfigs.OriginConfig, remoteAddr string) (net.C
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(tlsHost) > 0 {
|
||||
tlsConfig.ServerName = tlsHost
|
||||
}
|
||||
|
||||
conn, err = tls.DialWithDialer(&dialer, "tcp", origin.Addr.Host+":"+origin.Addr.PortRange, tlsConfig)
|
||||
conn, err = tls.DialWithDialer(&dialer, "tcp", originAddr, tlsConfig)
|
||||
}
|
||||
|
||||
// TODO 需要在合适的时机删除TOA记录
|
||||
if err == nil || i == retries {
|
||||
return conn, err
|
||||
return conn, originAddr, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
originAddr = origin.Addr.PickAddress()
|
||||
|
||||
// 端口跟随
|
||||
if origin.FollowPort && serverPort > 0 {
|
||||
originAddr = configutils.QuoteIP(origin.Addr.Host) + ":" + types.String(serverPort)
|
||||
}
|
||||
|
||||
switch origin.Addr.Protocol {
|
||||
case "", serverconfigs.ProtocolTCP, serverconfigs.ProtocolHTTP:
|
||||
// TODO 支持TCP4/TCP6
|
||||
// TODO 支持指定特定网卡
|
||||
// TODO Addr支持端口范围,如果有多个端口时,随机一个端口使用
|
||||
return net.DialTimeout("tcp", origin.Addr.Host+":"+origin.Addr.PortRange, origin.ConnTimeoutDuration())
|
||||
originConn, err = net.DialTimeout("tcp", originAddr, origin.ConnTimeoutDuration())
|
||||
return originConn, originAddr, err
|
||||
case serverconfigs.ProtocolTLS, serverconfigs.ProtocolHTTPS:
|
||||
// TODO 支持TCP4/TCP6
|
||||
// TODO 支持指定特定网卡
|
||||
// TODO Addr支持端口范围,如果有多个端口时,随机一个端口使用
|
||||
|
||||
var tlsConfig = &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
@@ -95,17 +111,22 @@ func OriginConnect(origin *serverconfigs.OriginConfig, remoteAddr string) (net.C
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tls.Dial("tcp", origin.Addr.Host+":"+origin.Addr.PortRange, tlsConfig)
|
||||
case serverconfigs.ProtocolUDP:
|
||||
addr, err := net.ResolveUDPAddr("udp", origin.Addr.Host+":"+origin.Addr.PortRange)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if len(tlsHost) > 0 {
|
||||
tlsConfig.ServerName = tlsHost
|
||||
}
|
||||
return net.DialUDP("udp", nil, addr)
|
||||
|
||||
originConn, err = tls.Dial("tcp", originAddr, tlsConfig)
|
||||
return originConn, originAddr, err
|
||||
case serverconfigs.ProtocolUDP:
|
||||
addr, err := net.ResolveUDPAddr("udp", originAddr)
|
||||
if err != nil {
|
||||
return nil, originAddr, err
|
||||
}
|
||||
originConn, err = net.DialUDP("udp", nil, addr)
|
||||
return originConn, originAddr, err
|
||||
}
|
||||
|
||||
// TODO 支持从Unix、Pipe、HTTP、HTTPS中读取数据
|
||||
|
||||
return nil, errors.New("invalid origin scheme '" + origin.Addr.Protocol.String() + "'")
|
||||
return nil, originAddr, errors.New("invalid origin scheme '" + origin.Addr.Protocol.String() + "'")
|
||||
}
|
||||
|
||||
@@ -203,12 +203,12 @@ func (this *UpgradeManager) unzip(zipPath string) error {
|
||||
}
|
||||
|
||||
// 先改先前的可执行文件
|
||||
err := os.Rename(target+"/bin/edge-node", target+"/bin/.edge-node.old")
|
||||
err := os.Rename(target+"/bin/edge-node", target+"/bin/.edge-node.dist")
|
||||
hasBackup := err == nil
|
||||
defer func() {
|
||||
if !isOk && hasBackup {
|
||||
// 失败时还原
|
||||
_ = os.Rename(target+"/bin/.edge-node.old", target+"/bin/edge-node")
|
||||
_ = os.Rename(target+"/bin/.edge-node.dist", target+"/bin/edge-node")
|
||||
}
|
||||
}()
|
||||
|
||||
|
||||
@@ -136,6 +136,10 @@ func (this *Regexp) Match(s []byte) bool {
|
||||
return this.rawRegexp.Match(s)
|
||||
}
|
||||
|
||||
func (this *Regexp) FindStringSubmatch(s string) []string {
|
||||
return this.rawRegexp.FindStringSubmatch(s)
|
||||
}
|
||||
|
||||
// ParseKeywords 提取表达式中的关键词
|
||||
func (this *Regexp) ParseKeywords(exp string) (keywords []string) {
|
||||
if len(exp) == 0 {
|
||||
|
||||
@@ -125,11 +125,36 @@ func BenchmarkRegexp_MatchString2(b *testing.B) {
|
||||
func BenchmarkRegexp_MatchString_CaseSensitive(b *testing.B) {
|
||||
var r = re.MustCompile("(abc|def|ghi)")
|
||||
b.Log("keywords:", r.Keywords())
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
r.MatchString("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkRegexp_MatchString_CaseSensitive2(b *testing.B) {
|
||||
var r = regexp.MustCompile("(abc|def|ghi)")
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
r.MatchString("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkRegexp_MatchString_VS_FindSubString1(b *testing.B) {
|
||||
var r = re.MustCompile("(?i)(chrome)")
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = r.Raw().MatchString("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkRegexp_MatchString_VS_FindSubString2(b *testing.B) {
|
||||
var r = re.MustCompile("(?i)(chrome)")
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = r.Raw().FindStringSubmatch("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36")
|
||||
}
|
||||
}
|
||||
|
||||
func testCompareStrings(s1 []string, s2 []string) bool {
|
||||
if len(s1) != len(s2) {
|
||||
return false
|
||||
|
||||
@@ -123,6 +123,10 @@ func (this *RPCClient) ServerDailyStatRPC() pb.ServerDailyStatServiceClient {
|
||||
return pb.NewServerDailyStatServiceClient(this.pickConn())
|
||||
}
|
||||
|
||||
func (this *RPCClient) ServerBandwidthStatRPC() pb.ServerBandwidthStatServiceClient {
|
||||
return pb.NewServerBandwidthStatServiceClient(this.pickConn())
|
||||
}
|
||||
|
||||
func (this *RPCClient) MetricStatRPC() pb.MetricStatServiceClient {
|
||||
return pb.NewMetricStatServiceClient(this.pickConn())
|
||||
}
|
||||
|
||||
152
internal/stats/bandwidth_stat_manager.go
Normal file
152
internal/stats/bandwidth_stat_manager.go
Normal file
@@ -0,0 +1,152 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package stats
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/events"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
||||
"github.com/iwind/TeaGo/logs"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var SharedBandwidthStatManager = NewBandwidthStatManager()
|
||||
|
||||
func init() {
|
||||
events.On(events.EventLoaded, func() {
|
||||
goman.New(func() {
|
||||
SharedBandwidthStatManager.Start()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
type BandwidthStat struct {
|
||||
Day string
|
||||
TimeAt string
|
||||
UserId int64
|
||||
ServerId int64
|
||||
|
||||
CurrentBytes int64
|
||||
CurrentTimestamp int64
|
||||
MaxBytes int64
|
||||
}
|
||||
|
||||
// BandwidthStatManager 服务带宽统计
|
||||
type BandwidthStatManager struct {
|
||||
m map[string]*BandwidthStat // key => *BandwidthStat
|
||||
|
||||
lastTime string // 上一次执行的时间
|
||||
|
||||
ticker *time.Ticker
|
||||
locker sync.Mutex
|
||||
}
|
||||
|
||||
func NewBandwidthStatManager() *BandwidthStatManager {
|
||||
return &BandwidthStatManager{
|
||||
m: map[string]*BandwidthStat{},
|
||||
ticker: time.NewTicker(1 * time.Minute), // 时间小于1分钟是为了更快速地上传结果
|
||||
}
|
||||
}
|
||||
|
||||
func (this *BandwidthStatManager) Start() {
|
||||
for range this.ticker.C {
|
||||
err := this.Loop()
|
||||
if err != nil && !rpc.IsConnError(err) {
|
||||
remotelogs.Error("BANDWIDTH_STAT_MANAGER", err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (this *BandwidthStatManager) Loop() error {
|
||||
var now = time.Now()
|
||||
var day = timeutil.Format("Ymd", now)
|
||||
var currentTime = timeutil.FormatTime("Hi", now.Unix()/300*300)
|
||||
|
||||
if this.lastTime == currentTime {
|
||||
return nil
|
||||
}
|
||||
this.lastTime = currentTime
|
||||
|
||||
var pbStats = []*pb.ServerBandwidthStat{}
|
||||
|
||||
this.locker.Lock()
|
||||
for key, stat := range this.m {
|
||||
if stat.Day < day || stat.TimeAt < currentTime {
|
||||
pbStats = append(pbStats, &pb.ServerBandwidthStat{
|
||||
Id: 0,
|
||||
UserId: stat.UserId,
|
||||
ServerId: stat.ServerId,
|
||||
Day: stat.Day,
|
||||
TimeAt: stat.TimeAt,
|
||||
Bytes: stat.MaxBytes,
|
||||
})
|
||||
delete(this.m, key)
|
||||
}
|
||||
}
|
||||
this.locker.Unlock()
|
||||
|
||||
if len(pbStats) > 0 {
|
||||
// 上传
|
||||
rpcClient, err := rpc.SharedRPC()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = rpcClient.ServerBandwidthStatRPC().UploadServerBandwidthStats(rpcClient.Context(), &pb.UploadServerBandwidthStatsRequest{ServerBandwidthStats: pbStats})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Add 添加带宽数据
|
||||
func (this *BandwidthStatManager) Add(userId int64, serverId int64, bytes int64) {
|
||||
if serverId <= 0 || bytes == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
var now = time.Now()
|
||||
var timestamp = now.Unix()
|
||||
var day = timeutil.Format("Ymd", now)
|
||||
var timeAt = timeutil.FormatTime("Hi", now.Unix()/300*300)
|
||||
var key = types.String(serverId) + "@" + day + "@" + timeAt
|
||||
|
||||
this.locker.Lock()
|
||||
stat, ok := this.m[key]
|
||||
if ok {
|
||||
// 此刻如果发生用户ID(userId)的变化也忽略,等N分钟后有新记录后再换
|
||||
|
||||
if stat.CurrentTimestamp == timestamp {
|
||||
stat.CurrentBytes += bytes
|
||||
} else {
|
||||
stat.CurrentBytes = bytes
|
||||
stat.CurrentTimestamp = timestamp
|
||||
}
|
||||
if stat.CurrentBytes > stat.MaxBytes {
|
||||
stat.MaxBytes = stat.CurrentBytes
|
||||
}
|
||||
} else {
|
||||
this.m[key] = &BandwidthStat{
|
||||
Day: day,
|
||||
TimeAt: timeAt,
|
||||
UserId: userId,
|
||||
ServerId: serverId,
|
||||
CurrentBytes: bytes,
|
||||
MaxBytes: bytes,
|
||||
CurrentTimestamp: timestamp,
|
||||
}
|
||||
}
|
||||
this.locker.Unlock()
|
||||
}
|
||||
|
||||
func (this *BandwidthStatManager) Inspect() {
|
||||
this.locker.Lock()
|
||||
logs.PrintAsJSON(this.m)
|
||||
this.locker.Unlock()
|
||||
}
|
||||
33
internal/stats/bandwidth_stat_manager_test.go
Normal file
33
internal/stats/bandwidth_stat_manager_test.go
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package stats_test
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeNode/internal/stats"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestBandwidthStatManager_Add(t *testing.T) {
|
||||
var manager = stats.NewBandwidthStatManager()
|
||||
manager.Add(1, 1, 10)
|
||||
manager.Add(1, 1, 10)
|
||||
manager.Add(1, 1, 10)
|
||||
time.Sleep(1 * time.Second)
|
||||
manager.Add(1, 1, 15)
|
||||
time.Sleep(1 * time.Second)
|
||||
manager.Add(1, 1, 25)
|
||||
manager.Add(1, 1, 75)
|
||||
manager.Inspect()
|
||||
}
|
||||
|
||||
func TestBandwidthStatManager_Loop(t *testing.T) {
|
||||
var manager = stats.NewBandwidthStatManager()
|
||||
manager.Add(1, 1, 10)
|
||||
manager.Add(1, 1, 10)
|
||||
manager.Add(1, 1, 10)
|
||||
err := manager.Loop()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -123,7 +123,7 @@ func (this *HTTPRequestStatManager) AddRemoteAddr(serverId int64, remoteAddr str
|
||||
if remoteAddr[0] == '[' { // 排除IPv6
|
||||
return
|
||||
}
|
||||
index := strings.Index(remoteAddr, ":")
|
||||
var index = strings.Index(remoteAddr, ":")
|
||||
var ip string
|
||||
if index < 0 {
|
||||
ip = remoteAddr
|
||||
@@ -177,18 +177,18 @@ func (this *HTTPRequestStatManager) AddFirewallRuleGroupId(serverId int64, firew
|
||||
|
||||
// Loop 单个循环
|
||||
func (this *HTTPRequestStatManager) Loop() error {
|
||||
timeout := time.NewTimer(10 * time.Minute) // 执行的最大时间
|
||||
var timeout = time.NewTimer(10 * time.Minute) // 执行的最大时间
|
||||
Loop:
|
||||
for {
|
||||
select {
|
||||
case ipString := <-this.ipChan:
|
||||
// serverId@ip@bytes@isAttack
|
||||
pieces := strings.Split(ipString, "@")
|
||||
var pieces = strings.Split(ipString, "@")
|
||||
if len(pieces) < 4 {
|
||||
continue
|
||||
}
|
||||
serverId := pieces[0]
|
||||
ip := pieces[1]
|
||||
var serverId = pieces[0]
|
||||
var ip = pieces[1]
|
||||
|
||||
if iplibrary.SharedLibrary != nil {
|
||||
result, err := iplibrary.SharedLibrary.Lookup(ip)
|
||||
@@ -216,12 +216,12 @@ Loop:
|
||||
}
|
||||
}
|
||||
case userAgentString := <-this.userAgentChan:
|
||||
atIndex := strings.Index(userAgentString, "@")
|
||||
var atIndex = strings.Index(userAgentString, "@")
|
||||
if atIndex < 0 {
|
||||
continue
|
||||
}
|
||||
serverId := userAgentString[:atIndex]
|
||||
userAgent := userAgentString[atIndex+1:]
|
||||
var serverId = userAgentString[:atIndex]
|
||||
var userAgent = userAgentString[atIndex+1:]
|
||||
|
||||
var result = SharedUserAgentParser.Parse(userAgent)
|
||||
var osInfo = result.OS
|
||||
@@ -264,12 +264,12 @@ func (this *HTTPRequestStatManager) Upload() error {
|
||||
}
|
||||
|
||||
// 月份相关
|
||||
pbCities := []*pb.UploadServerHTTPRequestStatRequest_RegionCity{}
|
||||
pbProviders := []*pb.UploadServerHTTPRequestStatRequest_RegionProvider{}
|
||||
pbSystems := []*pb.UploadServerHTTPRequestStatRequest_System{}
|
||||
pbBrowsers := []*pb.UploadServerHTTPRequestStatRequest_Browser{}
|
||||
var pbCities = []*pb.UploadServerHTTPRequestStatRequest_RegionCity{}
|
||||
var pbProviders = []*pb.UploadServerHTTPRequestStatRequest_RegionProvider{}
|
||||
var pbSystems = []*pb.UploadServerHTTPRequestStatRequest_System{}
|
||||
var pbBrowsers = []*pb.UploadServerHTTPRequestStatRequest_Browser{}
|
||||
for k, stat := range this.cityMap {
|
||||
pieces := strings.SplitN(k, "@", 4)
|
||||
var pieces = strings.SplitN(k, "@", 4)
|
||||
pbCities = append(pbCities, &pb.UploadServerHTTPRequestStatRequest_RegionCity{
|
||||
ServerId: types.Int64(pieces[0]),
|
||||
CountryName: pieces[1],
|
||||
@@ -282,7 +282,7 @@ func (this *HTTPRequestStatManager) Upload() error {
|
||||
})
|
||||
}
|
||||
for k, count := range this.providerMap {
|
||||
pieces := strings.SplitN(k, "@", 2)
|
||||
var pieces = strings.SplitN(k, "@", 2)
|
||||
pbProviders = append(pbProviders, &pb.UploadServerHTTPRequestStatRequest_RegionProvider{
|
||||
ServerId: types.Int64(pieces[0]),
|
||||
Name: pieces[1],
|
||||
@@ -290,7 +290,7 @@ func (this *HTTPRequestStatManager) Upload() error {
|
||||
})
|
||||
}
|
||||
for k, count := range this.systemMap {
|
||||
pieces := strings.SplitN(k, "@", 3)
|
||||
var pieces = strings.SplitN(k, "@", 3)
|
||||
pbSystems = append(pbSystems, &pb.UploadServerHTTPRequestStatRequest_System{
|
||||
ServerId: types.Int64(pieces[0]),
|
||||
Name: pieces[1],
|
||||
@@ -299,7 +299,7 @@ func (this *HTTPRequestStatManager) Upload() error {
|
||||
})
|
||||
}
|
||||
for k, count := range this.browserMap {
|
||||
pieces := strings.SplitN(k, "@", 3)
|
||||
var pieces = strings.SplitN(k, "@", 3)
|
||||
pbBrowsers = append(pbBrowsers, &pb.UploadServerHTTPRequestStatRequest_Browser{
|
||||
ServerId: types.Int64(pieces[0]),
|
||||
Name: pieces[1],
|
||||
@@ -309,9 +309,9 @@ func (this *HTTPRequestStatManager) Upload() error {
|
||||
}
|
||||
|
||||
// 防火墙相关
|
||||
pbFirewallRuleGroups := []*pb.UploadServerHTTPRequestStatRequest_HTTPFirewallRuleGroup{}
|
||||
var pbFirewallRuleGroups = []*pb.UploadServerHTTPRequestStatRequest_HTTPFirewallRuleGroup{}
|
||||
for k, count := range this.dailyFirewallRuleGroupMap {
|
||||
pieces := strings.SplitN(k, "@", 3)
|
||||
var pieces = strings.SplitN(k, "@", 3)
|
||||
pbFirewallRuleGroups = append(pbFirewallRuleGroups, &pb.UploadServerHTTPRequestStatRequest_HTTPFirewallRuleGroup{
|
||||
ServerId: types.Int64(pieces[0]),
|
||||
HttpFirewallRuleGroupId: types.Int64(pieces[1]),
|
||||
|
||||
@@ -95,6 +95,10 @@ func (this *TrafficStatManager) Start(configFunc func() *nodeconfigs.NodeConfig)
|
||||
|
||||
// Add 添加流量
|
||||
func (this *TrafficStatManager) Add(serverId int64, domain string, bytes int64, cachedBytes int64, countRequests int64, countCachedRequests int64, countAttacks int64, attackBytes int64, checkingTrafficLimit bool, planId int64) {
|
||||
if serverId == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if bytes == 0 && countRequests == 0 {
|
||||
return
|
||||
}
|
||||
@@ -139,7 +143,7 @@ func (this *TrafficStatManager) Add(serverId int64, domain string, bytes int64,
|
||||
|
||||
// Upload 上传流量
|
||||
func (this *TrafficStatManager) Upload() error {
|
||||
config := this.configFunc()
|
||||
var config = this.configFunc()
|
||||
if config == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -150,8 +154,8 @@ func (this *TrafficStatManager) Upload() error {
|
||||
}
|
||||
|
||||
this.locker.Lock()
|
||||
itemMap := this.itemMap
|
||||
domainMap := this.domainsMap
|
||||
var itemMap = this.itemMap
|
||||
var domainMap = this.domainsMap
|
||||
this.itemMap = map[string]*TrafficItem{}
|
||||
this.domainsMap = map[string]*TrafficItem{}
|
||||
this.locker.Unlock()
|
||||
|
||||
@@ -25,3 +25,16 @@ func ListenReuseAddr(network string, addr string) (net.Listener, error) {
|
||||
}
|
||||
return config.Listen(context.Background(), network, addr)
|
||||
}
|
||||
|
||||
// ParseAddrHost 分析地址中的主机名部分
|
||||
func ParseAddrHost(addr string) string {
|
||||
if len(addr) == 0 {
|
||||
return addr
|
||||
}
|
||||
|
||||
host, _, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
return addr
|
||||
}
|
||||
return host
|
||||
}
|
||||
|
||||
14
internal/utils/net_test.go
Normal file
14
internal/utils/net_test.go
Normal file
@@ -0,0 +1,14 @@
|
||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||
|
||||
package utils_test
|
||||
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseAddrHost(t *testing.T) {
|
||||
for _, addr := range []string{"a", "example.com", "example.com:1234", "::1", "[::1]", "[::1]:8080"} {
|
||||
t.Log(addr + " => " + utils.ParseAddrHost(addr))
|
||||
}
|
||||
}
|
||||
@@ -35,7 +35,7 @@ func (this *GoGroupAction) Perform(waf *WAF, group *RuleGroup, set *RuleSet, req
|
||||
return true
|
||||
}
|
||||
|
||||
b, nextSet, err := nextGroup.MatchRequest(request)
|
||||
b, _, nextSet, err := nextGroup.MatchRequest(request)
|
||||
if err != nil {
|
||||
logs.Error(err)
|
||||
return true
|
||||
|
||||
@@ -40,7 +40,7 @@ func (this *GoSetAction) Perform(waf *WAF, group *RuleGroup, set *RuleSet, reque
|
||||
return true
|
||||
}
|
||||
|
||||
b, err := nextSet.MatchRequest(request)
|
||||
b, _, err := nextSet.MatchRequest(request)
|
||||
if err != nil {
|
||||
logs.Error(err)
|
||||
return true
|
||||
|
||||
@@ -30,7 +30,7 @@ func (this *CCCheckpoint) Start() {
|
||||
this.cache = ttlcache.NewCache()
|
||||
}
|
||||
|
||||
func (this *CCCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *CCCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
value = 0
|
||||
|
||||
if this.cache == nil {
|
||||
@@ -120,7 +120,7 @@ func (this *CCCheckpoint) RequestValue(req requests.Request, param string, optio
|
||||
return
|
||||
}
|
||||
|
||||
func (this *CCCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *CCCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
if this.IsRequest() {
|
||||
return this.RequestValue(req, param, options)
|
||||
}
|
||||
|
||||
@@ -5,20 +5,34 @@ package checkpoints
|
||||
import (
|
||||
"github.com/TeaOSLab/EdgeNode/internal/ttlcache"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/waf/requests"
|
||||
"github.com/TeaOSLab/EdgeNode/internal/zero"
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
"github.com/iwind/TeaGo/types"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var ccCache = ttlcache.NewCache()
|
||||
|
||||
var commonFileExtensionsMap = map[string]zero.Zero{
|
||||
".ico": zero.New(),
|
||||
".jpg": zero.New(),
|
||||
".jpeg": zero.New(),
|
||||
".gif": zero.New(),
|
||||
".png": zero.New(),
|
||||
".webp": zero.New(),
|
||||
".woff2": zero.New(),
|
||||
".js": zero.New(),
|
||||
".css": zero.New(),
|
||||
}
|
||||
|
||||
// CC2Checkpoint 新的CC
|
||||
type CC2Checkpoint struct {
|
||||
Checkpoint
|
||||
}
|
||||
|
||||
func (this *CC2Checkpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *CC2Checkpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
var keys = options.GetSlice("keys")
|
||||
var keyValues = []string{}
|
||||
for _, key := range keys {
|
||||
@@ -38,11 +52,25 @@ func (this *CC2Checkpoint) RequestValue(req requests.Request, param string, opti
|
||||
threshold = 1000
|
||||
}
|
||||
|
||||
var ignoreCommonFiles = options.GetBool("ignoreCommonFiles")
|
||||
if ignoreCommonFiles {
|
||||
var rawReq = req.WAFRaw()
|
||||
if len(rawReq.Referer()) > 0 {
|
||||
var ext = filepath.Ext(rawReq.URL.Path)
|
||||
if len(ext) > 0 {
|
||||
_, ok := commonFileExtensionsMap[strings.ToLower(ext)]
|
||||
if ok {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
value = ccCache.IncreaseInt64("WAF-CC-"+strings.Join(keyValues, "@"), 1, time.Now().Unix()+period, false)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (this *CC2Checkpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *CC2Checkpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -17,10 +17,10 @@ type CheckpointInterface interface {
|
||||
IsComposed() bool
|
||||
|
||||
// RequestValue get request value
|
||||
RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, sysErr error, userErr error)
|
||||
RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error)
|
||||
|
||||
// ResponseValue get response value
|
||||
ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, sysErr error, userErr error)
|
||||
ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error)
|
||||
|
||||
// ParamOptions param option list
|
||||
ParamOptions() *ParamOptions
|
||||
|
||||
@@ -11,7 +11,7 @@ type RequestAllCheckpoint struct {
|
||||
Checkpoint
|
||||
}
|
||||
|
||||
func (this *RequestAllCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestAllCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
valueBytes := []byte{}
|
||||
if len(req.WAFRaw().RequestURI) > 0 {
|
||||
valueBytes = append(valueBytes, req.WAFRaw().RequestURI...)
|
||||
@@ -28,10 +28,11 @@ func (this *RequestAllCheckpoint) RequestValue(req requests.Request, param strin
|
||||
valueBytes = append(valueBytes, ' ')
|
||||
|
||||
var bodyData = req.WAFGetCacheBody()
|
||||
hasRequestBody = true
|
||||
if len(bodyData) == 0 {
|
||||
data, err := req.WAFReadBody(utils.MaxBodySize) // read body
|
||||
if err != nil {
|
||||
return "", err, nil
|
||||
return "", hasRequestBody, err, nil
|
||||
}
|
||||
|
||||
bodyData = data
|
||||
@@ -46,7 +47,7 @@ func (this *RequestAllCheckpoint) RequestValue(req requests.Request, param strin
|
||||
return
|
||||
}
|
||||
|
||||
func (this *RequestAllCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestAllCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
value = ""
|
||||
if this.IsRequest() {
|
||||
return this.RequestValue(req, param, options)
|
||||
|
||||
@@ -18,7 +18,7 @@ func TestRequestAllCheckpoint_RequestValue(t *testing.T) {
|
||||
}
|
||||
|
||||
checkpoint := new(RequestAllCheckpoint)
|
||||
v, sysErr, userErr := checkpoint.RequestValue(requests.NewTestRequest(req), "", nil)
|
||||
v, _, sysErr, userErr := checkpoint.RequestValue(requests.NewTestRequest(req), "", nil)
|
||||
if sysErr != nil {
|
||||
t.Fatal(sysErr)
|
||||
}
|
||||
@@ -42,7 +42,7 @@ func TestRequestAllCheckpoint_RequestValue_Max(t *testing.T) {
|
||||
}
|
||||
|
||||
checkpoint := new(RequestBodyCheckpoint)
|
||||
value, err, _ := checkpoint.RequestValue(requests.NewTestRequest(req), "", nil)
|
||||
value, _, err, _ := checkpoint.RequestValue(requests.NewTestRequest(req), "", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -65,6 +65,6 @@ func BenchmarkRequestAllCheckpoint_RequestValue(b *testing.B) {
|
||||
|
||||
checkpoint := new(RequestAllCheckpoint)
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _, _ = checkpoint.RequestValue(requests.NewTestRequest(req), "", nil)
|
||||
_, _, _, _ = checkpoint.RequestValue(requests.NewTestRequest(req), "", nil)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,11 +9,11 @@ type RequestArgCheckpoint struct {
|
||||
Checkpoint
|
||||
}
|
||||
|
||||
func (this *RequestArgCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
return req.WAFRaw().URL.Query().Get(param), nil, nil
|
||||
func (this *RequestArgCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
return req.WAFRaw().URL.Query().Get(param), hasRequestBody, nil, nil
|
||||
}
|
||||
|
||||
func (this *RequestArgCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestArgCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
if this.IsRequest() {
|
||||
return this.RequestValue(req, param, options)
|
||||
}
|
||||
|
||||
@@ -9,12 +9,12 @@ type RequestArgsCheckpoint struct {
|
||||
Checkpoint
|
||||
}
|
||||
|
||||
func (this *RequestArgsCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestArgsCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
value = req.WAFRaw().URL.RawQuery
|
||||
return
|
||||
}
|
||||
|
||||
func (this *RequestArgsCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestArgsCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
if this.IsRequest() {
|
||||
return this.RequestValue(req, param, options)
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ type RequestBodyCheckpoint struct {
|
||||
Checkpoint
|
||||
}
|
||||
|
||||
func (this *RequestBodyCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestBodyCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
if this.RequestBodyIsEmpty(req) {
|
||||
value = ""
|
||||
return
|
||||
@@ -23,10 +23,11 @@ func (this *RequestBodyCheckpoint) RequestValue(req requests.Request, param stri
|
||||
}
|
||||
|
||||
var bodyData = req.WAFGetCacheBody()
|
||||
hasRequestBody = true
|
||||
if len(bodyData) == 0 {
|
||||
data, err := req.WAFReadBody(utils.MaxBodySize) // read body
|
||||
if err != nil {
|
||||
return "", err, nil
|
||||
return "", hasRequestBody, err, nil
|
||||
}
|
||||
|
||||
bodyData = data
|
||||
@@ -34,10 +35,10 @@ func (this *RequestBodyCheckpoint) RequestValue(req requests.Request, param stri
|
||||
req.WAFRestoreBody(data)
|
||||
}
|
||||
|
||||
return bodyData, nil, nil
|
||||
return bodyData, hasRequestBody, nil, nil
|
||||
}
|
||||
|
||||
func (this *RequestBodyCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestBodyCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
if this.IsRequest() {
|
||||
return this.RequestValue(req, param, options)
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ func TestRequestBodyCheckpoint_RequestValue_Max(t *testing.T) {
|
||||
}
|
||||
|
||||
checkpoint := new(RequestBodyCheckpoint)
|
||||
value, err, _ := checkpoint.RequestValue(requests.NewTestRequest(req), "", nil)
|
||||
value, _, err, _ := checkpoint.RequestValue(requests.NewTestRequest(req), "", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -9,12 +9,12 @@ type RequestContentTypeCheckpoint struct {
|
||||
Checkpoint
|
||||
}
|
||||
|
||||
func (this *RequestContentTypeCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestContentTypeCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
value = req.WAFRaw().Header.Get("Content-Type")
|
||||
return
|
||||
}
|
||||
|
||||
func (this *RequestContentTypeCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestContentTypeCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
if this.IsRequest() {
|
||||
return this.RequestValue(req, param, options)
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ type RequestCookieCheckpoint struct {
|
||||
Checkpoint
|
||||
}
|
||||
|
||||
func (this *RequestCookieCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestCookieCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
cookie, err := req.WAFRaw().Cookie(param)
|
||||
if err != nil {
|
||||
value = ""
|
||||
@@ -20,7 +20,7 @@ func (this *RequestCookieCheckpoint) RequestValue(req requests.Request, param st
|
||||
return
|
||||
}
|
||||
|
||||
func (this *RequestCookieCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestCookieCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
if this.IsRequest() {
|
||||
return this.RequestValue(req, param, options)
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ type RequestCookiesCheckpoint struct {
|
||||
Checkpoint
|
||||
}
|
||||
|
||||
func (this *RequestCookiesCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestCookiesCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
var cookies = []string{}
|
||||
for _, cookie := range req.WAFRaw().Cookies() {
|
||||
cookies = append(cookies, url.QueryEscape(cookie.Name)+"="+url.QueryEscape(cookie.Value))
|
||||
@@ -20,7 +20,7 @@ func (this *RequestCookiesCheckpoint) RequestValue(req requests.Request, param s
|
||||
return
|
||||
}
|
||||
|
||||
func (this *RequestCookiesCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestCookiesCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
if this.IsRequest() {
|
||||
return this.RequestValue(req, param, options)
|
||||
}
|
||||
|
||||
@@ -12,7 +12,9 @@ type RequestFormArgCheckpoint struct {
|
||||
Checkpoint
|
||||
}
|
||||
|
||||
func (this *RequestFormArgCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestFormArgCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
hasRequestBody = true
|
||||
|
||||
if this.RequestBodyIsEmpty(req) {
|
||||
value = ""
|
||||
return
|
||||
@@ -27,7 +29,7 @@ func (this *RequestFormArgCheckpoint) RequestValue(req requests.Request, param s
|
||||
if len(bodyData) == 0 {
|
||||
data, err := req.WAFReadBody(utils.MaxBodySize) // read body
|
||||
if err != nil {
|
||||
return "", err, nil
|
||||
return "", hasRequestBody, err, nil
|
||||
}
|
||||
|
||||
bodyData = data
|
||||
@@ -37,10 +39,10 @@ func (this *RequestFormArgCheckpoint) RequestValue(req requests.Request, param s
|
||||
|
||||
// TODO improve performance
|
||||
values, _ := url.ParseQuery(string(bodyData))
|
||||
return values.Get(param), nil, nil
|
||||
return values.Get(param), hasRequestBody, nil, nil
|
||||
}
|
||||
|
||||
func (this *RequestFormArgCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestFormArgCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
if this.IsRequest() {
|
||||
return this.RequestValue(req, param, options)
|
||||
}
|
||||
|
||||
@@ -14,15 +14,15 @@ func (this *RequestGeneralHeaderLengthCheckpoint) IsComposed() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (this *RequestGeneralHeaderLengthCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestGeneralHeaderLengthCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
value = false
|
||||
|
||||
headers := options.GetSlice("headers")
|
||||
var headers = options.GetSlice("headers")
|
||||
if len(headers) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
length := options.GetInt("length")
|
||||
var length = options.GetInt("length")
|
||||
|
||||
for _, header := range headers {
|
||||
v := req.WAFRaw().Header.Get(types.String(header))
|
||||
@@ -35,6 +35,6 @@ func (this *RequestGeneralHeaderLengthCheckpoint) RequestValue(req requests.Requ
|
||||
return
|
||||
}
|
||||
|
||||
func (this *RequestGeneralHeaderLengthCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestGeneralHeaderLengthCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -15,11 +15,11 @@ func (this *RequestGeoCityNameCheckpoint) IsComposed() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (this *RequestGeoCityNameCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestGeoCityNameCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
value = req.Format("${geo.city.name}")
|
||||
return
|
||||
}
|
||||
|
||||
func (this *RequestGeoCityNameCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestGeoCityNameCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
return this.RequestValue(req, param, options)
|
||||
}
|
||||
|
||||
@@ -15,11 +15,11 @@ func (this *RequestGeoCountryNameCheckpoint) IsComposed() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (this *RequestGeoCountryNameCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestGeoCountryNameCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
value = req.Format("${geo.country.name}")
|
||||
return
|
||||
}
|
||||
|
||||
func (this *RequestGeoCountryNameCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestGeoCountryNameCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
return this.RequestValue(req, param, options)
|
||||
}
|
||||
|
||||
@@ -15,11 +15,11 @@ func (this *RequestGeoProvinceNameCheckpoint) IsComposed() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (this *RequestGeoProvinceNameCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestGeoProvinceNameCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
value = req.Format("${geo.province.name}")
|
||||
return
|
||||
}
|
||||
|
||||
func (this *RequestGeoProvinceNameCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestGeoProvinceNameCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
return this.RequestValue(req, param, options)
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ type RequestHeaderCheckpoint struct {
|
||||
Checkpoint
|
||||
}
|
||||
|
||||
func (this *RequestHeaderCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestHeaderCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
v, found := req.WAFRaw().Header[param]
|
||||
if !found {
|
||||
value = ""
|
||||
@@ -20,7 +20,7 @@ func (this *RequestHeaderCheckpoint) RequestValue(req requests.Request, param st
|
||||
return
|
||||
}
|
||||
|
||||
func (this *RequestHeaderCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestHeaderCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
if this.IsRequest() {
|
||||
return this.RequestValue(req, param, options)
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ type RequestHeadersCheckpoint struct {
|
||||
Checkpoint
|
||||
}
|
||||
|
||||
func (this *RequestHeadersCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestHeadersCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
var headers = []string{}
|
||||
for k, v := range req.WAFRaw().Header {
|
||||
for _, subV := range v {
|
||||
@@ -23,7 +23,7 @@ func (this *RequestHeadersCheckpoint) RequestValue(req requests.Request, param s
|
||||
return
|
||||
}
|
||||
|
||||
func (this *RequestHeadersCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestHeadersCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
if this.IsRequest() {
|
||||
return this.RequestValue(req, param, options)
|
||||
}
|
||||
|
||||
@@ -9,12 +9,12 @@ type RequestHostCheckpoint struct {
|
||||
Checkpoint
|
||||
}
|
||||
|
||||
func (this *RequestHostCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestHostCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
value = req.WAFRaw().Host
|
||||
return
|
||||
}
|
||||
|
||||
func (this *RequestHostCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestHostCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
if this.IsRequest() {
|
||||
return this.RequestValue(req, param, options)
|
||||
}
|
||||
|
||||
@@ -15,11 +15,11 @@ func (this *RequestISPNameCheckpoint) IsComposed() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (this *RequestISPNameCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestISPNameCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
value = req.Format("${isp.name}")
|
||||
return
|
||||
}
|
||||
|
||||
func (this *RequestISPNameCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestISPNameCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
return this.RequestValue(req, param, options)
|
||||
}
|
||||
|
||||
@@ -14,12 +14,13 @@ type RequestJSONArgCheckpoint struct {
|
||||
Checkpoint
|
||||
}
|
||||
|
||||
func (this *RequestJSONArgCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestJSONArgCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
var bodyData = req.WAFGetCacheBody()
|
||||
hasRequestBody = true
|
||||
if len(bodyData) == 0 {
|
||||
data, err := req.WAFReadBody(wafutils.MaxBodySize) // read body
|
||||
if err != nil {
|
||||
return "", err, nil
|
||||
return "", hasRequestBody, err, nil
|
||||
}
|
||||
|
||||
bodyData = data
|
||||
@@ -31,17 +32,17 @@ func (this *RequestJSONArgCheckpoint) RequestValue(req requests.Request, param s
|
||||
var m interface{} = nil
|
||||
err := json.Unmarshal(bodyData, &m)
|
||||
if err != nil || m == nil {
|
||||
return "", nil, err
|
||||
return "", hasRequestBody, nil, err
|
||||
}
|
||||
|
||||
value = utils.Get(m, strings.Split(param, "."))
|
||||
if value != nil {
|
||||
return value, nil, err
|
||||
return value, hasRequestBody, nil, err
|
||||
}
|
||||
return "", nil, nil
|
||||
return "", hasRequestBody, nil, nil
|
||||
}
|
||||
|
||||
func (this *RequestJSONArgCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestJSONArgCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
if this.IsRequest() {
|
||||
return this.RequestValue(req, param, options)
|
||||
}
|
||||
|
||||
@@ -9,12 +9,12 @@ type RequestLengthCheckpoint struct {
|
||||
Checkpoint
|
||||
}
|
||||
|
||||
func (this *RequestLengthCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestLengthCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
value = req.WAFRaw().ContentLength
|
||||
return
|
||||
}
|
||||
|
||||
func (this *RequestLengthCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestLengthCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
if this.IsRequest() {
|
||||
return this.RequestValue(req, param, options)
|
||||
}
|
||||
|
||||
@@ -9,12 +9,12 @@ type RequestMethodCheckpoint struct {
|
||||
Checkpoint
|
||||
}
|
||||
|
||||
func (this *RequestMethodCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestMethodCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
value = req.WAFRaw().Method
|
||||
return
|
||||
}
|
||||
|
||||
func (this *RequestMethodCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestMethodCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
if this.IsRequest() {
|
||||
return this.RequestValue(req, param, options)
|
||||
}
|
||||
|
||||
@@ -9,11 +9,11 @@ type RequestPathCheckpoint struct {
|
||||
Checkpoint
|
||||
}
|
||||
|
||||
func (this *RequestPathCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
return req.WAFRaw().URL.Path, nil, nil
|
||||
func (this *RequestPathCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
return req.WAFRaw().URL.Path, false, nil, nil
|
||||
}
|
||||
|
||||
func (this *RequestPathCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestPathCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
if this.IsRequest() {
|
||||
return this.RequestValue(req, param, options)
|
||||
}
|
||||
|
||||
@@ -9,12 +9,12 @@ type RequestProtoCheckpoint struct {
|
||||
Checkpoint
|
||||
}
|
||||
|
||||
func (this *RequestProtoCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestProtoCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
value = req.WAFRaw().Proto
|
||||
return
|
||||
}
|
||||
|
||||
func (this *RequestProtoCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestProtoCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
if this.IsRequest() {
|
||||
return this.RequestValue(req, param, options)
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ type RequestRawRemoteAddrCheckpoint struct {
|
||||
Checkpoint
|
||||
}
|
||||
|
||||
func (this *RequestRawRemoteAddrCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestRawRemoteAddrCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
host, _, err := net.SplitHostPort(req.WAFRaw().RemoteAddr)
|
||||
if err == nil {
|
||||
value = host
|
||||
@@ -20,7 +20,7 @@ func (this *RequestRawRemoteAddrCheckpoint) RequestValue(req requests.Request, p
|
||||
return
|
||||
}
|
||||
|
||||
func (this *RequestRawRemoteAddrCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestRawRemoteAddrCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
if this.IsRequest() {
|
||||
return this.RequestValue(req, param, options)
|
||||
}
|
||||
|
||||
@@ -9,12 +9,12 @@ type RequestRefererCheckpoint struct {
|
||||
Checkpoint
|
||||
}
|
||||
|
||||
func (this *RequestRefererCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestRefererCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
value = req.WAFRaw().Referer()
|
||||
return
|
||||
}
|
||||
|
||||
func (this *RequestRefererCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestRefererCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
if this.IsRequest() {
|
||||
return this.RequestValue(req, param, options)
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ type RequestRefererBlockCheckpoint struct {
|
||||
|
||||
// RequestValue 计算checkpoint值
|
||||
// 选项:allowEmpty, allowSameDomain, allowDomains
|
||||
func (this *RequestRefererBlockCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestRefererBlockCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
var referer = req.WAFRaw().Referer()
|
||||
|
||||
if len(referer) == 0 {
|
||||
@@ -61,6 +61,6 @@ func (this *RequestRefererBlockCheckpoint) RequestValue(req requests.Request, pa
|
||||
return
|
||||
}
|
||||
|
||||
func (this *RequestRefererBlockCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestRefererBlockCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -9,12 +9,12 @@ type RequestRemoteAddrCheckpoint struct {
|
||||
Checkpoint
|
||||
}
|
||||
|
||||
func (this *RequestRemoteAddrCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestRemoteAddrCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
value = req.WAFRemoteIP()
|
||||
return
|
||||
}
|
||||
|
||||
func (this *RequestRemoteAddrCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestRemoteAddrCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
if this.IsRequest() {
|
||||
return this.RequestValue(req, param, options)
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ type RequestRemotePortCheckpoint struct {
|
||||
Checkpoint
|
||||
}
|
||||
|
||||
func (this *RequestRemotePortCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestRemotePortCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
_, port, err := net.SplitHostPort(req.WAFRaw().RemoteAddr)
|
||||
if err == nil {
|
||||
value = types.Int(port)
|
||||
@@ -21,7 +21,7 @@ func (this *RequestRemotePortCheckpoint) RequestValue(req requests.Request, para
|
||||
return
|
||||
}
|
||||
|
||||
func (this *RequestRemotePortCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestRemotePortCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
if this.IsRequest() {
|
||||
return this.RequestValue(req, param, options)
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ type RequestRemoteUserCheckpoint struct {
|
||||
Checkpoint
|
||||
}
|
||||
|
||||
func (this *RequestRemoteUserCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestRemoteUserCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
username, _, ok := req.WAFRaw().BasicAuth()
|
||||
if !ok {
|
||||
value = ""
|
||||
@@ -19,7 +19,7 @@ func (this *RequestRemoteUserCheckpoint) RequestValue(req requests.Request, para
|
||||
return
|
||||
}
|
||||
|
||||
func (this *RequestRemoteUserCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestRemoteUserCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
if this.IsRequest() {
|
||||
return this.RequestValue(req, param, options)
|
||||
}
|
||||
|
||||
@@ -9,12 +9,12 @@ type RequestSchemeCheckpoint struct {
|
||||
Checkpoint
|
||||
}
|
||||
|
||||
func (this *RequestSchemeCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestSchemeCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
value = req.Format("${scheme}")
|
||||
return
|
||||
}
|
||||
|
||||
func (this *RequestSchemeCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestSchemeCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
if this.IsRequest() {
|
||||
return this.RequestValue(req, param, options)
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ type RequestUploadCheckpoint struct {
|
||||
Checkpoint
|
||||
}
|
||||
|
||||
func (this *RequestUploadCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestUploadCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
if this.RequestBodyIsEmpty(req) {
|
||||
value = ""
|
||||
return
|
||||
@@ -36,6 +36,7 @@ func (this *RequestUploadCheckpoint) RequestValue(req requests.Request, param st
|
||||
return
|
||||
}
|
||||
|
||||
hasRequestBody = true
|
||||
if req.WAFRaw().MultipartForm == nil {
|
||||
var bodyData = req.WAFGetCacheBody()
|
||||
if len(bodyData) == 0 {
|
||||
@@ -121,7 +122,7 @@ func (this *RequestUploadCheckpoint) RequestValue(req requests.Request, param st
|
||||
return
|
||||
}
|
||||
|
||||
func (this *RequestUploadCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestUploadCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
if this.IsRequest() {
|
||||
return this.RequestValue(req, param, options)
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ type RequestURICheckpoint struct {
|
||||
Checkpoint
|
||||
}
|
||||
|
||||
func (this *RequestURICheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestURICheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
if len(req.WAFRaw().RequestURI) > 0 {
|
||||
value = req.WAFRaw().RequestURI
|
||||
} else if req.WAFRaw().URL != nil {
|
||||
@@ -18,7 +18,7 @@ func (this *RequestURICheckpoint) RequestValue(req requests.Request, param strin
|
||||
return
|
||||
}
|
||||
|
||||
func (this *RequestURICheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestURICheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
if this.IsRequest() {
|
||||
return this.RequestValue(req, param, options)
|
||||
}
|
||||
|
||||
@@ -9,11 +9,11 @@ type RequestURLCheckpoint struct {
|
||||
Checkpoint
|
||||
}
|
||||
|
||||
func (this *RequestURLCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
return req.Format("${requestURL}"), nil, nil
|
||||
func (this *RequestURLCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
return req.Format("${requestURL}"), hasRequestBody, nil, nil
|
||||
}
|
||||
|
||||
func (this *RequestURLCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestURLCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
if this.IsRequest() {
|
||||
return this.RequestValue(req, param, options)
|
||||
}
|
||||
|
||||
@@ -9,12 +9,12 @@ type RequestUserAgentCheckpoint struct {
|
||||
Checkpoint
|
||||
}
|
||||
|
||||
func (this *RequestUserAgentCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestUserAgentCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
value = req.WAFRaw().UserAgent()
|
||||
return
|
||||
}
|
||||
|
||||
func (this *RequestUserAgentCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *RequestUserAgentCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
if this.IsRequest() {
|
||||
return this.RequestValue(req, param, options)
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
// ${responseBody}
|
||||
// ResponseBodyCheckpoint ${responseBody}
|
||||
type ResponseBodyCheckpoint struct {
|
||||
Checkpoint
|
||||
}
|
||||
@@ -16,12 +16,12 @@ func (this *ResponseBodyCheckpoint) IsRequest() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (this *ResponseBodyCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *ResponseBodyCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
value = ""
|
||||
return
|
||||
}
|
||||
|
||||
func (this *ResponseBodyCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *ResponseBodyCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
if resp.ContentLength == 0 {
|
||||
value = ""
|
||||
return
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
// ${bytesSent}
|
||||
// ResponseBytesSentCheckpoint ${bytesSent}
|
||||
type ResponseBytesSentCheckpoint struct {
|
||||
Checkpoint
|
||||
}
|
||||
@@ -14,12 +14,12 @@ func (this *ResponseBytesSentCheckpoint) IsRequest() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (this *ResponseBytesSentCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *ResponseBytesSentCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
value = 0
|
||||
return
|
||||
}
|
||||
|
||||
func (this *ResponseBytesSentCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *ResponseBytesSentCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
value = 0
|
||||
if resp != nil {
|
||||
value = resp.ContentLength
|
||||
|
||||
@@ -18,12 +18,12 @@ func (this *ResponseGeneralHeaderLengthCheckpoint) IsComposed() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (this *ResponseGeneralHeaderLengthCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *ResponseGeneralHeaderLengthCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (this *ResponseGeneralHeaderLengthCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *ResponseGeneralHeaderLengthCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
value = false
|
||||
|
||||
headers := options.GetSlice("headers")
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"github.com/iwind/TeaGo/maps"
|
||||
)
|
||||
|
||||
// ${responseHeader.arg}
|
||||
// ResponseHeaderCheckpoint ${responseHeader.arg}
|
||||
type ResponseHeaderCheckpoint struct {
|
||||
Checkpoint
|
||||
}
|
||||
@@ -14,12 +14,12 @@ func (this *ResponseHeaderCheckpoint) IsRequest() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (this *ResponseHeaderCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *ResponseHeaderCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
value = ""
|
||||
return
|
||||
}
|
||||
|
||||
func (this *ResponseHeaderCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *ResponseHeaderCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
if resp != nil && resp.Header != nil {
|
||||
value = resp.Header.Get(param)
|
||||
} else {
|
||||
|
||||
@@ -14,12 +14,12 @@ func (this *ResponseStatusCheckpoint) IsRequest() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (this *ResponseStatusCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *ResponseStatusCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
value = 0
|
||||
return
|
||||
}
|
||||
|
||||
func (this *ResponseStatusCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *ResponseStatusCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
if resp != nil {
|
||||
value = resp.StatusCode
|
||||
}
|
||||
|
||||
@@ -10,11 +10,11 @@ type SampleRequestCheckpoint struct {
|
||||
Checkpoint
|
||||
}
|
||||
|
||||
func (this *SampleRequestCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *SampleRequestCheckpoint) RequestValue(req requests.Request, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (this *SampleRequestCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, sysErr error, userErr error) {
|
||||
func (this *SampleRequestCheckpoint) ResponseValue(req requests.Request, resp *requests.Response, param string, options maps.Map) (value interface{}, hasRequestBody bool, sysErr error, userErr error) {
|
||||
if this.IsRequest() {
|
||||
return this.RequestValue(req, param, options)
|
||||
}
|
||||
|
||||
@@ -184,11 +184,14 @@ func (this *Rule) Init() error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (this *Rule) MatchRequest(req requests.Request) (b bool, err error) {
|
||||
func (this *Rule) MatchRequest(req requests.Request) (b bool, hasRequestBody bool, err error) {
|
||||
if this.singleCheckpoint != nil {
|
||||
value, err, _ := this.singleCheckpoint.RequestValue(req, this.singleParam, this.CheckpointOptions)
|
||||
value, hasCheckedRequestBody, err, _ := this.singleCheckpoint.RequestValue(req, this.singleParam, this.CheckpointOptions)
|
||||
if hasCheckedRequestBody {
|
||||
hasRequestBody = true
|
||||
}
|
||||
if err != nil {
|
||||
return false, err
|
||||
return false, hasRequestBody, err
|
||||
}
|
||||
|
||||
// execute filters
|
||||
@@ -198,10 +201,10 @@ func (this *Rule) MatchRequest(req requests.Request) (b bool, err error) {
|
||||
|
||||
// if is composed checkpoint, we just returns true or false
|
||||
if this.singleCheckpoint.IsComposed() {
|
||||
return types.Bool(value), nil
|
||||
return types.Bool(value), hasRequestBody, nil
|
||||
}
|
||||
|
||||
return this.Test(value), nil
|
||||
return this.Test(value), hasRequestBody, nil
|
||||
}
|
||||
|
||||
value := configutils.ParseVariables(this.Param, func(varName string) (value string) {
|
||||
@@ -213,14 +216,20 @@ func (this *Rule) MatchRequest(req requests.Request) (b bool, err error) {
|
||||
}
|
||||
|
||||
if len(pieces) == 1 {
|
||||
value1, err1, _ := point.RequestValue(req, "", this.CheckpointOptions)
|
||||
value1, hasCheckRequestBody, err1, _ := point.RequestValue(req, "", this.CheckpointOptions)
|
||||
if hasCheckRequestBody {
|
||||
hasRequestBody = true
|
||||
}
|
||||
if err1 != nil {
|
||||
err = err1
|
||||
}
|
||||
return types.String(value1)
|
||||
}
|
||||
|
||||
value1, err1, _ := point.RequestValue(req, pieces[1], this.CheckpointOptions)
|
||||
value1, hasCheckRequestBody, err1, _ := point.RequestValue(req, pieces[1], this.CheckpointOptions)
|
||||
if hasCheckRequestBody {
|
||||
hasRequestBody = true
|
||||
}
|
||||
if err1 != nil {
|
||||
err = err1
|
||||
}
|
||||
@@ -228,19 +237,22 @@ func (this *Rule) MatchRequest(req requests.Request) (b bool, err error) {
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
return false, hasRequestBody, err
|
||||
}
|
||||
|
||||
return this.Test(value), nil
|
||||
return this.Test(value), hasRequestBody, nil
|
||||
}
|
||||
|
||||
func (this *Rule) MatchResponse(req requests.Request, resp *requests.Response) (b bool, err error) {
|
||||
func (this *Rule) MatchResponse(req requests.Request, resp *requests.Response) (b bool, hasRequestBody bool, err error) {
|
||||
if this.singleCheckpoint != nil {
|
||||
// if is request param
|
||||
if this.singleCheckpoint.IsRequest() {
|
||||
value, err, _ := this.singleCheckpoint.RequestValue(req, this.singleParam, this.CheckpointOptions)
|
||||
value, hasCheckRequestBody, err, _ := this.singleCheckpoint.RequestValue(req, this.singleParam, this.CheckpointOptions)
|
||||
if hasCheckRequestBody {
|
||||
hasRequestBody = true
|
||||
}
|
||||
if err != nil {
|
||||
return false, err
|
||||
return false, hasRequestBody, err
|
||||
}
|
||||
|
||||
// execute filters
|
||||
@@ -248,21 +260,24 @@ func (this *Rule) MatchResponse(req requests.Request, resp *requests.Response) (
|
||||
value = this.execFilter(value)
|
||||
}
|
||||
|
||||
return this.Test(value), nil
|
||||
return this.Test(value), hasRequestBody, nil
|
||||
}
|
||||
|
||||
// response param
|
||||
value, err, _ := this.singleCheckpoint.ResponseValue(req, resp, this.singleParam, this.CheckpointOptions)
|
||||
value, hasCheckRequestBody, err, _ := this.singleCheckpoint.ResponseValue(req, resp, this.singleParam, this.CheckpointOptions)
|
||||
if hasCheckRequestBody {
|
||||
hasRequestBody = true
|
||||
}
|
||||
if err != nil {
|
||||
return false, err
|
||||
return false, hasRequestBody, err
|
||||
}
|
||||
|
||||
// if is composed checkpoint, we just returns true or false
|
||||
if this.singleCheckpoint.IsComposed() {
|
||||
return types.Bool(value), nil
|
||||
return types.Bool(value), hasRequestBody, nil
|
||||
}
|
||||
|
||||
return this.Test(value), nil
|
||||
return this.Test(value), hasRequestBody, nil
|
||||
}
|
||||
|
||||
value := configutils.ParseVariables(this.Param, func(varName string) (value string) {
|
||||
@@ -275,13 +290,19 @@ func (this *Rule) MatchResponse(req requests.Request, resp *requests.Response) (
|
||||
|
||||
if len(pieces) == 1 {
|
||||
if point.IsRequest() {
|
||||
value1, err1, _ := point.RequestValue(req, "", this.CheckpointOptions)
|
||||
value1, hasCheckRequestBody, err1, _ := point.RequestValue(req, "", this.CheckpointOptions)
|
||||
if hasCheckRequestBody {
|
||||
hasRequestBody = true
|
||||
}
|
||||
if err1 != nil {
|
||||
err = err1
|
||||
}
|
||||
return types.String(value1)
|
||||
} else {
|
||||
value1, err1, _ := point.ResponseValue(req, resp, "", this.CheckpointOptions)
|
||||
value1, hasCheckRequestBody, err1, _ := point.ResponseValue(req, resp, "", this.CheckpointOptions)
|
||||
if hasCheckRequestBody {
|
||||
hasRequestBody = true
|
||||
}
|
||||
if err1 != nil {
|
||||
err = err1
|
||||
}
|
||||
@@ -290,13 +311,19 @@ func (this *Rule) MatchResponse(req requests.Request, resp *requests.Response) (
|
||||
}
|
||||
|
||||
if point.IsRequest() {
|
||||
value1, err1, _ := point.RequestValue(req, pieces[1], this.CheckpointOptions)
|
||||
value1, hasCheckRequestBody, err1, _ := point.RequestValue(req, pieces[1], this.CheckpointOptions)
|
||||
if hasCheckRequestBody {
|
||||
hasRequestBody = true
|
||||
}
|
||||
if err1 != nil {
|
||||
err = err1
|
||||
}
|
||||
return types.String(value1)
|
||||
} else {
|
||||
value1, err1, _ := point.ResponseValue(req, resp, pieces[1], this.CheckpointOptions)
|
||||
value1, hasCheckRequestBody, err1, _ := point.ResponseValue(req, resp, pieces[1], this.CheckpointOptions)
|
||||
if hasCheckRequestBody {
|
||||
hasRequestBody = true
|
||||
}
|
||||
if err1 != nil {
|
||||
err = err1
|
||||
}
|
||||
@@ -305,10 +332,10 @@ func (this *Rule) MatchResponse(req requests.Request, resp *requests.Response) (
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
return false, hasRequestBody, err
|
||||
}
|
||||
|
||||
return this.Test(value), nil
|
||||
return this.Test(value), hasRequestBody, nil
|
||||
}
|
||||
|
||||
func (this *Rule) Test(value interface{}) bool {
|
||||
|
||||
@@ -75,7 +75,7 @@ func (this *RuleGroup) RemoveRuleSet(id int64) {
|
||||
this.RuleSets = result
|
||||
}
|
||||
|
||||
func (this *RuleGroup) MatchRequest(req requests.Request) (b bool, set *RuleSet, err error) {
|
||||
func (this *RuleGroup) MatchRequest(req requests.Request) (b bool, hasRequestBody bool, set *RuleSet, err error) {
|
||||
if !this.hasRuleSets {
|
||||
return
|
||||
}
|
||||
@@ -83,18 +83,18 @@ func (this *RuleGroup) MatchRequest(req requests.Request) (b bool, set *RuleSet,
|
||||
if !set.IsOn {
|
||||
continue
|
||||
}
|
||||
b, err = set.MatchRequest(req)
|
||||
b, hasRequestBody, err = set.MatchRequest(req)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
return false, hasRequestBody, nil, err
|
||||
}
|
||||
if b {
|
||||
return true, set, nil
|
||||
return true, hasRequestBody, set, nil
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (this *RuleGroup) MatchResponse(req requests.Request, resp *requests.Response) (b bool, set *RuleSet, err error) {
|
||||
func (this *RuleGroup) MatchResponse(req requests.Request, resp *requests.Response) (b bool, hasRequestBody bool, set *RuleSet, err error) {
|
||||
if !this.hasRuleSets {
|
||||
return
|
||||
}
|
||||
@@ -102,12 +102,12 @@ func (this *RuleGroup) MatchResponse(req requests.Request, resp *requests.Respon
|
||||
if !set.IsOn {
|
||||
continue
|
||||
}
|
||||
b, err = set.MatchResponse(req, resp)
|
||||
b, hasRequestBody, err = set.MatchResponse(req, resp)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
return false, hasRequestBody, nil, err
|
||||
}
|
||||
if b {
|
||||
return true, set, nil
|
||||
return true, hasRequestBody, set, nil
|
||||
}
|
||||
}
|
||||
return
|
||||
|
||||
@@ -167,89 +167,105 @@ func (this *RuleSet) PerformActions(waf *WAF, group *RuleGroup, req requests.Req
|
||||
return true
|
||||
}
|
||||
|
||||
func (this *RuleSet) MatchRequest(req requests.Request) (b bool, err error) {
|
||||
func (this *RuleSet) MatchRequest(req requests.Request) (b bool, hasRequestBody bool, err error) {
|
||||
// 是否忽略局域网IP
|
||||
if this.IgnoreLocal && utils.IsLocalIP(req.WAFRemoteIP()) {
|
||||
return false, nil
|
||||
return false, hasRequestBody, nil
|
||||
}
|
||||
|
||||
if !this.hasRules {
|
||||
return false, nil
|
||||
return false, hasRequestBody, nil
|
||||
}
|
||||
switch this.Connector {
|
||||
case RuleConnectorAnd:
|
||||
for _, rule := range this.Rules {
|
||||
b1, err1 := rule.MatchRequest(req)
|
||||
b1, hasCheckRequestBody, err1 := rule.MatchRequest(req)
|
||||
if hasCheckRequestBody {
|
||||
hasRequestBody = true
|
||||
}
|
||||
if err1 != nil {
|
||||
return false, err1
|
||||
return false, hasRequestBody, err1
|
||||
}
|
||||
if !b1 {
|
||||
return false, nil
|
||||
return false, hasRequestBody, nil
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
return true, hasRequestBody, nil
|
||||
case RuleConnectorOr:
|
||||
for _, rule := range this.Rules {
|
||||
b1, err1 := rule.MatchRequest(req)
|
||||
b1, hasCheckRequestBody, err1 := rule.MatchRequest(req)
|
||||
if hasCheckRequestBody {
|
||||
hasRequestBody = true
|
||||
}
|
||||
if err1 != nil {
|
||||
return false, err1
|
||||
return false, hasRequestBody, err1
|
||||
}
|
||||
if b1 {
|
||||
return true, nil
|
||||
return true, hasRequestBody, nil
|
||||
}
|
||||
}
|
||||
default: // same as And
|
||||
for _, rule := range this.Rules {
|
||||
b1, err1 := rule.MatchRequest(req)
|
||||
b1, hasCheckRequestBody, err1 := rule.MatchRequest(req)
|
||||
if hasCheckRequestBody {
|
||||
hasRequestBody = true
|
||||
}
|
||||
if err1 != nil {
|
||||
return false, err1
|
||||
return false, hasRequestBody, err1
|
||||
}
|
||||
if !b1 {
|
||||
return false, nil
|
||||
return false, hasRequestBody, nil
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
return true, hasRequestBody, nil
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (this *RuleSet) MatchResponse(req requests.Request, resp *requests.Response) (b bool, err error) {
|
||||
func (this *RuleSet) MatchResponse(req requests.Request, resp *requests.Response) (b bool, hasRequestBody bool, err error) {
|
||||
if !this.hasRules {
|
||||
return false, nil
|
||||
return false, hasRequestBody, nil
|
||||
}
|
||||
switch this.Connector {
|
||||
case RuleConnectorAnd:
|
||||
for _, rule := range this.Rules {
|
||||
b1, err1 := rule.MatchResponse(req, resp)
|
||||
b1, hasCheckRequestBody, err1 := rule.MatchResponse(req, resp)
|
||||
if hasCheckRequestBody {
|
||||
hasRequestBody = true
|
||||
}
|
||||
if err1 != nil {
|
||||
return false, err1
|
||||
return false, hasRequestBody, err1
|
||||
}
|
||||
if !b1 {
|
||||
return false, nil
|
||||
return false, hasRequestBody, nil
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
return true, hasRequestBody, nil
|
||||
case RuleConnectorOr:
|
||||
for _, rule := range this.Rules {
|
||||
b1, err1 := rule.MatchResponse(req, resp)
|
||||
// 对于OR连接符,只需要判断最先匹配的一条规则中的hasRequestBody即可
|
||||
b1, hasCheckRequestBody, err1 := rule.MatchResponse(req, resp)
|
||||
if err1 != nil {
|
||||
return false, err1
|
||||
return false, hasCheckRequestBody, err1
|
||||
}
|
||||
if b1 {
|
||||
return true, nil
|
||||
return true, hasCheckRequestBody, nil
|
||||
}
|
||||
}
|
||||
default: // same as And
|
||||
for _, rule := range this.Rules {
|
||||
b1, err1 := rule.MatchResponse(req, resp)
|
||||
b1, hasCheckRequestBody, err1 := rule.MatchResponse(req, resp)
|
||||
if hasCheckRequestBody {
|
||||
hasRequestBody = true
|
||||
}
|
||||
if err1 != nil {
|
||||
return false, err1
|
||||
return false, hasRequestBody, err1
|
||||
}
|
||||
if !b1 {
|
||||
return false, nil
|
||||
return false, hasRequestBody, nil
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
return true, hasRequestBody, nil
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user