Compare commits
67 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
df31921954 | ||
|
|
299abb7b04 | ||
|
|
85ce63b4d3 | ||
|
|
9d9ae288bd | ||
|
|
c0a35eb5e7 | ||
|
|
d1d0ff062b | ||
|
|
396e8a22c4 | ||
|
|
4f040db1ef | ||
|
|
e3cf111344 | ||
|
|
28cb3c383d | ||
|
|
10665c0f37 | ||
|
|
4096f11909 | ||
|
|
5df209b6d5 | ||
|
|
db353fe025 | ||
|
|
30c3e143b8 | ||
|
|
15fe7b33a4 | ||
|
|
17e0666ba4 | ||
|
|
3f24bfaaf5 | ||
|
|
ceadcfece9 | ||
|
|
bc706237ef | ||
|
|
b8c5a78f2e | ||
|
|
9ad1c3a3c8 | ||
|
|
8cfde43f5d | ||
|
|
6f843b071a | ||
|
|
a72b025900 | ||
|
|
dfb66775d7 | ||
|
|
c4dca2df30 | ||
|
|
a3525bdaa4 | ||
|
|
09390bbb97 | ||
|
|
70ae4391d7 | ||
|
|
85931f55e1 | ||
|
|
0f5f03c9ed | ||
|
|
1181a0585b | ||
|
|
fd6fa929de | ||
|
|
73888c98a8 | ||
|
|
04ebfbea8a | ||
|
|
2b650fd285 | ||
|
|
515a590681 | ||
|
|
62f9d1f09a | ||
|
|
25c11f3d69 | ||
|
|
fde18c3b82 | ||
|
|
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() {
|
function build() {
|
||||||
ROOT=$(dirname $0)
|
ROOT=$(dirname $0)
|
||||||
NAME="edge-node"
|
NAME="edge-node"
|
||||||
VERSION=$(lookup-version $ROOT/../internal/const/const.go)
|
VERSION=$(lookup-version "$ROOT"/../internal/const/const.go)
|
||||||
DIST=$ROOT/"../dist/${NAME}"
|
DIST=$ROOT/"../dist/${NAME}"
|
||||||
MUSL_DIR="/usr/local/opt/musl-cross/bin"
|
MUSL_DIR="/usr/local/opt/musl-cross/bin"
|
||||||
GCC_X86_64_DIR="/usr/local/Cellar/x86_64-unknown-linux-gnu/10.3.0/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}
|
ARCH=${2}
|
||||||
TAG=${3}
|
TAG=${3}
|
||||||
|
|
||||||
if [ -z $OS ]; then
|
if [ -z "$OS" ]; then
|
||||||
echo "usage: build.sh OS ARCH"
|
echo "usage: build.sh OS ARCH"
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
if [ -z $ARCH ]; then
|
if [ -z "$ARCH" ]; then
|
||||||
echo "usage: build.sh OS ARCH"
|
echo "usage: build.sh OS ARCH"
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
if [ -z $TAG ]; then
|
if [ -z "$TAG" ]; then
|
||||||
TAG="community"
|
TAG="community"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "checking ..."
|
echo "checking ..."
|
||||||
ZIP_PATH=$(which zip)
|
ZIP_PATH=$(which zip)
|
||||||
if [ -z $ZIP_PATH ]; then
|
if [ -z "$ZIP_PATH" ]; then
|
||||||
echo "we need 'zip' command to compress files"
|
echo "we need 'zip' command to compress files"
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
@@ -36,28 +36,28 @@ function build() {
|
|||||||
ZIP="${NAME}-${OS}-${ARCH}-${TAG}-v${VERSION}.zip"
|
ZIP="${NAME}-${OS}-${ARCH}-${TAG}-v${VERSION}.zip"
|
||||||
|
|
||||||
echo "copying ..."
|
echo "copying ..."
|
||||||
if [ ! -d $DIST ]; then
|
if [ ! -d "$DIST" ]; then
|
||||||
mkdir $DIST
|
mkdir "$DIST"
|
||||||
mkdir $DIST/bin
|
mkdir "$DIST"/bin
|
||||||
mkdir $DIST/configs
|
mkdir "$DIST"/configs
|
||||||
mkdir $DIST/logs
|
mkdir "$DIST"/logs
|
||||||
mkdir $DIST/data
|
mkdir "$DIST"/data
|
||||||
|
|
||||||
if [ "$TAG" = "plus" ]; then
|
if [ "$TAG" = "plus" ]; then
|
||||||
mkdir $DIST/scripts
|
mkdir "$DIST"/scripts
|
||||||
mkdir $DIST/scripts/js
|
mkdir "$DIST"/scripts/js
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cp $ROOT/configs/api.template.yaml $DIST/configs
|
cp "$ROOT"/configs/api.template.yaml "$DIST"/configs
|
||||||
cp -R $ROOT/www $DIST/
|
cp -R "$ROOT"/www "$DIST"/
|
||||||
cp -R $ROOT/pages $DIST/
|
cp -R "$ROOT"/pages "$DIST"/
|
||||||
cp -R $ROOT/resources $DIST/
|
cp -R "$ROOT"/resources "$DIST"/
|
||||||
|
|
||||||
# we support TOA on linux/amd64 only
|
# we support TOA on linux/amd64 only
|
||||||
if [ $OS == "linux" -a $ARCH == "amd64" ]
|
if [ "$OS" == "linux" -a "$ARCH" == "amd64" ]
|
||||||
then
|
then
|
||||||
cp -R $ROOT/edge-toa $DIST
|
cp -R "$ROOT"/edge-toa "$DIST"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "building ..."
|
echo "building ..."
|
||||||
@@ -112,14 +112,14 @@ function build() {
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
if [ ! -z $CC_PATH ]; then
|
if [ ! -z $CC_PATH ]; then
|
||||||
env CC=$MUSL_DIR/$CC_PATH CXX=$MUSL_DIR/$CXX_PATH GOOS=${OS} GOARCH=${ARCH} CGO_ENABLED=1 go build -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
|
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
|
fi
|
||||||
|
|
||||||
# delete hidden files
|
# delete hidden files
|
||||||
find $DIST -name ".DS_Store" -delete
|
find "$DIST" -name ".DS_Store" -delete
|
||||||
find $DIST -name ".gitignore" -delete
|
find "$DIST" -name ".gitignore" -delete
|
||||||
|
|
||||||
echo "zip files"
|
echo "zip files"
|
||||||
cd "${DIST}/../" || exit
|
cd "${DIST}/../" || exit
|
||||||
@@ -135,15 +135,15 @@ function build() {
|
|||||||
|
|
||||||
function lookup-version() {
|
function lookup-version() {
|
||||||
FILE=$1
|
FILE=$1
|
||||||
VERSION_DATA=$(cat $FILE)
|
VERSION_DATA=$(cat "$FILE")
|
||||||
re="Version[ ]+=[ ]+\"([0-9.]+)\""
|
re="Version[ ]+=[ ]+\"([0-9.]+)\""
|
||||||
if [[ $VERSION_DATA =~ $re ]]; then
|
if [[ $VERSION_DATA =~ $re ]]; then
|
||||||
VERSION=${BASH_REMATCH[1]}
|
VERSION=${BASH_REMATCH[1]}
|
||||||
echo $VERSION
|
echo "$VERSION"
|
||||||
else
|
else
|
||||||
echo "could not match version"
|
echo "could not match version"
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
build $1 $2 $3
|
build "$1" "$2" "$3"
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/apps"
|
"github.com/TeaOSLab/EdgeNode/internal/apps"
|
||||||
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
||||||
@@ -19,7 +20,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
app := apps.NewAppCmd().
|
var app = apps.NewAppCmd().
|
||||||
Version(teaconst.Version).
|
Version(teaconst.Version).
|
||||||
Product(teaconst.ProductName).
|
Product(teaconst.ProductName).
|
||||||
Usage(teaconst.ProcessName + " [-v|start|stop|restart|status|quit|test|reload|service|daemon|pprof|accesslog]").
|
Usage(teaconst.ProcessName + " [-v|start|stop|restart|status|quit|test|reload|service|daemon|pprof|accesslog]").
|
||||||
@@ -67,24 +68,30 @@ func main() {
|
|||||||
fmt.Println("done")
|
fmt.Println("done")
|
||||||
})
|
})
|
||||||
app.On("pprof", func() {
|
app.On("pprof", func() {
|
||||||
// TODO 自己指定端口
|
var flagSet = flag.NewFlagSet("pprof", flag.ExitOnError)
|
||||||
addr := "127.0.0.1:6060"
|
var addr string
|
||||||
|
flagSet.StringVar(&addr, "addr", "", "")
|
||||||
|
_ = flagSet.Parse(os.Args[2:])
|
||||||
|
|
||||||
|
if len(addr) == 0 {
|
||||||
|
addr = "127.0.0.1:6060"
|
||||||
|
}
|
||||||
logs.Println("starting with pprof '" + addr + "'...")
|
logs.Println("starting with pprof '" + addr + "'...")
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
err := http.ListenAndServe(addr, nil)
|
err := http.ListenAndServe(addr, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logs.Println("[error]" + err.Error())
|
logs.Println("[ERROR]" + err.Error())
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
node := nodes.NewNode()
|
var node = nodes.NewNode()
|
||||||
node.Start()
|
node.Start()
|
||||||
})
|
})
|
||||||
app.On("dbstat", func() {
|
app.On("dbstat", func() {
|
||||||
teaconst.EnableDBStat = true
|
teaconst.EnableDBStat = true
|
||||||
|
|
||||||
node := nodes.NewNode()
|
var node = nodes.NewNode()
|
||||||
node.Start()
|
node.Start()
|
||||||
})
|
})
|
||||||
app.On("trackers", func() {
|
app.On("trackers", func() {
|
||||||
@@ -154,7 +161,7 @@ func main() {
|
|||||||
app.On("ip.drop", func() {
|
app.On("ip.drop", func() {
|
||||||
var args = os.Args[2:]
|
var args = os.Args[2:]
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
fmt.Println("Usage: edge-node ip.drop IP [--timeout=SECONDS]")
|
fmt.Println("Usage: edge-node ip.drop IP [--timeout=SECONDS] [--async]")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var ip = args[0]
|
var ip = args[0]
|
||||||
@@ -168,6 +175,11 @@ func main() {
|
|||||||
if ok {
|
if ok {
|
||||||
timeoutSeconds = types.Int(timeout[0])
|
timeoutSeconds = types.Int(timeout[0])
|
||||||
}
|
}
|
||||||
|
var async = false
|
||||||
|
_, ok = options["async"]
|
||||||
|
if ok {
|
||||||
|
async = true
|
||||||
|
}
|
||||||
|
|
||||||
fmt.Println("drop ip '" + ip + "' for '" + types.String(timeoutSeconds) + "' seconds")
|
fmt.Println("drop ip '" + ip + "' for '" + types.String(timeoutSeconds) + "' seconds")
|
||||||
var sock = gosock.NewTmpSock(teaconst.ProcessName)
|
var sock = gosock.NewTmpSock(teaconst.ProcessName)
|
||||||
@@ -176,6 +188,7 @@ func main() {
|
|||||||
Params: map[string]interface{}{
|
Params: map[string]interface{}{
|
||||||
"ip": ip,
|
"ip": ip,
|
||||||
"timeoutSeconds": timeoutSeconds,
|
"timeoutSeconds": timeoutSeconds,
|
||||||
|
"async": async,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -306,7 +319,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
app.Run(func() {
|
app.Run(func() {
|
||||||
node := nodes.NewNode()
|
var node = nodes.NewNode()
|
||||||
node.Start()
|
node.Start()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
3
dist/.gitignore
vendored
3
dist/.gitignore
vendored
@@ -1 +1,2 @@
|
|||||||
*.zip
|
*.zip
|
||||||
|
edge-node
|
||||||
42
go.mod
42
go.mod
@@ -1,9 +1,11 @@
|
|||||||
module github.com/TeaOSLab/EdgeNode
|
module github.com/TeaOSLab/EdgeNode
|
||||||
|
|
||||||
go 1.15
|
go 1.18
|
||||||
|
|
||||||
replace (
|
replace (
|
||||||
github.com/TeaOSLab/EdgeCommon => ../EdgeCommon
|
github.com/TeaOSLab/EdgeCommon => ../EdgeCommon
|
||||||
|
github.com/fsnotify/fsnotify => /Users/WorkSpace/Projects/fsnotify
|
||||||
|
rogchap.com/v8go => /Users/Workspace/Projects/v8go
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@@ -11,20 +13,18 @@ require (
|
|||||||
github.com/andybalholm/brotli v1.0.4
|
github.com/andybalholm/brotli v1.0.4
|
||||||
github.com/biessek/golang-ico v0.0.0-20180326222316-d348d9ea4670
|
github.com/biessek/golang-ico v0.0.0-20180326222316-d348d9ea4670
|
||||||
github.com/cespare/xxhash v1.1.0
|
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/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f
|
||||||
github.com/fsnotify/fsnotify v1.5.1
|
github.com/fsnotify/fsnotify v1.5.1
|
||||||
github.com/go-redis/redis/v8 v8.11.5
|
github.com/go-redis/redis/v8 v8.11.5
|
||||||
github.com/golang/protobuf v1.5.2
|
github.com/golang/protobuf v1.5.2
|
||||||
github.com/google/nftables v0.0.0-20220407195405-950e408d48c6
|
github.com/google/nftables v0.0.0-20220407195405-950e408d48c6
|
||||||
github.com/iwind/TeaGo v0.0.0-20220304043459-0dd944a5b475
|
github.com/iwind/TeaGo v0.0.0-20220807030847-31de8e1cbe55
|
||||||
github.com/iwind/gofcgi v0.0.0-20210528023741-a92711d45f11
|
github.com/iwind/gofcgi v0.0.0-20210528023741-a92711d45f11
|
||||||
github.com/iwind/gosock v0.0.0-20211103081026-ee4652210ca4
|
github.com/iwind/gosock v0.0.0-20211103081026-ee4652210ca4
|
||||||
github.com/iwind/gowebp v0.0.0-20211029040624-7331ecc78ed8
|
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.8
|
||||||
github.com/klauspost/compress v1.15.2 // indirect
|
|
||||||
github.com/kr/text v0.2.0 // indirect
|
|
||||||
github.com/mattn/go-sqlite3 v1.14.9
|
github.com/mattn/go-sqlite3 v1.14.9
|
||||||
|
github.com/mdlayher/netlink v1.4.2
|
||||||
github.com/miekg/dns v1.1.43
|
github.com/miekg/dns v1.1.43
|
||||||
github.com/mssola/user_agent v0.5.3
|
github.com/mssola/user_agent v0.5.3
|
||||||
github.com/pires/go-proxyproto v0.6.1
|
github.com/pires/go-proxyproto v0.6.1
|
||||||
@@ -34,6 +34,32 @@ require (
|
|||||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad
|
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad
|
||||||
golang.org/x/text v0.3.7
|
golang.org/x/text v0.3.7
|
||||||
google.golang.org/grpc v1.45.0
|
google.golang.org/grpc v1.45.0
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
rogchap.com/v8go v0.7.0
|
||||||
|
)
|
||||||
|
|
||||||
|
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/go-sql-driver/mysql v1.5.0 // 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
|
||||||
)
|
)
|
||||||
|
|||||||
45
go.sum
45
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/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 h1:4Ei0/BRroMF9FaXDG2e4OxwFcuW2vcXd+A6tyqTJUQQ=
|
||||||
github.com/chai2010/webp v1.1.0/go.mod h1:LP12PG5IFmLGHUU26tBiCBKnghxx3toZFwDjOYvd3Ow=
|
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.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/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
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=
|
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||||
@@ -50,7 +46,6 @@ 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.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/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/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/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.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.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||||
@@ -65,7 +60,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-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 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
||||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
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/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/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
@@ -96,17 +90,15 @@ 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/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 h1:btadZscaRmsi/+fOhkyUguRpSnrf6dykNEWxDeUCj9I=
|
||||||
github.com/google/nftables v0.0.0-20220407195405-950e408d48c6/go.mod h1:0F8on3JWMkm+xahTHItkiu/E1SPqMd0TOxNweQv8ptE=
|
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/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/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/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-20220807023459-448081424640 h1:nBVQzDI4mrQS+Egg+Li6BGiTToBsv+XTck+BItgI52k=
|
||||||
github.com/iwind/TeaGo v0.0.0-20220304043459-0dd944a5b475 h1:EseyfFaQOjWanGiby9KMw7PjDBMg/95tLDgIw/ns0Cw=
|
github.com/iwind/TeaGo v0.0.0-20220807023459-448081424640/go.mod h1:+K2l6Num4Evl0jH7TYlZJ1oFJX8sA8YUC31Pb+I1mJk=
|
||||||
github.com/iwind/TeaGo v0.0.0-20220304043459-0dd944a5b475/go.mod h1:HRHK0zoC/og3c9/hKosD9yYVMTnnzm3PgXUdhRYHaLc=
|
github.com/iwind/TeaGo v0.0.0-20220807030847-31de8e1cbe55 h1:shQNx0flJFBwKsGE7Hs3bI2bDz+YF0zl/4qE8B2KRiY=
|
||||||
|
github.com/iwind/TeaGo v0.0.0-20220807030847-31de8e1cbe55/go.mod h1:fi/Pq+/5m2HZoseM+39dMF57ANXRt6w4PkGu3NXPc5s=
|
||||||
github.com/iwind/gofcgi v0.0.0-20210528023741-a92711d45f11 h1:DaQjoWZhLNxjhIXedVg4/vFEtHkZhK4IjIwsWdyzBLg=
|
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/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 h1:VWGsCqTzObdlbf7UUE3oceIpcEKi4C/YBUszQXk118A=
|
||||||
github.com/iwind/gosock v0.0.0-20211103081026-ee4652210ca4/go.mod h1:H5Q7SXwbx3a97ecJkaS2sD77gspzE7HFUafBO0peEyA=
|
github.com/iwind/gosock v0.0.0-20211103081026-ee4652210ca4/go.mod h1:H5Q7SXwbx3a97ecJkaS2sD77gspzE7HFUafBO0peEyA=
|
||||||
github.com/iwind/gowebp v0.0.0-20211029040624-7331ecc78ed8 h1:AojsHz9Es9B3He2MQQxeRq3TyD//o9huxUo7r1wh44g=
|
github.com/iwind/gowebp v0.0.0-20211029040624-7331ecc78ed8 h1:AojsHz9Es9B3He2MQQxeRq3TyD//o9huxUo7r1wh44g=
|
||||||
@@ -123,11 +115,10 @@ github.com/jsimonetti/rtnetlink v0.0.0-20210212075122-66c871082f2b/go.mod h1:8w9
|
|||||||
github.com/jsimonetti/rtnetlink v0.0.0-20210525051524-4cc836578190/go.mod h1:NmKSdU4VGSiv1bMsdqNALI4RSvvjtz65tTMCnD05qLo=
|
github.com/jsimonetti/rtnetlink v0.0.0-20210525051524-4cc836578190/go.mod h1:NmKSdU4VGSiv1bMsdqNALI4RSvvjtz65tTMCnD05qLo=
|
||||||
github.com/jsimonetti/rtnetlink v0.0.0-20211022192332-93da33804786 h1:N527AHMa793TP5z5GNAn/VLPzlc0ewzWdeP/25gDfgQ=
|
github.com/jsimonetti/rtnetlink v0.0.0-20211022192332-93da33804786 h1:N527AHMa793TP5z5GNAn/VLPzlc0ewzWdeP/25gDfgQ=
|
||||||
github.com/jsimonetti/rtnetlink v0.0.0-20211022192332-93da33804786/go.mod h1:v4hqbTdfQngbVSZJVWUhGE/lbTFf9jb+ygmNUDQMuOs=
|
github.com/jsimonetti/rtnetlink v0.0.0-20211022192332-93da33804786/go.mod h1:v4hqbTdfQngbVSZJVWUhGE/lbTFf9jb+ygmNUDQMuOs=
|
||||||
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 h1:LvL4XsI70QxOGHed6yhQtAU34Kx3Qq2wwBzGFKY8zKk=
|
||||||
github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e/go.mod h1:kLgvv7o6UM+0QSf0QjAse3wReFDsb9qbZJdfexWlrQw=
|
github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e/go.mod h1:kLgvv7o6UM+0QSf0QjAse3wReFDsb9qbZJdfexWlrQw=
|
||||||
github.com/klauspost/compress v1.15.2 h1:3WH+AG7s2+T8o3nrM/8u2rdqUEcQhmga7smjrT41nAw=
|
github.com/klauspost/compress v1.15.8 h1:JahtItbkWjf2jzm/T+qgMxkP9EMHsqEUA6vCMGmXvhA=
|
||||||
github.com/klauspost/compress v1.15.2/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
|
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.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
||||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
@@ -162,29 +153,20 @@ github.com/mdlayher/socket v0.0.0-20211102153432-57e3fa563ecb h1:2dC7L10LmTqlyMV
|
|||||||
github.com/mdlayher/socket v0.0.0-20211102153432-57e3fa563ecb/go.mod h1:nFZ1EtZYK8Gi/k6QNu7z7CgO20i/4ExeQswwWuPmG/g=
|
github.com/mdlayher/socket v0.0.0-20211102153432-57e3fa563ecb/go.mod h1:nFZ1EtZYK8Gi/k6QNu7z7CgO20i/4ExeQswwWuPmG/g=
|
||||||
github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg=
|
github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg=
|
||||||
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
|
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
|
||||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
|
||||||
github.com/mssola/user_agent v0.5.3 h1:lBRPML9mdFuIZgI2cmlQ+atbpJdLdeVl2IDodjBR578=
|
github.com/mssola/user_agent v0.5.3 h1:lBRPML9mdFuIZgI2cmlQ+atbpJdLdeVl2IDodjBR578=
|
||||||
github.com/mssola/user_agent v0.5.3/go.mod h1:TTPno8LPY3wAIEKRpAtkdMT0f8SE24pLRGPahjCH4uw=
|
github.com/mssola/user_agent v0.5.3/go.mod h1:TTPno8LPY3wAIEKRpAtkdMT0f8SE24pLRGPahjCH4uw=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
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.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
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.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.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.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.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 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.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.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.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 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/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 h1:EBupykFmo22SDjv4fQVQd2J9NOoLPmyZA/15ldOGkPw=
|
||||||
github.com/pires/go-proxyproto v0.6.1/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY=
|
github.com/pires/go-proxyproto v0.6.1/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY=
|
||||||
@@ -199,7 +181,6 @@ github.com/shirou/gopsutil/v3 v3.22.2/go.mod h1:WapW1AOOPlHyXr+yOyw3uYx36enocrtS
|
|||||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ=
|
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ=
|
||||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||||
@@ -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-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-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-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-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-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
@@ -292,7 +272,6 @@ golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/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-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-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-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-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@@ -303,7 +282,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-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-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-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-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-20210123111255-9b0068b26619/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@@ -314,7 +292,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-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-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-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-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-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-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
@@ -324,14 +301,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-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-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-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-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 h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0=
|
||||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
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-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.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
@@ -346,7 +319,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-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-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-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.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.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
||||||
golang.org/x/tools v0.1.8 h1:P1HhGGuLW4aAclzjtmJdf0mJOjVUZUzOTqkAkWL+l6w=
|
golang.org/x/tools v0.1.8 h1:P1HhGGuLW4aAclzjtmJdf0mJOjVUZUzOTqkAkWL+l6w=
|
||||||
@@ -402,13 +374,14 @@ 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.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.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 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-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=
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.2.1/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY=
|
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 h1:MNh1AVMyVX23VUHE2O27jm6lNj3vjO5DexS4A1xvnzk=
|
||||||
honnef.co/go/tools v0.2.2/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY=
|
honnef.co/go/tools v0.2.2/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY=
|
||||||
|
rogchap.com/v8go v0.7.0 h1:kgjbiO4zE5itA962ze6Hqmbs4HgZbGzmueCXsZtremg=
|
||||||
rogchap.com/v8go v0.7.0/go.mod h1:MxgP3pL2MW4dpme/72QRs8sgNMmM0pRc8DPhcuLWPAs=
|
rogchap.com/v8go v0.7.0/go.mod h1:MxgP3pL2MW4dpme/72QRs8sgNMmM0pRc8DPhcuLWPAs=
|
||||||
|
|||||||
@@ -89,7 +89,10 @@ func (this *LogWriter) Write(message string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.c <- message
|
select {
|
||||||
|
case this.c <- message:
|
||||||
|
default:
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *LogWriter) Close() {
|
func (this *LogWriter) Close() {
|
||||||
|
|||||||
@@ -227,7 +227,7 @@ func (this *FileList) PurgeLFU(count int, callback func(hash string) error) erro
|
|||||||
if notFound {
|
if notFound {
|
||||||
_, err = db.deleteHitByHashStmt.Exec(hash)
|
_, err = db.deleteHitByHashStmt.Exec(hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return db.WrapError(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -359,14 +359,14 @@ func (this *FileList) remove(hash string) (notFound bool, err error) {
|
|||||||
|
|
||||||
_, err = db.deleteByHashStmt.Exec(hash)
|
_, err = db.deleteByHashStmt.Exec(hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, db.WrapError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
atomic.AddInt64(&this.total, -1)
|
atomic.AddInt64(&this.total, -1)
|
||||||
|
|
||||||
_, err = db.deleteHitByHashStmt.Exec(hash)
|
_, err = db.deleteHitByHashStmt.Exec(hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, db.WrapError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if this.onRemove != nil {
|
if this.onRemove != nil {
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type FileListDB struct {
|
type FileListDB struct {
|
||||||
|
dbPath string
|
||||||
|
|
||||||
readDB *dbs.DB
|
readDB *dbs.DB
|
||||||
writeDB *dbs.DB
|
writeDB *dbs.DB
|
||||||
|
|
||||||
@@ -49,6 +51,8 @@ func NewFileListDB() *FileListDB {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *FileListDB) Open(dbPath string) error {
|
func (this *FileListDB) Open(dbPath string) error {
|
||||||
|
this.dbPath = dbPath
|
||||||
|
|
||||||
// write db
|
// write db
|
||||||
writeDB, err := sql.Open("sqlite3", "file:"+dbPath+"?cache=private&mode=rwc&_journal_mode=WAL&_sync=OFF&_cache_size=32000&_secure_delete=FAST")
|
writeDB, err := sql.Open("sqlite3", "file:"+dbPath+"?cache=private&mode=rwc&_journal_mode=WAL&_sync=OFF&_cache_size=32000&_secure_delete=FAST")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -185,7 +189,7 @@ func (this *FileListDB) Add(hash string, item *Item) error {
|
|||||||
// 放入队列
|
// 放入队列
|
||||||
_, err := this.insertStmt.Exec(hash, item.Key, item.HeaderSize, item.BodySize, item.MetaSize, item.ExpiredAt, item.StaleAt, item.Host, item.ServerId, utils.UnixTime())
|
_, err := this.insertStmt.Exec(hash, item.Key, item.HeaderSize, item.BodySize, item.MetaSize, item.ExpiredAt, item.StaleAt, item.Host, item.ServerId, utils.UnixTime())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return this.WrapError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -249,7 +253,7 @@ func (this *FileListDB) ListLFUItems(count int) (hashList []string, err error) {
|
|||||||
func (this *FileListDB) IncreaseHit(hash string) error {
|
func (this *FileListDB) IncreaseHit(hash string) error {
|
||||||
var week = timeutil.Format("YW")
|
var week = timeutil.Format("YW")
|
||||||
_, err := this.increaseHitStmt.Exec(hash, week, week, week, week)
|
_, err := this.increaseHitStmt.Exec(hash, week, week, week, week)
|
||||||
return err
|
return this.WrapError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *FileListDB) CleanPrefix(prefix string) error {
|
func (this *FileListDB) CleanPrefix(prefix string) error {
|
||||||
@@ -262,7 +266,7 @@ func (this *FileListDB) CleanPrefix(prefix string) error {
|
|||||||
for {
|
for {
|
||||||
result, err := this.writeDB.Exec(`UPDATE "`+this.itemsTableName+`" SET expiredAt=0,staleAt=? WHERE id IN (SELECT id FROM "`+this.itemsTableName+`" WHERE expiredAt>0 AND createdAt<=? AND INSTR("key", ?)=1 LIMIT `+types.String(count)+`)`, unixTime+int64(staleLife), unixTime, prefix)
|
result, err := this.writeDB.Exec(`UPDATE "`+this.itemsTableName+`" SET expiredAt=0,staleAt=? WHERE id IN (SELECT id FROM "`+this.itemsTableName+`" WHERE expiredAt>0 AND createdAt<=? AND INSTR("key", ?)=1 LIMIT `+types.String(count)+`)`, unixTime+int64(staleLife), unixTime, prefix)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return this.WrapError(err)
|
||||||
}
|
}
|
||||||
affectedRows, err := result.RowsAffected()
|
affectedRows, err := result.RowsAffected()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -281,7 +285,7 @@ func (this *FileListDB) CleanAll() error {
|
|||||||
|
|
||||||
_, err := this.deleteAllStmt.Exec()
|
_, err := this.deleteAllStmt.Exec()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return this.WrapError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -351,6 +355,13 @@ func (this *FileListDB) Close() error {
|
|||||||
return errors.New("close database failed: " + strings.Join(errStrings, ", "))
|
return errors.New("close database failed: " + strings.Join(errStrings, ", "))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *FileListDB) WrapError(err error) error {
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return errors.New(err.Error() + "(file: " + this.dbPath + ")")
|
||||||
|
}
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
func (this *FileListDB) initTables(times int) error {
|
func (this *FileListDB) initTables(times int) error {
|
||||||
{
|
{
|
||||||
@@ -393,10 +404,10 @@ ON "` + this.itemsTableName + `" (
|
|||||||
if dropErr == nil {
|
if dropErr == nil {
|
||||||
return this.initTables(times + 1)
|
return this.initTables(times + 1)
|
||||||
}
|
}
|
||||||
return err
|
return this.WrapError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return this.WrapError(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -421,10 +432,10 @@ ON "` + this.hitsTableName + `" (
|
|||||||
if dropErr == nil {
|
if dropErr == nil {
|
||||||
return this.initTables(times + 1)
|
return this.initTables(times + 1)
|
||||||
}
|
}
|
||||||
return err
|
return this.WrapError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return this.WrapError(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,44 +3,24 @@
|
|||||||
package caches
|
package caches
|
||||||
|
|
||||||
import (
|
import (
|
||||||
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
minOpenFilesValue int32 = 4
|
|
||||||
maxOpenFilesValue int32 = 65535
|
|
||||||
|
|
||||||
modeSlow int32 = 1
|
modeSlow int32 = 1
|
||||||
modeFast int32 = 2
|
modeFast int32 = 2
|
||||||
)
|
)
|
||||||
|
|
||||||
// MaxOpenFiles max open files manager
|
// MaxOpenFiles max open files manager
|
||||||
type MaxOpenFiles struct {
|
type MaxOpenFiles struct {
|
||||||
step int32
|
ticker *time.Ticker
|
||||||
maxOpenFiles int32
|
mode int32
|
||||||
ptr *int32
|
|
||||||
ticker *time.Ticker
|
|
||||||
mode int32
|
|
||||||
|
|
||||||
lastOpens int32
|
|
||||||
currentOpens int32
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMaxOpenFiles(step int32) *MaxOpenFiles {
|
func NewMaxOpenFiles() *MaxOpenFiles {
|
||||||
if step <= 0 {
|
var f = &MaxOpenFiles{}
|
||||||
step = 2
|
|
||||||
}
|
|
||||||
var f = &MaxOpenFiles{
|
|
||||||
step: step,
|
|
||||||
maxOpenFiles: minOpenFilesValue,
|
|
||||||
}
|
|
||||||
if teaconst.DiskIsFast {
|
|
||||||
f.maxOpenFiles = 32
|
|
||||||
}
|
|
||||||
f.ptr = &f.maxOpenFiles
|
|
||||||
f.ticker = time.NewTicker(1 * time.Second)
|
f.ticker = time.NewTicker(1 * time.Second)
|
||||||
f.init()
|
f.init()
|
||||||
return f
|
return f
|
||||||
@@ -49,50 +29,24 @@ func NewMaxOpenFiles(step int32) *MaxOpenFiles {
|
|||||||
func (this *MaxOpenFiles) init() {
|
func (this *MaxOpenFiles) init() {
|
||||||
goman.New(func() {
|
goman.New(func() {
|
||||||
for range this.ticker.C {
|
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
|
// reset mode
|
||||||
atomic.StoreInt32(&this.mode, 0)
|
atomic.StoreInt32(&this.mode, modeFast)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *MaxOpenFiles) Fast() {
|
func (this *MaxOpenFiles) Fast() {
|
||||||
if atomic.LoadInt32(&this.mode) == 0 {
|
atomic.AddInt32(&this.mode, modeFast)
|
||||||
this.mode = modeFast
|
}
|
||||||
}
|
|
||||||
atomic.AddInt32(&this.currentOpens, 1)
|
func (this *MaxOpenFiles) FinishAll() {
|
||||||
|
this.Fast()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *MaxOpenFiles) Slow() {
|
func (this *MaxOpenFiles) Slow() {
|
||||||
atomic.StoreInt32(&this.mode, modeSlow)
|
atomic.StoreInt32(&this.mode, modeSlow)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *MaxOpenFiles) Max() int32 {
|
func (this *MaxOpenFiles) Next() bool {
|
||||||
if atomic.LoadInt32(&this.mode) == modeSlow {
|
return atomic.LoadInt32(&this.mode) != modeSlow
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
var v = atomic.LoadInt32(this.ptr)
|
|
||||||
if v <= minOpenFilesValue {
|
|
||||||
return minOpenFilesValue
|
|
||||||
}
|
|
||||||
return v
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,20 +9,27 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestNewMaxOpenFiles(t *testing.T) {
|
func TestNewMaxOpenFiles(t *testing.T) {
|
||||||
var maxOpenFiles = caches.NewMaxOpenFiles(2)
|
var maxOpenFiles = caches.NewMaxOpenFiles()
|
||||||
maxOpenFiles.Fast()
|
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)
|
time.Sleep(1 * time.Second)
|
||||||
t.Log(maxOpenFiles.Max())
|
t.Log("slow 1 second:", maxOpenFiles.Next())
|
||||||
|
|
||||||
maxOpenFiles.Slow()
|
maxOpenFiles.Slow()
|
||||||
t.Log(maxOpenFiles.Max())
|
t.Log("slow:", maxOpenFiles.Next())
|
||||||
|
|
||||||
maxOpenFiles.Slow()
|
maxOpenFiles.Fast()
|
||||||
t.Log(maxOpenFiles.Max())
|
t.Log("fast:", maxOpenFiles.Next())
|
||||||
|
|
||||||
maxOpenFiles.Slow()
|
|
||||||
t.Log(maxOpenFiles.Max())
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ package caches
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"io/ioutil"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PartialRanges 内容分区范围定义
|
// PartialRanges 内容分区范围定义
|
||||||
@@ -30,7 +30,7 @@ func NewPartialRangesFromJSON(data []byte) (*PartialRanges, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewPartialRangesFromFile(path string) (*PartialRanges, error) {
|
func NewPartialRangesFromFile(path string) (*PartialRanges, error) {
|
||||||
data, err := ioutil.ReadFile(path)
|
data, err := os.ReadFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -116,12 +116,12 @@ func (this *PartialRanges) WriteToFile(path string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("convert to json failed: " + err.Error())
|
return errors.New("convert to json failed: " + err.Error())
|
||||||
}
|
}
|
||||||
return ioutil.WriteFile(path, data, 0666)
|
return os.WriteFile(path, data, 0666)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadFromFile 从文件中读取
|
// ReadFromFile 从文件中读取
|
||||||
func (this *PartialRanges) ReadFromFile(path string) (*PartialRanges, error) {
|
func (this *PartialRanges) ReadFromFile(path string) (*PartialRanges, error) {
|
||||||
data, err := ioutil.ReadFile(path)
|
data, err := os.ReadFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,13 +63,15 @@ const (
|
|||||||
var sharedWritingFileKeyMap = map[string]zero.Zero{} // key => bool
|
var sharedWritingFileKeyMap = map[string]zero.Zero{} // key => bool
|
||||||
var sharedWritingFileKeyLocker = sync.Mutex{}
|
var sharedWritingFileKeyLocker = sync.Mutex{}
|
||||||
|
|
||||||
var maxOpenFiles = NewMaxOpenFiles(2)
|
var maxOpenFiles = NewMaxOpenFiles()
|
||||||
|
|
||||||
const maxOpenFilesSlowCost = 500 * time.Microsecond // 0.5ms
|
const maxOpenFilesSlowCost = 1000 * time.Microsecond // us
|
||||||
|
const protectingLoadWhenDump = false
|
||||||
|
|
||||||
// FileStorage 文件缓存
|
// FileStorage 文件缓存
|
||||||
// 文件结构:
|
//
|
||||||
// [expires time] | [ status ] | [url length] | [header length] | [body length] | [url] [header data] [body data]
|
// 文件结构:
|
||||||
|
// [expires time] | [ status ] | [url length] | [header length] | [body length] | [url] [header data] [body data]
|
||||||
type FileStorage struct {
|
type FileStorage struct {
|
||||||
policy *serverconfigs.HTTPCachePolicy
|
policy *serverconfigs.HTTPCachePolicy
|
||||||
options *serverconfigs.HTTPFileCacheStorage // 二级缓存
|
options *serverconfigs.HTTPFileCacheStorage // 二级缓存
|
||||||
@@ -428,7 +430,7 @@ func (this *FileStorage) openWriter(key string, expiredAt int64, status int, siz
|
|||||||
return nil, ErrFileIsWriting
|
return nil, ErrFileIsWriting
|
||||||
}
|
}
|
||||||
|
|
||||||
if !isFlushing && len(sharedWritingFileKeyMap) >= int(maxOpenFiles.Max()) {
|
if !isFlushing && !maxOpenFiles.Next() {
|
||||||
sharedWritingFileKeyLocker.Unlock()
|
sharedWritingFileKeyLocker.Unlock()
|
||||||
return nil, ErrTooManyOpenFiles
|
return nil, ErrTooManyOpenFiles
|
||||||
}
|
}
|
||||||
@@ -439,6 +441,9 @@ func (this *FileStorage) openWriter(key string, expiredAt int64, status int, siz
|
|||||||
if !isOk {
|
if !isOk {
|
||||||
sharedWritingFileKeyLocker.Lock()
|
sharedWritingFileKeyLocker.Lock()
|
||||||
delete(sharedWritingFileKeyMap, key)
|
delete(sharedWritingFileKeyMap, key)
|
||||||
|
if len(sharedWritingFileKeyMap) == 0 {
|
||||||
|
maxOpenFiles.FinishAll()
|
||||||
|
}
|
||||||
sharedWritingFileKeyLocker.Unlock()
|
sharedWritingFileKeyLocker.Unlock()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@@ -608,12 +613,18 @@ func (this *FileStorage) openWriter(key string, expiredAt int64, status int, siz
|
|||||||
return NewPartialFileWriter(writer, key, expiredAt, isNewCreated, isPartial, partialBodyOffset, ranges, func() {
|
return NewPartialFileWriter(writer, key, expiredAt, isNewCreated, isPartial, partialBodyOffset, ranges, func() {
|
||||||
sharedWritingFileKeyLocker.Lock()
|
sharedWritingFileKeyLocker.Lock()
|
||||||
delete(sharedWritingFileKeyMap, key)
|
delete(sharedWritingFileKeyMap, key)
|
||||||
|
if len(sharedWritingFileKeyMap) == 0 {
|
||||||
|
maxOpenFiles.FinishAll()
|
||||||
|
}
|
||||||
sharedWritingFileKeyLocker.Unlock()
|
sharedWritingFileKeyLocker.Unlock()
|
||||||
}), nil
|
}), nil
|
||||||
} else {
|
} else {
|
||||||
return NewFileWriter(this, writer, key, expiredAt, -1, func() {
|
return NewFileWriter(this, writer, key, expiredAt, -1, func() {
|
||||||
sharedWritingFileKeyLocker.Lock()
|
sharedWritingFileKeyLocker.Lock()
|
||||||
delete(sharedWritingFileKeyMap, key)
|
delete(sharedWritingFileKeyMap, key)
|
||||||
|
if len(sharedWritingFileKeyMap) == 0 {
|
||||||
|
maxOpenFiles.FinishAll()
|
||||||
|
}
|
||||||
sharedWritingFileKeyLocker.Unlock()
|
sharedWritingFileKeyLocker.Unlock()
|
||||||
}), nil
|
}), nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
"github.com/iwind/TeaGo/Tea"
|
"github.com/iwind/TeaGo/Tea"
|
||||||
_ "github.com/iwind/TeaGo/bootstrap"
|
_ "github.com/iwind/TeaGo/bootstrap"
|
||||||
"github.com/iwind/TeaGo/logs"
|
"github.com/iwind/TeaGo/logs"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -152,7 +152,7 @@ func TestFileStorage_OpenWriter_HTTP(t *testing.T) {
|
|||||||
"Last-Modified": []string{"Wed, 06 Jan 2021 10:03:29 GMT"},
|
"Last-Modified": []string{"Wed, 06 Jan 2021 10:03:29 GMT"},
|
||||||
"Server": []string{"CDN-Server"},
|
"Server": []string{"CDN-Server"},
|
||||||
},
|
},
|
||||||
Body: ioutil.NopCloser(bytes.NewBuffer([]byte("THIS IS HTTP BODY"))),
|
Body: io.NopCloser(bytes.NewBuffer([]byte("THIS IS HTTP BODY"))),
|
||||||
}
|
}
|
||||||
|
|
||||||
for k, v := range resp.Header {
|
for k, v := range resp.Header {
|
||||||
|
|||||||
@@ -95,7 +95,8 @@ func (this *MemoryStorage) Init() error {
|
|||||||
// 启动定时Flush memory to disk任务
|
// 启动定时Flush memory to disk任务
|
||||||
if this.parentStorage != nil {
|
if this.parentStorage != nil {
|
||||||
// TODO 应该根据磁盘性能决定线程数
|
// TODO 应该根据磁盘性能决定线程数
|
||||||
var threads = 1
|
// TODO 线程数应该可以在缓存策略和节点中设定
|
||||||
|
var threads = runtime.NumCPU()
|
||||||
|
|
||||||
for i := 0; i < threads; i++ {
|
for i := 0; i < threads; i++ {
|
||||||
goman.New(func() {
|
goman.New(func() {
|
||||||
@@ -438,16 +439,18 @@ func (this *MemoryStorage) startFlush() {
|
|||||||
if statCount == 100 {
|
if statCount == 100 {
|
||||||
statCount = 0
|
statCount = 0
|
||||||
|
|
||||||
loadStat, err := load.Avg()
|
if protectingLoadWhenDump {
|
||||||
if err == nil && loadStat != nil {
|
loadStat, err := load.Avg()
|
||||||
if loadStat.Load1 > 10 {
|
if err == nil && loadStat != nil {
|
||||||
writeDelayMS = 100
|
if loadStat.Load1 > 10 {
|
||||||
} else if loadStat.Load1 > 3 {
|
writeDelayMS = 100
|
||||||
writeDelayMS = 50
|
} else if loadStat.Load1 > 3 {
|
||||||
} else if loadStat.Load1 > 2 {
|
writeDelayMS = 50
|
||||||
writeDelayMS = 10
|
} else if loadStat.Load1 > 2 {
|
||||||
} else {
|
writeDelayMS = 10
|
||||||
writeDelayMS = 0
|
} else {
|
||||||
|
writeDelayMS = 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -304,3 +305,30 @@ func TestMemoryStorage_Stop(t *testing.T) {
|
|||||||
|
|
||||||
t.Log(len(m))
|
t.Log(len(m))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkValuesMap(b *testing.B) {
|
||||||
|
var m = map[uint64]*MemoryItem{}
|
||||||
|
var count = 1_000_000
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
m[uint64(i)] = &MemoryItem{
|
||||||
|
ExpiresAt: time.Now().Unix(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.Log(len(m))
|
||||||
|
|
||||||
|
var locker = sync.Mutex{}
|
||||||
|
b.ResetTimer()
|
||||||
|
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
for pb.Next() {
|
||||||
|
locker.Lock()
|
||||||
|
_, ok := m[uint64(rands.Int(0, 1_000_000))]
|
||||||
|
_ = ok
|
||||||
|
locker.Unlock()
|
||||||
|
|
||||||
|
locker.Lock()
|
||||||
|
delete(m, uint64(rands.Int(2, 1000000)))
|
||||||
|
locker.Unlock()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ package caches_test
|
|||||||
import (
|
import (
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/caches"
|
"github.com/TeaOSLab/EdgeNode/internal/caches"
|
||||||
"github.com/iwind/TeaGo/types"
|
"github.com/iwind/TeaGo/types"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@@ -16,7 +15,7 @@ func TestPartialFileWriter_Write(t *testing.T) {
|
|||||||
_ = os.Remove(path)
|
_ = os.Remove(path)
|
||||||
|
|
||||||
var reader = func() {
|
var reader = func() {
|
||||||
data, err := ioutil.ReadFile(path)
|
data, err := os.ReadFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
package compressions
|
package compressions
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
@@ -10,6 +11,10 @@ import (
|
|||||||
var sharedBrotliReaderPool *ReaderPool
|
var sharedBrotliReaderPool *ReaderPool
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
if teaconst.IsDaemon {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var maxSize = utils.SystemMemoryGB() * 256
|
var maxSize = utils.SystemMemoryGB() * 256
|
||||||
if maxSize == 0 {
|
if maxSize == 0 {
|
||||||
maxSize = 256
|
maxSize = 256
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
package compressions
|
package compressions
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
@@ -10,6 +11,10 @@ import (
|
|||||||
var sharedDeflateReaderPool *ReaderPool
|
var sharedDeflateReaderPool *ReaderPool
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
if teaconst.IsDaemon {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var maxSize = utils.SystemMemoryGB() * 256
|
var maxSize = utils.SystemMemoryGB() * 256
|
||||||
if maxSize == 0 {
|
if maxSize == 0 {
|
||||||
maxSize = 256
|
maxSize = 256
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
package compressions
|
package compressions
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
@@ -10,6 +11,10 @@ import (
|
|||||||
var sharedGzipReaderPool *ReaderPool
|
var sharedGzipReaderPool *ReaderPool
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
if teaconst.IsDaemon {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var maxSize = utils.SystemMemoryGB() * 256
|
var maxSize = utils.SystemMemoryGB() * 256
|
||||||
if maxSize == 0 {
|
if maxSize == 0 {
|
||||||
maxSize = 256
|
maxSize = 256
|
||||||
|
|||||||
25
internal/compressions/reader_pool_zstd.go
Normal file
25
internal/compressions/reader_pool_zstd.go
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||||
|
|
||||||
|
package compressions
|
||||||
|
|
||||||
|
import (
|
||||||
|
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
var sharedZSTDReaderPool *ReaderPool
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
if teaconst.IsDaemon {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
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"
|
ContentEncodingBr ContentEncoding = "br"
|
||||||
ContentEncodingGzip ContentEncoding = "gzip"
|
ContentEncodingGzip ContentEncoding = "gzip"
|
||||||
ContentEncodingDeflate ContentEncoding = "deflate"
|
ContentEncodingDeflate ContentEncoding = "deflate"
|
||||||
|
ContentEncodingZSTD ContentEncoding = "zstd"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrNotSupportedContentEncoding = errors.New("not supported content encoding")
|
var ErrNotSupportedContentEncoding = errors.New("not supported content encoding")
|
||||||
|
|
||||||
// AllEncodings 当前支持的所有编码
|
// AllEncodings 当前支持的所有编码
|
||||||
func AllEncodings() []ContentEncoding {
|
func AllEncodings() []ContentEncoding {
|
||||||
return []ContentEncoding{ContentEncodingBr, ContentEncodingGzip, ContentEncodingDeflate}
|
return []ContentEncoding{
|
||||||
|
ContentEncodingBr,
|
||||||
|
ContentEncodingGzip,
|
||||||
|
ContentEncodingZSTD,
|
||||||
|
ContentEncodingDeflate,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewReader 获取Reader
|
// NewReader 获取Reader
|
||||||
@@ -32,6 +38,8 @@ func NewReader(reader io.Reader, contentEncoding ContentEncoding) (Reader, error
|
|||||||
return NewGzipReader(reader)
|
return NewGzipReader(reader)
|
||||||
case ContentEncodingDeflate:
|
case ContentEncodingDeflate:
|
||||||
return NewDeflateReader(reader)
|
return NewDeflateReader(reader)
|
||||||
|
case ContentEncodingZSTD:
|
||||||
|
return NewZSTDReader(reader)
|
||||||
}
|
}
|
||||||
return nil, ErrNotSupportedContentEncoding
|
return nil, ErrNotSupportedContentEncoding
|
||||||
}
|
}
|
||||||
@@ -45,6 +53,8 @@ func NewWriter(writer io.Writer, compressType serverconfigs.HTTPCompressionType,
|
|||||||
return NewDeflateWriter(writer, level)
|
return NewDeflateWriter(writer, level)
|
||||||
case serverconfigs.HTTPCompressionTypeBrotli:
|
case serverconfigs.HTTPCompressionTypeBrotli:
|
||||||
return NewBrotliWriter(writer, level)
|
return NewBrotliWriter(writer, level)
|
||||||
|
case serverconfigs.HTTPCompressionTypeZSTD:
|
||||||
|
return NewZSTDWriter(writer, level)
|
||||||
}
|
}
|
||||||
return nil, errors.New("invalid compression type '" + compressType + "'")
|
return nil, errors.New("invalid compression type '" + compressType + "'")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,10 +5,46 @@ package compressions_test
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/compressions"
|
"github.com/TeaOSLab/EdgeNode/internal/compressions"
|
||||||
|
stringutil "github.com/iwind/TeaGo/utils/string"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestBrotliWriter_LargeFile(t *testing.T) {
|
||||||
|
var data = []byte{}
|
||||||
|
for i := 0; i < 1024*1024; i++ {
|
||||||
|
data = append(data, stringutil.Rand(32)...)
|
||||||
|
}
|
||||||
|
t.Log(len(data)/1024/1024, "M")
|
||||||
|
|
||||||
|
var before = time.Now()
|
||||||
|
defer func() {
|
||||||
|
t.Log(time.Since(before).Seconds()*1000, "ms")
|
||||||
|
}()
|
||||||
|
|
||||||
|
var buf = &bytes.Buffer{}
|
||||||
|
writer, err := compressions.NewBrotliWriter(buf, 5)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var offset = 0
|
||||||
|
var size = 4096
|
||||||
|
for offset < len(data) {
|
||||||
|
_, err = writer.Write(data[offset : offset+size])
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
offset += size
|
||||||
|
}
|
||||||
|
|
||||||
|
err = writer.Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func BenchmarkBrotliWriter_Write(b *testing.B) {
|
func BenchmarkBrotliWriter_Write(b *testing.B) {
|
||||||
var data = []byte(strings.Repeat("A", 1024))
|
var data = []byte(strings.Repeat("A", 1024))
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
package compressions
|
package compressions
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
"github.com/andybalholm/brotli"
|
"github.com/andybalholm/brotli"
|
||||||
"io"
|
"io"
|
||||||
@@ -11,6 +12,10 @@ import (
|
|||||||
var sharedBrotliWriterPool *WriterPool
|
var sharedBrotliWriterPool *WriterPool
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
if teaconst.IsDaemon {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var maxSize = utils.SystemMemoryGB() * 256
|
var maxSize = utils.SystemMemoryGB() * 256
|
||||||
if maxSize == 0 {
|
if maxSize == 0 {
|
||||||
maxSize = 256
|
maxSize = 256
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ package compressions
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"compress/flate"
|
"compress/flate"
|
||||||
|
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
@@ -11,6 +12,10 @@ import (
|
|||||||
var sharedDeflateWriterPool *WriterPool
|
var sharedDeflateWriterPool *WriterPool
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
if teaconst.IsDaemon {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var maxSize = utils.SystemMemoryGB() * 256
|
var maxSize = utils.SystemMemoryGB() * 256
|
||||||
if maxSize == 0 {
|
if maxSize == 0 {
|
||||||
maxSize = 256
|
maxSize = 256
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ package compressions
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"compress/gzip"
|
"compress/gzip"
|
||||||
|
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
@@ -11,6 +12,10 @@ import (
|
|||||||
var sharedGzipWriterPool *WriterPool
|
var sharedGzipWriterPool *WriterPool
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
if teaconst.IsDaemon {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var maxSize = utils.SystemMemoryGB() * 256
|
var maxSize = utils.SystemMemoryGB() * 256
|
||||||
if maxSize == 0 {
|
if maxSize == 0 {
|
||||||
maxSize = 256
|
maxSize = 256
|
||||||
|
|||||||
26
internal/compressions/writer_pool_zstd.go
Normal file
26
internal/compressions/writer_pool_zstd.go
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||||
|
|
||||||
|
package compressions
|
||||||
|
|
||||||
|
import (
|
||||||
|
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
|
"github.com/klauspost/compress/zstd"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
var sharedZSTDWriterPool *WriterPool
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
if teaconst.IsDaemon {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -3,20 +3,21 @@ package configs
|
|||||||
import (
|
import (
|
||||||
"github.com/iwind/TeaGo/Tea"
|
"github.com/iwind/TeaGo/Tea"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"io/ioutil"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
// APIConfig 节点API配置
|
// APIConfig 节点API配置
|
||||||
type APIConfig struct {
|
type APIConfig struct {
|
||||||
RPC struct {
|
RPC struct {
|
||||||
Endpoints []string `yaml:"endpoints"`
|
Endpoints []string `yaml:"endpoints"`
|
||||||
|
DisableUpdate bool `yaml:"disableUpdate"`
|
||||||
} `yaml:"rpc"`
|
} `yaml:"rpc"`
|
||||||
NodeId string `yaml:"nodeId"`
|
NodeId string `yaml:"nodeId"`
|
||||||
Secret string `yaml:"secret"`
|
Secret string `yaml:"secret"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadAPIConfig() (*APIConfig, error) {
|
func LoadAPIConfig() (*APIConfig, error) {
|
||||||
data, err := ioutil.ReadFile(Tea.ConfigFile("api.yaml"))
|
data, err := os.ReadFile(Tea.ConfigFile("api.yaml"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -30,12 +31,12 @@ func LoadAPIConfig() (*APIConfig, error) {
|
|||||||
return config, nil
|
return config, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保存到文件
|
// WriteFile 保存到文件
|
||||||
func (this *APIConfig) WriteFile(path string) error {
|
func (this *APIConfig) WriteFile(path string) error {
|
||||||
data, err := yaml.Marshal(this)
|
data, err := yaml.Marshal(this)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = ioutil.WriteFile(path, data, 0666)
|
err = os.WriteFile(path, data, 0666)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,15 @@
|
|||||||
package configs
|
package configs_test
|
||||||
|
|
||||||
import "testing"
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/configs"
|
||||||
|
_ "github.com/iwind/TeaGo/bootstrap"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
func TestLoadAPIConfig(t *testing.T) {
|
func TestLoadAPIConfig(t *testing.T) {
|
||||||
config, err := LoadAPIConfig()
|
config, err := configs.LoadAPIConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
t.Log(config)
|
t.Logf("%+v", config)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
package configs
|
package configs
|
||||||
|
|
||||||
// 集群配置
|
// ClusterConfig 集群配置
|
||||||
type ClusterConfig struct {
|
type ClusterConfig struct {
|
||||||
RPC struct {
|
RPC struct {
|
||||||
Endpoints []string `yaml:"endpoints"`
|
Endpoints []string `yaml:"endpoints"`
|
||||||
|
DisableUpdate bool `yaml:"disableUpdate"`
|
||||||
} `yaml:"rpc"`
|
} `yaml:"rpc"`
|
||||||
ClusterId string `yaml:"clusterId"`
|
ClusterId string `yaml:"clusterId"`
|
||||||
Secret string `yaml:"secret"`
|
Secret string `yaml:"secret"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package teaconst
|
package teaconst
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Version = "0.4.8.1"
|
Version = "0.5.0"
|
||||||
|
|
||||||
ProductName = "Edge Node"
|
ProductName = "Edge Node"
|
||||||
ProcessName = "edge-node"
|
ProcessName = "edge-node"
|
||||||
|
|||||||
@@ -2,7 +2,10 @@
|
|||||||
|
|
||||||
package teaconst
|
package teaconst
|
||||||
|
|
||||||
import "github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// 流量统计
|
// 流量统计
|
||||||
@@ -12,6 +15,7 @@ var (
|
|||||||
|
|
||||||
NodeId int64 = 0
|
NodeId int64 = 0
|
||||||
NodeIdString = ""
|
NodeIdString = ""
|
||||||
|
IsDaemon = len(os.Args) > 1 && os.Args[1] == "daemon"
|
||||||
|
|
||||||
GlobalProductName = nodeconfigs.DefaultProductName
|
GlobalProductName = nodeconfigs.DefaultProductName
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ func init() {
|
|||||||
|
|
||||||
nodeConfig, _ := nodeconfigs.SharedNodeConfig()
|
nodeConfig, _ := nodeconfigs.SharedNodeConfig()
|
||||||
if nodeConfig != nil {
|
if nodeConfig != nil {
|
||||||
err := SharedDDoSProtectionManager.Apply(nodeConfig.DDOSProtection)
|
err := SharedDDoSProtectionManager.Apply(nodeConfig.DDoSProtection)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
remotelogs.Error("FIREWALL", "apply DDoS protection failed: "+err.Error())
|
remotelogs.Error("FIREWALL", "apply DDoS protection failed: "+err.Error())
|
||||||
}
|
}
|
||||||
@@ -43,7 +43,7 @@ func init() {
|
|||||||
events.On(events.EventNFTablesReady, func() {
|
events.On(events.EventNFTablesReady, func() {
|
||||||
nodeConfig, _ := nodeconfigs.SharedNodeConfig()
|
nodeConfig, _ := nodeconfigs.SharedNodeConfig()
|
||||||
if nodeConfig != nil {
|
if nodeConfig != nil {
|
||||||
err := SharedDDoSProtectionManager.Apply(nodeConfig.DDOSProtection)
|
err := SharedDDoSProtectionManager.Apply(nodeConfig.DDoSProtection)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
remotelogs.Error("FIREWALL", "apply DDoS protection failed: "+err.Error())
|
remotelogs.Error("FIREWALL", "apply DDoS protection failed: "+err.Error())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
package firewalls
|
package firewalls
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
"github.com/iwind/TeaGo/types"
|
"github.com/iwind/TeaGo/types"
|
||||||
@@ -75,6 +76,24 @@ func (this *Firewalld) AllowPort(port int, protocol string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *Firewalld) AllowPortRangesPermanently(portRanges [][2]int, protocol string) error {
|
||||||
|
for _, portRange := range portRanges {
|
||||||
|
var port = this.PortRangeString(portRange, protocol)
|
||||||
|
|
||||||
|
{
|
||||||
|
var cmd = exec.Command(this.exe, "--add-port="+port, "--permanent")
|
||||||
|
this.pushCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var cmd = exec.Command(this.exe, "--add-port="+port)
|
||||||
|
this.pushCmd(cmd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (this *Firewalld) RemovePort(port int, protocol string) error {
|
func (this *Firewalld) RemovePort(port int, protocol string) error {
|
||||||
if !this.isReady {
|
if !this.isReady {
|
||||||
return nil
|
return nil
|
||||||
@@ -84,6 +103,30 @@ func (this *Firewalld) RemovePort(port int, protocol string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *Firewalld) RemovePortRangePermanently(portRange [2]int, protocol string) error {
|
||||||
|
var port = this.PortRangeString(portRange, protocol)
|
||||||
|
|
||||||
|
{
|
||||||
|
var cmd = exec.Command(this.exe, "--remove-port="+port, "--permanent")
|
||||||
|
this.pushCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var cmd = exec.Command(this.exe, "--remove-port="+port)
|
||||||
|
this.pushCmd(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Firewalld) PortRangeString(portRange [2]int, protocol string) string {
|
||||||
|
if portRange[0] == portRange[1] {
|
||||||
|
return types.String(portRange[0]) + "/" + protocol
|
||||||
|
} else {
|
||||||
|
return types.String(portRange[0]) + "-" + types.String(portRange[1]) + "/" + protocol
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (this *Firewalld) RejectSourceIP(ip string, timeoutSeconds int) error {
|
func (this *Firewalld) RejectSourceIP(ip string, timeoutSeconds int) error {
|
||||||
if !this.isReady {
|
if !this.isReady {
|
||||||
return nil
|
return nil
|
||||||
@@ -101,7 +144,7 @@ func (this *Firewalld) RejectSourceIP(ip string, timeoutSeconds int) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *Firewalld) DropSourceIP(ip string, timeoutSeconds int) error {
|
func (this *Firewalld) DropSourceIP(ip string, timeoutSeconds int, async bool) error {
|
||||||
if !this.isReady {
|
if !this.isReady {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -114,7 +157,15 @@ func (this *Firewalld) DropSourceIP(ip string, timeoutSeconds int) error {
|
|||||||
args = append(args, "--timeout="+types.String(timeoutSeconds)+"s")
|
args = append(args, "--timeout="+types.String(timeoutSeconds)+"s")
|
||||||
}
|
}
|
||||||
var cmd = exec.Command(this.exe, args...)
|
var cmd = exec.Command(this.exe, args...)
|
||||||
this.pushCmd(cmd)
|
if async {
|
||||||
|
this.pushCmd(cmd)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("run command failed '" + cmd.String() + "': " + err.Error())
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,10 @@ type FirewallInterface interface {
|
|||||||
RejectSourceIP(ip string, timeoutSeconds int) error
|
RejectSourceIP(ip string, timeoutSeconds int) error
|
||||||
|
|
||||||
// DropSourceIP 丢弃某个源IP数据
|
// DropSourceIP 丢弃某个源IP数据
|
||||||
DropSourceIP(ip string, timeoutSeconds int) error
|
// ip 要封禁的IP
|
||||||
|
// timeoutSeconds 过期时间
|
||||||
|
// async 是否异步
|
||||||
|
DropSourceIP(ip string, timeoutSeconds int, async bool) error
|
||||||
|
|
||||||
// RemoveSourceIP 删除某个源IP
|
// RemoveSourceIP 删除某个源IP
|
||||||
RemoveSourceIP(ip string) error
|
RemoveSourceIP(ip string) error
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ func (this *MockFirewall) RejectSourceIP(ip string, timeoutSeconds int) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DropSourceIP 丢弃某个源IP数据
|
// DropSourceIP 丢弃某个源IP数据
|
||||||
func (this *MockFirewall) DropSourceIP(ip string, timeoutSeconds int) error {
|
func (this *MockFirewall) DropSourceIP(ip string, timeoutSeconds int, async bool) error {
|
||||||
_ = ip
|
_ = ip
|
||||||
_ = timeoutSeconds
|
_ = timeoutSeconds
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -7,8 +7,10 @@ package firewalls
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
|
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/events"
|
"github.com/TeaOSLab/EdgeNode/internal/events"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/firewalls/nftables"
|
"github.com/TeaOSLab/EdgeNode/internal/firewalls/nftables"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
"github.com/iwind/TeaGo/types"
|
"github.com/iwind/TeaGo/types"
|
||||||
"net"
|
"net"
|
||||||
@@ -21,9 +23,13 @@ import (
|
|||||||
|
|
||||||
// check nft status, if being enabled we load it automatically
|
// check nft status, if being enabled we load it automatically
|
||||||
func init() {
|
func init() {
|
||||||
|
if teaconst.IsDaemon {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if runtime.GOOS == "linux" {
|
if runtime.GOOS == "linux" {
|
||||||
var ticker = time.NewTicker(3 * time.Minute)
|
var ticker = time.NewTicker(3 * time.Minute)
|
||||||
go func() {
|
goman.New(func() {
|
||||||
for range ticker.C {
|
for range ticker.C {
|
||||||
// if already ready, we break
|
// if already ready, we break
|
||||||
if nftablesIsReady {
|
if nftablesIsReady {
|
||||||
@@ -48,7 +54,7 @@ func init() {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,9 +80,16 @@ func (this *nftablesTableDefinition) protocol() string {
|
|||||||
return "ip"
|
return "ip"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type blockIPItem struct {
|
||||||
|
action string
|
||||||
|
ip string
|
||||||
|
timeoutSeconds int
|
||||||
|
}
|
||||||
|
|
||||||
func NewNFTablesFirewall() (*NFTablesFirewall, error) {
|
func NewNFTablesFirewall() (*NFTablesFirewall, error) {
|
||||||
var firewall = &NFTablesFirewall{
|
var firewall = &NFTablesFirewall{
|
||||||
conn: nftables.NewConn(),
|
conn: nftables.NewConn(),
|
||||||
|
dropIPQueue: make(chan *blockIPItem, 4096),
|
||||||
}
|
}
|
||||||
err := firewall.init()
|
err := firewall.init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -98,6 +111,8 @@ type NFTablesFirewall struct {
|
|||||||
denyIPv6Set *nftables.Set
|
denyIPv6Set *nftables.Set
|
||||||
|
|
||||||
firewalld *Firewalld
|
firewalld *Firewalld
|
||||||
|
|
||||||
|
dropIPQueue chan *blockIPItem
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *NFTablesFirewall) init() error {
|
func (this *NFTablesFirewall) init() error {
|
||||||
@@ -243,6 +258,18 @@ func (this *NFTablesFirewall) init() error {
|
|||||||
nftablesIsReady = true
|
nftablesIsReady = true
|
||||||
nftablesInstance = this
|
nftablesInstance = this
|
||||||
|
|
||||||
|
goman.New(func() {
|
||||||
|
for ipItem := range this.dropIPQueue {
|
||||||
|
switch ipItem.action {
|
||||||
|
case "drop":
|
||||||
|
err = this.DropSourceIP(ipItem.ip, ipItem.timeoutSeconds, false)
|
||||||
|
if err != nil {
|
||||||
|
remotelogs.Warn("NFTABLES", "drop ip '"+ipItem.ip+"' failed: "+err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// load firewalld
|
// load firewalld
|
||||||
var firewalld = NewFirewalld()
|
var firewalld = NewFirewalld()
|
||||||
if firewalld.IsReady() {
|
if firewalld.IsReady() {
|
||||||
@@ -307,16 +334,29 @@ func (this *NFTablesFirewall) AllowSourceIP(ip string) error {
|
|||||||
// RejectSourceIP 拒绝某个源IP连接
|
// RejectSourceIP 拒绝某个源IP连接
|
||||||
// we did not create set for drop ip, so we reuse DropSourceIP() method here
|
// we did not create set for drop ip, so we reuse DropSourceIP() method here
|
||||||
func (this *NFTablesFirewall) RejectSourceIP(ip string, timeoutSeconds int) error {
|
func (this *NFTablesFirewall) RejectSourceIP(ip string, timeoutSeconds int) error {
|
||||||
return this.DropSourceIP(ip, timeoutSeconds)
|
return this.DropSourceIP(ip, timeoutSeconds, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DropSourceIP 丢弃某个源IP数据
|
// DropSourceIP 丢弃某个源IP数据
|
||||||
func (this *NFTablesFirewall) DropSourceIP(ip string, timeoutSeconds int) error {
|
func (this *NFTablesFirewall) DropSourceIP(ip string, timeoutSeconds int, async bool) error {
|
||||||
var data = net.ParseIP(ip)
|
var data = net.ParseIP(ip)
|
||||||
if data == nil {
|
if data == nil {
|
||||||
return errors.New("invalid ip '" + ip + "'")
|
return errors.New("invalid ip '" + ip + "'")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if async {
|
||||||
|
select {
|
||||||
|
case this.dropIPQueue <- &blockIPItem{
|
||||||
|
action: "drop",
|
||||||
|
ip: ip,
|
||||||
|
timeoutSeconds: timeoutSeconds,
|
||||||
|
}:
|
||||||
|
default:
|
||||||
|
return errors.New("drop ip queue is full")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
if strings.Contains(ip, ":") { // ipv6
|
if strings.Contains(ip, ":") { // ipv6
|
||||||
if this.denyIPv6Set == nil {
|
if this.denyIPv6Set == nil {
|
||||||
return errors.New("ipv6 ip set is nil")
|
return errors.New("ipv6 ip set is nil")
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ func (this *NFTablesFirewall) RejectSourceIP(ip string, timeoutSeconds int) erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DropSourceIP 丢弃某个源IP数据
|
// DropSourceIP 丢弃某个源IP数据
|
||||||
func (this *NFTablesFirewall) DropSourceIP(ip string, timeoutSeconds int) error {
|
func (this *NFTablesFirewall) DropSourceIP(ip string, timeoutSeconds int, async bool) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
package goman
|
package goman
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@@ -14,6 +15,10 @@ var instanceId = uint64(0)
|
|||||||
|
|
||||||
// New 新创建goroutine
|
// New 新创建goroutine
|
||||||
func New(f func()) {
|
func New(f func()) {
|
||||||
|
if teaconst.IsDaemon {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
_, file, line, _ := runtime.Caller(1)
|
_, file, line, _ := runtime.Caller(1)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
@@ -42,6 +47,10 @@ func New(f func()) {
|
|||||||
|
|
||||||
// NewWithArgs 创建带有参数的goroutine
|
// NewWithArgs 创建带有参数的goroutine
|
||||||
func NewWithArgs(f func(args ...interface{}), args ...interface{}) {
|
func NewWithArgs(f func(args ...interface{}), args ...interface{}) {
|
||||||
|
if teaconst.IsDaemon {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
_, file, line, _ := runtime.Caller(1)
|
_, file, line, _ := runtime.Caller(1)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ package iplibrary
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"io/ioutil"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@@ -62,7 +62,7 @@ func getIpInfo(cityId int64, line []byte) *IpInfo {
|
|||||||
|
|
||||||
func NewIP2Region(path string) (*IP2Region, error) {
|
func NewIP2Region(path string) (*IP2Region, error) {
|
||||||
var region = &IP2Region{}
|
var region = &IP2Region{}
|
||||||
region.dbData, err = ioutil.ReadFile(path)
|
region.dbData, err = os.ReadFile(path)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ func TestIPItem_Memory(t *testing.T) {
|
|||||||
for i := 0; i < 2_000_000; i ++ {
|
for i := 0; i < 2_000_000; i ++ {
|
||||||
list.Add(&IPItem{
|
list.Add(&IPItem{
|
||||||
Type: "ip",
|
Type: "ip",
|
||||||
Id: int64(i),
|
Id: uint64(i),
|
||||||
IPFrom: utils.IP2Long("192.168.1.1"),
|
IPFrom: utils.IP2Long("192.168.1.1"),
|
||||||
IPTo: 0,
|
IPTo: 0,
|
||||||
ExpiredAt: time.Now().Unix(),
|
ExpiredAt: time.Now().Unix(),
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ type IPListDB struct {
|
|||||||
cleanTicker *time.Ticker
|
cleanTicker *time.Ticker
|
||||||
|
|
||||||
dir string
|
dir string
|
||||||
|
|
||||||
|
isClosed bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewIPListDB() (*IPListDB, error) {
|
func NewIPListDB() (*IPListDB, error) {
|
||||||
@@ -56,6 +58,12 @@ func (this *IPListDB) init() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
db.SetMaxOpenConns(1)
|
db.SetMaxOpenConns(1)
|
||||||
|
|
||||||
|
//_, err = db.Exec("VACUUM")
|
||||||
|
//if err != nil {
|
||||||
|
// return err
|
||||||
|
//}
|
||||||
|
|
||||||
this.db = db
|
this.db = db
|
||||||
|
|
||||||
// 初始化数据库
|
// 初始化数据库
|
||||||
@@ -117,6 +125,7 @@ ON "` + this.itemTableName + `" (
|
|||||||
|
|
||||||
goman.New(func() {
|
goman.New(func() {
|
||||||
events.On(events.EventQuit, func() {
|
events.On(events.EventQuit, func() {
|
||||||
|
_ = this.Close()
|
||||||
this.cleanTicker.Stop()
|
this.cleanTicker.Stop()
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -133,11 +142,19 @@ ON "` + this.itemTableName + `" (
|
|||||||
|
|
||||||
// DeleteExpiredItems 删除过期的条目
|
// DeleteExpiredItems 删除过期的条目
|
||||||
func (this *IPListDB) DeleteExpiredItems() error {
|
func (this *IPListDB) DeleteExpiredItems() error {
|
||||||
|
if this.isClosed {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
_, err := this.deleteExpiredItemsStmt.Exec(time.Now().Unix() - 7*86400)
|
_, err := this.deleteExpiredItemsStmt.Exec(time.Now().Unix() - 7*86400)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *IPListDB) AddItem(item *pb.IPItem) error {
|
func (this *IPListDB) AddItem(item *pb.IPItem) error {
|
||||||
|
if this.isClosed {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
_, err := this.deleteItemStmt.Exec(item.Id)
|
_, err := this.deleteItemStmt.Exec(item.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -147,6 +164,10 @@ func (this *IPListDB) AddItem(item *pb.IPItem) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *IPListDB) ReadItems(offset int64, size int64) (items []*pb.IPItem, err error) {
|
func (this *IPListDB) ReadItems(offset int64, size int64) (items []*pb.IPItem, err error) {
|
||||||
|
if this.isClosed {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
rows, err := this.selectItemsStmt.Query(offset, size)
|
rows, err := this.selectItemsStmt.Query(offset, size)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -169,6 +190,10 @@ func (this *IPListDB) ReadItems(offset int64, size int64) (items []*pb.IPItem, e
|
|||||||
|
|
||||||
// ReadMaxVersion 读取当前最大版本号
|
// ReadMaxVersion 读取当前最大版本号
|
||||||
func (this *IPListDB) ReadMaxVersion() int64 {
|
func (this *IPListDB) ReadMaxVersion() int64 {
|
||||||
|
if this.isClosed {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
row := this.selectMaxVersionStmt.QueryRow()
|
row := this.selectMaxVersionStmt.QueryRow()
|
||||||
if row == nil {
|
if row == nil {
|
||||||
return 0
|
return 0
|
||||||
@@ -182,6 +207,8 @@ func (this *IPListDB) ReadMaxVersion() int64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *IPListDB) Close() error {
|
func (this *IPListDB) Close() error {
|
||||||
|
this.isClosed = true
|
||||||
|
|
||||||
if this.db != nil {
|
if this.db != nil {
|
||||||
_ = this.deleteExpiredItemsStmt.Close()
|
_ = this.deleteExpiredItemsStmt.Close()
|
||||||
_ = this.deleteItemStmt.Close()
|
_ = this.deleteItemStmt.Close()
|
||||||
|
|||||||
@@ -53,6 +53,11 @@ func TestIPListDB_ReadItems(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
_ = db.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
items, err := db.ReadItems(0, 2)
|
items, err := db.ReadItems(0, 2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ func TestIPList_Contains(t *testing.T) {
|
|||||||
list := NewIPList()
|
list := NewIPList()
|
||||||
for i := 0; i < 255; i++ {
|
for i := 0; i < 255; i++ {
|
||||||
list.AddDelay(&IPItem{
|
list.AddDelay(&IPItem{
|
||||||
Id: int64(i),
|
Id: uint64(i),
|
||||||
IPFrom: utils.IP2Long(strconv.Itoa(i) + ".168.0.1"),
|
IPFrom: utils.IP2Long(strconv.Itoa(i) + ".168.0.1"),
|
||||||
IPTo: utils.IP2Long(strconv.Itoa(i) + ".168.255.1"),
|
IPTo: utils.IP2Long(strconv.Itoa(i) + ".168.255.1"),
|
||||||
ExpiredAt: 0,
|
ExpiredAt: 0,
|
||||||
@@ -152,7 +152,7 @@ func TestIPList_Contains(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for i := 0; i < 255; i++ {
|
for i := 0; i < 255; i++ {
|
||||||
list.AddDelay(&IPItem{
|
list.AddDelay(&IPItem{
|
||||||
Id: int64(1000 + i),
|
Id: uint64(1000 + i),
|
||||||
IPFrom: utils.IP2Long("192.167.2." + strconv.Itoa(i)),
|
IPFrom: utils.IP2Long("192.167.2." + strconv.Itoa(i)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -172,7 +172,7 @@ func TestIPList_Contains_Many(t *testing.T) {
|
|||||||
list := NewIPList()
|
list := NewIPList()
|
||||||
for i := 0; i < 1_000_000; i++ {
|
for i := 0; i < 1_000_000; i++ {
|
||||||
list.AddDelay(&IPItem{
|
list.AddDelay(&IPItem{
|
||||||
Id: int64(i),
|
Id: uint64(i),
|
||||||
IPFrom: utils.IP2Long(strconv.Itoa(rands.Int(0, 255)) + "." + strconv.Itoa(rands.Int(0, 255)) + "." + strconv.Itoa(rands.Int(0, 255)) + "." + strconv.Itoa(rands.Int(0, 255))),
|
IPFrom: utils.IP2Long(strconv.Itoa(rands.Int(0, 255)) + "." + strconv.Itoa(rands.Int(0, 255)) + "." + strconv.Itoa(rands.Int(0, 255)) + "." + strconv.Itoa(rands.Int(0, 255))),
|
||||||
IPTo: utils.IP2Long(strconv.Itoa(rands.Int(0, 255)) + "." + strconv.Itoa(rands.Int(0, 255)) + "." + strconv.Itoa(rands.Int(0, 255)) + "." + strconv.Itoa(rands.Int(0, 255))),
|
IPTo: utils.IP2Long(strconv.Itoa(rands.Int(0, 255)) + "." + strconv.Itoa(rands.Int(0, 255)) + "." + strconv.Itoa(rands.Int(0, 255)) + "." + strconv.Itoa(rands.Int(0, 255))),
|
||||||
ExpiredAt: 0,
|
ExpiredAt: 0,
|
||||||
@@ -217,7 +217,7 @@ func TestIPList_ContainsIPStrings(t *testing.T) {
|
|||||||
list := NewIPList()
|
list := NewIPList()
|
||||||
for i := 0; i < 255; i++ {
|
for i := 0; i < 255; i++ {
|
||||||
list.Add(&IPItem{
|
list.Add(&IPItem{
|
||||||
Id: int64(i),
|
Id: uint64(i),
|
||||||
IPFrom: utils.IP2Long(strconv.Itoa(i) + ".168.0.1"),
|
IPFrom: utils.IP2Long(strconv.Itoa(i) + ".168.0.1"),
|
||||||
IPTo: utils.IP2Long(strconv.Itoa(i) + ".168.255.1"),
|
IPTo: utils.IP2Long(strconv.Itoa(i) + ".168.255.1"),
|
||||||
ExpiredAt: 0,
|
ExpiredAt: 0,
|
||||||
@@ -305,7 +305,7 @@ func BenchmarkIPList_Contains(b *testing.B) {
|
|||||||
var list = NewIPList()
|
var list = NewIPList()
|
||||||
for i := 1; i < 200_000; i++ {
|
for i := 1; i < 200_000; i++ {
|
||||||
list.AddDelay(&IPItem{
|
list.AddDelay(&IPItem{
|
||||||
Id: int64(i),
|
Id: uint64(i),
|
||||||
IPFrom: utils.IP2Long(strconv.Itoa(rands.Int(0, 255)) + "." + strconv.Itoa(rands.Int(0, 255)) + ".0.1"),
|
IPFrom: utils.IP2Long(strconv.Itoa(rands.Int(0, 255)) + "." + strconv.Itoa(rands.Int(0, 255)) + ".0.1"),
|
||||||
IPTo: utils.IP2Long(strconv.Itoa(rands.Int(0, 255)) + "." + strconv.Itoa(rands.Int(0, 255)) + ".0.1"),
|
IPTo: utils.IP2Long(strconv.Itoa(rands.Int(0, 255)) + "." + strconv.Itoa(rands.Int(0, 255)) + ".0.1"),
|
||||||
ExpiredAt: time.Now().Unix() + 60,
|
ExpiredAt: time.Now().Unix() + 60,
|
||||||
|
|||||||
@@ -106,6 +106,7 @@ func BenchmarkIP2RegionLibrary_Lookup(b *testing.B) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
|
b.ResetTimer()
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
_, _ = library.Lookup("8.8.8.8")
|
_, _ = library.Lookup("8.8.8.8")
|
||||||
|
|||||||
@@ -5,14 +5,23 @@ package iplibrary
|
|||||||
import (
|
import (
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
|
"github.com/iwind/TeaGo/Tea"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AllowIP 检查IP是否被允许访问
|
// AllowIP 检查IP是否被允许访问
|
||||||
// 如果一个IP不在任何名单中,则允许访问
|
// 如果一个IP不在任何名单中,则允许访问
|
||||||
func AllowIP(ip string, serverId int64) (canGoNext bool, inAllowList bool) {
|
func AllowIP(ip string, serverId int64) (canGoNext bool, inAllowList bool) {
|
||||||
// 放行lo
|
if !Tea.IsTesting() { // 如果在测试环境,我们不加入一些白名单,以便于可以在本地和局域网正常测试
|
||||||
if ip == "127.0.0.1" {
|
// 放行lo
|
||||||
return true, true
|
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)
|
var ipLong = utils.IP2Long(ip)
|
||||||
@@ -20,12 +29,6 @@ func AllowIP(ip string, serverId int64) (canGoNext bool, inAllowList bool) {
|
|||||||
return false, false
|
return false, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// check node
|
|
||||||
nodeConfig, err := nodeconfigs.SharedNodeConfig()
|
|
||||||
if err == nil && nodeConfig.IPIsAutoAllowed(ip) {
|
|
||||||
return true, true
|
|
||||||
}
|
|
||||||
|
|
||||||
// check white lists
|
// check white lists
|
||||||
if GlobalWhiteIPList.Contains(ipLong) {
|
if GlobalWhiteIPList.Contains(ipLong) {
|
||||||
return true, true
|
return true, true
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import (
|
|||||||
"github.com/iwind/TeaGo/Tea"
|
"github.com/iwind/TeaGo/Tea"
|
||||||
_ "github.com/iwind/TeaGo/bootstrap"
|
_ "github.com/iwind/TeaGo/bootstrap"
|
||||||
"github.com/iwind/TeaGo/types"
|
"github.com/iwind/TeaGo/types"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@@ -90,7 +89,7 @@ func (this *CityManager) Lookup(provinceId int64, cityName string) (cityId int64
|
|||||||
|
|
||||||
// 从缓存中读取
|
// 从缓存中读取
|
||||||
func (this *CityManager) load() error {
|
func (this *CityManager) load() error {
|
||||||
data, err := ioutil.ReadFile(this.cacheFile)
|
data, err := os.ReadFile(this.cacheFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
return nil
|
return nil
|
||||||
@@ -119,7 +118,7 @@ func (this *CityManager) loop() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
resp, err := rpcClient.RegionCityRPC().FindAllEnabledRegionCities(rpcClient.Context(), &pb.FindAllEnabledRegionCitiesRequest{})
|
resp, err := rpcClient.RegionCityRPC().FindAllRegionCities(rpcClient.Context(), &pb.FindAllRegionCitiesRequest{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -151,6 +150,6 @@ func (this *CityManager) loop() error {
|
|||||||
|
|
||||||
// 保存到本地缓存
|
// 保存到本地缓存
|
||||||
|
|
||||||
err = ioutil.WriteFile(this.cacheFile, data, 0666)
|
err = os.WriteFile(this.cacheFile, data, 0666)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
||||||
"github.com/iwind/TeaGo/Tea"
|
"github.com/iwind/TeaGo/Tea"
|
||||||
_ "github.com/iwind/TeaGo/bootstrap"
|
_ "github.com/iwind/TeaGo/bootstrap"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@@ -89,7 +88,7 @@ func (this *CountryManager) Lookup(countryName string) (countryId int64) {
|
|||||||
|
|
||||||
// 从缓存中读取
|
// 从缓存中读取
|
||||||
func (this *CountryManager) load() error {
|
func (this *CountryManager) load() error {
|
||||||
data, err := ioutil.ReadFile(this.cacheFile)
|
data, err := os.ReadFile(this.cacheFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
return nil
|
return nil
|
||||||
@@ -118,7 +117,7 @@ func (this *CountryManager) loop() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
resp, err := rpcClient.RegionCountryRPC().FindAllEnabledRegionCountries(rpcClient.Context(), &pb.FindAllEnabledRegionCountriesRequest{})
|
resp, err := rpcClient.RegionCountryRPC().FindAllRegionCountries(rpcClient.Context(), &pb.FindAllRegionCountriesRequest{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -149,6 +148,6 @@ func (this *CountryManager) loop() error {
|
|||||||
this.locker.Unlock()
|
this.locker.Unlock()
|
||||||
|
|
||||||
// 保存到本地缓存
|
// 保存到本地缓存
|
||||||
err = ioutil.WriteFile(this.cacheFile, data, 0666)
|
err = os.WriteFile(this.cacheFile, data, 0666)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
||||||
"github.com/iwind/TeaGo/Tea"
|
"github.com/iwind/TeaGo/Tea"
|
||||||
_ "github.com/iwind/TeaGo/bootstrap"
|
_ "github.com/iwind/TeaGo/bootstrap"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@@ -89,7 +88,7 @@ func (this *ProviderManager) Lookup(providerName string) (providerId int64) {
|
|||||||
|
|
||||||
// 从缓存中读取
|
// 从缓存中读取
|
||||||
func (this *ProviderManager) load() error {
|
func (this *ProviderManager) load() error {
|
||||||
data, err := ioutil.ReadFile(this.cacheFile)
|
data, err := os.ReadFile(this.cacheFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
return nil
|
return nil
|
||||||
@@ -118,7 +117,7 @@ func (this *ProviderManager) loop() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
resp, err := rpcClient.RegionProviderRPC().FindAllEnabledRegionProviders(rpcClient.Context(), &pb.FindAllEnabledRegionProvidersRequest{})
|
resp, err := rpcClient.RegionProviderRPC().FindAllRegionProviders(rpcClient.Context(), &pb.FindAllRegionProvidersRequest{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -150,6 +149,6 @@ func (this *ProviderManager) loop() error {
|
|||||||
|
|
||||||
// 保存到本地缓存
|
// 保存到本地缓存
|
||||||
|
|
||||||
err = ioutil.WriteFile(this.cacheFile, data, 0666)
|
err = os.WriteFile(this.cacheFile, data, 0666)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
||||||
"github.com/iwind/TeaGo/Tea"
|
"github.com/iwind/TeaGo/Tea"
|
||||||
_ "github.com/iwind/TeaGo/bootstrap"
|
_ "github.com/iwind/TeaGo/bootstrap"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@@ -93,7 +92,7 @@ func (this *ProvinceManager) Lookup(provinceName string) (provinceId int64) {
|
|||||||
|
|
||||||
// 从缓存中读取
|
// 从缓存中读取
|
||||||
func (this *ProvinceManager) load() error {
|
func (this *ProvinceManager) load() error {
|
||||||
data, err := ioutil.ReadFile(this.cacheFile)
|
data, err := os.ReadFile(this.cacheFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
return nil
|
return nil
|
||||||
@@ -122,7 +121,7 @@ func (this *ProvinceManager) loop() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
resp, err := rpcClient.RegionProvinceRPC().FindAllEnabledRegionProvincesWithCountryId(rpcClient.Context(), &pb.FindAllEnabledRegionProvincesWithCountryIdRequest{
|
resp, err := rpcClient.RegionProvinceRPC().FindAllRegionProvincesWithRegionCountryId(rpcClient.Context(), &pb.FindAllRegionProvincesWithRegionCountryIdRequest{
|
||||||
RegionCountryId: ChinaCountryId,
|
RegionCountryId: ChinaCountryId,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -156,6 +155,6 @@ func (this *ProvinceManager) loop() error {
|
|||||||
|
|
||||||
// 保存到本地缓存
|
// 保存到本地缓存
|
||||||
|
|
||||||
err = ioutil.WriteFile(this.cacheFile, data, 0666)
|
err = os.WriteFile(this.cacheFile, data, 0666)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,18 +23,19 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const MaxQueueSize = 2048
|
const MaxQueueSize = 256 // TODO 可以配置,可以在单个任务里配置
|
||||||
|
|
||||||
// Task 单个指标任务
|
// Task 单个指标任务
|
||||||
// 数据库存储:
|
// 数据库存储:
|
||||||
// data/
|
//
|
||||||
// metric.$ID.db
|
// data/
|
||||||
// stats
|
// metric.$ID.db
|
||||||
// id, keys, value, time, serverId, hash
|
// stats
|
||||||
// 原理:
|
// id, keys, value, time, serverId, hash
|
||||||
// 添加或者有变更时 isUploaded = false
|
// 原理:
|
||||||
// 上传时检查 isUploaded 状态
|
// 添加或者有变更时 isUploaded = false
|
||||||
// 只上传每个服务中排序最前面的 N 个数据
|
// 上传时检查 isUploaded 状态
|
||||||
|
// 只上传每个服务中排序最前面的 N 个数据
|
||||||
type Task struct {
|
type Task struct {
|
||||||
item *serverconfigs.MetricItemConfig
|
item *serverconfigs.MetricItemConfig
|
||||||
isLoaded bool
|
isLoaded bool
|
||||||
@@ -210,7 +211,7 @@ func (this *Task) Start() error {
|
|||||||
var tr = trackers.Begin("[METRIC]UPLOAD_STATS")
|
var tr = trackers.Begin("[METRIC]UPLOAD_STATS")
|
||||||
err := this.Upload(1 * time.Second)
|
err := this.Upload(1 * time.Second)
|
||||||
tr.End()
|
tr.End()
|
||||||
if err != nil {
|
if err != nil && !rpc.IsConnError(err) {
|
||||||
remotelogs.Error("METRIC", "upload stats failed: "+err.Error())
|
remotelogs.Error("METRIC", "upload stats failed: "+err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -372,7 +373,9 @@ func (this *Task) Upload(pauseDuration time.Duration) error {
|
|||||||
for _, serverId := range serverIds {
|
for _, serverId := range serverIds {
|
||||||
for _, currentTime := range times {
|
for _, currentTime := range times {
|
||||||
idStrings, err := func(serverId int64, currentTime string) (ids []string, err error) {
|
idStrings, err := func(serverId int64, currentTime string) (ids []string, err error) {
|
||||||
|
var t = trackers.Begin("[METRIC]SELECT_TOP_STMT")
|
||||||
rows, err := this.selectTopStmt.Query(serverId, this.item.Version, currentTime)
|
rows, err := this.selectTopStmt.Query(serverId, this.item.Version, currentTime)
|
||||||
|
t.End()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -406,7 +406,7 @@ func (this *APIStream) handleCheckLocalFirewall(message *pb.NodeStreamMessage) e
|
|||||||
"version": version,
|
"version": version,
|
||||||
}
|
}
|
||||||
|
|
||||||
var protectionConfig = sharedNodeConfig.DDOSProtection
|
var protectionConfig = sharedNodeConfig.DDoSProtection
|
||||||
err = firewalls.SharedDDoSProtectionManager.Apply(protectionConfig)
|
err = firewalls.SharedDDoSProtectionManager.Apply(protectionConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.replyFail(message.RequestId, dataMessage.Name+"was installed, but apply DDoS protection config failed: "+err.Error())
|
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"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
||||||
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/iplibrary"
|
"github.com/TeaOSLab/EdgeNode/internal/iplibrary"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/stats"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/ttlcache"
|
"github.com/TeaOSLab/EdgeNode/internal/ttlcache"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/waf"
|
"github.com/TeaOSLab/EdgeNode/internal/waf"
|
||||||
"github.com/iwind/TeaGo/types"
|
"github.com/iwind/TeaGo/types"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
@@ -26,6 +28,8 @@ type ClientConn struct {
|
|||||||
hasDeadline bool
|
hasDeadline bool
|
||||||
hasRead bool
|
hasRead bool
|
||||||
|
|
||||||
|
isLO bool // 是否为环路
|
||||||
|
|
||||||
hasResetSYNFlood bool
|
hasResetSYNFlood bool
|
||||||
|
|
||||||
BaseClientConn
|
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) {
|
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.isTLS {
|
||||||
if !this.hasDeadline {
|
if !this.hasDeadline {
|
||||||
_ = this.rawConn.SetReadDeadline(time.Now().Add(time.Duration(nodeconfigs.DefaultTLSHandshakeTimeout) * time.Second)) // TODO 握手超时时间可以设置
|
_ = 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)
|
n, err = this.rawConn.Read(b)
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
atomic.AddUint64(&teaconst.InTrafficBytes, uint64(n))
|
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
|
var isHandshakeError = err != nil && os.IsTimeout(err) && !this.hasRead
|
||||||
if isHandshakeError {
|
if isHandshakeError {
|
||||||
_ = this.SetLinger(0)
|
_ = this.SetLinger(0)
|
||||||
}
|
}
|
||||||
var synFloodConfig = sharedNodeConfig.SYNFloodConfig()
|
|
||||||
if synFloodConfig != nil && synFloodConfig.IsOn {
|
// SYN Flood检测
|
||||||
if isHandshakeError {
|
if this.serverId == 0 || !this.hasResetSYNFlood {
|
||||||
this.increaseSYNFlood(synFloodConfig)
|
var synFloodConfig = sharedNodeConfig.SYNFloodConfig()
|
||||||
} else if err == nil && !this.hasResetSYNFlood {
|
if synFloodConfig != nil && synFloodConfig.IsOn {
|
||||||
this.hasResetSYNFlood = true
|
if isHandshakeError {
|
||||||
this.resetSYNFlood()
|
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)
|
n, err = this.rawConn.Write(b)
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
atomic.AddUint64(&teaconst.OutTrafficBytes, uint64(n))
|
atomic.AddUint64(&teaconst.OutTrafficBytes, uint64(n))
|
||||||
|
|
||||||
|
// 统计当前服务带宽
|
||||||
|
if this.serverId > 0 {
|
||||||
|
if !this.isLO { // 环路不统计带宽,避免缓存预热等行为产生带宽
|
||||||
|
stats.SharedBandwidthStatManager.Add(this.userId, this.serverId, int64(n))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,6 +129,7 @@ func (this *ClientConn) Close() error {
|
|||||||
err := this.rawConn.Close()
|
err := this.rawConn.Close()
|
||||||
|
|
||||||
// 单个服务并发数限制
|
// 单个服务并发数限制
|
||||||
|
// 不能加条件限制,因为服务配置随时有变化
|
||||||
sharedClientConnLimiter.Remove(this.rawConn.RemoteAddr().String())
|
sharedClientConnLimiter.Remove(this.rawConn.RemoteAddr().String())
|
||||||
|
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -8,8 +8,10 @@ type BaseClientConn struct {
|
|||||||
rawConn net.Conn
|
rawConn net.Conn
|
||||||
|
|
||||||
isBound bool
|
isBound bool
|
||||||
|
userId int64
|
||||||
serverId int64
|
serverId int64
|
||||||
remoteAddr string
|
remoteAddr string
|
||||||
|
hasLimit bool
|
||||||
|
|
||||||
isClosed bool
|
isClosed bool
|
||||||
}
|
}
|
||||||
@@ -31,11 +33,32 @@ func (this *BaseClientConn) Bind(serverId int64, remoteAddr string, maxConnsPerS
|
|||||||
this.isBound = true
|
this.isBound = true
|
||||||
this.serverId = serverId
|
this.serverId = serverId
|
||||||
this.remoteAddr = remoteAddr
|
this.remoteAddr = remoteAddr
|
||||||
|
this.hasLimit = true
|
||||||
|
|
||||||
// 检查是否可以连接
|
// 检查是否可以连接
|
||||||
return sharedClientConnLimiter.Add(this.rawConn.RemoteAddr().String(), serverId, remoteAddr, maxConnsPerServer, maxConnsPerIP)
|
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
|
// RawIP 原本IP
|
||||||
func (this *BaseClientConn) RawIP() string {
|
func (this *BaseClientConn) RawIP() string {
|
||||||
ip, _, _ := net.SplitHostPort(this.rawConn.RemoteAddr().String())
|
ip, _, _ := net.SplitHostPort(this.rawConn.RemoteAddr().String())
|
||||||
|
|||||||
@@ -11,4 +11,16 @@ type ClientConnInterface interface {
|
|||||||
|
|
||||||
// Bind 绑定服务
|
// Bind 绑定服务
|
||||||
Bind(serverId int64, remoteAddr string, maxConnsPerServer int, maxConnsPerIP int) bool
|
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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ func (this *ClientListener) Accept() (net.Conn, error) {
|
|||||||
if beingDenied {
|
if beingDenied {
|
||||||
var fw = firewalls.Firewall()
|
var fw = firewalls.Firewall()
|
||||||
if fw != nil && !fw.IsMock() {
|
if fw != nil && !fw.IsMock() {
|
||||||
_ = fw.DropSourceIP(ip, 60)
|
_ = fw.DropSourceIP(ip, 120, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
||||||
"github.com/iwind/TeaGo/Tea"
|
"github.com/iwind/TeaGo/Tea"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"regexp"
|
"regexp"
|
||||||
@@ -236,7 +235,7 @@ func (this *HTTPCacheTaskManager) fetchKey(key *pb.HTTPCacheTaskKey) error {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
// 读取内容,以便于生成缓存
|
// 读取内容,以便于生成缓存
|
||||||
_, _ = io.Copy(ioutil.Discard, resp.Body)
|
_, _ = io.Copy(io.Discard, resp.Body)
|
||||||
|
|
||||||
// 处理502
|
// 处理502
|
||||||
if resp.StatusCode == http.StatusBadGateway {
|
if resp.StatusCode == http.StatusBadGateway {
|
||||||
|
|||||||
@@ -22,16 +22,18 @@ var SharedHTTPClientPool = NewHTTPClientPool()
|
|||||||
|
|
||||||
// HTTPClientPool 客户端池
|
// HTTPClientPool 客户端池
|
||||||
type HTTPClientPool struct {
|
type HTTPClientPool struct {
|
||||||
clientExpiredDuration time.Duration
|
clientsMap map[string]*HTTPClient // backend key => client
|
||||||
clientsMap map[string]*HTTPClient // backend key => client
|
|
||||||
locker sync.Mutex
|
cleanTicker *time.Ticker
|
||||||
|
|
||||||
|
locker sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewHTTPClientPool 获取新对象
|
// NewHTTPClientPool 获取新对象
|
||||||
func NewHTTPClientPool() *HTTPClientPool {
|
func NewHTTPClientPool() *HTTPClientPool {
|
||||||
var pool = &HTTPClientPool{
|
var pool = &HTTPClientPool{
|
||||||
clientExpiredDuration: 3600 * time.Second,
|
cleanTicker: time.NewTicker(1 * time.Hour),
|
||||||
clientsMap: map[string]*HTTPClient{},
|
clientsMap: map[string]*HTTPClient{},
|
||||||
}
|
}
|
||||||
|
|
||||||
goman.New(func() {
|
goman.New(func() {
|
||||||
@@ -52,11 +54,22 @@ func (this *HTTPClientPool) Client(req *HTTPRequest,
|
|||||||
}
|
}
|
||||||
|
|
||||||
var key = origin.UniqueKey() + "@" + originAddr
|
var key = origin.UniqueKey() + "@" + originAddr
|
||||||
|
var isLnRequest = origin.Id == 0
|
||||||
|
|
||||||
|
this.locker.RLock()
|
||||||
|
client, found := this.clientsMap[key]
|
||||||
|
this.locker.RUnlock()
|
||||||
|
if found {
|
||||||
|
client.UpdateAccessTime()
|
||||||
|
return client.RawClient(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 这里不能使用RLock,避免因为并发生成多个同样的client实例
|
||||||
this.locker.Lock()
|
this.locker.Lock()
|
||||||
defer this.locker.Unlock()
|
defer this.locker.Unlock()
|
||||||
|
|
||||||
client, found := this.clientsMap[key]
|
// 再次查找
|
||||||
|
client, found = this.clientsMap[key]
|
||||||
if found {
|
if found {
|
||||||
client.UpdateAccessTime()
|
client.UpdateAccessTime()
|
||||||
return client.RawClient(), nil
|
return client.RawClient(), nil
|
||||||
@@ -89,6 +102,17 @@ func (this *HTTPClientPool) Client(req *HTTPRequest,
|
|||||||
idleConns = numberCPU * 8
|
idleConns = numberCPU * 8
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 可以判断为Ln节点请求
|
||||||
|
if isLnRequest {
|
||||||
|
maxConnections *= 8
|
||||||
|
idleConns *= 8
|
||||||
|
idleTimeout *= 4
|
||||||
|
} else if sharedNodeConfig != nil && sharedNodeConfig.Level > 1 {
|
||||||
|
// Ln节点可以适当增加连接数
|
||||||
|
maxConnections *= 2
|
||||||
|
idleConns *= 2
|
||||||
|
}
|
||||||
|
|
||||||
// TLS通讯
|
// TLS通讯
|
||||||
var tlsConfig = &tls.Config{
|
var tlsConfig = &tls.Config{
|
||||||
InsecureSkipVerify: true,
|
InsecureSkipVerify: true,
|
||||||
@@ -135,8 +159,9 @@ func (this *HTTPClientPool) Client(req *HTTPRequest,
|
|||||||
MaxConnsPerHost: maxConnections,
|
MaxConnsPerHost: maxConnections,
|
||||||
IdleConnTimeout: idleTimeout,
|
IdleConnTimeout: idleTimeout,
|
||||||
ExpectContinueTimeout: 1 * time.Second,
|
ExpectContinueTimeout: 1 * time.Second,
|
||||||
TLSHandshakeTimeout: 3 * time.Second,
|
TLSHandshakeTimeout: 5 * time.Second,
|
||||||
TLSClientConfig: tlsConfig,
|
TLSClientConfig: tlsConfig,
|
||||||
|
ReadBufferSize: 8 * 1024,
|
||||||
Proxy: nil,
|
Proxy: nil,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -170,13 +195,12 @@ func (this *HTTPClientPool) Client(req *HTTPRequest,
|
|||||||
|
|
||||||
// 清理不使用的Client
|
// 清理不使用的Client
|
||||||
func (this *HTTPClientPool) cleanClients() {
|
func (this *HTTPClientPool) cleanClients() {
|
||||||
var ticker = time.NewTicker(this.clientExpiredDuration)
|
for range this.cleanTicker.C {
|
||||||
for range ticker.C {
|
var nowTime = time.Now().Unix()
|
||||||
currentAt := time.Now().Unix()
|
|
||||||
|
|
||||||
this.locker.Lock()
|
this.locker.Lock()
|
||||||
for k, client := range this.clientsMap {
|
for k, client := range this.clientsMap {
|
||||||
if client.AccessTime() < currentAt+86400 { // 超过 N 秒没有调用就关闭
|
if client.AccessTime() < nowTime+86400 { // 超过 N 秒没有调用就关闭
|
||||||
delete(this.clientsMap, k)
|
delete(this.clientsMap, k)
|
||||||
client.Close()
|
client.Close()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ func TestHTTPClientPool_Client(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestHTTPClientPool_cleanClients(t *testing.T) {
|
func TestHTTPClientPool_cleanClients(t *testing.T) {
|
||||||
origin := &serverconfigs.OriginConfig{
|
var origin = &serverconfigs.OriginConfig{
|
||||||
Id: 1,
|
Id: 1,
|
||||||
Version: 2,
|
Version: 2,
|
||||||
Addr: &serverconfigs.NetworkAddressConfig{Host: "127.0.0.1", PortRange: "1234"},
|
Addr: &serverconfigs.NetworkAddressConfig{Host: "127.0.0.1", PortRange: "1234"},
|
||||||
@@ -48,8 +48,7 @@ func TestHTTPClientPool_cleanClients(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
pool := NewHTTPClientPool()
|
var pool = NewHTTPClientPool()
|
||||||
pool.clientExpiredDuration = 2 * time.Second
|
|
||||||
|
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
t.Log("get", i)
|
t.Log("get", i)
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import (
|
|||||||
"github.com/iwind/TeaGo/maps"
|
"github.com/iwind/TeaGo/maps"
|
||||||
"github.com/iwind/TeaGo/types"
|
"github.com/iwind/TeaGo/types"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
@@ -89,7 +88,9 @@ type HTTPRequest struct {
|
|||||||
firewallRuleSetId int64
|
firewallRuleSetId int64
|
||||||
firewallRuleId int64
|
firewallRuleId int64
|
||||||
firewallActions []string
|
firewallActions []string
|
||||||
tags []string
|
wafHasRequestBody bool
|
||||||
|
|
||||||
|
tags []string
|
||||||
|
|
||||||
logAttrs map[string]string
|
logAttrs map[string]string
|
||||||
|
|
||||||
@@ -149,7 +150,7 @@ func (this *HTTPRequest) Do() {
|
|||||||
// Web配置
|
// Web配置
|
||||||
err := this.configureWeb(this.ReqServer.Web, true, 0)
|
err := this.configureWeb(this.ReqServer.Web, true, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.write50x(err, http.StatusInternalServerError, false)
|
this.write50x(err, http.StatusInternalServerError, "Failed to configure the server", "配置服务失败", false)
|
||||||
this.doEnd()
|
this.doEnd()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -164,6 +165,16 @@ func (this *HTTPRequest) Do() {
|
|||||||
return
|
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 {
|
if !this.isLnRequest {
|
||||||
// 特殊URL处理
|
// 特殊URL处理
|
||||||
if len(this.rawURI) > 1 && this.rawURI[1] == '.' {
|
if len(this.rawURI) > 1 && this.rawURI[1] == '.' {
|
||||||
@@ -191,6 +202,24 @@ func (this *HTTPRequest) Do() {
|
|||||||
return
|
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
|
// WAF
|
||||||
if this.web.FirewallRef != nil && this.web.FirewallRef.IsOn {
|
if this.web.FirewallRef != nil && this.web.FirewallRef.IsOn {
|
||||||
if this.doWAFRequest() {
|
if this.doWAFRequest() {
|
||||||
@@ -233,6 +262,12 @@ func (this *HTTPRequest) Do() {
|
|||||||
|
|
||||||
// 开始调用
|
// 开始调用
|
||||||
func (this *HTTPRequest) doBegin() {
|
func (this *HTTPRequest) doBegin() {
|
||||||
|
// 是否找不到域名匹配
|
||||||
|
if this.ReqServer.Id == 0 {
|
||||||
|
this.doMismatch()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if !this.isLnRequest {
|
if !this.isLnRequest {
|
||||||
// 处理request limit
|
// 处理request limit
|
||||||
if this.web.RequestLimit != nil &&
|
if this.web.RequestLimit != nil &&
|
||||||
@@ -248,38 +283,12 @@ func (this *HTTPRequest) doBegin() {
|
|||||||
this.web.AccessLogRef.IsOn &&
|
this.web.AccessLogRef.IsOn &&
|
||||||
this.web.AccessLogRef.ContainsField(serverconfigs.HTTPAccessLogFieldRequestBody) {
|
this.web.AccessLogRef.ContainsField(serverconfigs.HTTPAccessLogFieldRequestBody) {
|
||||||
var err error
|
var err error
|
||||||
this.requestBodyData, err = ioutil.ReadAll(io.LimitReader(this.RawReq.Body, AccessLogMaxRequestBodySize))
|
this.requestBodyData, err = io.ReadAll(io.LimitReader(this.RawReq.Body, AccessLogMaxRequestBodySize))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.write50x(err, http.StatusBadGateway, false)
|
this.write50x(err, http.StatusBadGateway, "Failed to read request body for access log", "为访问日志读取请求Body失败", false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.RawReq.Body = ioutil.NopCloser(io.MultiReader(bytes.NewBuffer(this.requestBodyData), this.RawReq.Body))
|
this.RawReq.Body = io.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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 跳转
|
// 跳转
|
||||||
@@ -349,7 +358,7 @@ func (this *HTTPRequest) doEnd() {
|
|||||||
|
|
||||||
// 流量统计
|
// 流量统计
|
||||||
// TODO 增加是否开启开关
|
// TODO 增加是否开启开关
|
||||||
if this.ReqServer != nil {
|
if this.ReqServer != nil && this.ReqServer.Id > 0 {
|
||||||
var countCached int64 = 0
|
var countCached int64 = 0
|
||||||
var cachedBytes int64 = 0
|
var cachedBytes int64 = 0
|
||||||
|
|
||||||
@@ -366,17 +375,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())
|
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() {
|
if metrics.SharedManager.HasHTTPMetrics() {
|
||||||
this.doMetricsResponse()
|
this.doMetricsResponse()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 统计
|
// 统计
|
||||||
if this.web.StatRef != nil && this.web.StatRef.IsOn {
|
if this.web.StatRef != nil && this.web.StatRef.IsOn {
|
||||||
// 放到最后执行
|
// 放到最后执行
|
||||||
this.doStat()
|
this.doStat()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1104,7 +1113,7 @@ func (this *HTTPRequest) requestRemoteAddr(supportVar bool) string {
|
|||||||
// 获取请求的客户端地址列表
|
// 获取请求的客户端地址列表
|
||||||
func (this *HTTPRequest) requestRemoteAddrs() (result []string) {
|
func (this *HTTPRequest) requestRemoteAddrs() (result []string) {
|
||||||
// X-Forwarded-For
|
// X-Forwarded-For
|
||||||
forwardedFor := this.RawReq.Header.Get("X-Forwarded-For")
|
var forwardedFor = this.RawReq.Header.Get("X-Forwarded-For")
|
||||||
if len(forwardedFor) > 0 {
|
if len(forwardedFor) > 0 {
|
||||||
commaIndex := strings.Index(forwardedFor, ",")
|
commaIndex := strings.Index(forwardedFor, ",")
|
||||||
if commaIndex > 0 {
|
if commaIndex > 0 {
|
||||||
@@ -1129,13 +1138,16 @@ func (this *HTTPRequest) requestRemoteAddrs() (result []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Remote-Addr
|
// Remote-Addr
|
||||||
remoteAddr := this.RawReq.RemoteAddr
|
{
|
||||||
host, _, err := net.SplitHostPort(remoteAddr)
|
var remoteAddr = this.RawReq.RemoteAddr
|
||||||
if err == nil {
|
host, _, err := net.SplitHostPort(remoteAddr)
|
||||||
result = append(result, host)
|
if err == nil {
|
||||||
} else {
|
result = append(result, host)
|
||||||
result = append(result, remoteAddr)
|
} else {
|
||||||
|
result = append(result, remoteAddr)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1377,12 +1389,12 @@ func (this *HTTPRequest) Cookie(name string) string {
|
|||||||
return c.Value
|
return c.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteHeader 删除Header
|
// DeleteHeader 删除请求Header
|
||||||
func (this *HTTPRequest) DeleteHeader(name string) {
|
func (this *HTTPRequest) DeleteHeader(name string) {
|
||||||
this.RawReq.Header.Del(name)
|
this.RawReq.Header.Del(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetHeader 设置Header
|
// SetHeader 设置请求Header
|
||||||
func (this *HTTPRequest) SetHeader(name string, values []string) {
|
func (this *HTTPRequest) SetHeader(name string, values []string) {
|
||||||
this.RawReq.Header[name] = values
|
this.RawReq.Header[name] = values
|
||||||
}
|
}
|
||||||
@@ -1699,7 +1711,12 @@ func (this *HTTPRequest) canIgnore(err error) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 客户端主动取消
|
// 客户端主动取消
|
||||||
if err == errWritingToClient || err == context.Canceled || err == io.ErrShortWrite || strings.Contains(err.Error(), "write: connection timed out") || strings.Contains(err.Error(), "write: broken pipe") {
|
if err == errWritingToClient ||
|
||||||
|
err == context.Canceled ||
|
||||||
|
err == io.ErrShortWrite ||
|
||||||
|
strings.Contains(err.Error(), "write: connection") ||
|
||||||
|
strings.Contains(err.Error(), "write: broken pipe") ||
|
||||||
|
strings.Contains(err.Error(), "write tcp") {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ package nodes
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -26,14 +26,14 @@ func (this *HTTPRequest) doAuth() (shouldStop bool) {
|
|||||||
subReq.Proto = this.RawReq.Proto
|
subReq.Proto = this.RawReq.Proto
|
||||||
subReq.ProtoMinor = this.RawReq.ProtoMinor
|
subReq.ProtoMinor = this.RawReq.ProtoMinor
|
||||||
subReq.ProtoMajor = this.RawReq.ProtoMajor
|
subReq.ProtoMajor = this.RawReq.ProtoMajor
|
||||||
subReq.Body = ioutil.NopCloser(bytes.NewReader([]byte{}))
|
subReq.Body = io.NopCloser(bytes.NewReader([]byte{}))
|
||||||
subReq.Header.Set("Referer", this.URL())
|
subReq.Header.Set("Referer", this.URL())
|
||||||
var writer = NewEmptyResponseWriter(this.writer)
|
var writer = NewEmptyResponseWriter(this.writer)
|
||||||
this.doSubRequest(writer, subReq)
|
this.doSubRequest(writer, subReq)
|
||||||
return writer.StatusCode(), nil
|
return writer.StatusCode(), nil
|
||||||
}, this.Format)
|
}, this.Format)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.write50x(err, http.StatusInternalServerError, false)
|
this.write50x(err, http.StatusInternalServerError, "Failed to execute the AuthPolicy", "认证策略执行失败", false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if b {
|
if b {
|
||||||
|
|||||||
@@ -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 {
|
if !this.isLnRequest && !isPartialCache && len(eTag) > 0 && this.requestHeader("If-None-Match") == eTag {
|
||||||
// 自定义Header
|
// 自定义Header
|
||||||
this.processResponseHeaders(http.StatusNotModified)
|
this.processResponseHeaders(http.StatusNotModified)
|
||||||
|
this.addExpiresHeader(reader.ExpiresAt())
|
||||||
this.writer.WriteHeader(http.StatusNotModified)
|
this.writer.WriteHeader(http.StatusNotModified)
|
||||||
this.isCached = true
|
this.isCached = true
|
||||||
this.cacheRef = nil
|
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 {
|
if !this.isLnRequest && !isPartialCache && len(modifiedTime) > 0 && this.requestHeader("If-Modified-Since") == modifiedTime {
|
||||||
// 自定义Header
|
// 自定义Header
|
||||||
this.processResponseHeaders(http.StatusNotModified)
|
this.processResponseHeaders(http.StatusNotModified)
|
||||||
|
this.addExpiresHeader(reader.ExpiresAt())
|
||||||
this.writer.WriteHeader(http.StatusNotModified)
|
this.writer.WriteHeader(http.StatusNotModified)
|
||||||
this.isCached = true
|
this.isCached = true
|
||||||
this.cacheRef = nil
|
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.Overwrite || len(this.writer.Header().Get("Expires")) == 0 {
|
||||||
if this.cacheRef.ExpiresTime.AutoCalculate {
|
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().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 {
|
} else if this.cacheRef.ExpiresTime.Duration != nil {
|
||||||
var duration = this.cacheRef.ExpiresTime.Duration.Duration()
|
var duration = this.cacheRef.ExpiresTime.Duration.Duration()
|
||||||
if duration > 0 {
|
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().Set("Expires", utils.GMTTime(time.Now().Add(duration)).Format("Mon, 2 Jan 2006 15:04:05")+" GMT")
|
||||||
|
this.writer.Header().Del("Cache-Control")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,32 +1,59 @@
|
|||||||
package nodes
|
package nodes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
|
||||||
"github.com/iwind/TeaGo/lists"
|
"github.com/iwind/TeaGo/lists"
|
||||||
"github.com/iwind/TeaGo/types"
|
"github.com/iwind/TeaGo/types"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const httpStatusPageTemplate = `<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>${status} ${statusMessage}</title>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<h1>${status} ${statusMessage}</h1>
|
||||||
|
<p>${message}</p>
|
||||||
|
|
||||||
|
<address>Request ID: ${requestId}.</address>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>`
|
||||||
|
|
||||||
func (this *HTTPRequest) write404() {
|
func (this *HTTPRequest) write404() {
|
||||||
if this.doPage(http.StatusNotFound) {
|
this.writeCode(http.StatusNotFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *HTTPRequest) writeCode(statusCode int) {
|
||||||
|
if this.doPage(statusCode) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.processResponseHeaders(http.StatusNotFound)
|
var pageContent = configutils.ParseVariables(httpStatusPageTemplate, func(varName string) (value string) {
|
||||||
this.writer.WriteHeader(http.StatusNotFound)
|
switch varName {
|
||||||
_, _ = this.writer.Write([]byte("404 page not found: '" + this.URL() + "'" + " (Request Id: " + this.requestId + ")"))
|
case "status":
|
||||||
|
return types.String(statusCode)
|
||||||
|
case "statusMessage":
|
||||||
|
return http.StatusText(statusCode)
|
||||||
|
case "requestId":
|
||||||
|
return this.requestId
|
||||||
|
case "message":
|
||||||
|
return "" // 空
|
||||||
|
}
|
||||||
|
return "${" + varName + "}"
|
||||||
|
})
|
||||||
|
|
||||||
|
this.processResponseHeaders(statusCode)
|
||||||
|
this.writer.WriteHeader(statusCode)
|
||||||
|
|
||||||
|
_, _ = this.writer.Write([]byte(pageContent))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *HTTPRequest) writeCode(code int) {
|
func (this *HTTPRequest) write50x(err error, statusCode int, enMessage string, zhMessage string, canTryStale bool) {
|
||||||
if this.doPage(code) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.processResponseHeaders(code)
|
|
||||||
this.writer.WriteHeader(code)
|
|
||||||
_, _ = this.writer.Write([]byte(types.String(code) + " " + http.StatusText(code) + ": '" + this.URL() + "'" + " (Request Id: " + this.requestId + ")"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *HTTPRequest) write50x(err error, statusCode int, canTryStale bool) {
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.addError(err)
|
this.addError(err)
|
||||||
}
|
}
|
||||||
@@ -37,7 +64,7 @@ func (this *HTTPRequest) write50x(err error, statusCode int, canTryStale bool) {
|
|||||||
this.web.Cache.Stale != nil &&
|
this.web.Cache.Stale != nil &&
|
||||||
this.web.Cache.Stale.IsOn &&
|
this.web.Cache.Stale.IsOn &&
|
||||||
(len(this.web.Cache.Stale.Status) == 0 || lists.ContainsInt(this.web.Cache.Stale.Status, statusCode)) {
|
(len(this.web.Cache.Stale.Status) == 0 || lists.ContainsInt(this.web.Cache.Stale.Status, statusCode)) {
|
||||||
ok := this.doCacheRead(true)
|
var ok = this.doCacheRead(true)
|
||||||
if ok {
|
if ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -47,7 +74,34 @@ func (this *HTTPRequest) write50x(err error, statusCode int, canTryStale bool) {
|
|||||||
if this.doPage(statusCode) {
|
if this.doPage(statusCode) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 内置HTML模板
|
||||||
|
var pageContent = configutils.ParseVariables(httpStatusPageTemplate, func(varName string) (value string) {
|
||||||
|
switch varName {
|
||||||
|
case "status":
|
||||||
|
return types.String(statusCode)
|
||||||
|
case "statusMessage":
|
||||||
|
return http.StatusText(statusCode)
|
||||||
|
case "requestId":
|
||||||
|
return this.requestId
|
||||||
|
case "message":
|
||||||
|
var acceptLanguages = this.RawReq.Header.Get("Accept-Language")
|
||||||
|
if len(acceptLanguages) > 0 {
|
||||||
|
var index = strings.Index(acceptLanguages, ",")
|
||||||
|
if index > 0 {
|
||||||
|
var firstLanguage = acceptLanguages[:index]
|
||||||
|
if firstLanguage == "zh-CN" {
|
||||||
|
return "网站出了一点小问题,原因:" + zhMessage + "。"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "The site is unavailable now, cause: " + enMessage + "."
|
||||||
|
}
|
||||||
|
return "${" + varName + "}"
|
||||||
|
})
|
||||||
|
|
||||||
this.processResponseHeaders(statusCode)
|
this.processResponseHeaders(statusCode)
|
||||||
this.writer.WriteHeader(statusCode)
|
this.writer.WriteHeader(statusCode)
|
||||||
_, _ = this.writer.Write([]byte(types.String(statusCode) + " " + http.StatusText(statusCode) + " (Request Id: " + this.requestId + ")"))
|
|
||||||
|
_, _ = this.writer.Write([]byte(pageContent))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ func (this *HTTPRequest) doFastcgi() (shouldStop bool) {
|
|||||||
|
|
||||||
client, err := fcgi.SharedPool(fastcgi.Network(), fastcgi.RealAddress(), uint(poolSize)).Client()
|
client, err := fcgi.SharedPool(fastcgi.Network(), fastcgi.RealAddress(), uint(poolSize)).Client()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.write50x(err, http.StatusInternalServerError, false)
|
this.write50x(err, http.StatusInternalServerError, "Failed to create Fastcgi pool", "Fastcgi池生成失败", false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,13 +159,13 @@ func (this *HTTPRequest) doFastcgi() (shouldStop bool) {
|
|||||||
|
|
||||||
resp, stderr, err := client.Call(fcgiReq)
|
resp, stderr, err := client.Call(fcgiReq)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.write50x(err, http.StatusInternalServerError, false)
|
this.write50x(err, http.StatusInternalServerError, "Failed to read Fastcgi", "读取Fastcgi失败", false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(stderr) > 0 {
|
if len(stderr) > 0 {
|
||||||
err := errors.New("Fastcgi Error: " + strings.TrimSpace(string(stderr)) + " script: " + maps.NewMap(params).GetString("SCRIPT_FILENAME"))
|
err := errors.New("Fastcgi Error: " + strings.TrimSpace(string(stderr)) + " script: " + maps.NewMap(params).GetString("SCRIPT_FILENAME"))
|
||||||
this.write50x(err, http.StatusInternalServerError, false)
|
this.write50x(err, http.StatusInternalServerError, "Failed to read Fastcgi", "读取Fastcgi失败", false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ func (this *HTTPRequest) doRequestLimit() (shouldStop bool) {
|
|||||||
|
|
||||||
// 设置连接相关参数
|
// 设置连接相关参数
|
||||||
if this.web.RequestLimit.MaxConns > 0 || this.web.RequestLimit.MaxConnsPerIP > 0 {
|
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 {
|
if requestConn != nil {
|
||||||
clientConn, ok := requestConn.(ClientConnInterface)
|
clientConn, ok := requestConn.(ClientConnInterface)
|
||||||
if ok && !clientConn.IsBound() {
|
if ok && !clientConn.IsBound() {
|
||||||
|
|||||||
@@ -16,6 +16,6 @@ func (this *HTTPRequest) checkLnRequest() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *HTTPRequest) getLnOrigin() *serverconfigs.OriginConfig {
|
func (this *HTTPRequest) getLnOrigin(excludingNodeIds []int64) (originConfig *serverconfigs.OriginConfig, lnNodeId int64, hasMultipleNodes bool) {
|
||||||
return nil
|
return nil, 0, false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -149,8 +149,7 @@ func (this *HTTPRequest) log() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 请求Body
|
// 请求Body
|
||||||
// TODO 考虑在被攻击时记录攻击的requestBody(如果requestBody匹配规则的话),但要考虑请求尺寸、数据库容量,避免因为日志而导致服务不稳定
|
if (ref != nil && ref.ContainsField(serverconfigs.HTTPAccessLogFieldRequestBody)) || this.wafHasRequestBody {
|
||||||
if ref != nil && ref.ContainsField(serverconfigs.HTTPAccessLogFieldRequestBody) {
|
|
||||||
accessLog.RequestBody = this.requestBodyData
|
accessLog.RequestBody = this.requestBodyData
|
||||||
|
|
||||||
if len(accessLog.RequestBody) > AccessLogMaxRequestBodySize {
|
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/EdgeCommon/pkg/serverconfigs/shared"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
|
"github.com/iwind/TeaGo/types"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
@@ -20,10 +21,32 @@ func (this *HTTPRequest) doReverseProxy() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var retries = 3
|
||||||
|
|
||||||
|
var failedOriginIds []int64
|
||||||
|
var failedLnNodeIds []int64
|
||||||
|
|
||||||
|
for i := 0; i < retries; i++ {
|
||||||
|
originId, lnNodeId, shouldRetry := this.doOriginRequest(failedOriginIds, failedLnNodeIds, i == 0, i == retries-1)
|
||||||
|
if !shouldRetry {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if originId > 0 {
|
||||||
|
failedOriginIds = append(failedOriginIds, originId)
|
||||||
|
}
|
||||||
|
if lnNodeId > 0 {
|
||||||
|
failedLnNodeIds = append(failedLnNodeIds, lnNodeId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 请求源站
|
||||||
|
func (this *HTTPRequest) doOriginRequest(failedOriginIds []int64, failedLnNodeIds []int64, isFirstTry bool, isLastRetry bool) (originId int64, lnNodeId int64, shouldRetry bool) {
|
||||||
// 对URL的处理
|
// 对URL的处理
|
||||||
var stripPrefix = this.reverseProxy.StripPrefix
|
var stripPrefix = this.reverseProxy.StripPrefix
|
||||||
var requestURI = this.reverseProxy.RequestURI
|
var requestURI = this.reverseProxy.RequestURI
|
||||||
var requestURIHasVariables = this.reverseProxy.RequestURIHasVariables()
|
var requestURIHasVariables = this.reverseProxy.RequestURIHasVariables()
|
||||||
|
var oldURI = this.uri
|
||||||
|
|
||||||
var requestHost = ""
|
var requestHost = ""
|
||||||
if this.reverseProxy.RequestHostType == serverconfigs.RequestHostTypeCustomized {
|
if this.reverseProxy.RequestHostType == serverconfigs.RequestHostTypeCustomized {
|
||||||
@@ -40,24 +63,39 @@ func (this *HTTPRequest) doReverseProxy() {
|
|||||||
var origin *serverconfigs.OriginConfig
|
var origin *serverconfigs.OriginConfig
|
||||||
|
|
||||||
// 二级节点
|
// 二级节点
|
||||||
|
var hasMultipleLnNodes = false
|
||||||
if this.cacheRef != nil {
|
if this.cacheRef != nil {
|
||||||
origin = this.getLnOrigin()
|
origin, lnNodeId, hasMultipleLnNodes = this.getLnOrigin(failedLnNodeIds)
|
||||||
if origin != nil {
|
if origin != nil {
|
||||||
// 强制变更原来访问的域名
|
// 强制变更原来访问的域名
|
||||||
requestHost = this.ReqHost
|
requestHost = this.ReqHost
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 回源Header中去除If-None-Match和If-Modified-Since
|
||||||
|
if !this.cacheRef.EnableIfNoneMatch {
|
||||||
|
this.DeleteHeader("If-None-Match")
|
||||||
|
}
|
||||||
|
if !this.cacheRef.EnableIfModifiedSince {
|
||||||
|
this.DeleteHeader("If-Modified-Since")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 自定义源站
|
// 自定义源站
|
||||||
if origin == nil {
|
if origin == nil {
|
||||||
origin = this.reverseProxy.NextOrigin(requestCall)
|
if !isFirstTry {
|
||||||
|
origin = this.reverseProxy.AnyOrigin(requestCall, failedOriginIds)
|
||||||
|
}
|
||||||
|
if origin == nil {
|
||||||
|
origin = this.reverseProxy.NextOrigin(requestCall)
|
||||||
|
}
|
||||||
requestCall.CallResponseCallbacks(this.writer)
|
requestCall.CallResponseCallbacks(this.writer)
|
||||||
if origin == nil {
|
if origin == nil {
|
||||||
err := errors.New(this.URL() + ": no available origin sites for reverse proxy")
|
err := errors.New(this.URL() + ": no available origin sites for reverse proxy")
|
||||||
remotelogs.ServerError(this.ReqServer.Id, "HTTP_REQUEST_REVERSE_PROXY", err.Error(), "", nil)
|
remotelogs.ServerError(this.ReqServer.Id, "HTTP_REQUEST_REVERSE_PROXY", err.Error(), "", nil)
|
||||||
this.write50x(err, http.StatusBadGateway, true)
|
this.write50x(err, http.StatusBadGateway, "No origin site yet", "尚未配置源站", true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
originId = origin.Id
|
||||||
|
|
||||||
if len(origin.StripPrefix) > 0 {
|
if len(origin.StripPrefix) > 0 {
|
||||||
stripPrefix = origin.StripPrefix
|
stripPrefix = origin.StripPrefix
|
||||||
@@ -77,9 +115,9 @@ func (this *HTTPRequest) doReverseProxy() {
|
|||||||
|
|
||||||
// 处理Scheme
|
// 处理Scheme
|
||||||
if origin.Addr == nil {
|
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())
|
remotelogs.Error("HTTP_REQUEST_REVERSE_PROXY", err.Error())
|
||||||
this.write50x(err, http.StatusBadGateway, true)
|
this.write50x(err, http.StatusBadGateway, "Origin site did not has a valid address", "源站尚未配置地址", true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.RawReq.URL.Scheme = origin.Addr.Protocol.Primary().Scheme()
|
this.RawReq.URL.Scheme = origin.Addr.Protocol.Primary().Scheme()
|
||||||
@@ -124,6 +162,18 @@ func (this *HTTPRequest) doReverseProxy() {
|
|||||||
if origin.Addr.HostHasVariables() {
|
if origin.Addr.HostHasVariables() {
|
||||||
originAddr = this.Format(originAddr)
|
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, "No port in origin site address", "源站地址中没有配置端口", true)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
originAddr = originAddr[:originHostIndex+1] + types.String(this.requestServerPort())
|
||||||
|
}
|
||||||
this.originAddr = originAddr
|
this.originAddr = originAddr
|
||||||
|
|
||||||
// RequestHost
|
// RequestHost
|
||||||
@@ -133,6 +183,12 @@ func (this *HTTPRequest) doReverseProxy() {
|
|||||||
} else {
|
} else {
|
||||||
this.RawReq.Host = requestHost
|
this.RawReq.Host = requestHost
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 是否移除端口
|
||||||
|
if this.reverseProxy.RequestHostExcludingPort {
|
||||||
|
this.RawReq.Host = utils.ParseAddrHost(this.RawReq.Host)
|
||||||
|
}
|
||||||
|
|
||||||
this.RawReq.URL.Host = this.RawReq.Host
|
this.RawReq.URL.Host = this.RawReq.Host
|
||||||
} else if this.reverseProxy.RequestHostType == serverconfigs.RequestHostTypeOrigin {
|
} else if this.reverseProxy.RequestHostType == serverconfigs.RequestHostTypeOrigin {
|
||||||
// 源站主机名
|
// 源站主机名
|
||||||
@@ -144,9 +200,21 @@ func (this *HTTPRequest) doReverseProxy() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.RawReq.Host = hostname
|
this.RawReq.Host = hostname
|
||||||
|
|
||||||
|
// 是否移除端口
|
||||||
|
if this.reverseProxy.RequestHostExcludingPort {
|
||||||
|
this.RawReq.Host = utils.ParseAddrHost(this.RawReq.Host)
|
||||||
|
}
|
||||||
|
|
||||||
this.RawReq.URL.Host = this.RawReq.Host
|
this.RawReq.URL.Host = this.RawReq.Host
|
||||||
} else {
|
} else {
|
||||||
this.RawReq.URL.Host = this.ReqHost
|
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
|
// 重组请求URL
|
||||||
@@ -172,15 +240,15 @@ func (this *HTTPRequest) doReverseProxy() {
|
|||||||
|
|
||||||
// 判断是否为Websocket请求
|
// 判断是否为Websocket请求
|
||||||
if this.RawReq.Header.Get("Upgrade") == "websocket" {
|
if this.RawReq.Header.Get("Upgrade") == "websocket" {
|
||||||
this.doWebsocket()
|
this.doWebsocket(requestHost)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取请求客户端
|
// 获取请求客户端
|
||||||
client, err := SharedHTTPClientPool.Client(this, origin, originAddr, this.reverseProxy.ProxyProtocol, this.reverseProxy.FollowRedirects)
|
client, err := SharedHTTPClientPool.Client(this, origin, originAddr, this.reverseProxy.ProxyProtocol, this.reverseProxy.FollowRedirects)
|
||||||
if err != nil {
|
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)
|
this.write50x(err, http.StatusBadGateway, "Failed to create origin site client", "构造源站客户端失败", true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,24 +264,41 @@ func (this *HTTPRequest) doReverseProxy() {
|
|||||||
// 客户端取消请求,则不提示
|
// 客户端取消请求,则不提示
|
||||||
httpErr, ok := err.(*url.Error)
|
httpErr, ok := err.(*url.Error)
|
||||||
if !ok {
|
if !ok {
|
||||||
SharedOriginStateManager.Fail(origin, this.reverseProxy, func() {
|
SharedOriginStateManager.Fail(origin, requestHost, this.reverseProxy, func() {
|
||||||
this.reverseProxy.ResetScheduling()
|
this.reverseProxy.ResetScheduling()
|
||||||
})
|
})
|
||||||
this.write50x(err, http.StatusBadGateway, true)
|
this.write50x(err, http.StatusBadGateway, "Failed to read origin site", "源站读取失败", 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 {
|
} else if httpErr.Err != context.Canceled {
|
||||||
SharedOriginStateManager.Fail(origin, this.reverseProxy, func() {
|
SharedOriginStateManager.Fail(origin, requestHost, this.reverseProxy, func() {
|
||||||
this.reverseProxy.ResetScheduling()
|
this.reverseProxy.ResetScheduling()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 是否需要重试
|
||||||
|
if (originId > 0 || (lnNodeId > 0 && hasMultipleLnNodes)) && !isLastRetry {
|
||||||
|
shouldRetry = true
|
||||||
|
this.uri = oldURI // 恢复备份
|
||||||
|
|
||||||
|
if resp != nil && resp.Body != nil {
|
||||||
|
_ = resp.Body.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
if httpErr.Err != io.EOF {
|
||||||
|
remotelogs.Warn("HTTP_REQUEST_REVERSE_PROXY", this.URL()+": Request origin server failed: "+err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if httpErr.Timeout() {
|
if httpErr.Timeout() {
|
||||||
this.write50x(err, http.StatusGatewayTimeout, true)
|
this.write50x(err, http.StatusGatewayTimeout, "Read origin site timeout", "源站读取超时", true)
|
||||||
} else if httpErr.Temporary() {
|
} else if httpErr.Temporary() {
|
||||||
this.write50x(err, http.StatusServiceUnavailable, true)
|
this.write50x(err, http.StatusServiceUnavailable, "Origin site unavailable now", "源站当前不可用", true)
|
||||||
} else {
|
} else {
|
||||||
this.write50x(err, http.StatusBadGateway, true)
|
this.write50x(err, http.StatusBadGateway, "Failed to read origin site", "源站读取失败", true)
|
||||||
}
|
}
|
||||||
if httpErr.Err != io.EOF {
|
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 {
|
} else {
|
||||||
// 是否为客户端方面的错误
|
// 是否为客户端方面的错误
|
||||||
@@ -233,7 +318,7 @@ func (this *HTTPRequest) doReverseProxy() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !isClientError {
|
if !isClientError {
|
||||||
this.write50x(err, http.StatusBadGateway, true)
|
this.write50x(err, http.StatusBadGateway, "Failed to read origin site", "源站读取失败", true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if resp != nil && resp.Body != nil {
|
if resp != nil && resp.Body != nil {
|
||||||
@@ -252,7 +337,7 @@ func (this *HTTPRequest) doReverseProxy() {
|
|||||||
if this.doWAFResponse(resp) {
|
if this.doWAFResponse(resp) {
|
||||||
err = resp.Body.Close()
|
err = resp.Body.Close()
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
@@ -262,7 +347,7 @@ func (this *HTTPRequest) doReverseProxy() {
|
|||||||
if len(this.web.Pages) > 0 && this.doPage(resp.StatusCode) {
|
if len(this.web.Pages) > 0 && this.doPage(resp.StatusCode) {
|
||||||
err = resp.Body.Close()
|
err = resp.Body.Close()
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
@@ -364,13 +449,13 @@ func (this *HTTPRequest) doReverseProxy() {
|
|||||||
var closeErr = resp.Body.Close()
|
var closeErr = resp.Body.Close()
|
||||||
if closeErr != nil {
|
if closeErr != nil {
|
||||||
if !this.canIgnore(closeErr) {
|
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 err != nil && err != io.EOF {
|
||||||
if !this.canIgnore(err) {
|
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)
|
this.addError(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -379,4 +464,6 @@ func (this *HTTPRequest) doReverseProxy() {
|
|||||||
if (err == nil || err == io.EOF) && (closeErr == nil || closeErr == io.EOF) {
|
if (err == nil || err == io.EOF) && (closeErr == nil || closeErr == io.EOF) {
|
||||||
this.writer.SetOk()
|
this.writer.SetOk()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ func (this *HTTPRequest) doRoot() (isBreak bool) {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
this.write50x(err, http.StatusInternalServerError, true)
|
this.write50x(err, http.StatusInternalServerError, "Failed to stat the file", "查看文件统计信息失败", true)
|
||||||
if !this.canIgnore(err) {
|
if !this.canIgnore(err) {
|
||||||
logs.Error(err)
|
logs.Error(err)
|
||||||
}
|
}
|
||||||
@@ -145,7 +145,7 @@ func (this *HTTPRequest) doRoot() (isBreak bool) {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
this.write50x(err, http.StatusInternalServerError, true)
|
this.write50x(err, http.StatusInternalServerError, "Failed to stat the file", "查看文件统计信息失败", true)
|
||||||
if !this.canIgnore(err) {
|
if !this.canIgnore(err) {
|
||||||
logs.Error(err)
|
logs.Error(err)
|
||||||
}
|
}
|
||||||
@@ -285,7 +285,7 @@ func (this *HTTPRequest) doRoot() (isBreak bool) {
|
|||||||
|
|
||||||
fileReader, err := os.OpenFile(filePath, os.O_RDONLY, 0444)
|
fileReader, err := os.OpenFile(filePath, os.O_RDONLY, 0444)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.write50x(err, http.StatusInternalServerError, true)
|
this.write50x(err, http.StatusInternalServerError, "Failed to open the file", "试图打开文件失败", true)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ func (this *HTTPRequest) doURL(method string, url string, host string, statusCod
|
|||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
remotelogs.Error("HTTP_REQUEST_URL", req.URL.String()+": "+err.Error())
|
remotelogs.Error("HTTP_REQUEST_URL", req.URL.String()+": "+err.Error())
|
||||||
this.write50x(err, http.StatusInternalServerError, false)
|
this.write50x(err, http.StatusInternalServerError, "Failed to read url", "读取URL失败", false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
|
|||||||
@@ -180,14 +180,14 @@ var httpRequestTimestamp int64
|
|||||||
var httpRequestId int32 = 1_000_000
|
var httpRequestId int32 = 1_000_000
|
||||||
|
|
||||||
func httpRequestNextId() string {
|
func httpRequestNextId() string {
|
||||||
var unixTime = utils.UnixTimeMilli()
|
unixTime, unixTimeString := utils.UnixTimeMilliString()
|
||||||
if unixTime > httpRequestTimestamp {
|
if unixTime > httpRequestTimestamp {
|
||||||
atomic.StoreInt32(&httpRequestId, 1_000_000)
|
atomic.StoreInt32(&httpRequestId, 1_000_000)
|
||||||
httpRequestTimestamp = unixTime
|
httpRequestTimestamp = unixTime
|
||||||
}
|
}
|
||||||
|
|
||||||
// timestamp + requestId + nodeId
|
// timestamp + nodeId + requestId
|
||||||
return strconv.FormatInt(unixTime, 10) + teaconst.NodeIdString + strconv.Itoa(int(atomic.AddInt32(&httpRequestId, 1)))
|
return unixTimeString + teaconst.NodeIdString + strconv.Itoa(int(atomic.AddInt32(&httpRequestId, 1)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否可以接受某个编码
|
// 检查是否可以接受某个编码
|
||||||
@@ -208,20 +208,3 @@ func httpAcceptEncoding(acceptEncodings string, encoding string) bool {
|
|||||||
}
|
}
|
||||||
return false
|
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
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import (
|
|||||||
"github.com/iwind/TeaGo/lists"
|
"github.com/iwind/TeaGo/lists"
|
||||||
"github.com/iwind/TeaGo/types"
|
"github.com/iwind/TeaGo/types"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -53,11 +52,21 @@ func (this *HTTPRequest) doWAFRequest() (blocked bool) {
|
|||||||
return true
|
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 {
|
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 {
|
if blocked {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -68,7 +77,7 @@ func (this *HTTPRequest) doWAFRequest() (blocked bool) {
|
|||||||
|
|
||||||
// 公用的防火墙设置
|
// 公用的防火墙设置
|
||||||
if this.ReqServer.HTTPFirewallPolicy != nil && this.ReqServer.HTTPFirewallPolicy.IsOn {
|
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 {
|
if blocked {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -80,15 +89,21 @@ func (this *HTTPRequest) doWAFRequest() (blocked bool) {
|
|||||||
return
|
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 {
|
if firewallPolicy == nil || !firewallPolicy.IsOn || firewallPolicy.Inbound == nil || !firewallPolicy.Inbound.IsOn || firewallPolicy.Mode == firewallconfigs.FirewallModeBypass {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查IP白名单
|
// 检查IP白名单
|
||||||
remoteAddrs := this.requestRemoteAddrs()
|
var remoteAddrs []string
|
||||||
inbound := firewallPolicy.Inbound
|
if len(this.remoteAddr) > 0 {
|
||||||
|
remoteAddrs = []string{this.remoteAddr}
|
||||||
|
} else {
|
||||||
|
remoteAddrs = this.requestRemoteAddrs()
|
||||||
|
}
|
||||||
|
|
||||||
|
var inbound = firewallPolicy.Inbound
|
||||||
if inbound == nil {
|
if inbound == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -159,13 +174,18 @@ func (this *HTTPRequest) checkWAFRequest(firewallPolicy *firewallconfigs.HTTPFir
|
|||||||
if len(regionConfig.DenyCountryIds) > 0 && len(result.Country) > 0 {
|
if len(regionConfig.DenyCountryIds) > 0 && len(result.Country) > 0 {
|
||||||
countryId := iplibrary.SharedCountryManager.Lookup(result.Country)
|
countryId := iplibrary.SharedCountryManager.Lookup(result.Country)
|
||||||
if countryId > 0 && lists.ContainsInt64(regionConfig.DenyCountryIds, countryId) {
|
if countryId > 0 && lists.ContainsInt64(regionConfig.DenyCountryIds, countryId) {
|
||||||
// TODO 可以配置对封禁的处理方式等
|
this.firewallPolicyId = firewallPolicy.Id
|
||||||
// TODO 需要记录日志信息
|
|
||||||
this.writer.WriteHeader(http.StatusForbidden)
|
this.writeCode(http.StatusForbidden)
|
||||||
|
this.writer.Flush()
|
||||||
this.writer.Close()
|
this.writer.Close()
|
||||||
|
|
||||||
// 停止日志
|
// 停止日志
|
||||||
this.disableLog = true
|
if !logDenying {
|
||||||
|
this.disableLog = true
|
||||||
|
} else {
|
||||||
|
this.tags = append(this.tags, "denyCountry")
|
||||||
|
}
|
||||||
|
|
||||||
return true, false
|
return true, false
|
||||||
}
|
}
|
||||||
@@ -173,15 +193,20 @@ func (this *HTTPRequest) checkWAFRequest(firewallPolicy *firewallconfigs.HTTPFir
|
|||||||
|
|
||||||
// 检查省份封禁
|
// 检查省份封禁
|
||||||
if len(regionConfig.DenyProvinceIds) > 0 && len(result.Province) > 0 {
|
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) {
|
if provinceId > 0 && lists.ContainsInt64(regionConfig.DenyProvinceIds, provinceId) {
|
||||||
// TODO 可以配置对封禁的处理方式等
|
this.firewallPolicyId = firewallPolicy.Id
|
||||||
// TODO 需要记录日志信息
|
|
||||||
this.writer.WriteHeader(http.StatusForbidden)
|
this.writeCode(http.StatusForbidden)
|
||||||
|
this.writer.Flush()
|
||||||
this.writer.Close()
|
this.writer.Close()
|
||||||
|
|
||||||
// 停止日志
|
// 停止日志
|
||||||
this.disableLog = true
|
if !logDenying {
|
||||||
|
this.disableLog = true
|
||||||
|
} else {
|
||||||
|
this.tags = append(this.tags, "denyProvince")
|
||||||
|
}
|
||||||
|
|
||||||
return true, false
|
return true, false
|
||||||
}
|
}
|
||||||
@@ -199,7 +224,10 @@ func (this *HTTPRequest) checkWAFRequest(firewallPolicy *firewallconfigs.HTTPFir
|
|||||||
return
|
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 err != nil {
|
||||||
if !this.canIgnore(err) {
|
if !this.canIgnore(err) {
|
||||||
remotelogs.Error("HTTP_REQUEST_WAF", this.rawURI+": "+err.Error())
|
remotelogs.Error("HTTP_REQUEST_WAF", this.rawURI+": "+err.Error())
|
||||||
@@ -238,9 +266,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 {
|
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 {
|
if blocked {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -248,7 +282,7 @@ func (this *HTTPRequest) doWAFResponse(resp *http.Response) (blocked bool) {
|
|||||||
|
|
||||||
// 公用的防火墙设置
|
// 公用的防火墙设置
|
||||||
if this.ReqServer.HTTPFirewallPolicy != nil && this.ReqServer.HTTPFirewallPolicy.IsOn {
|
if this.ReqServer.HTTPFirewallPolicy != nil && this.ReqServer.HTTPFirewallPolicy.IsOn {
|
||||||
blocked := this.checkWAFResponse(this.ReqServer.HTTPFirewallPolicy, resp, forceLog)
|
blocked := this.checkWAFResponse(this.ReqServer.HTTPFirewallPolicy, resp, forceLog, forceLogRequestBody)
|
||||||
if blocked {
|
if blocked {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -256,7 +290,7 @@ func (this *HTTPRequest) doWAFResponse(resp *http.Response) (blocked bool) {
|
|||||||
return
|
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 {
|
if firewallPolicy == nil || !firewallPolicy.IsOn || !firewallPolicy.Outbound.IsOn || firewallPolicy.Mode == firewallconfigs.FirewallModeBypass {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -266,7 +300,10 @@ func (this *HTTPRequest) checkWAFResponse(firewallPolicy *firewallconfigs.HTTPFi
|
|||||||
return
|
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 err != nil {
|
||||||
if !this.canIgnore(err) {
|
if !this.canIgnore(err) {
|
||||||
remotelogs.Error("HTTP_REQUEST_WAF", this.rawURI+": "+err.Error())
|
remotelogs.Error("HTTP_REQUEST_WAF", this.rawURI+": "+err.Error())
|
||||||
@@ -321,7 +358,7 @@ func (this *HTTPRequest) WAFSetCacheBody(body []byte) {
|
|||||||
// WAFReadBody 读取Body
|
// WAFReadBody 读取Body
|
||||||
func (this *HTTPRequest) WAFReadBody(max int64) (data []byte, err error) {
|
func (this *HTTPRequest) WAFReadBody(max int64) (data []byte, err error) {
|
||||||
if this.RawReq.ContentLength > 0 {
|
if this.RawReq.ContentLength > 0 {
|
||||||
data, err = ioutil.ReadAll(io.LimitReader(this.RawReq.Body, max))
|
data, err = io.ReadAll(io.LimitReader(this.RawReq.Body, max))
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
@@ -330,7 +367,7 @@ func (this *HTTPRequest) WAFReadBody(max int64) (data []byte, err error) {
|
|||||||
// WAFRestoreBody 恢复Body
|
// WAFRestoreBody 恢复Body
|
||||||
func (this *HTTPRequest) WAFRestoreBody(data []byte) {
|
func (this *HTTPRequest) WAFRestoreBody(data []byte) {
|
||||||
if len(data) > 0 {
|
if len(data) > 0 {
|
||||||
this.RawReq.Body = ioutil.NopCloser(io.MultiReader(bytes.NewBuffer(data), this.RawReq.Body))
|
this.RawReq.Body = io.NopCloser(io.MultiReader(bytes.NewBuffer(data), this.RawReq.Body))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// 处理Websocket请求
|
// 处理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 {
|
if this.web.WebsocketRef == nil || !this.web.WebsocketRef.IsOn || this.web.Websocket == nil || !this.web.Websocket.IsOn {
|
||||||
this.writer.WriteHeader(http.StatusForbidden)
|
this.writer.WriteHeader(http.StatusForbidden)
|
||||||
this.addError(errors.New("websocket have not been enabled yet"))
|
this.addError(errors.New("websocket have not been enabled yet"))
|
||||||
@@ -41,12 +41,12 @@ func (this *HTTPRequest) doWebsocket() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO 增加N次错误重试,重试的时候需要尝试不同的源站
|
// TODO 增加N次错误重试,重试的时候需要尝试不同的源站
|
||||||
originConn, err := OriginConnect(this.origin, this.RawReq.RemoteAddr)
|
originConn, _, err := OriginConnect(this.origin, this.requestServerPort(), this.RawReq.RemoteAddr, requestHost)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.write50x(err, http.StatusBadGateway, false)
|
this.write50x(err, http.StatusBadGateway, "Failed to connect origin site", "源站连接失败", false)
|
||||||
|
|
||||||
// 增加失败次数
|
// 增加失败次数
|
||||||
SharedOriginStateManager.Fail(this.origin, this.reverseProxy, func() {
|
SharedOriginStateManager.Fail(this.origin, requestHost, this.reverseProxy, func() {
|
||||||
this.reverseProxy.ResetScheduling()
|
this.reverseProxy.ResetScheduling()
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -65,13 +65,13 @@ func (this *HTTPRequest) doWebsocket() {
|
|||||||
|
|
||||||
err = this.RawReq.Write(originConn)
|
err = this.RawReq.Write(originConn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.write50x(err, http.StatusBadGateway, false)
|
this.write50x(err, http.StatusBadGateway, "Failed to write request to origin site", "源站请求初始化失败", false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
clientConn, _, err := this.writer.Hijack()
|
clientConn, _, err := this.writer.Hijack()
|
||||||
if err != nil || clientConn == nil {
|
if err != nil || clientConn == nil {
|
||||||
this.write50x(err, http.StatusInternalServerError, false)
|
this.write50x(err, http.StatusInternalServerError, "Failed to get origin site connection", "获取源站连接失败", false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ import (
|
|||||||
_ "image/jpeg"
|
_ "image/jpeg"
|
||||||
_ "image/png"
|
_ "image/png"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/textproto"
|
"net/textproto"
|
||||||
@@ -509,7 +508,7 @@ func (this *HTTPWriter) PrepareWebP(resp *http.Response, size int64) {
|
|||||||
|
|
||||||
var contentEncoding = this.GetHeader("Content-Encoding")
|
var contentEncoding = this.GetHeader("Content-Encoding")
|
||||||
switch contentEncoding {
|
switch contentEncoding {
|
||||||
case "gzip", "deflate", "br":
|
case "gzip", "deflate", "br", "zstd":
|
||||||
reader, err := compressions.NewReader(resp.Body, contentEncoding)
|
reader, err := compressions.NewReader(resp.Body, contentEncoding)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
@@ -524,7 +523,7 @@ func (this *HTTPWriter) PrepareWebP(resp *http.Response, size int64) {
|
|||||||
|
|
||||||
this.webpOriginContentType = contentType
|
this.webpOriginContentType = contentType
|
||||||
this.webpIsEncoding = true
|
this.webpIsEncoding = true
|
||||||
resp.Body = ioutil.NopCloser(&bytes.Buffer{})
|
resp.Body = io.NopCloser(&bytes.Buffer{})
|
||||||
this.delayRead = true
|
this.delayRead = true
|
||||||
|
|
||||||
this.Header().Del("Content-Length")
|
this.Header().Del("Content-Length")
|
||||||
@@ -547,7 +546,7 @@ func (this *HTTPWriter) PrepareCompression(resp *http.Response, size int64) {
|
|||||||
var contentEncoding = this.GetHeader("Content-Encoding")
|
var contentEncoding = this.GetHeader("Content-Encoding")
|
||||||
|
|
||||||
if this.compressionConfig == nil || !this.compressionConfig.IsOn {
|
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)
|
reader, err := compressions.NewReader(resp.Body, contentEncoding)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
@@ -564,7 +563,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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ func (this *Listener) Listen() error {
|
|||||||
if this.group == nil {
|
if this.group == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
protocol := this.group.Protocol()
|
var protocol = this.group.Protocol()
|
||||||
if protocol.IsUDPFamily() {
|
if protocol.IsUDPFamily() {
|
||||||
return this.listenUDP()
|
return this.listenUDP()
|
||||||
}
|
}
|
||||||
@@ -54,7 +54,7 @@ func (this *Listener) listenTCP() error {
|
|||||||
if this.group == nil {
|
if this.group == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
protocol := this.group.Protocol()
|
var protocol = this.group.Protocol()
|
||||||
|
|
||||||
tcpListener, err := this.createTCPListener()
|
tcpListener, err := this.createTCPListener()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -153,7 +153,7 @@ func (this *Listener) Close() error {
|
|||||||
|
|
||||||
// 创建TCP监听器
|
// 创建TCP监听器
|
||||||
func (this *Listener) createTCPListener() (net.Listener, error) {
|
func (this *Listener) createTCPListener() (net.Listener, error) {
|
||||||
listenConfig := net.ListenConfig{
|
var listenConfig = net.ListenConfig{
|
||||||
Control: nil,
|
Control: nil,
|
||||||
KeepAlive: 0,
|
KeepAlive: 0,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,10 @@ func (this *BaseListener) buildTLSConfig() *tls.Config {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if tlsPolicy == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
tlsPolicy.CheckOCSP()
|
tlsPolicy.CheckOCSP()
|
||||||
|
|
||||||
return tlsPolicy.TLSConfig(), nil
|
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) {
|
func (this *BaseListener) matchSSL(domain string) (*sslconfigs.SSLPolicy, *tls.Certificate, error) {
|
||||||
group := this.Group
|
var group = this.Group
|
||||||
|
|
||||||
if group == nil {
|
if group == nil {
|
||||||
return nil, nil, errors.New("no configure found")
|
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)
|
cert, ok := sslConfig.MatchDomain(domain)
|
||||||
if ok {
|
if ok {
|
||||||
return sslConfig, cert, nil
|
return sslConfig, cert, nil
|
||||||
@@ -127,7 +131,7 @@ func (this *BaseListener) findNamedServer(name string) (serverConfig *serverconf
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
matchDomainStrictly := sharedNodeConfig.GlobalConfig != nil && sharedNodeConfig.GlobalConfig.HTTPAll.MatchDomainStrictly
|
var matchDomainStrictly = sharedNodeConfig.GlobalConfig != nil && sharedNodeConfig.GlobalConfig.HTTPAll.MatchDomainStrictly
|
||||||
|
|
||||||
if sharedNodeConfig.GlobalConfig != nil &&
|
if sharedNodeConfig.GlobalConfig != nil &&
|
||||||
len(sharedNodeConfig.GlobalConfig.HTTPAll.DefaultDomain) > 0 &&
|
len(sharedNodeConfig.GlobalConfig.HTTPAll.DefaultDomain) > 0 &&
|
||||||
@@ -144,9 +148,9 @@ func (this *BaseListener) findNamedServer(name string) (serverConfig *serverconf
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 如果没有找到,则匹配到第一个
|
// 如果没有找到,则匹配到第一个
|
||||||
group := this.Group
|
var group = this.Group
|
||||||
currentServers := group.Servers()
|
var currentServers = group.Servers()
|
||||||
countServers := len(currentServers)
|
var countServers = len(currentServers)
|
||||||
if countServers == 0 {
|
if countServers == 0 {
|
||||||
return nil, ""
|
return nil, ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package nodes
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeutils"
|
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/zero"
|
"github.com/TeaOSLab/EdgeNode/internal/zero"
|
||||||
@@ -140,10 +139,10 @@ func (this *HTTPListener) ServeHTTP(rawWriter http.ResponseWriter, rawReq *http.
|
|||||||
// TLS域名
|
// TLS域名
|
||||||
if this.isIP(reqHost) {
|
if this.isIP(reqHost) {
|
||||||
if rawReq.TLS != nil {
|
if rawReq.TLS != nil {
|
||||||
serverName := rawReq.TLS.ServerName
|
var serverName = rawReq.TLS.ServerName
|
||||||
if len(serverName) > 0 {
|
if len(serverName) > 0 {
|
||||||
// 端口
|
// 端口
|
||||||
index := strings.LastIndex(reqHost, ":")
|
var index = strings.LastIndex(reqHost, ":")
|
||||||
if index >= 0 {
|
if index >= 0 {
|
||||||
reqHost = serverName + reqHost[index:]
|
reqHost = serverName + reqHost[index:]
|
||||||
} else {
|
} else {
|
||||||
@@ -155,7 +154,7 @@ func (this *HTTPListener) ServeHTTP(rawWriter http.ResponseWriter, rawReq *http.
|
|||||||
|
|
||||||
// 防止空Host
|
// 防止空Host
|
||||||
if len(reqHost) == 0 {
|
if len(reqHost) == 0 {
|
||||||
ctx := rawReq.Context()
|
var ctx = rawReq.Context()
|
||||||
if ctx != nil {
|
if ctx != nil {
|
||||||
addr := ctx.Value(http.LocalAddrContextKey)
|
addr := ctx.Value(http.LocalAddrContextKey)
|
||||||
if addr != nil {
|
if addr != nil {
|
||||||
@@ -172,11 +171,25 @@ func (this *HTTPListener) ServeHTTP(rawWriter http.ResponseWriter, rawReq *http.
|
|||||||
server, serverName := this.findNamedServer(domain)
|
server, serverName := this.findNamedServer(domain)
|
||||||
if server == nil {
|
if server == nil {
|
||||||
if server == nil {
|
if server == nil {
|
||||||
this.handleMismatch(rawReq, rawWriter)
|
// 增加默认的一个服务
|
||||||
return
|
server = this.emptyServer()
|
||||||
} else {
|
} else {
|
||||||
serverName = domain
|
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()
|
req.Do()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理域名不匹配的情况
|
// 检查host是否为IP
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *HTTPListener) isIP(host string) bool {
|
func (this *HTTPListener) isIP(host string) bool {
|
||||||
// IPv6
|
// IPv6
|
||||||
if strings.Index(host, "[") > -1 {
|
if strings.Index(host, "[") > -1 {
|
||||||
@@ -251,3 +223,21 @@ func (this *HTTPListener) isIP(host string) bool {
|
|||||||
|
|
||||||
return true
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,11 +5,14 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/firewalls"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
"github.com/iwind/TeaGo/Tea"
|
"github.com/iwind/TeaGo/Tea"
|
||||||
"github.com/iwind/TeaGo/lists"
|
"github.com/iwind/TeaGo/lists"
|
||||||
"github.com/iwind/TeaGo/maps"
|
"github.com/iwind/TeaGo/maps"
|
||||||
|
"github.com/iwind/TeaGo/types"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"regexp"
|
"regexp"
|
||||||
@@ -31,15 +34,19 @@ type ListenerManager struct {
|
|||||||
retryListenerMap map[string]*Listener // 需要重试的监听器 addr => Listener
|
retryListenerMap map[string]*Listener // 需要重试的监听器 addr => Listener
|
||||||
ticker *time.Ticker
|
ticker *time.Ticker
|
||||||
|
|
||||||
lastPortStrings string
|
firewalld *firewalls.Firewalld
|
||||||
|
lastPortStrings string
|
||||||
|
lastTCPPortRanges [][2]int
|
||||||
|
lastUDPPortRanges [][2]int
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewListenerManager 获取新对象
|
// NewListenerManager 获取新对象
|
||||||
func NewListenerManager() *ListenerManager {
|
func NewListenerManager() *ListenerManager {
|
||||||
manager := &ListenerManager{
|
var manager = &ListenerManager{
|
||||||
listenersMap: map[string]*Listener{},
|
listenersMap: map[string]*Listener{},
|
||||||
retryListenerMap: map[string]*Listener{},
|
retryListenerMap: map[string]*Listener{},
|
||||||
ticker: time.NewTicker(1 * time.Minute),
|
ticker: time.NewTicker(1 * time.Minute),
|
||||||
|
firewalld: firewalls.NewFirewalld(),
|
||||||
}
|
}
|
||||||
|
|
||||||
// 提升测试效率
|
// 提升测试效率
|
||||||
@@ -147,7 +154,7 @@ func (this *ListenerManager) Start(node *nodeconfigs.NodeConfig) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 加入到firewalld
|
// 加入到firewalld
|
||||||
this.addToFirewalld(groupAddrs)
|
go this.addToFirewalld(groupAddrs)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -226,8 +233,14 @@ func (this *ListenerManager) addToFirewalld(groupAddrs []string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if this.firewalld == nil || !this.firewalld.IsReady() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// 组合端口号
|
// 组合端口号
|
||||||
var ports = []string{}
|
var portStrings = []string{}
|
||||||
|
var udpPorts = []int{}
|
||||||
|
var tcpPorts = []int{}
|
||||||
for _, addr := range groupAddrs {
|
for _, addr := range groupAddrs {
|
||||||
var protocol = "tcp"
|
var protocol = "tcp"
|
||||||
if strings.HasPrefix(addr, "udp") {
|
if strings.HasPrefix(addr, "udp") {
|
||||||
@@ -237,52 +250,72 @@ func (this *ListenerManager) addToFirewalld(groupAddrs []string) {
|
|||||||
var lastIndex = strings.LastIndex(addr, ":")
|
var lastIndex = strings.LastIndex(addr, ":")
|
||||||
if lastIndex > 0 {
|
if lastIndex > 0 {
|
||||||
var portString = addr[lastIndex+1:]
|
var portString = addr[lastIndex+1:]
|
||||||
ports = append(ports, portString+"/"+protocol)
|
portStrings = append(portStrings, portString+"/"+protocol)
|
||||||
|
|
||||||
|
switch protocol {
|
||||||
|
case "tcp":
|
||||||
|
tcpPorts = append(tcpPorts, types.Int(portString))
|
||||||
|
case "udp":
|
||||||
|
udpPorts = append(udpPorts, types.Int(portString))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(ports) == 0 {
|
if len(portStrings) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否有变化
|
// 检查是否有变化
|
||||||
sort.Strings(ports)
|
sort.Strings(portStrings)
|
||||||
var newPortStrings = strings.Join(ports, ",")
|
var newPortStrings = strings.Join(portStrings, ",")
|
||||||
if newPortStrings == this.lastPortStrings {
|
if newPortStrings == this.lastPortStrings {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.lastPortStrings = newPortStrings
|
this.lastPortStrings = newPortStrings
|
||||||
|
|
||||||
firewallCmd, err := exec.LookPath("firewall-cmd")
|
remotelogs.Println("FIREWALLD", "opening ports automatically ...")
|
||||||
if err != nil || len(firewallCmd) == 0 {
|
defer func() {
|
||||||
return
|
remotelogs.Println("FIREWALLD", "open ports successfully")
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 合并端口
|
||||||
|
var tcpPortRanges = utils.MergePorts(tcpPorts)
|
||||||
|
var udpPortRanges = utils.MergePorts(udpPorts)
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
this.lastTCPPortRanges = tcpPortRanges
|
||||||
|
this.lastUDPPortRanges = udpPortRanges
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 删除老的不存在的端口
|
||||||
|
var tcpPortRangesMap = map[string]bool{}
|
||||||
|
var udpPortRangesMap = map[string]bool{}
|
||||||
|
for _, portRange := range tcpPortRanges {
|
||||||
|
tcpPortRangesMap[this.firewalld.PortRangeString(portRange, "tcp")] = true
|
||||||
|
}
|
||||||
|
for _, portRange := range udpPortRanges {
|
||||||
|
udpPortRangesMap[this.firewalld.PortRangeString(portRange, "udp")] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查状态
|
for _, portRange := range this.lastTCPPortRanges {
|
||||||
err = exec.Command(firewallCmd, "--state").Run()
|
var s = this.firewalld.PortRangeString(portRange, "tcp")
|
||||||
if err != nil {
|
_, ok := tcpPortRangesMap[s]
|
||||||
return
|
if ok {
|
||||||
}
|
continue
|
||||||
|
|
||||||
remotelogs.Println("FIREWALLD", "open ports automatically")
|
|
||||||
for _, port := range ports {
|
|
||||||
{
|
|
||||||
// TODO 需要支持sudo
|
|
||||||
var cmd = exec.Command(firewallCmd, "--add-port="+port, "--permanent")
|
|
||||||
err = cmd.Run()
|
|
||||||
if err != nil {
|
|
||||||
remotelogs.Warn("FIREWALLD", "'"+cmd.String()+"': "+err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
// TODO 需要支持sudo
|
|
||||||
var cmd = exec.Command(firewallCmd, "--add-port="+port)
|
|
||||||
err = cmd.Run()
|
|
||||||
if err != nil {
|
|
||||||
remotelogs.Warn("FIREWALLD", "'"+cmd.String()+"': "+err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
remotelogs.Println("FIREWALLD", "remove port '"+s+"'")
|
||||||
|
_ = this.firewalld.RemovePortRangePermanently(portRange, "tcp")
|
||||||
}
|
}
|
||||||
|
for _, portRange := range this.lastUDPPortRanges {
|
||||||
|
var s = this.firewalld.PortRangeString(portRange, "udp")
|
||||||
|
_, ok := udpPortRangesMap[s]
|
||||||
|
if ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
remotelogs.Println("FIREWALLD", "remove port '"+s+"'")
|
||||||
|
_ = this.firewalld.RemovePortRangePermanently(portRange, "udp")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加新的
|
||||||
|
_ = this.firewalld.AllowPortRangesPermanently(tcpPortRanges, "tcp")
|
||||||
|
_ = this.firewalld.AllowPortRangesPermanently(udpPortRanges, "udp")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,10 +4,12 @@ import (
|
|||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/stats"
|
"github.com/TeaOSLab/EdgeNode/internal/stats"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
|
"github.com/iwind/TeaGo/types"
|
||||||
"github.com/pires/go-proxyproto"
|
"github.com/pires/go-proxyproto"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -18,6 +20,8 @@ type TCPListener struct {
|
|||||||
BaseListener
|
BaseListener
|
||||||
|
|
||||||
Listener net.Listener
|
Listener net.Listener
|
||||||
|
|
||||||
|
port int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *TCPListener) Serve() error {
|
func (this *TCPListener) Serve() error {
|
||||||
@@ -26,6 +30,14 @@ func (this *TCPListener) Serve() error {
|
|||||||
listener = tls.NewListener(listener, this.buildTLSConfig())
|
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 {
|
for {
|
||||||
conn, err := listener.Accept()
|
conn, err := listener.Accept()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -60,6 +72,25 @@ func (this *TCPListener) handleConn(conn net.Conn) error {
|
|||||||
return errors.New("no ReverseProxy configured for the server")
|
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() {
|
if this.reachedTrafficLimit() {
|
||||||
// 关闭连接
|
// 关闭连接
|
||||||
@@ -78,8 +109,9 @@ func (this *TCPListener) handleConn(conn net.Conn) error {
|
|||||||
// 记录域名排行
|
// 记录域名排行
|
||||||
tlsConn, ok := conn.(*tls.Conn)
|
tlsConn, ok := conn.(*tls.Conn)
|
||||||
var recordStat = false
|
var recordStat = false
|
||||||
|
var serverName = ""
|
||||||
if ok {
|
if ok {
|
||||||
var serverName = tlsConn.ConnectionState().ServerName
|
serverName = tlsConn.ConnectionState().ServerName
|
||||||
if len(serverName) > 0 {
|
if len(serverName) > 0 {
|
||||||
// 统计
|
// 统计
|
||||||
stats.SharedTrafficStatManager.Add(server.Id, serverName, 0, 0, 1, 0, 0, 0, server.ShouldCheckTrafficLimit(), server.PlanId())
|
stats.SharedTrafficStatManager.Add(server.Id, serverName, 0, 0, 1, 0, 0, 0, server.ShouldCheckTrafficLimit(), server.PlanId())
|
||||||
@@ -92,8 +124,9 @@ func (this *TCPListener) handleConn(conn net.Conn) error {
|
|||||||
stats.SharedTrafficStatManager.Add(server.Id, "", 0, 0, 1, 0, 0, 0, server.ShouldCheckTrafficLimit(), server.PlanId())
|
stats.SharedTrafficStatManager.Add(server.Id, "", 0, 0, 1, 0, 0, 0, server.ShouldCheckTrafficLimit(), server.PlanId())
|
||||||
}
|
}
|
||||||
|
|
||||||
originConn, err := this.connectOrigin(server.Id, server.ReverseProxy, conn.RemoteAddr().String())
|
originConn, err := this.connectOrigin(server.Id, serverName, server.ReverseProxy, conn.RemoteAddr().String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
_ = conn.Close()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,26 +220,64 @@ func (this *TCPListener) Close() error {
|
|||||||
return this.Listener.Close()
|
return this.Listener.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *TCPListener) connectOrigin(serverId int64, reverseProxy *serverconfigs.ReverseProxyConfig, remoteAddr string) (conn net.Conn, err error) {
|
// 连接源站
|
||||||
|
func (this *TCPListener) connectOrigin(serverId int64, requestHost string, reverseProxy *serverconfigs.ReverseProxyConfig, remoteAddr string) (conn net.Conn, err error) {
|
||||||
if reverseProxy == nil {
|
if reverseProxy == nil {
|
||||||
return nil, errors.New("no reverse proxy config")
|
return nil, errors.New("no reverse proxy config")
|
||||||
}
|
}
|
||||||
|
|
||||||
retries := 3
|
var requestCall = shared.NewRequestCall()
|
||||||
|
requestCall.Domain = requestHost
|
||||||
|
|
||||||
|
var retries = 3
|
||||||
|
var addr string
|
||||||
|
|
||||||
|
var failedOriginIds []int64
|
||||||
|
|
||||||
for i := 0; i < retries; i++ {
|
for i := 0; i < retries; i++ {
|
||||||
origin := reverseProxy.NextOrigin(nil)
|
var origin *serverconfigs.OriginConfig
|
||||||
|
if len(failedOriginIds) > 0 {
|
||||||
|
origin = reverseProxy.AnyOrigin(requestCall, failedOriginIds)
|
||||||
|
}
|
||||||
|
if origin == nil {
|
||||||
|
origin = reverseProxy.NextOrigin(requestCall)
|
||||||
|
}
|
||||||
if origin == nil {
|
if origin == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
conn, err = OriginConnect(origin, remoteAddr)
|
|
||||||
|
// 回源主机名
|
||||||
|
if len(origin.RequestHost) > 0 {
|
||||||
|
requestHost = origin.RequestHost
|
||||||
|
} else if len(reverseProxy.RequestHost) > 0 {
|
||||||
|
requestHost = reverseProxy.RequestHost
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, addr, err = OriginConnect(origin, this.port, remoteAddr, requestHost)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
remotelogs.ServerError(serverId, "TCP_LISTENER", "unable to connect origin: "+origin.Addr.Host+":"+origin.Addr.PortRange+": "+err.Error(), "", nil)
|
failedOriginIds = append(failedOriginIds, origin.Id)
|
||||||
|
|
||||||
|
remotelogs.ServerError(serverId, "TCP_LISTENER", "unable to connect origin server: "+addr+": "+err.Error(), "", nil)
|
||||||
|
|
||||||
|
SharedOriginStateManager.Fail(origin, requestHost, reverseProxy, func() {
|
||||||
|
reverseProxy.ResetScheduling()
|
||||||
|
})
|
||||||
|
|
||||||
continue
|
continue
|
||||||
} else {
|
} else {
|
||||||
|
if !origin.IsOk {
|
||||||
|
SharedOriginStateManager.Success(origin, func() {
|
||||||
|
reverseProxy.ResetScheduling()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err = errors.New("no origin can be used")
|
|
||||||
|
if err == nil {
|
||||||
|
err = errors.New("server '" + types.String(serverId) + "': no available origin server can be used")
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/stats"
|
"github.com/TeaOSLab/EdgeNode/internal/stats"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
|
"github.com/iwind/TeaGo/types"
|
||||||
"github.com/pires/go-proxyproto"
|
"github.com/pires/go-proxyproto"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -14,6 +15,10 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
UDPConnLifeSeconds = 30
|
||||||
|
)
|
||||||
|
|
||||||
type UDPListener struct {
|
type UDPListener struct {
|
||||||
BaseListener
|
BaseListener
|
||||||
|
|
||||||
@@ -25,11 +30,21 @@ type UDPListener struct {
|
|||||||
|
|
||||||
reverseProxy *serverconfigs.ReverseProxyConfig
|
reverseProxy *serverconfigs.ReverseProxyConfig
|
||||||
|
|
||||||
|
port int
|
||||||
|
|
||||||
isClosed bool
|
isClosed bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *UDPListener) Serve() error {
|
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 {
|
if firstServer == nil {
|
||||||
return errors.New("no server available")
|
return errors.New("no server available")
|
||||||
}
|
}
|
||||||
@@ -110,7 +125,7 @@ func (this *UDPListener) Reload(group *serverconfigs.ServerAddressGroup) {
|
|||||||
this.Reset()
|
this.Reset()
|
||||||
|
|
||||||
// 重置配置
|
// 重置配置
|
||||||
firstServer := this.Group.FirstServer()
|
var firstServer = this.Group.FirstServer()
|
||||||
if firstServer == nil {
|
if firstServer == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -122,17 +137,41 @@ func (this *UDPListener) connectOrigin(serverId int64, reverseProxy *serverconfi
|
|||||||
return nil, errors.New("no reverse proxy config")
|
return nil, errors.New("no reverse proxy config")
|
||||||
}
|
}
|
||||||
|
|
||||||
retries := 3
|
var retries = 3
|
||||||
|
var addr string
|
||||||
|
|
||||||
|
var failedOriginIds []int64
|
||||||
|
|
||||||
for i := 0; i < retries; i++ {
|
for i := 0; i < retries; i++ {
|
||||||
origin := reverseProxy.NextOrigin(nil)
|
var origin *serverconfigs.OriginConfig
|
||||||
|
if len(failedOriginIds) > 0 {
|
||||||
|
origin = reverseProxy.AnyOrigin(nil, failedOriginIds)
|
||||||
|
}
|
||||||
|
if origin == nil {
|
||||||
|
origin = reverseProxy.NextOrigin(nil)
|
||||||
|
}
|
||||||
if origin == nil {
|
if origin == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
conn, err = OriginConnect(origin, remoteAddr.String())
|
|
||||||
|
conn, addr, err = OriginConnect(origin, this.port, remoteAddr.String(), "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
remotelogs.ServerError(serverId, "UDP_LISTENER", "unable to connect origin: "+origin.Addr.Host+":"+origin.Addr.PortRange+": "+err.Error(), "", nil)
|
failedOriginIds = append(failedOriginIds, origin.Id)
|
||||||
|
|
||||||
|
remotelogs.ServerError(serverId, "UDP_LISTENER", "unable to connect origin server: "+addr+": "+err.Error(), "", nil)
|
||||||
|
|
||||||
|
SharedOriginStateManager.Fail(origin, "", reverseProxy, func() {
|
||||||
|
reverseProxy.ResetScheduling()
|
||||||
|
})
|
||||||
|
|
||||||
continue
|
continue
|
||||||
} else {
|
} else {
|
||||||
|
if !origin.IsOk {
|
||||||
|
SharedOriginStateManager.Success(origin, func() {
|
||||||
|
reverseProxy.ResetScheduling()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// PROXY Protocol
|
// PROXY Protocol
|
||||||
if reverseProxy != nil &&
|
if reverseProxy != nil &&
|
||||||
reverseProxy.ProxyProtocol != nil &&
|
reverseProxy.ProxyProtocol != nil &&
|
||||||
@@ -159,14 +198,17 @@ func (this *UDPListener) connectOrigin(serverId int64, reverseProxy *serverconfi
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err = errors.New("no origin can be used")
|
|
||||||
|
if err == nil {
|
||||||
|
err = errors.New("server '" + types.String(serverId) + "': no available origin server can be used")
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 回收连接
|
// 回收连接
|
||||||
func (this *UDPListener) gcConns() {
|
func (this *UDPListener) gcConns() {
|
||||||
this.connLocker.Lock()
|
this.connLocker.Lock()
|
||||||
closingConns := []*UDPConn{}
|
var closingConns = []*UDPConn{}
|
||||||
for addr, conn := range this.connMap {
|
for addr, conn := range this.connMap {
|
||||||
if !conn.IsOk() {
|
if !conn.IsOk() {
|
||||||
closingConns = append(closingConns, conn)
|
closingConns = append(closingConns, conn)
|
||||||
@@ -191,7 +233,7 @@ type UDPConn struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewUDPConn(server *serverconfigs.ServerConfig, addr net.Addr, proxyConn *net.UDPConn, serverConn *net.UDPConn) *UDPConn {
|
func NewUDPConn(server *serverconfigs.ServerConfig, addr net.Addr, proxyConn *net.UDPConn, serverConn *net.UDPConn) *UDPConn {
|
||||||
conn := &UDPConn{
|
var conn = &UDPConn{
|
||||||
addr: addr,
|
addr: addr,
|
||||||
proxyConn: proxyConn,
|
proxyConn: proxyConn,
|
||||||
serverConn: serverConn,
|
serverConn: serverConn,
|
||||||
@@ -205,7 +247,7 @@ func NewUDPConn(server *serverconfigs.ServerConfig, addr net.Addr, proxyConn *ne
|
|||||||
}
|
}
|
||||||
|
|
||||||
goman.New(func() {
|
goman.New(func() {
|
||||||
buffer := utils.BytePool4k.Get()
|
var buffer = utils.BytePool4k.Get()
|
||||||
defer func() {
|
defer func() {
|
||||||
utils.BytePool4k.Put(buffer)
|
utils.BytePool4k.Put(buffer)
|
||||||
}()
|
}()
|
||||||
@@ -220,9 +262,13 @@ func NewUDPConn(server *serverconfigs.ServerConfig, addr net.Addr, proxyConn *ne
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
// 记录流量
|
// 记录流量和带宽
|
||||||
if server != nil {
|
if server != nil {
|
||||||
|
// 流量
|
||||||
stats.SharedTrafficStatManager.Add(server.Id, "", int64(n), 0, 0, 0, 0, 0, server.ShouldCheckTrafficLimit(), server.PlanId())
|
stats.SharedTrafficStatManager.Add(server.Id, "", int64(n), 0, 0, 0, 0, 0, server.ShouldCheckTrafficLimit(), server.PlanId())
|
||||||
|
|
||||||
|
// 带宽
|
||||||
|
stats.SharedBandwidthStatManager.Add(server.UserId, server.Id, int64(n))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -256,5 +302,5 @@ func (this *UDPConn) IsOk() bool {
|
|||||||
if !this.isOk {
|
if !this.isOk {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return time.Now().Unix()-this.activatedAt < 30 // 如果超过 N 秒没有活动我们认为是超时
|
return time.Now().Unix()-this.activatedAt < UDPConnLifeSeconds // 如果超过 N 秒没有活动我们认为是超时
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ import (
|
|||||||
"github.com/iwind/TeaGo/types"
|
"github.com/iwind/TeaGo/types"
|
||||||
"github.com/iwind/gosock/pkg/gosock"
|
"github.com/iwind/gosock/pkg/gosock"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
@@ -207,8 +206,9 @@ func (this *Node) Start() {
|
|||||||
|
|
||||||
// Daemon 实现守护进程
|
// Daemon 实现守护进程
|
||||||
func (this *Node) Daemon() {
|
func (this *Node) Daemon() {
|
||||||
isDebug := lists.ContainsString(os.Args, "debug")
|
teaconst.IsDaemon = true
|
||||||
isDebug = true
|
|
||||||
|
var isDebug = lists.ContainsString(os.Args, "debug")
|
||||||
for {
|
for {
|
||||||
conn, err := this.sock.Dial()
|
conn, err := this.sock.Dial()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -227,13 +227,18 @@ func (this *Node) Daemon() {
|
|||||||
_ = os.Setenv("EdgeDaemon", "on")
|
_ = os.Setenv("EdgeDaemon", "on")
|
||||||
_ = os.Setenv("EdgeBackground", "on")
|
_ = os.Setenv("EdgeBackground", "on")
|
||||||
|
|
||||||
cmd := exec.Command(exe)
|
var cmd = exec.Command(exe)
|
||||||
|
var buf = &bytes.Buffer{}
|
||||||
|
cmd.Stderr = buf
|
||||||
err = cmd.Start()
|
err = cmd.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = cmd.Wait()
|
err = cmd.Wait()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if isDebug {
|
||||||
|
log.Println("[DAEMON]" + buf.String())
|
||||||
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -291,12 +296,20 @@ func (this *Node) loop() error {
|
|||||||
var nodeCtx = rpcClient.Context()
|
var nodeCtx = rpcClient.Context()
|
||||||
tasksResp, err := rpcClient.NodeTaskRPC().FindNodeTasks(nodeCtx, &pb.FindNodeTasksRequest{})
|
tasksResp, err := rpcClient.NodeTaskRPC().FindNodeTasks(nodeCtx, &pb.FindNodeTasksRequest{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if rpc.IsConnError(err) && !Tea.IsTesting() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
return errors.New("read node tasks failed: " + err.Error())
|
return errors.New("read node tasks failed: " + err.Error())
|
||||||
}
|
}
|
||||||
for _, task := range tasksResp.NodeTasks {
|
for _, task := range tasksResp.NodeTasks {
|
||||||
switch task.Type {
|
switch task.Type {
|
||||||
case "ipItemChanged":
|
case "ipItemChanged":
|
||||||
iplibrary.IPListUpdateNotify <- true
|
// 防止阻塞
|
||||||
|
select {
|
||||||
|
case iplibrary.IPListUpdateNotify <- true:
|
||||||
|
default:
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// 修改为已同步
|
// 修改为已同步
|
||||||
_, err = rpcClient.NodeTaskRPC().ReportNodeTaskDone(nodeCtx, &pb.ReportNodeTaskDoneRequest{
|
_, err = rpcClient.NodeTaskRPC().ReportNodeTaskDone(nodeCtx, &pb.ReportNodeTaskDoneRequest{
|
||||||
@@ -333,11 +346,12 @@ func (this *Node) loop() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
case "nodeVersionChanged":
|
case "nodeVersionChanged":
|
||||||
goman.New(func() {
|
if !sharedUpgradeManager.IsInstalling() {
|
||||||
sharedUpgradeManager.Start()
|
goman.New(func() {
|
||||||
})
|
sharedUpgradeManager.Start()
|
||||||
|
})
|
||||||
|
}
|
||||||
case "scriptsChanged":
|
case "scriptsChanged":
|
||||||
err = this.reloadCommonScripts()
|
err = this.reloadCommonScripts()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -386,7 +400,7 @@ func (this *Node) loop() error {
|
|||||||
}
|
}
|
||||||
if len(resp.DdosProtectionJSON) == 0 {
|
if len(resp.DdosProtectionJSON) == 0 {
|
||||||
if sharedNodeConfig != nil {
|
if sharedNodeConfig != nil {
|
||||||
sharedNodeConfig.DDOSProtection = nil
|
sharedNodeConfig.DDoSProtection = nil
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var ddosProtectionConfig = &ddosconfigs.ProtectionConfig{}
|
var ddosProtectionConfig = &ddosconfigs.ProtectionConfig{}
|
||||||
@@ -395,6 +409,10 @@ func (this *Node) loop() error {
|
|||||||
return errors.New("decode DDoS protection config failed: " + err.Error())
|
return errors.New("decode DDoS protection config failed: " + err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if sharedNodeConfig != nil {
|
||||||
|
sharedNodeConfig.DDoSProtection = ddosProtectionConfig
|
||||||
|
}
|
||||||
|
|
||||||
err = firewalls.SharedDDoSProtectionManager.Apply(ddosProtectionConfig)
|
err = firewalls.SharedDDoSProtectionManager.Apply(ddosProtectionConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// 不阻塞
|
// 不阻塞
|
||||||
@@ -601,7 +619,7 @@ func (this *Node) startSyncTimer() {
|
|||||||
// 检查集群设置
|
// 检查集群设置
|
||||||
func (this *Node) checkClusterConfig() error {
|
func (this *Node) checkClusterConfig() error {
|
||||||
configFile := Tea.ConfigFile("cluster.yaml")
|
configFile := Tea.ConfigFile("cluster.yaml")
|
||||||
data, err := ioutil.ReadFile(configFile)
|
data, err := os.ReadFile(configFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -631,11 +649,13 @@ func (this *Node) checkClusterConfig() error {
|
|||||||
if len(resp.Endpoints) == 0 {
|
if len(resp.Endpoints) == 0 {
|
||||||
resp.Endpoints = []string{}
|
resp.Endpoints = []string{}
|
||||||
}
|
}
|
||||||
apiConfig := &configs.APIConfig{
|
var apiConfig = &configs.APIConfig{
|
||||||
RPC: struct {
|
RPC: struct {
|
||||||
Endpoints []string `yaml:"endpoints"`
|
Endpoints []string `yaml:"endpoints"`
|
||||||
|
DisableUpdate bool `yaml:"disableUpdate"`
|
||||||
}{
|
}{
|
||||||
Endpoints: resp.Endpoints,
|
Endpoints: resp.Endpoints,
|
||||||
|
DisableUpdate: false,
|
||||||
},
|
},
|
||||||
NodeId: resp.UniqueId,
|
NodeId: resp.UniqueId,
|
||||||
Secret: resp.Secret,
|
Secret: resp.Secret,
|
||||||
@@ -669,7 +689,7 @@ func (this *Node) listenSock() error {
|
|||||||
if this.sock.IsListening() {
|
if this.sock.IsListening() {
|
||||||
reply, err := this.sock.Send(&gosock.Command{Code: "pid"})
|
reply, err := this.sock.Send(&gosock.Command{Code: "pid"})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return errors.New("error: the process is already running, pid: " + maps.NewMap(reply.Params).GetString("pid"))
|
return errors.New("error: the process is already running, pid: " + types.String(maps.NewMap(reply.Params).GetInt("pid")))
|
||||||
} else {
|
} else {
|
||||||
return errors.New("error: the process is already running")
|
return errors.New("error: the process is already running")
|
||||||
}
|
}
|
||||||
@@ -707,6 +727,7 @@ func (this *Node) listenSock() error {
|
|||||||
_ = this.sock.Close()
|
_ = this.sock.Close()
|
||||||
|
|
||||||
events.Notify(events.EventQuit)
|
events.Notify(events.EventQuit)
|
||||||
|
events.Notify(events.EventTerminated)
|
||||||
|
|
||||||
// 监控连接数,如果连接数为0,则退出进程
|
// 监控连接数,如果连接数为0,则退出进程
|
||||||
goman.New(func() {
|
goman.New(func() {
|
||||||
@@ -770,7 +791,8 @@ func (this *Node) listenSock() error {
|
|||||||
var m = maps.NewMap(cmd.Params)
|
var m = maps.NewMap(cmd.Params)
|
||||||
var ip = m.GetString("ip")
|
var ip = m.GetString("ip")
|
||||||
var timeSeconds = m.GetInt("timeoutSeconds")
|
var timeSeconds = m.GetInt("timeoutSeconds")
|
||||||
err := firewalls.Firewall().DropSourceIP(ip, timeSeconds)
|
var async = m.GetBool("async")
|
||||||
|
err := firewalls.Firewall().DropSourceIP(ip, timeSeconds, async)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = cmd.Reply(&gosock.Command{
|
_ = cmd.Reply(&gosock.Command{
|
||||||
Params: map[string]interface{}{
|
Params: map[string]interface{}{
|
||||||
@@ -993,7 +1015,7 @@ func (this *Node) checkDisk() {
|
|||||||
"/sys/block/vda/queue/rotational",
|
"/sys/block/vda/queue/rotational",
|
||||||
"/sys/block/sda/queue/rotational",
|
"/sys/block/sda/queue/rotational",
|
||||||
} {
|
} {
|
||||||
data, err := ioutil.ReadFile(path)
|
data, err := os.ReadFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
"github.com/iwind/TeaGo/Tea"
|
"github.com/iwind/TeaGo/Tea"
|
||||||
"github.com/iwind/TeaGo/logs"
|
"github.com/iwind/TeaGo/logs"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"syscall"
|
"syscall"
|
||||||
)
|
)
|
||||||
@@ -25,7 +24,7 @@ func (this *Node) handlePanic() {
|
|||||||
var panicFile = Tea.Root + "/logs/panic.log"
|
var panicFile = Tea.Root + "/logs/panic.log"
|
||||||
|
|
||||||
// 分析panic
|
// 分析panic
|
||||||
data, err := ioutil.ReadFile(panicFile)
|
data, err := os.ReadFile(panicFile)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
var index = bytes.Index(data, []byte("panic:"))
|
var index = bytes.Index(data, []byte("panic:"))
|
||||||
if index >= 0 {
|
if index >= 0 {
|
||||||
|
|||||||
@@ -30,10 +30,14 @@ type NodeStatusExecutor struct {
|
|||||||
cpuUpdatedTime time.Time
|
cpuUpdatedTime time.Time
|
||||||
cpuLogicalCount int
|
cpuLogicalCount int
|
||||||
cpuPhysicalCount int
|
cpuPhysicalCount int
|
||||||
|
|
||||||
|
ticker *time.Ticker
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewNodeStatusExecutor() *NodeStatusExecutor {
|
func NewNodeStatusExecutor() *NodeStatusExecutor {
|
||||||
return &NodeStatusExecutor{}
|
return &NodeStatusExecutor{
|
||||||
|
ticker: time.NewTicker(30 * time.Second),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *NodeStatusExecutor) Listen() {
|
func (this *NodeStatusExecutor) Listen() {
|
||||||
@@ -41,15 +45,12 @@ func (this *NodeStatusExecutor) Listen() {
|
|||||||
this.cpuUpdatedTime = time.Now()
|
this.cpuUpdatedTime = time.Now()
|
||||||
this.update()
|
this.update()
|
||||||
|
|
||||||
// TODO 这个时间间隔可以配置
|
|
||||||
var ticker = time.NewTicker(30 * time.Second)
|
|
||||||
|
|
||||||
events.OnKey(events.EventQuit, this, func() {
|
events.OnKey(events.EventQuit, this, func() {
|
||||||
remotelogs.Println("NODE_STATUS", "quit executor")
|
remotelogs.Println("NODE_STATUS", "quit executor")
|
||||||
ticker.Stop()
|
this.ticker.Stop()
|
||||||
})
|
})
|
||||||
|
|
||||||
for range ticker.C {
|
for range this.ticker.C {
|
||||||
this.isFirstTime = false
|
this.isFirstTime = false
|
||||||
this.update()
|
this.update()
|
||||||
}
|
}
|
||||||
@@ -68,6 +69,8 @@ func (this *NodeStatusExecutor) update() {
|
|||||||
status.BuildVersionCode = utils.VersionToLong(teaconst.Version)
|
status.BuildVersionCode = utils.VersionToLong(teaconst.Version)
|
||||||
status.OS = runtime.GOOS
|
status.OS = runtime.GOOS
|
||||||
status.Arch = runtime.GOARCH
|
status.Arch = runtime.GOARCH
|
||||||
|
exe, _ := os.Executable()
|
||||||
|
status.ExePath = exe
|
||||||
status.ConfigVersion = sharedNodeConfig.Version
|
status.ConfigVersion = sharedNodeConfig.Version
|
||||||
status.IsActive = true
|
status.IsActive = true
|
||||||
status.ConnectionCount = sharedListenerManager.TotalActiveConnections()
|
status.ConnectionCount = sharedListenerManager.TotalActiveConnections()
|
||||||
|
|||||||
@@ -8,5 +8,7 @@ type OriginState struct {
|
|||||||
CountFails int64
|
CountFails int64
|
||||||
UpdatedAt int64
|
UpdatedAt int64
|
||||||
Config *serverconfigs.OriginConfig
|
Config *serverconfigs.OriginConfig
|
||||||
|
Addr string
|
||||||
|
TLSHost string
|
||||||
ReverseProxy *serverconfigs.ReverseProxyConfig
|
ReverseProxy *serverconfigs.ReverseProxyConfig
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,10 @@ func init() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
maxOriginStates = 512 // 最多可以监控的源站状态数量
|
||||||
|
)
|
||||||
|
|
||||||
// OriginStateManager 源站状态管理
|
// OriginStateManager 源站状态管理
|
||||||
type OriginStateManager struct {
|
type OriginStateManager struct {
|
||||||
stateMap map[int64]*OriginState // originId => *OriginState
|
stateMap map[int64]*OriginState // originId => *OriginState
|
||||||
@@ -44,14 +48,6 @@ func NewOriginStateManager() *OriginStateManager {
|
|||||||
|
|
||||||
// Start 启动
|
// Start 启动
|
||||||
func (this *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() {
|
if Tea.IsTesting() {
|
||||||
this.ticker = time.NewTicker(10 * time.Second)
|
this.ticker = time.NewTicker(10 * time.Second)
|
||||||
}
|
}
|
||||||
@@ -71,7 +67,8 @@ func (this *OriginStateManager) Stop() {
|
|||||||
|
|
||||||
// Loop 单次循环检查
|
// Loop 单次循环检查
|
||||||
func (this *OriginStateManager) Loop() error {
|
func (this *OriginStateManager) Loop() error {
|
||||||
if sharedNodeConfig == nil {
|
var nodeConfig = sharedNodeConfig // 复制
|
||||||
|
if nodeConfig == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,12 +79,12 @@ func (this *OriginStateManager) Loop() error {
|
|||||||
this.locker.Lock()
|
this.locker.Lock()
|
||||||
for originId, state := range this.stateMap {
|
for originId, state := range this.stateMap {
|
||||||
// 检查Origin是否正在使用
|
// 检查Origin是否正在使用
|
||||||
config := sharedNodeConfig.FindOrigin(originId)
|
var originConfig = nodeConfig.FindOrigin(originId)
|
||||||
if config == nil || !config.IsOn {
|
if originConfig == nil || !originConfig.IsOn {
|
||||||
delete(this.stateMap, originId)
|
delete(this.stateMap, originId)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
state.Config = config
|
state.Config = originConfig
|
||||||
currentStates = append(currentStates, state)
|
currentStates = append(currentStates, state)
|
||||||
}
|
}
|
||||||
this.locker.Unlock()
|
this.locker.Unlock()
|
||||||
@@ -97,12 +94,12 @@ func (this *OriginStateManager) Loop() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var count = len(currentStates)
|
var count = len(currentStates)
|
||||||
wg := &sync.WaitGroup{}
|
var wg = &sync.WaitGroup{}
|
||||||
wg.Add(count)
|
wg.Add(count)
|
||||||
for _, state := range currentStates {
|
for _, state := range currentStates {
|
||||||
go func(state *OriginState) {
|
go func(state *OriginState) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
conn, err := OriginConnect(state.Config, "")
|
conn, _, err := OriginConnect(state.Config, 0, "", state.TLSHost)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
_ = conn.Close()
|
_ = conn.Close()
|
||||||
|
|
||||||
@@ -125,7 +122,7 @@ func (this *OriginStateManager) Loop() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fail 添加失败的源站
|
// 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 {
|
if origin == nil || origin.Id <= 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -139,6 +136,7 @@ func (this *OriginStateManager) Fail(origin *serverconfigs.OriginConfig, reverse
|
|||||||
state.Config.IsOk = true
|
state.Config.IsOk = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state.TLSHost = tlsHost
|
||||||
state.CountFails++
|
state.CountFails++
|
||||||
state.Config = origin
|
state.Config = origin
|
||||||
state.ReverseProxy = reverseProxy
|
state.ReverseProxy = reverseProxy
|
||||||
@@ -154,12 +152,17 @@ func (this *OriginStateManager) Fail(origin *serverconfigs.OriginConfig, reverse
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.stateMap[origin.Id] = &OriginState{
|
// 同时最多监控 N 个源站地址
|
||||||
CountFails: 1,
|
if len(this.stateMap) < maxOriginStates {
|
||||||
Config: origin,
|
this.stateMap[origin.Id] = &OriginState{
|
||||||
ReverseProxy: reverseProxy,
|
CountFails: 1,
|
||||||
UpdatedAt: timestamp,
|
Config: origin,
|
||||||
|
TLSHost: tlsHost,
|
||||||
|
ReverseProxy: reverseProxy,
|
||||||
|
UpdatedAt: timestamp,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
origin.IsOk = true
|
origin.IsOk = true
|
||||||
}
|
}
|
||||||
this.locker.Unlock()
|
this.locker.Unlock()
|
||||||
|
|||||||
@@ -3,47 +3,54 @@ package nodes
|
|||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"errors"
|
"errors"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
|
"github.com/iwind/TeaGo/types"
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
// OriginConnect 连接源站
|
// 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 {
|
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的连接
|
// 支持TOA的连接
|
||||||
// 这个条件很重要,如果没有传递remoteAddr,表示不使用TOA
|
// 这个条件很重要,如果没有传递remoteAddr,表示不使用TOA
|
||||||
if len(remoteAddr) > 0 {
|
if len(remoteAddr) > 0 {
|
||||||
toaConfig := sharedTOAManager.Config()
|
var toaConfig = sharedTOAManager.Config()
|
||||||
if toaConfig != nil && toaConfig.IsOn {
|
if toaConfig != nil && toaConfig.IsOn {
|
||||||
retries := 3
|
var retries = 3
|
||||||
for i := 1; i <= retries; i++ {
|
for i := 1; i <= retries; i++ {
|
||||||
port := int(toaConfig.RandLocalPort())
|
var port = int(toaConfig.RandLocalPort())
|
||||||
err := sharedTOAManager.SendMsg("add:" + strconv.Itoa(port) + ":" + remoteAddr)
|
err = sharedTOAManager.SendMsg("add:" + strconv.Itoa(port) + ":" + remoteAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
remotelogs.Error("TOA", "add failed: "+err.Error())
|
remotelogs.Error("TOA", "add failed: "+err.Error())
|
||||||
} else {
|
} else {
|
||||||
dialer := net.Dialer{
|
var dialer = net.Dialer{
|
||||||
Timeout: origin.ConnTimeoutDuration(),
|
Timeout: origin.ConnTimeoutDuration(),
|
||||||
LocalAddr: &net.TCPAddr{
|
LocalAddr: &net.TCPAddr{
|
||||||
Port: port,
|
Port: port,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
originAddr = origin.Addr.PickAddress()
|
||||||
|
|
||||||
|
// 端口跟随
|
||||||
|
if origin.FollowPort && serverPort > 0 {
|
||||||
|
originAddr = configutils.QuoteIP(origin.Addr.Host) + ":" + types.String(serverPort)
|
||||||
|
}
|
||||||
|
|
||||||
var conn net.Conn
|
var conn net.Conn
|
||||||
switch origin.Addr.Protocol {
|
switch origin.Addr.Protocol {
|
||||||
case "", serverconfigs.ProtocolTCP, serverconfigs.ProtocolHTTP:
|
case "", serverconfigs.ProtocolTCP, serverconfigs.ProtocolHTTP:
|
||||||
// TODO 支持TCP4/TCP6
|
// TODO 支持TCP4/TCP6
|
||||||
// TODO 支持指定特定网卡
|
// TODO 支持指定特定网卡
|
||||||
// TODO Addr支持端口范围,如果有多个端口时,随机一个端口使用
|
conn, err = dialer.Dial("tcp", originAddr)
|
||||||
conn, err = dialer.Dial("tcp", origin.Addr.Host+":"+origin.Addr.PortRange)
|
|
||||||
case serverconfigs.ProtocolTLS, serverconfigs.ProtocolHTTPS:
|
case serverconfigs.ProtocolTLS, serverconfigs.ProtocolHTTPS:
|
||||||
// TODO 支持TCP4/TCP6
|
// TODO 支持TCP4/TCP6
|
||||||
// TODO 支持指定特定网卡
|
// TODO 支持指定特定网卡
|
||||||
// TODO Addr支持端口范围,如果有多个端口时,随机一个端口使用
|
|
||||||
|
|
||||||
var tlsConfig = &tls.Config{
|
var tlsConfig = &tls.Config{
|
||||||
InsecureSkipVerify: true,
|
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记录
|
// TODO 需要在合适的时机删除TOA记录
|
||||||
if err == nil || i == retries {
|
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 {
|
switch origin.Addr.Protocol {
|
||||||
case "", serverconfigs.ProtocolTCP, serverconfigs.ProtocolHTTP:
|
case "", serverconfigs.ProtocolTCP, serverconfigs.ProtocolHTTP:
|
||||||
// TODO 支持TCP4/TCP6
|
// TODO 支持TCP4/TCP6
|
||||||
// TODO 支持指定特定网卡
|
// TODO 支持指定特定网卡
|
||||||
// TODO Addr支持端口范围,如果有多个端口时,随机一个端口使用
|
originConn, err = net.DialTimeout("tcp", originAddr, origin.ConnTimeoutDuration())
|
||||||
return net.DialTimeout("tcp", origin.Addr.Host+":"+origin.Addr.PortRange, origin.ConnTimeoutDuration())
|
return originConn, originAddr, err
|
||||||
case serverconfigs.ProtocolTLS, serverconfigs.ProtocolHTTPS:
|
case serverconfigs.ProtocolTLS, serverconfigs.ProtocolHTTPS:
|
||||||
// TODO 支持TCP4/TCP6
|
// TODO 支持TCP4/TCP6
|
||||||
// TODO 支持指定特定网卡
|
// TODO 支持指定特定网卡
|
||||||
// TODO Addr支持端口范围,如果有多个端口时,随机一个端口使用
|
|
||||||
|
|
||||||
var tlsConfig = &tls.Config{
|
var tlsConfig = &tls.Config{
|
||||||
InsecureSkipVerify: true,
|
InsecureSkipVerify: true,
|
||||||
@@ -95,17 +111,22 @@ func OriginConnect(origin *serverconfigs.OriginConfig, remoteAddr string) (net.C
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if len(tlsHost) > 0 {
|
||||||
return tls.Dial("tcp", origin.Addr.Host+":"+origin.Addr.PortRange, tlsConfig)
|
tlsConfig.ServerName = tlsHost
|
||||||
case serverconfigs.ProtocolUDP:
|
|
||||||
addr, err := net.ResolveUDPAddr("udp", origin.Addr.Host+":"+origin.Addr.PortRange)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
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中读取数据
|
// 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() + "'")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
"github.com/iwind/TeaGo/maps"
|
"github.com/iwind/TeaGo/maps"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"runtime"
|
"runtime"
|
||||||
@@ -104,7 +103,7 @@ func (this *SystemServiceManager) setupSystemd(params maps.Map) error {
|
|||||||
|
|
||||||
if output == "enabled" {
|
if output == "enabled" {
|
||||||
// 检查文件路径是否变化
|
// 检查文件路径是否变化
|
||||||
data, err := ioutil.ReadFile("/etc/systemd/system/" + teaconst.SystemdServiceName + ".service")
|
data, err := os.ReadFile("/etc/systemd/system/" + teaconst.SystemdServiceName + ".service")
|
||||||
if err == nil && bytes.Index(data, []byte(exe)) > 0 {
|
if err == nil && bytes.Index(data, []byte(exe)) > 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,6 +63,16 @@ func (this *SyncAPINodesTask) Stop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *SyncAPINodesTask) Loop() error {
|
func (this *SyncAPINodesTask) Loop() error {
|
||||||
|
config, err := configs.LoadAPIConfig()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 是否禁止自动升级
|
||||||
|
if config.RPC.DisableUpdate {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
var tr = trackers.Begin("SYNC_API_NODES")
|
var tr = trackers.Begin("SYNC_API_NODES")
|
||||||
defer tr.End()
|
defer tr.End()
|
||||||
|
|
||||||
@@ -76,7 +86,7 @@ func (this *SyncAPINodesTask) Loop() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
newEndpoints := []string{}
|
var newEndpoints = []string{}
|
||||||
for _, node := range resp.ApiNodes {
|
for _, node := range resp.ApiNodes {
|
||||||
if !node.IsOn {
|
if !node.IsOn {
|
||||||
continue
|
continue
|
||||||
@@ -85,16 +95,12 @@ func (this *SyncAPINodesTask) Loop() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 和现有的对比
|
// 和现有的对比
|
||||||
config, err := configs.LoadAPIConfig()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if this.isSame(newEndpoints, config.RPC.Endpoints) {
|
if this.isSame(newEndpoints, config.RPC.Endpoints) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 测试是否有API节点可用
|
// 测试是否有API节点可用
|
||||||
hasOk := this.testEndpoints(newEndpoints)
|
var hasOk = this.testEndpoints(newEndpoints)
|
||||||
if !hasOk {
|
if !hasOk {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,8 +14,10 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
"github.com/iwind/TeaGo/Tea"
|
"github.com/iwind/TeaGo/Tea"
|
||||||
stringutil "github.com/iwind/TeaGo/utils/string"
|
stringutil "github.com/iwind/TeaGo/utils/string"
|
||||||
|
"github.com/iwind/gosock/pkg/gosock"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -46,15 +48,12 @@ func (this *UpgradeManager) Start() {
|
|||||||
}
|
}
|
||||||
this.isInstalling = true
|
this.isInstalling = true
|
||||||
|
|
||||||
// 还原安装状态
|
|
||||||
defer func() {
|
|
||||||
this.isInstalling = false
|
|
||||||
}()
|
|
||||||
|
|
||||||
remotelogs.Println("UPGRADE_MANAGER", "upgrading node ...")
|
remotelogs.Println("UPGRADE_MANAGER", "upgrading node ...")
|
||||||
err := this.install()
|
err := this.install()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
remotelogs.Error("UPGRADE_MANAGER", "download failed: "+err.Error())
|
remotelogs.Error("UPGRADE_MANAGER", "download failed: "+err.Error())
|
||||||
|
|
||||||
|
this.isInstalling = false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,9 +64,16 @@ func (this *UpgradeManager) Start() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
remotelogs.Error("UPGRADE_MANAGER", err.Error())
|
remotelogs.Error("UPGRADE_MANAGER", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.isInstalling = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsInstalling 检查是否正在安装
|
||||||
|
func (this *UpgradeManager) IsInstalling() bool {
|
||||||
|
return this.isInstalling
|
||||||
|
}
|
||||||
|
|
||||||
func (this *UpgradeManager) install() error {
|
func (this *UpgradeManager) install() error {
|
||||||
// 检查是否有已下载但未安装成功的
|
// 检查是否有已下载但未安装成功的
|
||||||
if len(this.lastFile) > 0 {
|
if len(this.lastFile) > 0 {
|
||||||
@@ -83,7 +89,7 @@ func (this *UpgradeManager) install() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 创建临时文件
|
// 创建临时文件
|
||||||
dir := Tea.Root + "/tmp"
|
var dir = Tea.Root + "/tmp"
|
||||||
_, err := os.Stat(dir)
|
_, err := os.Stat(dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
@@ -98,7 +104,7 @@ func (this *UpgradeManager) install() error {
|
|||||||
|
|
||||||
remotelogs.Println("UPGRADE_MANAGER", "downloading new node ...")
|
remotelogs.Println("UPGRADE_MANAGER", "downloading new node ...")
|
||||||
|
|
||||||
path := dir + "/edge-node" + ".tmp"
|
var path = dir + "/edge-node" + ".tmp"
|
||||||
fp, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0777)
|
fp, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0777)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -203,16 +209,16 @@ func (this *UpgradeManager) unzip(zipPath string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 先改先前的可执行文件
|
// 先改先前的可执行文件
|
||||||
err := os.Rename(target+"/bin/edge-node", target+"/bin/.edge-node.old")
|
err := os.Rename(target+"/bin/"+teaconst.ProcessName, target+"/bin/."+teaconst.ProcessName+".dist")
|
||||||
hasBackup := err == nil
|
var hasBackup = err == nil
|
||||||
defer func() {
|
defer func() {
|
||||||
if !isOk && hasBackup {
|
if !isOk && hasBackup {
|
||||||
// 失败时还原
|
// 失败时还原
|
||||||
_ = os.Rename(target+"/bin/.edge-node.old", target+"/bin/edge-node")
|
_ = os.Rename(target+"/bin/."+teaconst.ProcessName+".dist", target+"/bin/"+teaconst.ProcessName)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
unzip := utils.NewUnzip(zipPath, target, "edge-node/")
|
var unzip = utils.NewUnzip(zipPath, target, "edge-node/")
|
||||||
err = unzip.Run()
|
err = unzip.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -225,6 +231,9 @@ func (this *UpgradeManager) unzip(zipPath string) error {
|
|||||||
|
|
||||||
// 重启
|
// 重启
|
||||||
func (this *UpgradeManager) restart() error {
|
func (this *UpgradeManager) restart() error {
|
||||||
|
// 关闭当前sock,防止无法重启
|
||||||
|
_ = gosock.NewTmpSock(teaconst.ProcessName).Close()
|
||||||
|
|
||||||
// 重新启动
|
// 重新启动
|
||||||
if DaemonIsOn && DaemonPid == os.Getppid() {
|
if DaemonIsOn && DaemonPid == os.Getppid() {
|
||||||
utils.Exit() // TODO 试着更优雅重启
|
utils.Exit() // TODO 试着更优雅重启
|
||||||
@@ -241,7 +250,9 @@ func (this *UpgradeManager) restart() error {
|
|||||||
events.Notify(events.EventTerminated)
|
events.Notify(events.EventTerminated)
|
||||||
|
|
||||||
// 启动
|
// 启动
|
||||||
cmd := exec.Command(exe, "start")
|
exe = filepath.Dir(exe) + "/" + teaconst.ProcessName
|
||||||
|
|
||||||
|
var cmd = exec.Command(exe, "start")
|
||||||
err = cmd.Start()
|
err = cmd.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -136,6 +136,10 @@ func (this *Regexp) Match(s []byte) bool {
|
|||||||
return this.rawRegexp.Match(s)
|
return this.rawRegexp.Match(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *Regexp) FindStringSubmatch(s string) []string {
|
||||||
|
return this.rawRegexp.FindStringSubmatch(s)
|
||||||
|
}
|
||||||
|
|
||||||
// ParseKeywords 提取表达式中的关键词
|
// ParseKeywords 提取表达式中的关键词
|
||||||
func (this *Regexp) ParseKeywords(exp string) (keywords []string) {
|
func (this *Regexp) ParseKeywords(exp string) (keywords []string) {
|
||||||
if len(exp) == 0 {
|
if len(exp) == 0 {
|
||||||
|
|||||||
@@ -109,6 +109,8 @@ func TestRegexp_ParseKeywords3(t *testing.T) {
|
|||||||
|
|
||||||
func BenchmarkRegexp_MatchString(b *testing.B) {
|
func BenchmarkRegexp_MatchString(b *testing.B) {
|
||||||
var r = re.MustCompile("(?i)(onmouseover|onmousemove|onmousedown|onmouseup|onerror|onload|onclick|ondblclick|onkeydown|onkeyup|onkeypress)(\\s|%09|%0A|(\\+|%20))*(=|%3D)")
|
var r = re.MustCompile("(?i)(onmouseover|onmousemove|onmousedown|onmouseup|onerror|onload|onclick|ondblclick|onkeydown|onkeyup|onkeypress)(\\s|%09|%0A|(\\+|%20))*(=|%3D)")
|
||||||
|
b.ResetTimer()
|
||||||
|
|
||||||
//b.Log("keywords:", r.Keywords())
|
//b.Log("keywords:", r.Keywords())
|
||||||
for i := 0; i < b.N; i++ {
|
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")
|
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")
|
||||||
@@ -117,6 +119,8 @@ func BenchmarkRegexp_MatchString(b *testing.B) {
|
|||||||
|
|
||||||
func BenchmarkRegexp_MatchString2(b *testing.B) {
|
func BenchmarkRegexp_MatchString2(b *testing.B) {
|
||||||
var r = regexp.MustCompile("(?i)(onmouseover|onmousemove|onmousedown|onmouseup|onerror|onload|onclick|ondblclick|onkeydown|onkeyup|onkeypress)(\\s|%09|%0A|(\\+|%20))*(=|%3D)")
|
var r = regexp.MustCompile("(?i)(onmouseover|onmousemove|onmousedown|onmouseup|onerror|onload|onclick|ondblclick|onkeydown|onkeyup|onkeypress)(\\s|%09|%0A|(\\+|%20))*(=|%3D)")
|
||||||
|
b.ResetTimer()
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
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")
|
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")
|
||||||
}
|
}
|
||||||
@@ -125,11 +129,36 @@ func BenchmarkRegexp_MatchString2(b *testing.B) {
|
|||||||
func BenchmarkRegexp_MatchString_CaseSensitive(b *testing.B) {
|
func BenchmarkRegexp_MatchString_CaseSensitive(b *testing.B) {
|
||||||
var r = re.MustCompile("(abc|def|ghi)")
|
var r = re.MustCompile("(abc|def|ghi)")
|
||||||
b.Log("keywords:", r.Keywords())
|
b.Log("keywords:", r.Keywords())
|
||||||
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
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")
|
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 {
|
func testCompareStrings(s1 []string, s2 []string) bool {
|
||||||
if len(s1) != len(s2) {
|
if len(s1) != len(s2) {
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -123,6 +123,10 @@ func (this *RPCClient) ServerDailyStatRPC() pb.ServerDailyStatServiceClient {
|
|||||||
return pb.NewServerDailyStatServiceClient(this.pickConn())
|
return pb.NewServerDailyStatServiceClient(this.pickConn())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *RPCClient) ServerBandwidthStatRPC() pb.ServerBandwidthStatServiceClient {
|
||||||
|
return pb.NewServerBandwidthStatServiceClient(this.pickConn())
|
||||||
|
}
|
||||||
|
|
||||||
func (this *RPCClient) MetricStatRPC() pb.MetricStatServiceClient {
|
func (this *RPCClient) MetricStatRPC() pb.MetricStatServiceClient {
|
||||||
return pb.NewMetricStatServiceClient(this.pickConn())
|
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
|
if remoteAddr[0] == '[' { // 排除IPv6
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
index := strings.Index(remoteAddr, ":")
|
var index = strings.Index(remoteAddr, ":")
|
||||||
var ip string
|
var ip string
|
||||||
if index < 0 {
|
if index < 0 {
|
||||||
ip = remoteAddr
|
ip = remoteAddr
|
||||||
@@ -177,18 +177,18 @@ func (this *HTTPRequestStatManager) AddFirewallRuleGroupId(serverId int64, firew
|
|||||||
|
|
||||||
// Loop 单个循环
|
// Loop 单个循环
|
||||||
func (this *HTTPRequestStatManager) Loop() error {
|
func (this *HTTPRequestStatManager) Loop() error {
|
||||||
timeout := time.NewTimer(10 * time.Minute) // 执行的最大时间
|
var timeout = time.NewTimer(10 * time.Minute) // 执行的最大时间
|
||||||
Loop:
|
Loop:
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case ipString := <-this.ipChan:
|
case ipString := <-this.ipChan:
|
||||||
// serverId@ip@bytes@isAttack
|
// serverId@ip@bytes@isAttack
|
||||||
pieces := strings.Split(ipString, "@")
|
var pieces = strings.Split(ipString, "@")
|
||||||
if len(pieces) < 4 {
|
if len(pieces) < 4 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
serverId := pieces[0]
|
var serverId = pieces[0]
|
||||||
ip := pieces[1]
|
var ip = pieces[1]
|
||||||
|
|
||||||
if iplibrary.SharedLibrary != nil {
|
if iplibrary.SharedLibrary != nil {
|
||||||
result, err := iplibrary.SharedLibrary.Lookup(ip)
|
result, err := iplibrary.SharedLibrary.Lookup(ip)
|
||||||
@@ -216,12 +216,12 @@ Loop:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
case userAgentString := <-this.userAgentChan:
|
case userAgentString := <-this.userAgentChan:
|
||||||
atIndex := strings.Index(userAgentString, "@")
|
var atIndex = strings.Index(userAgentString, "@")
|
||||||
if atIndex < 0 {
|
if atIndex < 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
serverId := userAgentString[:atIndex]
|
var serverId = userAgentString[:atIndex]
|
||||||
userAgent := userAgentString[atIndex+1:]
|
var userAgent = userAgentString[atIndex+1:]
|
||||||
|
|
||||||
var result = SharedUserAgentParser.Parse(userAgent)
|
var result = SharedUserAgentParser.Parse(userAgent)
|
||||||
var osInfo = result.OS
|
var osInfo = result.OS
|
||||||
@@ -264,12 +264,12 @@ func (this *HTTPRequestStatManager) Upload() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 月份相关
|
// 月份相关
|
||||||
pbCities := []*pb.UploadServerHTTPRequestStatRequest_RegionCity{}
|
var pbCities = []*pb.UploadServerHTTPRequestStatRequest_RegionCity{}
|
||||||
pbProviders := []*pb.UploadServerHTTPRequestStatRequest_RegionProvider{}
|
var pbProviders = []*pb.UploadServerHTTPRequestStatRequest_RegionProvider{}
|
||||||
pbSystems := []*pb.UploadServerHTTPRequestStatRequest_System{}
|
var pbSystems = []*pb.UploadServerHTTPRequestStatRequest_System{}
|
||||||
pbBrowsers := []*pb.UploadServerHTTPRequestStatRequest_Browser{}
|
var pbBrowsers = []*pb.UploadServerHTTPRequestStatRequest_Browser{}
|
||||||
for k, stat := range this.cityMap {
|
for k, stat := range this.cityMap {
|
||||||
pieces := strings.SplitN(k, "@", 4)
|
var pieces = strings.SplitN(k, "@", 4)
|
||||||
pbCities = append(pbCities, &pb.UploadServerHTTPRequestStatRequest_RegionCity{
|
pbCities = append(pbCities, &pb.UploadServerHTTPRequestStatRequest_RegionCity{
|
||||||
ServerId: types.Int64(pieces[0]),
|
ServerId: types.Int64(pieces[0]),
|
||||||
CountryName: pieces[1],
|
CountryName: pieces[1],
|
||||||
@@ -282,7 +282,7 @@ func (this *HTTPRequestStatManager) Upload() error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
for k, count := range this.providerMap {
|
for k, count := range this.providerMap {
|
||||||
pieces := strings.SplitN(k, "@", 2)
|
var pieces = strings.SplitN(k, "@", 2)
|
||||||
pbProviders = append(pbProviders, &pb.UploadServerHTTPRequestStatRequest_RegionProvider{
|
pbProviders = append(pbProviders, &pb.UploadServerHTTPRequestStatRequest_RegionProvider{
|
||||||
ServerId: types.Int64(pieces[0]),
|
ServerId: types.Int64(pieces[0]),
|
||||||
Name: pieces[1],
|
Name: pieces[1],
|
||||||
@@ -290,7 +290,7 @@ func (this *HTTPRequestStatManager) Upload() error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
for k, count := range this.systemMap {
|
for k, count := range this.systemMap {
|
||||||
pieces := strings.SplitN(k, "@", 3)
|
var pieces = strings.SplitN(k, "@", 3)
|
||||||
pbSystems = append(pbSystems, &pb.UploadServerHTTPRequestStatRequest_System{
|
pbSystems = append(pbSystems, &pb.UploadServerHTTPRequestStatRequest_System{
|
||||||
ServerId: types.Int64(pieces[0]),
|
ServerId: types.Int64(pieces[0]),
|
||||||
Name: pieces[1],
|
Name: pieces[1],
|
||||||
@@ -299,7 +299,7 @@ func (this *HTTPRequestStatManager) Upload() error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
for k, count := range this.browserMap {
|
for k, count := range this.browserMap {
|
||||||
pieces := strings.SplitN(k, "@", 3)
|
var pieces = strings.SplitN(k, "@", 3)
|
||||||
pbBrowsers = append(pbBrowsers, &pb.UploadServerHTTPRequestStatRequest_Browser{
|
pbBrowsers = append(pbBrowsers, &pb.UploadServerHTTPRequestStatRequest_Browser{
|
||||||
ServerId: types.Int64(pieces[0]),
|
ServerId: types.Int64(pieces[0]),
|
||||||
Name: pieces[1],
|
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 {
|
for k, count := range this.dailyFirewallRuleGroupMap {
|
||||||
pieces := strings.SplitN(k, "@", 3)
|
var pieces = strings.SplitN(k, "@", 3)
|
||||||
pbFirewallRuleGroups = append(pbFirewallRuleGroups, &pb.UploadServerHTTPRequestStatRequest_HTTPFirewallRuleGroup{
|
pbFirewallRuleGroups = append(pbFirewallRuleGroups, &pb.UploadServerHTTPRequestStatRequest_HTTPFirewallRuleGroup{
|
||||||
ServerId: types.Int64(pieces[0]),
|
ServerId: types.Int64(pieces[0]),
|
||||||
HttpFirewallRuleGroupId: types.Int64(pieces[1]),
|
HttpFirewallRuleGroupId: types.Int64(pieces[1]),
|
||||||
@@ -343,9 +343,11 @@ func (this *HTTPRequestStatManager) Upload() error {
|
|||||||
if strings.Contains(err.Error(), "string field contains invalid UTF-8") {
|
if strings.Contains(err.Error(), "string field contains invalid UTF-8") {
|
||||||
for _, system := range pbSystems {
|
for _, system := range pbSystems {
|
||||||
system.Name = utils.ToValidUTF8string(system.Name)
|
system.Name = utils.ToValidUTF8string(system.Name)
|
||||||
|
system.Version = utils.ToValidUTF8string(system.Version)
|
||||||
}
|
}
|
||||||
for _, browser := range pbBrowsers {
|
for _, browser := range pbBrowsers {
|
||||||
browser.Name = utils.ToValidUTF8string(browser.Name)
|
browser.Name = utils.ToValidUTF8string(browser.Name)
|
||||||
|
browser.Version = utils.ToValidUTF8string(browser.Version)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 再次尝试
|
// 再次尝试
|
||||||
|
|||||||
@@ -95,6 +95,10 @@ func (this *TrafficStatManager) Start(configFunc func() *nodeconfigs.NodeConfig)
|
|||||||
|
|
||||||
// Add 添加流量
|
// 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) {
|
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 {
|
if bytes == 0 && countRequests == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -102,7 +106,7 @@ func (this *TrafficStatManager) Add(serverId int64, domain string, bytes int64,
|
|||||||
this.totalRequests++
|
this.totalRequests++
|
||||||
|
|
||||||
var timestamp = utils.FloorUnixTime(300)
|
var timestamp = utils.FloorUnixTime(300)
|
||||||
key := strconv.FormatInt(timestamp, 10) + strconv.FormatInt(serverId, 10)
|
var key = strconv.FormatInt(timestamp, 10) + strconv.FormatInt(serverId, 10)
|
||||||
this.locker.Lock()
|
this.locker.Lock()
|
||||||
|
|
||||||
// 总的流量
|
// 总的流量
|
||||||
@@ -139,7 +143,7 @@ func (this *TrafficStatManager) Add(serverId int64, domain string, bytes int64,
|
|||||||
|
|
||||||
// Upload 上传流量
|
// Upload 上传流量
|
||||||
func (this *TrafficStatManager) Upload() error {
|
func (this *TrafficStatManager) Upload() error {
|
||||||
config := this.configFunc()
|
var config = this.configFunc()
|
||||||
if config == nil {
|
if config == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -150,8 +154,8 @@ func (this *TrafficStatManager) Upload() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.locker.Lock()
|
this.locker.Lock()
|
||||||
itemMap := this.itemMap
|
var itemMap = this.itemMap
|
||||||
domainMap := this.domainsMap
|
var domainMap = this.domainsMap
|
||||||
this.itemMap = map[string]*TrafficItem{}
|
this.itemMap = map[string]*TrafficItem{}
|
||||||
this.domainsMap = map[string]*TrafficItem{}
|
this.domainsMap = map[string]*TrafficItem{}
|
||||||
this.locker.Unlock()
|
this.locker.Unlock()
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user