Compare commits
113 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9f469f5b77 | ||
|
|
5bc4f5b359 | ||
|
|
494ff5b5bb | ||
|
|
bac0060a74 | ||
|
|
062ca1cfcd | ||
|
|
8a4373e984 | ||
|
|
99670e46a5 | ||
|
|
64642c6680 | ||
|
|
f7a7b50eda | ||
|
|
f5265f1832 | ||
|
|
2d6f7522fc | ||
|
|
cfb15e764d | ||
|
|
406d8de999 | ||
|
|
2ca79953b9 | ||
|
|
d067cd8437 | ||
|
|
e4937685bc | ||
|
|
6ac2343aa7 | ||
|
|
1d80bc640d | ||
|
|
b19c431b89 | ||
|
|
a1e1b5ef98 | ||
|
|
e93275ac5c | ||
|
|
a6059ab070 | ||
|
|
5b0f94f317 | ||
|
|
0b4ac55aa6 | ||
|
|
9080c78b13 | ||
|
|
aa7d67e387 | ||
|
|
405b3615fe | ||
|
|
7534af09ed | ||
|
|
cd3bf0cad4 | ||
|
|
41ebdfb7d2 | ||
|
|
fa7d4963cb | ||
|
|
627d9721b3 | ||
|
|
8c3cd53dc3 | ||
|
|
5995be8489 | ||
|
|
eabaa84252 | ||
|
|
40e137e69e | ||
|
|
6f51fe52f8 | ||
|
|
dd4071e7dc | ||
|
|
7e5b3254eb | ||
|
|
ad1947379d | ||
|
|
2ff2afca36 | ||
|
|
9f36678d75 | ||
|
|
288074c8b3 | ||
|
|
0a290251cd | ||
|
|
edf98f1889 | ||
|
|
02c3d24038 | ||
|
|
2a2f4ff7b1 | ||
|
|
d416a2186c | ||
|
|
94b9c23323 | ||
|
|
70d8507c4b | ||
|
|
2eee314ec8 | ||
|
|
93521963b1 | ||
|
|
f456b6bc6b | ||
|
|
d30e2b2cc8 | ||
|
|
25bf4ab55a | ||
|
|
9536c49a8f | ||
|
|
3f19d88246 | ||
|
|
9931d057a9 | ||
|
|
c61ae6c8ba | ||
|
|
249dad3e97 | ||
|
|
f9fbd23a77 | ||
|
|
22eb143dee | ||
|
|
075c11a3cf | ||
|
|
f4258ed00e | ||
|
|
8ac115f865 | ||
|
|
53f109861c | ||
|
|
f034a1cfb3 | ||
|
|
ae74114fca | ||
|
|
cdfc37ac14 | ||
|
|
362a4d9ef4 | ||
|
|
7653c16586 | ||
|
|
f1a4a7ebc6 | ||
|
|
0f0436c7a8 | ||
|
|
cc881c070c | ||
|
|
10db4e3ccd | ||
|
|
a2a33a65e8 | ||
|
|
5a4162987c | ||
|
|
b46744cb13 | ||
|
|
8b7845fd15 | ||
|
|
a129178abc | ||
|
|
02875374aa | ||
|
|
8859625ce7 | ||
|
|
6dccd0ad46 | ||
|
|
18b76013b9 | ||
|
|
fa04c041df | ||
|
|
c8142741ff | ||
|
|
547874aa43 | ||
|
|
45c6b2ddac | ||
|
|
eb145393ab | ||
|
|
d767ca177a | ||
|
|
c8fc2815b9 | ||
|
|
3b2ba1aad6 | ||
|
|
33bb06fbc3 | ||
|
|
3793d684fa | ||
|
|
2390a3ef61 | ||
|
|
ffda81715f | ||
|
|
f991700031 | ||
|
|
e1ba6a90ff | ||
|
|
354161037b | ||
|
|
00d28df3ee | ||
|
|
23abed0949 | ||
|
|
2acf01dcb7 | ||
|
|
470c6a8b0e | ||
|
|
efc2810d1d | ||
|
|
de2374577f | ||
|
|
2a1f949c13 | ||
|
|
959e274063 | ||
|
|
b6a2bd37b1 | ||
|
|
3e60c9913a | ||
|
|
fd7f3f4029 | ||
|
|
2705a5d444 | ||
|
|
556055cfcb | ||
|
|
67a0d06944 |
74
.golangci.yaml
Normal file
74
.golangci.yaml
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
# https://golangci-lint.run/usage/configuration/
|
||||||
|
|
||||||
|
linters:
|
||||||
|
enable-all: true
|
||||||
|
disable:
|
||||||
|
- ifshort
|
||||||
|
- exhaustivestruct
|
||||||
|
- golint
|
||||||
|
- nosnakecase
|
||||||
|
- scopelint
|
||||||
|
- varcheck
|
||||||
|
- structcheck
|
||||||
|
- interfacer
|
||||||
|
- maligned
|
||||||
|
- deadcode
|
||||||
|
- dogsled
|
||||||
|
- wrapcheck
|
||||||
|
- wastedassign
|
||||||
|
- varnamelen
|
||||||
|
- testpackage
|
||||||
|
- thelper
|
||||||
|
- nilerr
|
||||||
|
- sqlclosecheck
|
||||||
|
- paralleltest
|
||||||
|
- nonamedreturns
|
||||||
|
- nlreturn
|
||||||
|
- nakedret
|
||||||
|
- ireturn
|
||||||
|
- interfacebloat
|
||||||
|
- gosmopolitan
|
||||||
|
- gomnd
|
||||||
|
- goerr113
|
||||||
|
- gochecknoglobals
|
||||||
|
- exhaustruct
|
||||||
|
- errorlint
|
||||||
|
- depguard
|
||||||
|
- exhaustive
|
||||||
|
- containedctx
|
||||||
|
- wsl
|
||||||
|
- cyclop
|
||||||
|
- dupword
|
||||||
|
- errchkjson
|
||||||
|
- contextcheck
|
||||||
|
- tagalign
|
||||||
|
- dupl
|
||||||
|
- forbidigo
|
||||||
|
- funlen
|
||||||
|
- goconst
|
||||||
|
- godox
|
||||||
|
- gosec
|
||||||
|
- lll
|
||||||
|
- nestif
|
||||||
|
- revive
|
||||||
|
- unparam
|
||||||
|
- stylecheck
|
||||||
|
- gocritic
|
||||||
|
- gofumpt
|
||||||
|
- gomoddirectives
|
||||||
|
- godot
|
||||||
|
- gofmt
|
||||||
|
- gocognit
|
||||||
|
- mirror
|
||||||
|
- gocyclo
|
||||||
|
- gochecknoinits
|
||||||
|
- gci
|
||||||
|
- maintidx
|
||||||
|
- prealloc
|
||||||
|
- goimports
|
||||||
|
- errname
|
||||||
|
- musttag
|
||||||
|
- forcetypeassert
|
||||||
|
- whitespace
|
||||||
|
- noctx
|
||||||
|
- rowserrcheck
|
||||||
@@ -6,8 +6,10 @@ function build() {
|
|||||||
VERSION=$(lookup-version "$ROOT"/../internal/const/const.go)
|
VERSION=$(lookup-version "$ROOT"/../internal/const/const.go)
|
||||||
DIST=$ROOT/"../dist/${NAME}"
|
DIST=$ROOT/"../dist/${NAME}"
|
||||||
MUSL_DIR="/usr/local/opt/musl-cross/bin"
|
MUSL_DIR="/usr/local/opt/musl-cross/bin"
|
||||||
GCC_X86_64_DIR="/usr/local/Cellar/x86_64-unknown-linux-gnu/10.3.0/bin"
|
|
||||||
GCC_ARM64_DIR="/usr/local/Cellar/aarch64-unknown-linux-gnu/10.3.0/bin"
|
# for macOS users: precompiled gcc can be downloaded from https://github.com/messense/homebrew-macos-cross-toolchains
|
||||||
|
GCC_X86_64_DIR="/usr/local/gcc/x86_64-unknown-linux-gnu/bin"
|
||||||
|
GCC_ARM64_DIR="//usr/local/gcc/aarch64-unknown-linux-gnu/bin"
|
||||||
|
|
||||||
OS=${1}
|
OS=${1}
|
||||||
ARCH=${2}
|
ARCH=${2}
|
||||||
@@ -49,15 +51,19 @@ function build() {
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cp "$ROOT"/configs/api.template.yaml "$DIST"/configs
|
cp "$ROOT"/configs/api_node.template.yaml "$DIST"/configs
|
||||||
cp "$ROOT"/configs/cluster.template.yaml "$DIST"/configs
|
cp "$ROOT"/configs/cluster.template.yaml "$DIST"/configs
|
||||||
cp -R "$ROOT"/www "$DIST"/
|
cp -R "$ROOT"/www "$DIST"/
|
||||||
cp -R "$ROOT"/pages "$DIST"/
|
cp -R "$ROOT"/pages "$DIST"/
|
||||||
|
|
||||||
# we support TOA on linux/amd64 only
|
# we support TOA on linux only
|
||||||
if [ "$OS" == "linux" ] && [ "$ARCH" == "amd64" ]
|
if [ "$OS" == "linux" ] && [ -f "${ROOT}/edge-toa/edge-toa-${ARCH}" ]
|
||||||
then
|
then
|
||||||
cp -R "$ROOT"/edge-toa "$DIST"
|
if [ ! -d "$DIST/edge-toa" ]
|
||||||
|
then
|
||||||
|
mkdir "$DIST/edge-toa"
|
||||||
|
fi
|
||||||
|
cp "${ROOT}/edge-toa/edge-toa-${ARCH}" "$DIST/edge-toa/edge-toa"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "building ..."
|
echo "building ..."
|
||||||
@@ -120,6 +126,11 @@ function build() {
|
|||||||
env GOOS="${OS}" GOARCH="${ARCH}" CGO_ENABLED=1 go build -trimpath -tags $BUILD_TAG -o "$DIST"/bin/${NAME} -ldflags="-s -w" "$ROOT"/../cmd/edge-node/main.go
|
env GOOS="${OS}" GOARCH="${ARCH}" CGO_ENABLED=1 go build -trimpath -tags $BUILD_TAG -o "$DIST"/bin/${NAME} -ldflags="-s -w" "$ROOT"/../cmd/edge-node/main.go
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ ! -f "${DIST}/bin/${NAME}" ]; then
|
||||||
|
echo "build failed!"
|
||||||
|
exit
|
||||||
|
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
|
||||||
|
|||||||
2
build/configs/.gitignore
vendored
2
build/configs/.gitignore
vendored
@@ -1,4 +1,6 @@
|
|||||||
node.json
|
node.json
|
||||||
api.yaml
|
api.yaml
|
||||||
|
api_node.yaml
|
||||||
cluster.yaml
|
cluster.yaml
|
||||||
|
api_cluster.yaml
|
||||||
*.cache
|
*.cache
|
||||||
@@ -1,2 +1,2 @@
|
|||||||
* `api.template.yaml` - API相关配置模板
|
* `api_node.template.yaml` - API相关配置模板
|
||||||
* `cluster.template.yaml` - 通过集群自动接入节点模板
|
* `cluster.template.yaml` - 通过集群自动接入节点模板
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
rpc:
|
|
||||||
endpoints: [ "" ]
|
|
||||||
nodeId: ""
|
|
||||||
secret: ""
|
|
||||||
3
build/configs/api_node.template.yaml
Normal file
3
build/configs/api_node.template.yaml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
rpc.endpoints: [ "" ]
|
||||||
|
nodeId: ""
|
||||||
|
secret: ""
|
||||||
Binary file not shown.
@@ -5,8 +5,12 @@ import (
|
|||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/apps"
|
"github.com/TeaOSLab/EdgeNode/internal/apps"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/configs"
|
||||||
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/nodes"
|
"github.com/TeaOSLab/EdgeNode/internal/nodes"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
|
fsutils "github.com/TeaOSLab/EdgeNode/internal/utils/fs"
|
||||||
|
"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"
|
||||||
"github.com/iwind/TeaGo/maps"
|
"github.com/iwind/TeaGo/maps"
|
||||||
@@ -16,17 +20,90 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
_ "net/http/pprof"
|
_ "net/http/pprof"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var 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|uninstall]").
|
||||||
Usage(teaconst.ProcessName + " [trackers|goman|conns|gc]").
|
Usage(teaconst.ProcessName + " [trackers|goman|conns|gc|bandwidth|disk|cache.garbage]").
|
||||||
Usage(teaconst.ProcessName + " [ip.drop|ip.reject|ip.remove|ip.close] IP")
|
Usage(teaconst.ProcessName + " [ip.drop|ip.reject|ip.remove|ip.close] IP")
|
||||||
|
|
||||||
|
app.On("start:before", func() {
|
||||||
|
// validate config
|
||||||
|
_, err := configs.LoadAPIConfig()
|
||||||
|
if err != nil {
|
||||||
|
// validate cluster config
|
||||||
|
_, clusterErr := configs.LoadClusterConfig()
|
||||||
|
if clusterErr != nil { // fail again
|
||||||
|
fmt.Println("[ERROR]start failed: load api config from '" + Tea.ConfigFile(configs.ConfigFileName) + "' failed: " + err.Error())
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
app.On("uninstall", func() {
|
||||||
|
// service
|
||||||
|
fmt.Println("Uninstall service ...")
|
||||||
|
var manager = utils.NewServiceManager(teaconst.ProcessName, teaconst.ProductName)
|
||||||
|
go func() {
|
||||||
|
_ = manager.Uninstall()
|
||||||
|
}()
|
||||||
|
|
||||||
|
// stop
|
||||||
|
fmt.Println("Stopping ...")
|
||||||
|
_, _ = gosock.NewTmpSock(teaconst.ProcessName).SendTimeout(&gosock.Command{Code: "stop"}, 1*time.Second)
|
||||||
|
|
||||||
|
// delete files
|
||||||
|
var exe, _ = os.Executable()
|
||||||
|
if len(exe) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var dir = filepath.Dir(filepath.Dir(exe)) // ROOT / bin / exe
|
||||||
|
|
||||||
|
// verify dir
|
||||||
|
{
|
||||||
|
fmt.Println("Checking '" + dir + "' ...")
|
||||||
|
for _, subDir := range []string{"bin/" + filepath.Base(exe), "configs", "logs"} {
|
||||||
|
_, err := os.Stat(dir + "/" + subDir)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("[ERROR]program directory structure has been broken, please remove it manually.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("Removing '" + dir + "' ...")
|
||||||
|
err := os.RemoveAll(dir)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("[ERROR]remove failed: " + err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// delete symbolic links
|
||||||
|
fmt.Println("Removing symbolic links ...")
|
||||||
|
_ = os.Remove("/usr/bin/" + teaconst.ProcessName)
|
||||||
|
_ = os.Remove("/var/log/" + teaconst.ProcessName)
|
||||||
|
|
||||||
|
// delete configs
|
||||||
|
// nothing to delete for EdgeNode
|
||||||
|
|
||||||
|
// delete sock
|
||||||
|
fmt.Println("Removing temporary files ...")
|
||||||
|
var tempDir = os.TempDir()
|
||||||
|
_ = os.Remove(tempDir + "/" + teaconst.ProcessName + ".sock")
|
||||||
|
_ = os.Remove(tempDir + "/" + teaconst.AccessLogSockName)
|
||||||
|
|
||||||
|
// cache ...
|
||||||
|
fmt.Println("Please delete cache directories by yourself.")
|
||||||
|
|
||||||
|
// done
|
||||||
|
fmt.Println("[DONE]")
|
||||||
|
})
|
||||||
app.On("test", func() {
|
app.On("test", func() {
|
||||||
err := nodes.NewNode().Test()
|
err := nodes.NewNode().Test()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -365,6 +442,68 @@ func main() {
|
|||||||
}
|
}
|
||||||
fmt.Println(string(statsJSON))
|
fmt.Println(string(statsJSON))
|
||||||
})
|
})
|
||||||
|
app.On("disk", func() {
|
||||||
|
var args = os.Args[2:]
|
||||||
|
if len(args) > 0 {
|
||||||
|
switch args[0] {
|
||||||
|
case "speed":
|
||||||
|
speedMB, isFast, err := fsutils.CheckDiskIsFast()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("[ERROR]" + err.Error())
|
||||||
|
} else {
|
||||||
|
fmt.Printf("Speed: %.0fMB/s\n", speedMB)
|
||||||
|
if isFast {
|
||||||
|
fmt.Println("IsFast: true")
|
||||||
|
} else {
|
||||||
|
fmt.Println("IsFast: false")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
fmt.Println("Usage: edge-node disk [speed]")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Println("Usage: edge-node disk [speed]")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
app.On("cache.garbage", func() {
|
||||||
|
fmt.Println("scanning ...")
|
||||||
|
|
||||||
|
var shouldDelete bool
|
||||||
|
for _, arg := range os.Args {
|
||||||
|
if strings.TrimLeft(arg, "-") == "delete" {
|
||||||
|
shouldDelete = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var sock = gosock.NewTmpSock(teaconst.ProcessName)
|
||||||
|
reply, err := sock.Send(&gosock.Command{
|
||||||
|
Code: "cache.garbage",
|
||||||
|
Params: map[string]any{"delete": shouldDelete},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("[ERROR]" + err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var params = maps.NewMap(reply.Params)
|
||||||
|
if params.GetBool("isOk") {
|
||||||
|
var count = params.GetInt("count")
|
||||||
|
fmt.Println("found", count, "bad caches")
|
||||||
|
|
||||||
|
if count > 0 {
|
||||||
|
fmt.Println("======")
|
||||||
|
var sampleFiles = params.GetSlice("sampleFiles")
|
||||||
|
for _, file := range sampleFiles {
|
||||||
|
fmt.Println(types.String(file))
|
||||||
|
}
|
||||||
|
if count > len(sampleFiles) {
|
||||||
|
fmt.Println("... more files")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Println("[ERROR]" + params.GetString("error"))
|
||||||
|
}
|
||||||
|
})
|
||||||
app.Run(func() {
|
app.Run(func() {
|
||||||
var node = nodes.NewNode()
|
var node = nodes.NewNode()
|
||||||
node.Start()
|
node.Start()
|
||||||
|
|||||||
30
go.mod
30
go.mod
@@ -13,32 +13,33 @@ require (
|
|||||||
github.com/aliyun/aliyun-oss-go-sdk v2.2.7+incompatible
|
github.com/aliyun/aliyun-oss-go-sdk v2.2.7+incompatible
|
||||||
github.com/andybalholm/brotli v1.0.5
|
github.com/andybalholm/brotli v1.0.5
|
||||||
github.com/aws/aws-sdk-go v1.44.279
|
github.com/aws/aws-sdk-go v1.44.279
|
||||||
|
github.com/baidubce/bce-sdk-go v0.9.153
|
||||||
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/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f
|
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f
|
||||||
github.com/fsnotify/fsnotify v1.6.0
|
github.com/fsnotify/fsnotify v1.6.0
|
||||||
github.com/go-redis/redis/v8 v8.11.5
|
github.com/go-redis/redis/v8 v8.11.5
|
||||||
github.com/golang/protobuf v1.5.3
|
|
||||||
github.com/google/nftables v0.1.0
|
github.com/google/nftables v0.1.0
|
||||||
github.com/huaweicloud/huaweicloud-sdk-go-obs v3.23.4+incompatible
|
github.com/huaweicloud/huaweicloud-sdk-go-obs v3.23.4+incompatible
|
||||||
github.com/iwind/TeaGo v0.0.0-20230630104525-161f0b32996d
|
github.com/iwind/TeaGo v0.0.0-20230630104525-161f0b32996d
|
||||||
github.com/iwind/gofcgi v0.0.0-20210528023741-a92711d45f11
|
github.com/iwind/gofcgi v0.0.0-20210528023741-a92711d45f11
|
||||||
github.com/iwind/gosock v0.0.0-20211103081026-ee4652210ca4
|
github.com/iwind/gosock v0.0.0-20211103081026-ee4652210ca4
|
||||||
github.com/iwind/gowebp v0.0.0-20230615040911-5013dbb9d508
|
github.com/iwind/gowebp v0.0.0-20230911074406-2e4e7fd0b59f
|
||||||
github.com/klauspost/compress v1.16.5
|
github.com/klauspost/compress v1.16.5
|
||||||
github.com/mattn/go-sqlite3 v1.14.9
|
github.com/mattn/go-sqlite3 v1.14.17
|
||||||
github.com/mdlayher/netlink v1.7.1
|
github.com/mdlayher/netlink v1.7.1
|
||||||
github.com/miekg/dns v1.1.43
|
github.com/miekg/dns v1.1.43
|
||||||
github.com/mssola/useragent v1.0.0
|
github.com/mssola/useragent v1.0.0
|
||||||
github.com/pires/go-proxyproto v0.6.1
|
github.com/pires/go-proxyproto v0.6.1
|
||||||
github.com/qiniu/go-sdk/v7 v7.16.0
|
github.com/qiniu/go-sdk/v7 v7.16.0
|
||||||
github.com/quic-go/quic-go v0.36.1
|
github.com/quic-go/quic-go v0.38.1
|
||||||
github.com/shirou/gopsutil/v3 v3.22.2
|
github.com/shirou/gopsutil/v3 v3.22.2
|
||||||
github.com/tencentyun/cos-go-sdk-v5 v0.7.41
|
github.com/tencentyun/cos-go-sdk-v5 v0.7.41
|
||||||
golang.org/x/image v0.7.0
|
golang.org/x/image v0.7.0
|
||||||
golang.org/x/net v0.12.0
|
golang.org/x/net v0.14.0
|
||||||
golang.org/x/sys v0.10.0
|
golang.org/x/sys v0.11.0
|
||||||
google.golang.org/grpc v1.55.0
|
google.golang.org/grpc v1.55.0
|
||||||
|
google.golang.org/protobuf v1.30.0
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -50,9 +51,10 @@ require (
|
|||||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
||||||
github.com/golang/mock v1.6.0 // indirect
|
github.com/golang/mock v1.6.0 // indirect
|
||||||
|
github.com/golang/protobuf v1.5.3 // indirect
|
||||||
github.com/google/go-cmp v0.5.9 // indirect
|
github.com/google/go-cmp v0.5.9 // indirect
|
||||||
github.com/google/go-querystring v1.0.0 // indirect
|
github.com/google/go-querystring v1.0.0 // indirect
|
||||||
github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8 // indirect
|
github.com/google/pprof v0.0.0-20230821062121-407c9e7a662f // indirect
|
||||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||||
github.com/josharian/native v1.0.0 // indirect
|
github.com/josharian/native v1.0.0 // indirect
|
||||||
github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e // indirect
|
github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e // indirect
|
||||||
@@ -60,23 +62,21 @@ require (
|
|||||||
github.com/mdlayher/socket v0.4.0 // indirect
|
github.com/mdlayher/socket v0.4.0 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.4.3 // indirect
|
github.com/mitchellh/mapstructure v1.4.3 // indirect
|
||||||
github.com/mozillazg/go-httpheader v0.2.1 // indirect
|
github.com/mozillazg/go-httpheader v0.2.1 // indirect
|
||||||
github.com/onsi/ginkgo/v2 v2.11.0 // indirect
|
github.com/onsi/ginkgo/v2 v2.12.0 // indirect
|
||||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||||
github.com/quic-go/qpack v0.4.0 // indirect
|
github.com/quic-go/qpack v0.4.0 // indirect
|
||||||
github.com/quic-go/qtls-go1-19 v0.3.2 // indirect
|
github.com/quic-go/qtls-go1-20 v0.3.3 // indirect
|
||||||
github.com/quic-go/qtls-go1-20 v0.2.2 // indirect
|
|
||||||
github.com/tdewolff/minify/v2 v2.12.7 // indirect
|
github.com/tdewolff/minify/v2 v2.12.7 // indirect
|
||||||
github.com/tdewolff/parse/v2 v2.6.6 // indirect
|
github.com/tdewolff/parse/v2 v2.6.6 // indirect
|
||||||
github.com/tklauser/go-sysconf v0.3.9 // indirect
|
github.com/tklauser/go-sysconf v0.3.9 // indirect
|
||||||
github.com/tklauser/numcpus v0.3.0 // indirect
|
github.com/tklauser/numcpus v0.3.0 // indirect
|
||||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||||
golang.org/x/crypto v0.11.0 // indirect
|
golang.org/x/crypto v0.12.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df // indirect
|
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 // indirect
|
||||||
golang.org/x/mod v0.12.0 // indirect
|
golang.org/x/mod v0.12.0 // indirect
|
||||||
golang.org/x/sync v0.3.0 // indirect
|
golang.org/x/sync v0.3.0 // indirect
|
||||||
golang.org/x/text v0.11.0 // indirect
|
golang.org/x/text v0.12.0 // indirect
|
||||||
golang.org/x/time v0.3.0 // indirect
|
golang.org/x/time v0.3.0 // indirect
|
||||||
golang.org/x/tools v0.11.0 // indirect
|
golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect
|
||||||
google.golang.org/protobuf v1.30.0 // indirect
|
|
||||||
)
|
)
|
||||||
|
|||||||
57
go.sum
57
go.sum
@@ -7,6 +7,8 @@ github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/
|
|||||||
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||||
github.com/aws/aws-sdk-go v1.44.279 h1:g23dxnYjIiPlQo0gIKNR0zVPsSvo1bj5frWln+5sfhk=
|
github.com/aws/aws-sdk-go v1.44.279 h1:g23dxnYjIiPlQo0gIKNR0zVPsSvo1bj5frWln+5sfhk=
|
||||||
github.com/aws/aws-sdk-go v1.44.279/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
|
github.com/aws/aws-sdk-go v1.44.279/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
|
||||||
|
github.com/baidubce/bce-sdk-go v0.9.153 h1:h5l2EXehe4C4/bdlAPBaULrbnEDgIu5HOYgniN7bjGM=
|
||||||
|
github.com/baidubce/bce-sdk-go v0.9.153/go.mod h1:zbYJMQwE4IZuyrJiFO8tO8NbtYiKTFTbwh4eIsqjVdg=
|
||||||
github.com/biessek/golang-ico v0.0.0-20180326222316-d348d9ea4670 h1:FQPKKjDhzG0T4ew6dm6MGrXb4PRAi8ZmTuYuxcF62BM=
|
github.com/biessek/golang-ico v0.0.0-20180326222316-d348d9ea4670 h1:FQPKKjDhzG0T4ew6dm6MGrXb4PRAi8ZmTuYuxcF62BM=
|
||||||
github.com/biessek/golang-ico v0.0.0-20180326222316-d348d9ea4670/go.mod h1:iRWAFbKXMMkVQyxZ1PfGlkBr1TjATx1zy2MRprV7A3Q=
|
github.com/biessek/golang-ico v0.0.0-20180326222316-d348d9ea4670/go.mod h1:iRWAFbKXMMkVQyxZ1PfGlkBr1TjATx1zy2MRprV7A3Q=
|
||||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||||
@@ -53,8 +55,10 @@ github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
|||||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
|
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
|
||||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||||
github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8 h1:n6vlPhxsA+BW/XsS5+uqi7GyzaLa5MH7qlSLBZtRdiA=
|
github.com/google/pprof v0.0.0-20230817174616-7a8ec2ada47b h1:h9U78+dx9a4BKdQkBBos92HalKpaGKHrp+3Uo6yTodo=
|
||||||
github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8/go.mod h1:Jh3hGz2jkYak8qXPD19ryItVnUgpgeqzdkY/D0EaeuA=
|
github.com/google/pprof v0.0.0-20230817174616-7a8ec2ada47b/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
|
||||||
|
github.com/google/pprof v0.0.0-20230821062121-407c9e7a662f h1:pDhu5sgp8yJlEF/g6osliIIpF9K4F5jvkULXa4daRDQ=
|
||||||
|
github.com/google/pprof v0.0.0-20230821062121-407c9e7a662f/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
|
||||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||||
github.com/huaweicloud/huaweicloud-sdk-go-obs v3.23.4+incompatible h1:XRAk4HBDLCYEdPLWtKf5iZhOi7lfx17aY0oSO9+mcg8=
|
github.com/huaweicloud/huaweicloud-sdk-go-obs v3.23.4+incompatible h1:XRAk4HBDLCYEdPLWtKf5iZhOi7lfx17aY0oSO9+mcg8=
|
||||||
@@ -69,6 +73,8 @@ github.com/iwind/gosock v0.0.0-20211103081026-ee4652210ca4 h1:VWGsCqTzObdlbf7UUE
|
|||||||
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-20230615040911-5013dbb9d508 h1:fjKiHAyPQmdwuw1DQ2BI1JTbhUWAtI3Kr9wIZQBdRgQ=
|
github.com/iwind/gowebp v0.0.0-20230615040911-5013dbb9d508 h1:fjKiHAyPQmdwuw1DQ2BI1JTbhUWAtI3Kr9wIZQBdRgQ=
|
||||||
github.com/iwind/gowebp v0.0.0-20230615040911-5013dbb9d508/go.mod h1:Re7TEhwL+ygnxFg52fC0PWy01ULAIZp2QR0q5WwEOQA=
|
github.com/iwind/gowebp v0.0.0-20230615040911-5013dbb9d508/go.mod h1:Re7TEhwL+ygnxFg52fC0PWy01ULAIZp2QR0q5WwEOQA=
|
||||||
|
github.com/iwind/gowebp v0.0.0-20230911074406-2e4e7fd0b59f h1:b+YNSK4PgRU4u5YuYW8W4dHO3LNsG7XvX2dJQK0jOf8=
|
||||||
|
github.com/iwind/gowebp v0.0.0-20230911074406-2e4e7fd0b59f/go.mod h1:Re7TEhwL+ygnxFg52fC0PWy01ULAIZp2QR0q5WwEOQA=
|
||||||
github.com/iwind/nftables v0.0.0-20230419014751-9f023a644ad4 h1:RPAH9Sj9l/20zH5zU5/iJGszfwPq6eLjoiC/n/asulA=
|
github.com/iwind/nftables v0.0.0-20230419014751-9f023a644ad4 h1:RPAH9Sj9l/20zH5zU5/iJGszfwPq6eLjoiC/n/asulA=
|
||||||
github.com/iwind/nftables v0.0.0-20230419014751-9f023a644ad4/go.mod h1:7OLL+86wZKfBnAJxNxmdcZ0ebbgdp/A28fcagx9oJqA=
|
github.com/iwind/nftables v0.0.0-20230419014751-9f023a644ad4/go.mod h1:7OLL+86wZKfBnAJxNxmdcZ0ebbgdp/A28fcagx9oJqA=
|
||||||
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
||||||
@@ -93,8 +99,8 @@ github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ic
|
|||||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
|
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
|
||||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
||||||
github.com/matryer/try v0.0.0-20161228173917-9ac251b645a2/go.mod h1:0KeJpeMD6o+O4hW7qJOT7vyQPKrWmj26uf5wMc/IiIs=
|
github.com/matryer/try v0.0.0-20161228173917-9ac251b645a2/go.mod h1:0KeJpeMD6o+O4hW7qJOT7vyQPKrWmj26uf5wMc/IiIs=
|
||||||
github.com/mattn/go-sqlite3 v1.14.9 h1:10HX2Td0ocZpYEjhilsuo6WWtUqttj2Kb0KtD86/KYA=
|
github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM=
|
||||||
github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||||
github.com/mdlayher/netlink v1.7.1 h1:FdUaT/e33HjEXagwELR8R3/KL1Fq5x3G5jgHLp/BTmg=
|
github.com/mdlayher/netlink v1.7.1 h1:FdUaT/e33HjEXagwELR8R3/KL1Fq5x3G5jgHLp/BTmg=
|
||||||
github.com/mdlayher/netlink v1.7.1/go.mod h1:nKO5CSjE/DJjVhk/TNp6vCE1ktVxEA8VEh8drhZzxsQ=
|
github.com/mdlayher/netlink v1.7.1/go.mod h1:nKO5CSjE/DJjVhk/TNp6vCE1ktVxEA8VEh8drhZzxsQ=
|
||||||
github.com/mdlayher/socket v0.4.0 h1:280wsy40IC9M9q1uPGcLBwXpcTQDtoGwVt+BNoITxIw=
|
github.com/mdlayher/socket v0.4.0 h1:280wsy40IC9M9q1uPGcLBwXpcTQDtoGwVt+BNoITxIw=
|
||||||
@@ -111,7 +117,10 @@ github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
|||||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||||
github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU=
|
github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU=
|
||||||
github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM=
|
github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM=
|
||||||
|
github.com/onsi/ginkgo/v2 v2.12.0 h1:UIVDowFPwpg6yMUpPjGkYvf06K3RAiJXUhCxEwQVHRI=
|
||||||
|
github.com/onsi/ginkgo/v2 v2.12.0/go.mod h1:ZNEzXISYlqpb8S36iN71ifqLi3vVD1rVJGvWRCJOUpQ=
|
||||||
github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc=
|
github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc=
|
||||||
|
github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
|
||||||
github.com/pires/go-proxyproto v0.6.1 h1:EBupykFmo22SDjv4fQVQd2J9NOoLPmyZA/15ldOGkPw=
|
github.com/pires/go-proxyproto v0.6.1 h1:EBupykFmo22SDjv4fQVQd2J9NOoLPmyZA/15ldOGkPw=
|
||||||
github.com/pires/go-proxyproto v0.6.1/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY=
|
github.com/pires/go-proxyproto v0.6.1/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY=
|
||||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||||
@@ -126,12 +135,14 @@ github.com/qiniu/go-sdk/v7 v7.16.0/go.mod h1:nqoYCNo53ZlGA521RvRethvxUDvXKt4gtYX
|
|||||||
github.com/qiniu/x v1.10.5/go.mod h1:03Ni9tj+N2h2aKnAz+6N0Xfl8FwMEDRC2PAlxekASDs=
|
github.com/qiniu/x v1.10.5/go.mod h1:03Ni9tj+N2h2aKnAz+6N0Xfl8FwMEDRC2PAlxekASDs=
|
||||||
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
|
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
|
||||||
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
|
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
|
||||||
github.com/quic-go/qtls-go1-19 v0.3.2 h1:tFxjCFcTQzK+oMxG6Zcvp4Dq8dx4yD3dDiIiyc86Z5U=
|
github.com/quic-go/qtls-go1-20 v0.3.2 h1:rRgN3WfnKbyik4dBV8A6girlJVxGand/d+jVKbQq5GI=
|
||||||
github.com/quic-go/qtls-go1-19 v0.3.2/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI=
|
github.com/quic-go/qtls-go1-20 v0.3.2/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
|
||||||
github.com/quic-go/qtls-go1-20 v0.2.2 h1:WLOPx6OY/hxtTxKV1Zrq20FtXtDEkeY00CGQm8GEa3E=
|
github.com/quic-go/qtls-go1-20 v0.3.3 h1:17/glZSLI9P9fDAeyCHBFSWSqJcwx1byhLwP5eUIDCM=
|
||||||
github.com/quic-go/qtls-go1-20 v0.2.2/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
|
github.com/quic-go/qtls-go1-20 v0.3.3/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
|
||||||
github.com/quic-go/quic-go v0.36.1 h1:WsG73nVtnDy1TiACxFxhQ3TqaW+DipmqzLEtNlAwZyY=
|
github.com/quic-go/quic-go v0.38.0 h1:T45lASr5q/TrVwt+jrVccmqHhPL2XuSyoCLVCpfOSLc=
|
||||||
github.com/quic-go/quic-go v0.36.1/go.mod h1:zPetvwDlILVxt15n3hr3Gf/I3mDf7LpLKPhR4Ez0AZQ=
|
github.com/quic-go/quic-go v0.38.0/go.mod h1:MPCuRq7KBK2hNcfKj/1iD1BGuN3eAYMeNxp3T42LRUg=
|
||||||
|
github.com/quic-go/quic-go v0.38.1 h1:M36YWA5dEhEeT+slOu/SwMEucbYd0YFidxG3KlGPZaE=
|
||||||
|
github.com/quic-go/quic-go v0.38.1/go.mod h1:ijnZM7JsFIkp4cRyjxJNIzdSfCLmUMg9wdyhGmg+SN4=
|
||||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||||
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
|
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
|
||||||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||||
@@ -170,10 +181,10 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
|
|||||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
|
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
|
||||||
golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA=
|
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
|
||||||
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
|
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
|
||||||
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME=
|
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 h1:m64FZMko/V45gv0bNmrNYoDEq8U5YUhetc9cBWKS1TQ=
|
||||||
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
|
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63/go.mod h1:0v4NqG35kSWCMzLaMeX+IQrlSnVE/bqGSyC2cz/9Le8=
|
||||||
golang.org/x/image v0.7.0 h1:gzS29xtG1J5ybQlv0PuyfE3nmc6R4qB73m6LUUmvFuw=
|
golang.org/x/image v0.7.0 h1:gzS29xtG1J5ybQlv0PuyfE3nmc6R4qB73m6LUUmvFuw=
|
||||||
golang.org/x/image v0.7.0/go.mod h1:nd/q4ef1AKKYl/4kft7g+6UyGbdiqWqTP1ZAbRoV7Rg=
|
golang.org/x/image v0.7.0/go.mod h1:nd/q4ef1AKKYl/4kft7g+6UyGbdiqWqTP1ZAbRoV7Rg=
|
||||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
@@ -188,8 +199,8 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b
|
|||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50=
|
golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
|
||||||
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
|
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
@@ -214,8 +225,8 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
|
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
|
||||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
@@ -228,8 +239,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
|||||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
|
golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
|
||||||
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
||||||
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
@@ -237,8 +248,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
|
|||||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
golang.org/x/tools v0.11.0 h1:EMCa6U9S2LtZXLAMoWiR/R8dAQFRqbAitmbJ2UKhoi8=
|
golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846 h1:Vve/L0v7CXXuxUmaMGIEK/dEeq7uiqb5qBgQrZzIE7E=
|
||||||
golang.org/x/tools v0.11.0/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8=
|
golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
@@ -263,3 +274,5 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
|
|||||||
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 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
rogchap.com/v8go v0.9.0 h1:wYbUCO4h6fjTamziHrzyrPnpFNuzPpjZY+nfmZjNaew=
|
||||||
|
rogchap.com/v8go v0.9.0/go.mod h1:MxgP3pL2MW4dpme/72QRs8sgNMmM0pRc8DPhcuLWPAs=
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package apps
|
package apps
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
||||||
executils "github.com/TeaOSLab/EdgeNode/internal/utils/exec"
|
executils "github.com/TeaOSLab/EdgeNode/internal/utils/exec"
|
||||||
@@ -10,6 +11,7 @@ import (
|
|||||||
"github.com/iwind/gosock/pkg/gosock"
|
"github.com/iwind/gosock/pkg/gosock"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -87,8 +89,8 @@ func (this *AppCmd) Print() {
|
|||||||
fmt.Println("")
|
fmt.Println("")
|
||||||
fmt.Println("Options:")
|
fmt.Println("Options:")
|
||||||
|
|
||||||
spaces := 20
|
var spaces = 20
|
||||||
max := 40
|
var max = 40
|
||||||
for _, option := range this.options {
|
for _, option := range this.options {
|
||||||
l := len(option.Code)
|
l := len(option.Code)
|
||||||
if l < max && l > spaces {
|
if l < max && l > spaces {
|
||||||
@@ -126,9 +128,12 @@ func (this *AppCmd) On(arg string, callback func()) {
|
|||||||
// Run 运行
|
// Run 运行
|
||||||
func (this *AppCmd) Run(main func()) {
|
func (this *AppCmd) Run(main func()) {
|
||||||
// 获取参数
|
// 获取参数
|
||||||
args := os.Args[1:]
|
var args = os.Args[1:]
|
||||||
if len(args) > 0 {
|
if len(args) > 0 {
|
||||||
switch args[0] {
|
var mainArg = args[0]
|
||||||
|
this.callDirective(mainArg + ":before")
|
||||||
|
|
||||||
|
switch mainArg {
|
||||||
case "-v", "version", "-version", "--version":
|
case "-v", "version", "-version", "--version":
|
||||||
this.runVersion()
|
this.runVersion()
|
||||||
return
|
return
|
||||||
@@ -151,19 +156,19 @@ func (this *AppCmd) Run(main func()) {
|
|||||||
|
|
||||||
// 查找指令
|
// 查找指令
|
||||||
for _, directive := range this.directives {
|
for _, directive := range this.directives {
|
||||||
if directive.Arg == args[0] {
|
if directive.Arg == mainArg {
|
||||||
directive.Callback()
|
directive.Callback()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("unknown command '" + args[0] + "'")
|
fmt.Println("unknown command '" + mainArg + "'")
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 日志
|
// 日志
|
||||||
writer := new(LogWriter)
|
var writer = new(LogWriter)
|
||||||
writer.Init()
|
writer.Init()
|
||||||
logs.SetWriter(writer)
|
logs.SetWriter(writer)
|
||||||
|
|
||||||
@@ -191,7 +196,7 @@ func (this *AppCmd) runStart() {
|
|||||||
|
|
||||||
_ = os.Setenv("EdgeBackground", "on")
|
_ = os.Setenv("EdgeBackground", "on")
|
||||||
|
|
||||||
cmd := exec.Command(os.Args[0])
|
var cmd = exec.Command(this.exe())
|
||||||
cmd.SysProcAttr = &syscall.SysProcAttr{
|
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||||
Foreground: false,
|
Foreground: false,
|
||||||
Setsid: true,
|
Setsid: true,
|
||||||
@@ -203,6 +208,9 @@ func (this *AppCmd) runStart() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// create symbolic links
|
||||||
|
_ = this.createSymLinks()
|
||||||
|
|
||||||
fmt.Println(this.product+" started ok, pid:", cmd.Process.Pid)
|
fmt.Println(this.product+" started ok, pid:", cmd.Process.Pid)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -277,3 +285,69 @@ func (this *AppCmd) ParseOptions(args []string) map[string][]string {
|
|||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *AppCmd) exe() string {
|
||||||
|
var exe, _ = os.Executable()
|
||||||
|
if len(exe) == 0 {
|
||||||
|
exe = os.Args[0]
|
||||||
|
}
|
||||||
|
return exe
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建软链接
|
||||||
|
func (this *AppCmd) createSymLinks() error {
|
||||||
|
if runtime.GOOS != "linux" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var exe, _ = os.Executable()
|
||||||
|
if len(exe) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var errorList = []string{}
|
||||||
|
|
||||||
|
// bin
|
||||||
|
{
|
||||||
|
var target = "/usr/bin/" + teaconst.ProcessName
|
||||||
|
old, _ := filepath.EvalSymlinks(target)
|
||||||
|
if old != exe {
|
||||||
|
_ = os.Remove(target)
|
||||||
|
err := os.Symlink(exe, target)
|
||||||
|
if err != nil {
|
||||||
|
errorList = append(errorList, err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// log
|
||||||
|
{
|
||||||
|
var realPath = filepath.Dir(filepath.Dir(exe)) + "/logs/run.log"
|
||||||
|
var target = "/var/log/" + teaconst.ProcessName + ".log"
|
||||||
|
old, _ := filepath.EvalSymlinks(target)
|
||||||
|
if old != realPath {
|
||||||
|
_ = os.Remove(target)
|
||||||
|
err := os.Symlink(realPath, target)
|
||||||
|
if err != nil {
|
||||||
|
errorList = append(errorList, err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(errorList) > 0 {
|
||||||
|
return errors.New(strings.Join(errorList, "\n"))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *AppCmd) callDirective(code string) {
|
||||||
|
for _, directive := range this.directives {
|
||||||
|
if directive.Arg == code {
|
||||||
|
if directive.Callback != nil {
|
||||||
|
directive.Callback()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,12 +7,12 @@ import "errors"
|
|||||||
// 常用的几个错误
|
// 常用的几个错误
|
||||||
var (
|
var (
|
||||||
ErrNotFound = errors.New("cache not found")
|
ErrNotFound = errors.New("cache not found")
|
||||||
ErrFileIsWriting = errors.New("the file is writing")
|
ErrFileIsWriting = errors.New("the cache file is updating")
|
||||||
ErrInvalidRange = errors.New("invalid range")
|
ErrInvalidRange = errors.New("invalid range")
|
||||||
ErrEntityTooLarge = errors.New("entity too large")
|
ErrEntityTooLarge = errors.New("entity too large")
|
||||||
ErrWritingUnavailable = errors.New("writing unavailable")
|
ErrWritingUnavailable = errors.New("writing unavailable")
|
||||||
ErrWritingQueueFull = errors.New("writing queue full")
|
ErrWritingQueueFull = errors.New("writing queue full")
|
||||||
ErrTooManyOpenFiles = errors.New("too many open files")
|
ErrServerIsBusy = errors.New("server is busy")
|
||||||
)
|
)
|
||||||
|
|
||||||
// CapacityError 容量错误
|
// CapacityError 容量错误
|
||||||
@@ -38,7 +38,7 @@ func CanIgnoreErr(err error) bool {
|
|||||||
err == ErrEntityTooLarge ||
|
err == ErrEntityTooLarge ||
|
||||||
err == ErrWritingUnavailable ||
|
err == ErrWritingUnavailable ||
|
||||||
err == ErrWritingQueueFull ||
|
err == ErrWritingQueueFull ||
|
||||||
err == ErrTooManyOpenFiles {
|
err == ErrServerIsBusy {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
_, ok := err.(*CapacityError)
|
_, ok := err.(*CapacityError)
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package caches
|
|||||||
import (
|
import (
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils/fasttime"
|
"github.com/TeaOSLab/EdgeNode/internal/utils/fasttime"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type ItemType = int
|
type ItemType = int
|
||||||
@@ -16,7 +15,7 @@ const (
|
|||||||
// 计算当前周
|
// 计算当前周
|
||||||
// 不要用YW,因为需要计算两周是否临近
|
// 不要用YW,因为需要计算两周是否临近
|
||||||
func currentWeek() int32 {
|
func currentWeek() int32 {
|
||||||
return int32(time.Now().Unix() / 86400)
|
return int32(fasttime.Now().Unix() / 86400)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Item struct {
|
type Item struct {
|
||||||
@@ -29,10 +28,7 @@ type Item struct {
|
|||||||
MetaSize int64 `json:"metaSize"`
|
MetaSize int64 `json:"metaSize"`
|
||||||
Host string `json:"host"` // 主机名
|
Host string `json:"host"` // 主机名
|
||||||
ServerId int64 `json:"serverId"` // 服务ID
|
ServerId int64 `json:"serverId"` // 服务ID
|
||||||
|
Week int32 `json:"week"`
|
||||||
Week1Hits int64 `json:"week1Hits"`
|
|
||||||
Week2Hits int64 `json:"week2Hits"`
|
|
||||||
Week int32 `json:"week"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *Item) IsExpired() bool {
|
func (this *Item) IsExpired() bool {
|
||||||
@@ -47,20 +43,6 @@ func (this *Item) Size() int64 {
|
|||||||
return this.HeaderSize + this.BodySize
|
return this.HeaderSize + this.BodySize
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *Item) IncreaseHit(week int32) {
|
|
||||||
if this.Week == week {
|
|
||||||
this.Week2Hits++
|
|
||||||
} else {
|
|
||||||
if week-this.Week == 1 {
|
|
||||||
this.Week1Hits = this.Week2Hits
|
|
||||||
} else {
|
|
||||||
this.Week1Hits = 0
|
|
||||||
}
|
|
||||||
this.Week2Hits = 1
|
|
||||||
this.Week = week
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *Item) RequestURI() string {
|
func (this *Item) RequestURI() string {
|
||||||
var schemeIndex = strings.Index(this.Key, "://")
|
var schemeIndex = strings.Index(this.Key, "://")
|
||||||
if schemeIndex <= 0 {
|
if schemeIndex <= 0 {
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||||
|
|
||||||
package caches
|
package caches_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/caches"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/zero"
|
"github.com/TeaOSLab/EdgeNode/internal/zero"
|
||||||
"github.com/iwind/TeaGo/rands"
|
"github.com/iwind/TeaGo/rands"
|
||||||
"github.com/iwind/TeaGo/types"
|
"github.com/iwind/TeaGo/types"
|
||||||
@@ -11,27 +12,14 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestItem_IncreaseHit(t *testing.T) {
|
|
||||||
var week = currentWeek()
|
|
||||||
|
|
||||||
var item = &Item{}
|
|
||||||
//item.Week = 2704
|
|
||||||
item.Week2Hits = 100
|
|
||||||
item.IncreaseHit(week)
|
|
||||||
t.Log("week:", item.Week, "week1:", item.Week1Hits, "week2:", item.Week2Hits)
|
|
||||||
|
|
||||||
item.IncreaseHit(week)
|
|
||||||
t.Log("week:", item.Week, "week1:", item.Week1Hits, "week2:", item.Week2Hits)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestItems_Memory(t *testing.T) {
|
func TestItems_Memory(t *testing.T) {
|
||||||
var stat = &runtime.MemStats{}
|
var stat = &runtime.MemStats{}
|
||||||
runtime.ReadMemStats(stat)
|
runtime.ReadMemStats(stat)
|
||||||
var memory1 = stat.HeapInuse
|
var memory1 = stat.HeapInuse
|
||||||
|
|
||||||
var items = []*Item{}
|
var items = []*caches.Item{}
|
||||||
for i := 0; i < 10_000_000; i++ {
|
for i := 0; i < 10_000_000; i++ {
|
||||||
items = append(items, &Item{
|
items = append(items, &caches.Item{
|
||||||
Key: types.String(i),
|
Key: types.String(i),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -41,18 +29,11 @@ func TestItems_Memory(t *testing.T) {
|
|||||||
|
|
||||||
t.Log(memory1, memory2, (memory2-memory1)/1024/1024, "M")
|
t.Log(memory1, memory2, (memory2-memory1)/1024/1024, "M")
|
||||||
|
|
||||||
var weekItems = make(map[string]*Item, 10_000_000)
|
|
||||||
|
|
||||||
for _, item := range items {
|
|
||||||
weekItems[item.Key] = item
|
|
||||||
}
|
|
||||||
|
|
||||||
runtime.ReadMemStats(stat)
|
runtime.ReadMemStats(stat)
|
||||||
var memory3 = stat.HeapInuse
|
var memory3 = stat.HeapInuse
|
||||||
t.Log(memory2, memory3, (memory3-memory2)/1024/1024, "M")
|
t.Log(memory2, memory3, (memory3-memory2)/1024/1024, "M")
|
||||||
|
|
||||||
time.Sleep(1 * time.Second)
|
time.Sleep(1 * time.Second)
|
||||||
t.Log(len(items), len(weekItems))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestItems_Memory2(t *testing.T) {
|
func TestItems_Memory2(t *testing.T) {
|
||||||
@@ -88,7 +69,7 @@ func TestItem_RequestURI(t *testing.T) {
|
|||||||
"https://goedge.cn:8080/hello/world",
|
"https://goedge.cn:8080/hello/world",
|
||||||
"https://goedge.cn/hello/world?v=1&t=123",
|
"https://goedge.cn/hello/world?v=1&t=123",
|
||||||
} {
|
} {
|
||||||
var item = &Item{Key: u}
|
var item = &caches.Item{Key: u}
|
||||||
t.Log(u, "=>", item.RequestURI())
|
t.Log(u, "=>", item.RequestURI())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ package caches
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"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/TeaOSLab/EdgeNode/internal/ttlcache"
|
"github.com/TeaOSLab/EdgeNode/internal/ttlcache"
|
||||||
@@ -134,7 +135,7 @@ func (this *FileList) Exist(hash string) (bool, error) {
|
|||||||
var expiredAt int64
|
var expiredAt int64
|
||||||
err := row.Scan(&expiredAt)
|
err := row.Scan(&expiredAt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == sql.ErrNoRows {
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
err = nil
|
err = nil
|
||||||
}
|
}
|
||||||
return false, err
|
return false, err
|
||||||
@@ -143,6 +144,18 @@ func (this *FileList) Exist(hash string) (bool, error) {
|
|||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *FileList) ExistQuick(hash string) (isReady bool, found bool) {
|
||||||
|
var db = this.GetDB(hash)
|
||||||
|
|
||||||
|
if !db.IsReady() || !db.HashMapIsLoaded() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
isReady = true
|
||||||
|
found = db.hashMap.Exist(hash)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// CleanPrefix 清理某个前缀的缓存数据
|
// CleanPrefix 清理某个前缀的缓存数据
|
||||||
func (this *FileList) CleanPrefix(prefix string) error {
|
func (this *FileList) CleanPrefix(prefix string) error {
|
||||||
if len(prefix) == 0 {
|
if len(prefix) == 0 {
|
||||||
@@ -256,16 +269,10 @@ func (this *FileList) PurgeLFU(count int, callback func(hash string) error) erro
|
|||||||
|
|
||||||
// 不在 rows.Next() 循环中操作是为了避免死锁
|
// 不在 rows.Next() 循环中操作是为了避免死锁
|
||||||
for _, hash := range hashStrings {
|
for _, hash := range hashStrings {
|
||||||
notFound, err := this.remove(hash)
|
_, err = this.remove(hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if notFound {
|
|
||||||
err = db.DeleteHitAsync(hash)
|
|
||||||
if err != nil {
|
|
||||||
return db.WrapError(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err = callback(hash)
|
err = callback(hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -372,6 +379,15 @@ func (this *FileList) GetDB(hash string) *FileListDB {
|
|||||||
return this.dbList[fnv.HashString(hash)%CountFileDB]
|
return this.dbList[fnv.HashString(hash)%CountFileDB]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *FileList) HashMapIsLoaded() bool {
|
||||||
|
for _, db := range this.dbList {
|
||||||
|
if !db.HashMapIsLoaded() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func (this *FileList) remove(hash string) (notFound bool, err error) {
|
func (this *FileList) remove(hash string) (notFound bool, err error) {
|
||||||
var db = this.GetDB(hash)
|
var db = this.GetDB(hash)
|
||||||
|
|
||||||
@@ -393,11 +409,6 @@ func (this *FileList) remove(hash string) (notFound bool, err error) {
|
|||||||
return false, db.WrapError(err)
|
return false, db.WrapError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = db.DeleteHitAsync(hash)
|
|
||||||
if err != nil {
|
|
||||||
return false, db.WrapError(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if this.onRemove != nil {
|
if this.onRemove != nil {
|
||||||
// when remove file item, no any extra information needed
|
// when remove file item, no any extra information needed
|
||||||
this.onRemove(nil)
|
this.onRemove(nil)
|
||||||
@@ -493,9 +504,6 @@ func (this *FileList) UpgradeV3(oldDir string, brokenOnError bool) error {
|
|||||||
MetaSize: metaSize,
|
MetaSize: metaSize,
|
||||||
Host: host,
|
Host: host,
|
||||||
ServerId: serverId,
|
ServerId: serverId,
|
||||||
Week1Hits: 0,
|
|
||||||
Week2Hits: 0,
|
|
||||||
Week: 0,
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if brokenOnError {
|
if brokenOnError {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ package caches
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
@@ -33,10 +34,10 @@ type FileListDB struct {
|
|||||||
hashMap *FileListHashMap
|
hashMap *FileListHashMap
|
||||||
|
|
||||||
itemsTableName string
|
itemsTableName string
|
||||||
hitsTableName string
|
|
||||||
|
|
||||||
isClosed bool
|
isClosed bool // 是否已关闭
|
||||||
isReady bool
|
isReady bool // 是否已完成初始化
|
||||||
|
hashMapIsLoaded bool // Hash是否已加载
|
||||||
|
|
||||||
// cacheItems
|
// cacheItems
|
||||||
existsByHashStmt *dbs.Stmt // 根据hash检查是否存在
|
existsByHashStmt *dbs.Stmt // 根据hash检查是否存在
|
||||||
@@ -56,11 +57,6 @@ type FileListDB struct {
|
|||||||
deleteAllStmt *dbs.Stmt // 删除所有数据
|
deleteAllStmt *dbs.Stmt // 删除所有数据
|
||||||
listOlderItemsStmt *dbs.Stmt // 读取较早存储的缓存
|
listOlderItemsStmt *dbs.Stmt // 读取较早存储的缓存
|
||||||
updateAccessWeekSQL string // 修改访问日期
|
updateAccessWeekSQL string // 修改访问日期
|
||||||
|
|
||||||
// hits
|
|
||||||
insertHitSQL string // 写入数据
|
|
||||||
increaseHitSQL string // 增加点击量
|
|
||||||
deleteHitByHashSQL string // 根据hash删除数据
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFileListDB() *FileListDB {
|
func NewFileListDB() *FileListDB {
|
||||||
@@ -83,7 +79,7 @@ func (this *FileListDB) Open(dbPath string) error {
|
|||||||
// 这里不能加 EXCLUSIVE 锁,不然异步事务可能会失败
|
// 这里不能加 EXCLUSIVE 锁,不然异步事务可能会失败
|
||||||
writeDB, err := dbs.OpenWriter("file:" + dbPath + "?cache=private&mode=rwc&_journal_mode=WAL&_sync=OFF&_cache_size=" + types.String(cacheSize) + "&_secure_delete=FAST")
|
writeDB, err := dbs.OpenWriter("file:" + dbPath + "?cache=private&mode=rwc&_journal_mode=WAL&_sync=OFF&_cache_size=" + types.String(cacheSize) + "&_secure_delete=FAST")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("open write database failed: " + err.Error())
|
return fmt.Errorf("open write database failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
writeDB.SetMaxOpenConns(1)
|
writeDB.SetMaxOpenConns(1)
|
||||||
@@ -125,7 +121,7 @@ func (this *FileListDB) Open(dbPath string) error {
|
|||||||
// read db
|
// read db
|
||||||
readDB, err := dbs.OpenReader("file:" + dbPath + "?cache=private&mode=ro&_journal_mode=WAL&_sync=OFF&_cache_size=" + types.String(cacheSize))
|
readDB, err := dbs.OpenReader("file:" + dbPath + "?cache=private&mode=ro&_journal_mode=WAL&_sync=OFF&_cache_size=" + types.String(cacheSize))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("open read database failed: " + err.Error())
|
return fmt.Errorf("open read database failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
readDB.SetMaxOpenConns(runtime.NumCPU())
|
readDB.SetMaxOpenConns(runtime.NumCPU())
|
||||||
@@ -141,12 +137,11 @@ func (this *FileListDB) Open(dbPath string) error {
|
|||||||
|
|
||||||
func (this *FileListDB) Init() error {
|
func (this *FileListDB) Init() error {
|
||||||
this.itemsTableName = "cacheItems"
|
this.itemsTableName = "cacheItems"
|
||||||
this.hitsTableName = "hits"
|
|
||||||
|
|
||||||
// 创建
|
// 创建
|
||||||
var err = this.initTables(1)
|
var err = this.initTables(1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("init tables failed: " + err.Error())
|
return fmt.Errorf("init tables failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 常用语句
|
// 常用语句
|
||||||
@@ -199,35 +194,10 @@ func (this *FileListDB) Init() error {
|
|||||||
|
|
||||||
this.updateAccessWeekSQL = `UPDATE "` + this.itemsTableName + `" SET "accessWeek"=? WHERE "hash"=?`
|
this.updateAccessWeekSQL = `UPDATE "` + this.itemsTableName + `" SET "accessWeek"=? WHERE "hash"=?`
|
||||||
|
|
||||||
this.insertHitSQL = `INSERT INTO "` + this.hitsTableName + `" ("hash", "week2Hits", "week") VALUES (?, 1, ?)`
|
|
||||||
|
|
||||||
this.increaseHitSQL = `INSERT INTO "` + this.hitsTableName + `" ("hash", "week2Hits", "week") VALUES (?, 1, ?) ON CONFLICT("hash") DO UPDATE SET "week1Hits"=IIF("week"=?, "week1Hits", "week2Hits"), "week2Hits"=IIF("week"=?, "week2Hits"+1, 1), "week"=?`
|
|
||||||
|
|
||||||
this.deleteHitByHashSQL = `DELETE FROM "` + this.hitsTableName + `" WHERE "hash"=?`
|
|
||||||
|
|
||||||
this.isReady = true
|
this.isReady = true
|
||||||
|
|
||||||
// 加载HashMap
|
// 加载HashMap
|
||||||
go func() {
|
go this.loadHashMap()
|
||||||
err := this.hashMap.Load(this)
|
|
||||||
if err != nil {
|
|
||||||
remotelogs.Error("LIST_FILE_DB", "load hash map failed: "+err.Error()+"(file: "+this.dbPath+")")
|
|
||||||
|
|
||||||
// 自动修复错误
|
|
||||||
// TODO 将来希望能尽可能恢复以往数据库中的内容
|
|
||||||
if strings.Contains(err.Error(), "database is closed") || strings.Contains(err.Error(), "database disk image is malformed") {
|
|
||||||
_ = this.Close()
|
|
||||||
this.deleteDB()
|
|
||||||
remotelogs.Println("LIST_FILE_DB", "recreating the database (file:"+this.dbPath+") ...")
|
|
||||||
err = this.Open(this.dbPath)
|
|
||||||
if err != nil {
|
|
||||||
remotelogs.Error("LIST_FILE_DB", "recreate the database failed: "+err.Error()+" (file:"+this.dbPath+")")
|
|
||||||
} else {
|
|
||||||
_ = this.Init()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -351,25 +321,18 @@ func (this *FileListDB) ListHashes(lastId int64) (hashList []string, maxId int64
|
|||||||
|
|
||||||
func (this *FileListDB) IncreaseHitAsync(hash string) error {
|
func (this *FileListDB) IncreaseHitAsync(hash string) error {
|
||||||
var week = timeutil.Format("YW")
|
var week = timeutil.Format("YW")
|
||||||
this.writeBatch.Add(this.increaseHitSQL, hash, week, week, week, week)
|
|
||||||
this.writeBatch.Add(this.updateAccessWeekSQL, week, hash)
|
this.writeBatch.Add(this.updateAccessWeekSQL, week, hash)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *FileListDB) DeleteHitAsync(hash string) error {
|
|
||||||
this.writeBatch.Add(this.deleteHitByHashSQL, hash)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *FileListDB) CleanPrefix(prefix string) error {
|
func (this *FileListDB) CleanPrefix(prefix string) error {
|
||||||
if !this.isReady {
|
if !this.isReady {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
var count = int64(10000)
|
var count = int64(10000)
|
||||||
var staleLife = 600 // TODO 需要可以设置
|
|
||||||
var unixTime = fasttime.Now().Unix() // 只删除当前的,不删除新的
|
var unixTime = fasttime.Now().Unix() // 只删除当前的,不删除新的
|
||||||
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+DefaultStaleCacheSeconds, unixTime, prefix)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return this.WrapError(err)
|
return this.WrapError(err)
|
||||||
}
|
}
|
||||||
@@ -413,15 +376,14 @@ func (this *FileListDB) CleanMatchKey(key string) error {
|
|||||||
queryKey = strings.Replace(queryKey, "*", "%", 1)
|
queryKey = strings.Replace(queryKey, "*", "%", 1)
|
||||||
|
|
||||||
// TODO 检查大批量数据下的操作性能
|
// TODO 检查大批量数据下的操作性能
|
||||||
var staleLife = 600 // TODO 需要可以设置
|
|
||||||
var unixTime = fasttime.Now().Unix() // 只删除当前的,不删除新的
|
var unixTime = fasttime.Now().Unix() // 只删除当前的,不删除新的
|
||||||
|
|
||||||
_, err = this.writeDB.Exec(`UPDATE "`+this.itemsTableName+`" SET "expiredAt"=0, "staleAt"=? WHERE "host" GLOB ? AND "host" NOT GLOB ? AND "key" LIKE ? ESCAPE '\'`, unixTime+int64(staleLife), host, "*."+host, queryKey)
|
_, err = this.writeDB.Exec(`UPDATE "`+this.itemsTableName+`" SET "expiredAt"=0, "staleAt"=? WHERE "host" GLOB ? AND "host" NOT GLOB ? AND "key" LIKE ? ESCAPE '\'`, unixTime+DefaultStaleCacheSeconds, host, "*."+host, queryKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = this.writeDB.Exec(`UPDATE "`+this.itemsTableName+`" SET "expiredAt"=0, "staleAt"=? WHERE "host" GLOB ? AND "host" NOT GLOB ? AND "key" LIKE ? ESCAPE '\'`, unixTime+int64(staleLife), host, "*."+host, queryKey+SuffixAll+"%")
|
_, err = this.writeDB.Exec(`UPDATE "`+this.itemsTableName+`" SET "expiredAt"=0, "staleAt"=? WHERE "host" GLOB ? AND "host" NOT GLOB ? AND "key" LIKE ? ESCAPE '\'`, unixTime+DefaultStaleCacheSeconds, host, "*."+host, queryKey+SuffixAll+"%")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -455,10 +417,9 @@ func (this *FileListDB) CleanMatchPrefix(prefix string) error {
|
|||||||
queryPrefix += "%"
|
queryPrefix += "%"
|
||||||
|
|
||||||
// TODO 检查大批量数据下的操作性能
|
// TODO 检查大批量数据下的操作性能
|
||||||
var staleLife = 600 // TODO 需要可以设置
|
|
||||||
var unixTime = fasttime.Now().Unix() // 只删除当前的,不删除新的
|
var unixTime = fasttime.Now().Unix() // 只删除当前的,不删除新的
|
||||||
|
|
||||||
_, err = this.writeDB.Exec(`UPDATE "`+this.itemsTableName+`" SET "expiredAt"=0, "staleAt"=? WHERE "host" GLOB ? AND "host" NOT GLOB ? AND "key" LIKE ? ESCAPE '\'`, unixTime+int64(staleLife), host, "*."+host, queryPrefix)
|
_, err = this.writeDB.Exec(`UPDATE "`+this.itemsTableName+`" SET "expiredAt"=0, "staleAt"=? WHERE "host" GLOB ? AND "host" NOT GLOB ? AND "key" LIKE ? ESCAPE '\'`, unixTime+DefaultStaleCacheSeconds, host, "*."+host, queryPrefix)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -539,7 +500,11 @@ func (this *FileListDB) WrapError(err error) error {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return errors.New(err.Error() + "(file: " + this.dbPath + ")")
|
return fmt.Errorf("%w (file: %s)", err, this.dbPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *FileListDB) HashMapIsLoaded() bool {
|
||||||
|
return this.hashMapIsLoaded
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
@@ -602,32 +567,9 @@ ALTER TABLE "cacheItems" ADD "accessWeek" varchar(6);
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 删除hits表
|
||||||
{
|
{
|
||||||
_, err := this.writeDB.Exec(`CREATE TABLE IF NOT EXISTS "` + this.hitsTableName + `" (
|
_, _ = this.writeDB.Exec(`DROP TABLE "hits"`)
|
||||||
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
|
|
||||||
"hash" varchar(32),
|
|
||||||
"week1Hits" integer DEFAULT 0,
|
|
||||||
"week2Hits" integer DEFAULT 0,
|
|
||||||
"week" varchar(6)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE UNIQUE INDEX IF NOT EXISTS "hits_hash"
|
|
||||||
ON "` + this.hitsTableName + `" (
|
|
||||||
"hash" ASC
|
|
||||||
);
|
|
||||||
`)
|
|
||||||
if err != nil {
|
|
||||||
// 尝试删除重建
|
|
||||||
if times < 3 {
|
|
||||||
_, dropErr := this.writeDB.Exec(`DROP TABLE "` + this.hitsTableName + `"`)
|
|
||||||
if dropErr == nil {
|
|
||||||
return this.initTables(times + 1)
|
|
||||||
}
|
|
||||||
return this.WrapError(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.WrapError(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -661,12 +603,11 @@ func (this *FileListDB) shouldRecover() bool {
|
|||||||
}
|
}
|
||||||
var errString = ""
|
var errString = ""
|
||||||
var shouldRecover = false
|
var shouldRecover = false
|
||||||
for result.Next() {
|
if result.Next() {
|
||||||
err = result.Scan(&errString)
|
_ = result.Scan(&errString)
|
||||||
if strings.TrimSpace(errString) != "ok" {
|
if strings.TrimSpace(errString) != "ok" {
|
||||||
shouldRecover = true
|
shouldRecover = true
|
||||||
}
|
}
|
||||||
break
|
|
||||||
}
|
}
|
||||||
_ = result.Close()
|
_ = result.Close()
|
||||||
return shouldRecover
|
return shouldRecover
|
||||||
@@ -678,3 +619,30 @@ func (this *FileListDB) deleteDB() {
|
|||||||
_ = os.Remove(this.dbPath + "-shm")
|
_ = os.Remove(this.dbPath + "-shm")
|
||||||
_ = os.Remove(this.dbPath + "-wal")
|
_ = os.Remove(this.dbPath + "-wal")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 加载Hash列表
|
||||||
|
func (this *FileListDB) loadHashMap() {
|
||||||
|
this.hashMapIsLoaded = false
|
||||||
|
|
||||||
|
err := this.hashMap.Load(this)
|
||||||
|
if err != nil {
|
||||||
|
remotelogs.Error("LIST_FILE_DB", "load hash map failed: "+err.Error()+"(file: "+this.dbPath+")")
|
||||||
|
|
||||||
|
// 自动修复错误
|
||||||
|
// TODO 将来希望能尽可能恢复以往数据库中的内容
|
||||||
|
if strings.Contains(err.Error(), "database is closed") || strings.Contains(err.Error(), "database disk image is malformed") {
|
||||||
|
_ = this.Close()
|
||||||
|
this.deleteDB()
|
||||||
|
remotelogs.Println("LIST_FILE_DB", "recreating the database (file:"+this.dbPath+") ...")
|
||||||
|
err = this.Open(this.dbPath)
|
||||||
|
if err != nil {
|
||||||
|
remotelogs.Error("LIST_FILE_DB", "recreate the database failed: "+err.Error()+" (file:"+this.dbPath+")")
|
||||||
|
} else {
|
||||||
|
_ = this.Init()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.hashMapIsLoaded = true
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import (
|
|||||||
"github.com/iwind/TeaGo/Tea"
|
"github.com/iwind/TeaGo/Tea"
|
||||||
_ "github.com/iwind/TeaGo/bootstrap"
|
_ "github.com/iwind/TeaGo/bootstrap"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestFileListDB_ListLFUItems(t *testing.T) {
|
func TestFileListDB_ListLFUItems(t *testing.T) {
|
||||||
@@ -34,28 +33,6 @@ func TestFileListDB_ListLFUItems(t *testing.T) {
|
|||||||
t.Log("[", len(hashList), "]", hashList)
|
t.Log("[", len(hashList), "]", hashList)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFileListDB_IncreaseHitAsync(t *testing.T) {
|
|
||||||
var db = caches.NewFileListDB()
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
_ = db.Close()
|
|
||||||
}()
|
|
||||||
|
|
||||||
err := db.Open(Tea.Root + "/data/cache-db-large.db")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = db.Init()
|
|
||||||
err = db.IncreaseHitAsync("4598e5231ba47d6ec7aa9ea640ff2eaf")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// wait transaction
|
|
||||||
time.Sleep(1 * time.Second)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFileListDB_CleanMatchKey(t *testing.T) {
|
func TestFileListDB_CleanMatchKey(t *testing.T) {
|
||||||
var db = caches.NewFileListDB()
|
var db = caches.NewFileListDB()
|
||||||
|
|
||||||
@@ -69,6 +46,9 @@ func TestFileListDB_CleanMatchKey(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
err = db.Init()
|
err = db.Init()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
err = db.CleanMatchKey("https://*.goedge.cn/large-text")
|
err = db.CleanMatchKey("https://*.goedge.cn/large-text")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -94,6 +74,9 @@ func TestFileListDB_CleanMatchPrefix(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
err = db.Init()
|
err = db.Init()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
err = db.CleanMatchPrefix("https://*.goedge.cn/large-text")
|
err = db.CleanMatchPrefix("https://*.goedge.cn/large-text")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -281,11 +281,6 @@ func TestFileList_PurgeLFU(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = list.IncreaseHit(stringutil.Md5("123456"))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var count = 0
|
var count = 0
|
||||||
err = list.PurgeLFU(caches.CountFileDB*2, func(hash string) error {
|
err = list.PurgeLFU(caches.CountFileDB*2, func(hash string) error {
|
||||||
t.Log(hash)
|
t.Log(hash)
|
||||||
@@ -356,41 +351,6 @@ func TestFileList_CleanAll(t *testing.T) {
|
|||||||
t.Log(list.Count())
|
t.Log(list.Count())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFileList_IncreaseHit(t *testing.T) {
|
|
||||||
var list = caches.NewFileList(Tea.Root + "/data/cache-index/p1")
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
_ = list.Close()
|
|
||||||
}()
|
|
||||||
|
|
||||||
err := list.Init()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
_ = list.Close()
|
|
||||||
}()
|
|
||||||
|
|
||||||
var before = time.Now()
|
|
||||||
defer func() {
|
|
||||||
t.Log(time.Since(before).Seconds()*1000, "ms")
|
|
||||||
}()
|
|
||||||
|
|
||||||
var count = 1_000_000
|
|
||||||
|
|
||||||
if !testutils.IsSingleTesting() {
|
|
||||||
count = 10
|
|
||||||
}
|
|
||||||
for i := 0; i < count; i++ {
|
|
||||||
err = list.IncreaseHit(stringutil.Md5("abc" + types.String(i)))
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
t.Log("ok")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFileList_UpgradeV3(t *testing.T) {
|
func TestFileList_UpgradeV3(t *testing.T) {
|
||||||
var list = caches.NewFileList(Tea.Root + "/data/cache-index/p43").(*caches.FileList)
|
var list = caches.NewFileList(Tea.Root + "/data/cache-index/p43").(*caches.FileList)
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package caches
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
|
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/zero"
|
|
||||||
"github.com/iwind/TeaGo/logs"
|
"github.com/iwind/TeaGo/logs"
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
@@ -19,9 +18,6 @@ type MemoryList struct {
|
|||||||
|
|
||||||
itemMaps map[string]map[string]*Item // prefix => { hash => item }
|
itemMaps map[string]map[string]*Item // prefix => { hash => item }
|
||||||
|
|
||||||
weekItemMaps map[int32]map[string]zero.Zero // week => { hash => Zero }
|
|
||||||
minWeek int32
|
|
||||||
|
|
||||||
prefixes []string
|
prefixes []string
|
||||||
locker sync.RWMutex
|
locker sync.RWMutex
|
||||||
onAdd func(item *Item)
|
onAdd func(item *Item)
|
||||||
@@ -32,9 +28,7 @@ type MemoryList struct {
|
|||||||
|
|
||||||
func NewMemoryList() ListInterface {
|
func NewMemoryList() ListInterface {
|
||||||
return &MemoryList{
|
return &MemoryList{
|
||||||
itemMaps: map[string]map[string]*Item{},
|
itemMaps: map[string]map[string]*Item{},
|
||||||
weekItemMaps: map[int32]map[string]zero.Zero{},
|
|
||||||
minWeek: currentWeek(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,7 +50,6 @@ func (this *MemoryList) Reset() error {
|
|||||||
for key := range this.itemMaps {
|
for key := range this.itemMaps {
|
||||||
this.itemMaps[key] = map[string]*Item{}
|
this.itemMaps[key] = map[string]*Item{}
|
||||||
}
|
}
|
||||||
this.weekItemMaps = map[int32]map[string]zero.Zero{}
|
|
||||||
this.locker.Unlock()
|
this.locker.Unlock()
|
||||||
|
|
||||||
atomic.StoreInt64(&this.count, 0)
|
atomic.StoreInt64(&this.count, 0)
|
||||||
@@ -65,10 +58,6 @@ func (this *MemoryList) Reset() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *MemoryList) Add(hash string, item *Item) error {
|
func (this *MemoryList) Add(hash string, item *Item) error {
|
||||||
if item.Week == 0 {
|
|
||||||
item.Week = currentWeek()
|
|
||||||
}
|
|
||||||
|
|
||||||
this.locker.Lock()
|
this.locker.Lock()
|
||||||
|
|
||||||
prefix := this.prefix(hash)
|
prefix := this.prefix(hash)
|
||||||
@@ -81,14 +70,6 @@ func (this *MemoryList) Add(hash string, item *Item) error {
|
|||||||
// 先删除,为了可以正确触发统计
|
// 先删除,为了可以正确触发统计
|
||||||
oldItem, ok := itemMap[hash]
|
oldItem, ok := itemMap[hash]
|
||||||
if ok {
|
if ok {
|
||||||
// 从week map中删除
|
|
||||||
if oldItem.Week > 0 {
|
|
||||||
wm, ok := this.weekItemMaps[oldItem.Week]
|
|
||||||
if ok {
|
|
||||||
delete(wm, hash)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 回调
|
// 回调
|
||||||
if this.onRemove != nil {
|
if this.onRemove != nil {
|
||||||
this.onRemove(oldItem)
|
this.onRemove(oldItem)
|
||||||
@@ -104,14 +85,6 @@ func (this *MemoryList) Add(hash string, item *Item) error {
|
|||||||
|
|
||||||
itemMap[hash] = item
|
itemMap[hash] = item
|
||||||
|
|
||||||
// week map
|
|
||||||
wm, ok := this.weekItemMaps[item.Week]
|
|
||||||
if ok {
|
|
||||||
wm[hash] = zero.New()
|
|
||||||
} else {
|
|
||||||
this.weekItemMaps[item.Week] = map[string]zero.Zero{hash: zero.New()}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.locker.Unlock()
|
this.locker.Unlock()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -242,14 +215,6 @@ func (this *MemoryList) Remove(hash string) error {
|
|||||||
|
|
||||||
atomic.AddInt64(&this.count, -1)
|
atomic.AddInt64(&this.count, -1)
|
||||||
delete(itemMap, hash)
|
delete(itemMap, hash)
|
||||||
|
|
||||||
// week map
|
|
||||||
if item.Week > 0 {
|
|
||||||
wm, ok := this.weekItemMaps[item.Week]
|
|
||||||
if ok {
|
|
||||||
delete(wm, hash)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.locker.Unlock()
|
this.locker.Unlock()
|
||||||
@@ -261,12 +226,12 @@ func (this *MemoryList) Remove(hash string) error {
|
|||||||
// callback 每次发现过期key的调用
|
// callback 每次发现过期key的调用
|
||||||
func (this *MemoryList) Purge(count int, callback func(hash string) error) (int, error) {
|
func (this *MemoryList) Purge(count int, callback func(hash string) error) (int, error) {
|
||||||
this.locker.Lock()
|
this.locker.Lock()
|
||||||
deletedHashList := []string{}
|
var deletedHashList = []string{}
|
||||||
|
|
||||||
if this.purgeIndex >= len(this.prefixes) {
|
if this.purgeIndex >= len(this.prefixes) {
|
||||||
this.purgeIndex = 0
|
this.purgeIndex = 0
|
||||||
}
|
}
|
||||||
prefix := this.prefixes[this.purgeIndex]
|
var prefix = this.prefixes[this.purgeIndex]
|
||||||
|
|
||||||
this.purgeIndex++
|
this.purgeIndex++
|
||||||
|
|
||||||
@@ -290,14 +255,6 @@ func (this *MemoryList) Purge(count int, callback func(hash string) error) (int,
|
|||||||
delete(itemMap, hash)
|
delete(itemMap, hash)
|
||||||
deletedHashList = append(deletedHashList, hash)
|
deletedHashList = append(deletedHashList, hash)
|
||||||
|
|
||||||
// week map
|
|
||||||
if item.Week > 0 {
|
|
||||||
wm, ok := this.weekItemMaps[item.Week]
|
|
||||||
if ok {
|
|
||||||
delete(wm, hash)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
countFound++
|
countFound++
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -322,63 +279,48 @@ func (this *MemoryList) PurgeLFU(count int, callback func(hash string) error) er
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var week = currentWeek()
|
|
||||||
if this.minWeek > week {
|
|
||||||
this.minWeek = week
|
|
||||||
}
|
|
||||||
|
|
||||||
var deletedHashList = []string{}
|
var deletedHashList = []string{}
|
||||||
|
|
||||||
|
var week = currentWeek()
|
||||||
|
var round = 0
|
||||||
|
|
||||||
|
this.locker.Lock()
|
||||||
|
|
||||||
Loop:
|
Loop:
|
||||||
for w := this.minWeek; w <= week; w++ {
|
for {
|
||||||
this.minWeek = w
|
var found = false
|
||||||
|
round++
|
||||||
|
for _, itemMap := range this.itemMaps {
|
||||||
|
for hash, item := range itemMap {
|
||||||
|
found = true
|
||||||
|
|
||||||
this.locker.Lock()
|
if week-item.Week <= 1 /** 最近有在使用 **/ && round <= 3 /** 查找轮数过多还不满足数量要求的就不再限制 **/ {
|
||||||
wm, ok := this.weekItemMaps[w]
|
continue
|
||||||
if ok {
|
|
||||||
var wc = len(wm)
|
|
||||||
if wc == 0 {
|
|
||||||
delete(this.weekItemMaps, w)
|
|
||||||
} else {
|
|
||||||
if wc <= count {
|
|
||||||
delete(this.weekItemMaps, w)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO 未来支持按照点击量排序
|
if this.onRemove != nil {
|
||||||
for hash := range wm {
|
this.onRemove(item)
|
||||||
count--
|
|
||||||
|
|
||||||
if count < 0 {
|
|
||||||
this.locker.Unlock()
|
|
||||||
break Loop
|
|
||||||
}
|
|
||||||
|
|
||||||
delete(wm, hash)
|
|
||||||
|
|
||||||
itemMap, ok := this.itemMaps[this.prefix(hash)]
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
item, ok := itemMap[hash]
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if this.onRemove != nil {
|
|
||||||
this.onRemove(item)
|
|
||||||
}
|
|
||||||
|
|
||||||
atomic.AddInt64(&this.count, -1)
|
|
||||||
delete(itemMap, hash)
|
|
||||||
deletedHashList = append(deletedHashList, hash)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
atomic.AddInt64(&this.count, -1)
|
||||||
|
delete(itemMap, hash)
|
||||||
|
deletedHashList = append(deletedHashList, hash)
|
||||||
|
|
||||||
|
count--
|
||||||
|
if count <= 0 {
|
||||||
|
break Loop
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
delete(this.weekItemMaps, w)
|
|
||||||
}
|
}
|
||||||
this.locker.Unlock()
|
if !found {
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.locker.Unlock()
|
||||||
|
|
||||||
// 执行外部操作
|
// 执行外部操作
|
||||||
for _, hash := range deletedHashList {
|
for _, hash := range deletedHashList {
|
||||||
if callback != nil {
|
if callback != nil {
|
||||||
@@ -451,23 +393,7 @@ func (this *MemoryList) IncreaseHit(hash string) error {
|
|||||||
|
|
||||||
item, ok := itemMap[hash]
|
item, ok := itemMap[hash]
|
||||||
if ok {
|
if ok {
|
||||||
var week = currentWeek()
|
item.Week = currentWeek()
|
||||||
|
|
||||||
// 交换位置
|
|
||||||
if item.Week > 0 && item.Week != week {
|
|
||||||
wm, ok := this.weekItemMaps[item.Week]
|
|
||||||
if ok {
|
|
||||||
delete(wm, hash)
|
|
||||||
}
|
|
||||||
wm, ok = this.weekItemMaps[week]
|
|
||||||
if ok {
|
|
||||||
wm[hash] = zero.New()
|
|
||||||
} else {
|
|
||||||
this.weekItemMaps[week] = map[string]zero.Zero{hash: zero.New()}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
item.IncreaseHit(week)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.locker.Unlock()
|
this.locker.Unlock()
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ import (
|
|||||||
"github.com/cespare/xxhash"
|
"github.com/cespare/xxhash"
|
||||||
"github.com/iwind/TeaGo/logs"
|
"github.com/iwind/TeaGo/logs"
|
||||||
"github.com/iwind/TeaGo/rands"
|
"github.com/iwind/TeaGo/rands"
|
||||||
|
"github.com/iwind/TeaGo/types"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@@ -32,7 +34,6 @@ func TestMemoryList_Add(t *testing.T) {
|
|||||||
})
|
})
|
||||||
t.Log(list.prefixes)
|
t.Log(list.prefixes)
|
||||||
logs.PrintAsJSON(list.itemMaps, t)
|
logs.PrintAsJSON(list.itemMaps, t)
|
||||||
logs.PrintAsJSON(list.weekItemMaps, t)
|
|
||||||
t.Log(list.Count())
|
t.Log(list.Count())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,7 +52,6 @@ func TestMemoryList_Remove(t *testing.T) {
|
|||||||
})
|
})
|
||||||
_ = list.Remove("b")
|
_ = list.Remove("b")
|
||||||
list.print(t)
|
list.print(t)
|
||||||
logs.PrintAsJSON(list.weekItemMaps, t)
|
|
||||||
t.Log(list.Count())
|
t.Log(list.Count())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,7 +83,6 @@ func TestMemoryList_Purge(t *testing.T) {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
list.print(t)
|
list.print(t)
|
||||||
logs.PrintAsJSON(list.weekItemMaps, t)
|
|
||||||
|
|
||||||
for i := 0; i < 1000; i++ {
|
for i := 0; i < 1000; i++ {
|
||||||
_, _ = list.Purge(100, func(hash string) error {
|
_, _ = list.Purge(100, func(hash string) error {
|
||||||
@@ -172,27 +171,64 @@ func TestMemoryList_CleanPrefix(t *testing.T) {
|
|||||||
t.Log(time.Since(before).Seconds()*1000, "ms")
|
t.Log(time.Since(before).Seconds()*1000, "ms")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMapRandomDelete(t *testing.T) {
|
||||||
|
var countMap = map[int]int{} // k => count
|
||||||
|
|
||||||
|
for j := 0; j < 1_000_000; j++ {
|
||||||
|
var m = map[int]bool{}
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
m[i] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
var count = 0
|
||||||
|
for k := range m {
|
||||||
|
delete(m, k)
|
||||||
|
count++
|
||||||
|
if count >= 10 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for k := range m {
|
||||||
|
countMap[k]++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var counts = []int{}
|
||||||
|
for _, count := range countMap {
|
||||||
|
counts = append(counts, count)
|
||||||
|
}
|
||||||
|
sort.Ints(counts)
|
||||||
|
t.Log("["+types.String(len(counts))+"]", counts)
|
||||||
|
}
|
||||||
|
|
||||||
func TestMemoryList_PurgeLFU(t *testing.T) {
|
func TestMemoryList_PurgeLFU(t *testing.T) {
|
||||||
var list = NewMemoryList().(*MemoryList)
|
var list = NewMemoryList().(*MemoryList)
|
||||||
list.minWeek = 2704
|
|
||||||
|
|
||||||
var before = time.Now()
|
var before = time.Now()
|
||||||
defer func() {
|
defer func() {
|
||||||
t.Log(time.Since(before).Seconds()*1000, "ms")
|
t.Log(time.Since(before).Seconds()*1000, "ms")
|
||||||
}()
|
}()
|
||||||
|
|
||||||
t.Log("current week:", currentWeek())
|
|
||||||
|
|
||||||
_ = list.Add("1", &Item{})
|
_ = list.Add("1", &Item{})
|
||||||
_ = list.Add("2", &Item{})
|
_ = list.Add("2", &Item{})
|
||||||
_ = list.Add("3", &Item{})
|
_ = list.Add("3", &Item{})
|
||||||
_ = list.Add("4", &Item{})
|
_ = list.Add("4", &Item{})
|
||||||
_ = list.Add("5", &Item{})
|
_ = list.Add("5", &Item{})
|
||||||
_ = list.Add("6", &Item{Week: 2704})
|
|
||||||
_ = list.Add("7", &Item{Week: 2704})
|
|
||||||
_ = list.Add("8", &Item{Week: 2705})
|
|
||||||
|
|
||||||
err := list.PurgeLFU(2, func(hash string) error {
|
//_ = list.IncreaseHit("1")
|
||||||
|
//_ = list.IncreaseHit("2")
|
||||||
|
//_ = list.IncreaseHit("3")
|
||||||
|
//_ = list.IncreaseHit("4")
|
||||||
|
//_ = list.IncreaseHit("5")
|
||||||
|
|
||||||
|
count, err := list.Count()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log("count items before purge:", count)
|
||||||
|
|
||||||
|
err = list.PurgeLFU(5, func(hash string) error {
|
||||||
t.Log("purge lfu:", hash)
|
t.Log("purge lfu:", hash)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
@@ -201,40 +237,18 @@ func TestMemoryList_PurgeLFU(t *testing.T) {
|
|||||||
}
|
}
|
||||||
t.Log("ok")
|
t.Log("ok")
|
||||||
|
|
||||||
logs.PrintAsJSON(list.weekItemMaps, t)
|
count, err = list.Count()
|
||||||
t.Log(list.Count())
|
if err != nil {
|
||||||
}
|
t.Fatal(err)
|
||||||
|
}
|
||||||
func TestMemoryList_IncreaseHit(t *testing.T) {
|
t.Log("count items left:", count)
|
||||||
var list = NewMemoryList().(*MemoryList)
|
|
||||||
var item = &Item{}
|
|
||||||
item.Week = 2705
|
|
||||||
item.Week2Hits = 100
|
|
||||||
|
|
||||||
_ = list.Add("a", &Item{})
|
|
||||||
_ = list.Add("a", item)
|
|
||||||
t.Log("hits1:", item.Week1Hits, "hits2:", item.Week2Hits, "week:", item.Week)
|
|
||||||
logs.PrintAsJSON(list.weekItemMaps, t)
|
|
||||||
|
|
||||||
_ = list.IncreaseHit("a")
|
|
||||||
t.Log("hits1:", item.Week1Hits, "hits2:", item.Week2Hits, "week:", item.Week)
|
|
||||||
logs.PrintAsJSON(list.weekItemMaps, t)
|
|
||||||
|
|
||||||
_ = list.IncreaseHit("a")
|
|
||||||
t.Log("hits1:", item.Week1Hits, "hits2:", item.Week2Hits, "week:", item.Week)
|
|
||||||
logs.PrintAsJSON(list.weekItemMaps, t)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMemoryList_CleanAll(t *testing.T) {
|
func TestMemoryList_CleanAll(t *testing.T) {
|
||||||
var list = NewMemoryList().(*MemoryList)
|
var list = NewMemoryList().(*MemoryList)
|
||||||
var item = &Item{}
|
|
||||||
item.Week = 2705
|
|
||||||
item.Week2Hits = 100
|
|
||||||
|
|
||||||
_ = list.Add("a", &Item{})
|
_ = list.Add("a", &Item{})
|
||||||
_ = list.CleanAll()
|
_ = list.CleanAll()
|
||||||
logs.PrintAsJSON(list.itemMaps, t)
|
logs.PrintAsJSON(list.itemMaps, t)
|
||||||
logs.PrintAsJSON(list.weekItemMaps, t)
|
|
||||||
t.Log(list.Count())
|
t.Log(list.Count())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package caches
|
package caches
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
|
||||||
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
||||||
@@ -8,6 +9,7 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
"github.com/iwind/TeaGo/lists"
|
"github.com/iwind/TeaGo/lists"
|
||||||
"github.com/iwind/TeaGo/types"
|
"github.com/iwind/TeaGo/types"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
@@ -148,8 +150,7 @@ func (this *Manager) FindPolicy(policyId int64) *serverconfigs.HTTPCachePolicy {
|
|||||||
this.locker.RLock()
|
this.locker.RLock()
|
||||||
defer this.locker.RUnlock()
|
defer this.locker.RUnlock()
|
||||||
|
|
||||||
p, _ := this.policyMap[policyId]
|
return this.policyMap[policyId]
|
||||||
return p
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindStorageWithPolicy 根据策略ID查找存储
|
// FindStorageWithPolicy 根据策略ID查找存储
|
||||||
@@ -157,8 +158,7 @@ func (this *Manager) FindStorageWithPolicy(policyId int64) StorageInterface {
|
|||||||
this.locker.RLock()
|
this.locker.RLock()
|
||||||
defer this.locker.RUnlock()
|
defer this.locker.RUnlock()
|
||||||
|
|
||||||
storage, _ := this.storageMap[policyId]
|
return this.storageMap[policyId]
|
||||||
return storage
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewStorageWithPolicy 根据策略获取存储对象
|
// NewStorageWithPolicy 根据策略获取存储对象
|
||||||
@@ -178,8 +178,30 @@ func (this *Manager) TotalDiskSize() int64 {
|
|||||||
defer this.locker.RUnlock()
|
defer this.locker.RUnlock()
|
||||||
|
|
||||||
var total = int64(0)
|
var total = int64(0)
|
||||||
|
var sidMap = map[string]bool{} // partition sid => bool
|
||||||
for _, storage := range this.storageMap {
|
for _, storage := range this.storageMap {
|
||||||
total += storage.TotalDiskSize()
|
// 这里不能直接用 storage.TotalDiskSize() 相加,因为多个缓存策略缓存目录可能处在同一个分区目录下
|
||||||
|
fileStorage, ok := storage.(*FileStorage)
|
||||||
|
if ok {
|
||||||
|
var options = fileStorage.options // copy
|
||||||
|
if options != nil {
|
||||||
|
var dir = options.Dir // copy
|
||||||
|
if len(dir) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var stat = &unix.Statfs_t{}
|
||||||
|
err := unix.Statfs(dir, stat)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var sid = fmt.Sprintf("%d_%d", stat.Fsid.Val[0], stat.Fsid.Val[1])
|
||||||
|
if sidMap[sid] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
sidMap[sid] = true
|
||||||
|
total += int64(stat.Blocks-stat.Bfree) * int64(stat.Bsize) // we add extra int64() for darwin
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if total < 0 {
|
if total < 0 {
|
||||||
@@ -234,3 +256,19 @@ func (this *Manager) FindAllStorages() []StorageInterface {
|
|||||||
}
|
}
|
||||||
return storages
|
return storages
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ScanGarbageCaches 清理目录中“失联”的缓存文件
|
||||||
|
func (this *Manager) ScanGarbageCaches(callback func(path string) error) error {
|
||||||
|
var storages = this.FindAllStorages()
|
||||||
|
for _, storage := range storages {
|
||||||
|
fileStorage, ok := storage.(*FileStorage)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
err := fileStorage.ScanGarbageCaches(callback)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,52 +0,0 @@
|
|||||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
|
||||||
|
|
||||||
package caches
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
modeSlow int32 = 1
|
|
||||||
modeFast int32 = 2
|
|
||||||
)
|
|
||||||
|
|
||||||
// MaxOpenFiles max open files manager
|
|
||||||
type MaxOpenFiles struct {
|
|
||||||
ticker *time.Ticker
|
|
||||||
mode int32
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewMaxOpenFiles() *MaxOpenFiles {
|
|
||||||
var f = &MaxOpenFiles{}
|
|
||||||
f.ticker = time.NewTicker(1 * time.Second)
|
|
||||||
f.init()
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *MaxOpenFiles) init() {
|
|
||||||
goman.New(func() {
|
|
||||||
for range this.ticker.C {
|
|
||||||
// reset mode
|
|
||||||
atomic.StoreInt32(&this.mode, modeFast)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *MaxOpenFiles) Fast() {
|
|
||||||
atomic.AddInt32(&this.mode, modeFast)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *MaxOpenFiles) FinishAll() {
|
|
||||||
this.Fast()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *MaxOpenFiles) Slow() {
|
|
||||||
atomic.StoreInt32(&this.mode, modeSlow)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *MaxOpenFiles) Next() bool {
|
|
||||||
return atomic.LoadInt32(&this.mode) != modeSlow
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
|
||||||
|
|
||||||
package caches_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/caches"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestNewMaxOpenFiles(t *testing.T) {
|
|
||||||
var maxOpenFiles = caches.NewMaxOpenFiles()
|
|
||||||
maxOpenFiles.Fast()
|
|
||||||
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())
|
|
||||||
|
|
||||||
time.Sleep(1 * time.Second)
|
|
||||||
t.Log("slow 1 second:", maxOpenFiles.Next())
|
|
||||||
|
|
||||||
maxOpenFiles.Slow()
|
|
||||||
t.Log("slow:", maxOpenFiles.Next())
|
|
||||||
|
|
||||||
maxOpenFiles.Fast()
|
|
||||||
t.Log("fast:", maxOpenFiles.Next())
|
|
||||||
}
|
|
||||||
@@ -3,6 +3,7 @@ package caches
|
|||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
rangeutils "github.com/TeaOSLab/EdgeNode/internal/utils/ranges"
|
rangeutils "github.com/TeaOSLab/EdgeNode/internal/utils/ranges"
|
||||||
"github.com/iwind/TeaGo/types"
|
"github.com/iwind/TeaGo/types"
|
||||||
"io"
|
"io"
|
||||||
@@ -19,7 +20,7 @@ type PartialFileReader struct {
|
|||||||
func NewPartialFileReader(fp *os.File) *PartialFileReader {
|
func NewPartialFileReader(fp *os.File) *PartialFileReader {
|
||||||
return &PartialFileReader{
|
return &PartialFileReader{
|
||||||
FileReader: NewFileReader(fp),
|
FileReader: NewFileReader(fp),
|
||||||
rangePath: partialRangesFilePath(fp.Name()),
|
rangePath: PartialRangesFilePath(fp.Name()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,7 +47,7 @@ func (this *PartialFileReader) InitAutoDiscard(autoDiscard bool) error {
|
|||||||
// 读取Range
|
// 读取Range
|
||||||
ranges, err := NewPartialRangesFromFile(this.rangePath)
|
ranges, err := NewPartialRangesFromFile(this.rangePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("read ranges failed: " + err.Error())
|
return fmt.Errorf("read ranges failed: %w", err)
|
||||||
}
|
}
|
||||||
this.ranges = ranges
|
this.ranges = ranges
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/trackers"
|
"github.com/TeaOSLab/EdgeNode/internal/trackers"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/utils/fasttime"
|
||||||
fsutils "github.com/TeaOSLab/EdgeNode/internal/utils/fs"
|
fsutils "github.com/TeaOSLab/EdgeNode/internal/utils/fs"
|
||||||
setutils "github.com/TeaOSLab/EdgeNode/internal/utils/sets"
|
setutils "github.com/TeaOSLab/EdgeNode/internal/utils/sets"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils/sizes"
|
"github.com/TeaOSLab/EdgeNode/internal/utils/sizes"
|
||||||
@@ -22,6 +23,8 @@ import (
|
|||||||
"github.com/iwind/TeaGo/rands"
|
"github.com/iwind/TeaGo/rands"
|
||||||
"github.com/iwind/TeaGo/types"
|
"github.com/iwind/TeaGo/types"
|
||||||
stringutil "github.com/iwind/TeaGo/utils/string"
|
stringutil "github.com/iwind/TeaGo/utils/string"
|
||||||
|
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||||
|
"github.com/shirou/gopsutil/v3/load"
|
||||||
"math"
|
"math"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@@ -55,17 +58,14 @@ const (
|
|||||||
HotItemLifeSeconds int64 = 3600 // 热点数据生命周期
|
HotItemLifeSeconds int64 = 3600 // 热点数据生命周期
|
||||||
FileToMemoryMaxSize = 32 * sizes.M // 可以从文件写入到内存的最大文件尺寸
|
FileToMemoryMaxSize = 32 * sizes.M // 可以从文件写入到内存的最大文件尺寸
|
||||||
FileTmpSuffix = ".tmp"
|
FileTmpSuffix = ".tmp"
|
||||||
MinDiskSpace uint64 = 5 << 30 // 当前磁盘最小剩余空间
|
DefaultMinDiskFreeSpace uint64 = 5 << 30 // 当前磁盘最小剩余空间
|
||||||
|
DefaultStaleCacheSeconds = 1200 // 过时缓存留存时间
|
||||||
|
HashKeyLength = 32
|
||||||
)
|
)
|
||||||
|
|
||||||
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()
|
|
||||||
|
|
||||||
const maxOpenFilesSlowCost = 1000 * time.Microsecond // us
|
|
||||||
const protectingLoadWhenDump = false
|
|
||||||
|
|
||||||
// FileStorage 文件缓存
|
// FileStorage 文件缓存
|
||||||
//
|
//
|
||||||
// 文件结构:
|
// 文件结构:
|
||||||
@@ -162,6 +162,7 @@ func (this *FileStorage) UpdatePolicy(newPolicy *serverconfigs.HTTPCachePolicy)
|
|||||||
IsFull: false,
|
IsFull: false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
this.subDirs = subDirs
|
||||||
this.checkDiskSpace()
|
this.checkDiskSpace()
|
||||||
|
|
||||||
err = newOptions.Init()
|
err = newOptions.Init()
|
||||||
@@ -262,7 +263,7 @@ func (this *FileStorage) Init() error {
|
|||||||
} else {
|
} else {
|
||||||
err = os.MkdirAll(dir, 0777)
|
err = os.MkdirAll(dir, 0777)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("[CACHE]can not create dir:" + err.Error())
|
return fmt.Errorf("[CACHE]can not create dir: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -302,6 +303,9 @@ func (this *FileStorage) Init() error {
|
|||||||
// 检查磁盘空间
|
// 检查磁盘空间
|
||||||
this.checkDiskSpace()
|
this.checkDiskSpace()
|
||||||
|
|
||||||
|
// clean *.trash directories
|
||||||
|
this.cleanAllDeletedDirs()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -424,7 +428,7 @@ func (this *FileStorage) openWriter(key string, expiredAt int64, status int, hea
|
|||||||
maxMemorySize = maxSize
|
maxMemorySize = maxSize
|
||||||
}
|
}
|
||||||
var memoryStorage = this.memoryStorage
|
var memoryStorage = this.memoryStorage
|
||||||
if !isFlushing && !isPartial && memoryStorage != nil && ((bodySize > 0 && bodySize < maxMemorySize) || bodySize < 0) {
|
if !fsutils.DiskIsExtremelyFast() && !isFlushing && !isPartial && memoryStorage != nil && ((bodySize > 0 && bodySize < maxMemorySize) || bodySize < 0) {
|
||||||
writer, err := memoryStorage.OpenWriter(key, expiredAt, status, headerSize, bodySize, maxMemorySize, false)
|
writer, err := memoryStorage.OpenWriter(key, expiredAt, status, headerSize, bodySize, maxMemorySize, false)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return writer, nil
|
return writer, nil
|
||||||
@@ -445,9 +449,9 @@ func (this *FileStorage) openWriter(key string, expiredAt int64, status int, hea
|
|||||||
return nil, ErrFileIsWriting
|
return nil, ErrFileIsWriting
|
||||||
}
|
}
|
||||||
|
|
||||||
if !isFlushing && !maxOpenFiles.Next() {
|
if !isFlushing && !fsutils.WriteReady() {
|
||||||
sharedWritingFileKeyLocker.Unlock()
|
sharedWritingFileKeyLocker.Unlock()
|
||||||
return nil, ErrTooManyOpenFiles
|
return nil, ErrServerIsBusy
|
||||||
}
|
}
|
||||||
|
|
||||||
sharedWritingFileKeyMap[key] = zero.New()
|
sharedWritingFileKeyMap[key] = zero.New()
|
||||||
@@ -456,9 +460,6 @@ func (this *FileStorage) openWriter(key string, expiredAt int64, status int, hea
|
|||||||
if !isOk {
|
if !isOk {
|
||||||
sharedWritingFileKeyLocker.Lock()
|
sharedWritingFileKeyLocker.Lock()
|
||||||
delete(sharedWritingFileKeyMap, key)
|
delete(sharedWritingFileKeyMap, key)
|
||||||
if len(sharedWritingFileKeyMap) == 0 {
|
|
||||||
maxOpenFiles.FinishAll()
|
|
||||||
}
|
|
||||||
sharedWritingFileKeyLocker.Unlock()
|
sharedWritingFileKeyLocker.Unlock()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@@ -490,7 +491,7 @@ func (this *FileStorage) openWriter(key string, expiredAt int64, status int, hea
|
|||||||
stat, err := os.Stat(cachePath)
|
stat, err := os.Stat(cachePath)
|
||||||
|
|
||||||
// 检查两次写入缓存的时间是否过于相近,分片内容不受此限制
|
// 检查两次写入缓存的时间是否过于相近,分片内容不受此限制
|
||||||
if err == nil && !isPartial && time.Now().Sub(stat.ModTime()) <= 1*time.Second {
|
if err == nil && !isPartial && time.Since(stat.ModTime()) <= 1*time.Second {
|
||||||
// 防止并发连续写入
|
// 防止并发连续写入
|
||||||
return nil, ErrFileIsWriting
|
return nil, ErrFileIsWriting
|
||||||
}
|
}
|
||||||
@@ -558,7 +559,6 @@ func (this *FileStorage) openWriter(key string, expiredAt int64, status int, hea
|
|||||||
if isNewCreated && existsFile {
|
if isNewCreated && existsFile {
|
||||||
flags |= os.O_TRUNC
|
flags |= os.O_TRUNC
|
||||||
}
|
}
|
||||||
var before = time.Now()
|
|
||||||
writer, err := os.OpenFile(tmpPath, flags, 0666)
|
writer, err := os.OpenFile(tmpPath, flags, 0666)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// TODO 检查在各个系统中的稳定性
|
// TODO 检查在各个系统中的稳定性
|
||||||
@@ -572,13 +572,6 @@ func (this *FileStorage) openWriter(key string, expiredAt int64, status int, hea
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !isFlushing {
|
|
||||||
if time.Since(before) >= maxOpenFilesSlowCost {
|
|
||||||
maxOpenFiles.Slow()
|
|
||||||
} else {
|
|
||||||
maxOpenFiles.Fast()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var removeOnFailure = true
|
var removeOnFailure = true
|
||||||
defer func() {
|
defer func() {
|
||||||
@@ -628,7 +621,9 @@ func (this *FileStorage) openWriter(key string, expiredAt int64, status int, hea
|
|||||||
metaBodySize = bodySize
|
metaBodySize = bodySize
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fsutils.WriteBegin()
|
||||||
_, err = writer.Write(metaBytes)
|
_, err = writer.Write(metaBytes)
|
||||||
|
fsutils.WriteEnd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -639,18 +634,12 @@ func (this *FileStorage) openWriter(key string, expiredAt int64, status int, hea
|
|||||||
return NewPartialFileWriter(writer, key, expiredAt, metaHeaderSize, metaBodySize, isNewCreated, isPartial, partialBodyOffset, partialRanges, func() {
|
return NewPartialFileWriter(writer, key, expiredAt, metaHeaderSize, metaBodySize, isNewCreated, isPartial, partialBodyOffset, partialRanges, 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, metaHeaderSize, metaBodySize, maxSize, func() {
|
return NewFileWriter(this, writer, key, expiredAt, metaHeaderSize, metaBodySize, maxSize, func() {
|
||||||
sharedWritingFileKeyLocker.Lock()
|
sharedWritingFileKeyLocker.Lock()
|
||||||
delete(sharedWritingFileKeyMap, key)
|
delete(sharedWritingFileKeyMap, key)
|
||||||
if len(sharedWritingFileKeyMap) == 0 {
|
|
||||||
maxOpenFiles.FinishAll()
|
|
||||||
}
|
|
||||||
sharedWritingFileKeyLocker.Unlock()
|
sharedWritingFileKeyLocker.Unlock()
|
||||||
}), nil
|
}), nil
|
||||||
}
|
}
|
||||||
@@ -764,7 +753,7 @@ func (this *FileStorage) CleanAll() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, info := range subDirs {
|
for _, info := range subDirs {
|
||||||
subDir := info.Name()
|
var subDir = info.Name()
|
||||||
|
|
||||||
// 检查目录名
|
// 检查目录名
|
||||||
if !dirNameReg.MatchString(subDir) {
|
if !dirNameReg.MatchString(subDir) {
|
||||||
@@ -772,7 +761,7 @@ func (this *FileStorage) CleanAll() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 修改目录名
|
// 修改目录名
|
||||||
tmpDir := dir + "/" + subDir + "-deleted"
|
var tmpDir = dir + "/" + subDir + "." + timeutil.Format("YmdHis") + ".trash"
|
||||||
err = os.Rename(dir+"/"+subDir, tmpDir)
|
err = os.Rename(dir+"/"+subDir, tmpDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -783,7 +772,11 @@ func (this *FileStorage) CleanAll() error {
|
|||||||
goman.New(func() {
|
goman.New(func() {
|
||||||
err = this.cleanDeletedDirs(dir)
|
err = this.cleanDeletedDirs(dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
remotelogs.Warn("CACHE", "delete '*-deleted' dirs failed: "+err.Error())
|
remotelogs.Warn("CACHE", "delete '*.trash' dirs failed: "+err.Error())
|
||||||
|
} else {
|
||||||
|
// try to clean again, to delete writing files when deleting
|
||||||
|
time.Sleep(10 * time.Minute)
|
||||||
|
_ = this.cleanDeletedDirs(dir)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -900,7 +893,7 @@ func (this *FileStorage) Stop() {
|
|||||||
|
|
||||||
// TotalDiskSize 消耗的磁盘尺寸
|
// TotalDiskSize 消耗的磁盘尺寸
|
||||||
func (this *FileStorage) TotalDiskSize() int64 {
|
func (this *FileStorage) TotalDiskSize() int64 {
|
||||||
stat, err := fsutils.StatCache(this.options.Dir)
|
stat, err := fsutils.StatDeviceCache(this.options.Dir)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return int64(stat.UsedSize())
|
return int64(stat.UsedSize())
|
||||||
}
|
}
|
||||||
@@ -940,7 +933,7 @@ func (this *FileStorage) keyPath(key string) (hash string, path string, diskIsFu
|
|||||||
|
|
||||||
// 获取Hash对应的文件路径
|
// 获取Hash对应的文件路径
|
||||||
func (this *FileStorage) hashPath(hash string) (path string, diskIsFull bool) {
|
func (this *FileStorage) hashPath(hash string) (path string, diskIsFull bool) {
|
||||||
if len(hash) != 32 {
|
if len(hash) != HashKeyLength {
|
||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
var dir string
|
var dir string
|
||||||
@@ -1004,28 +997,23 @@ func (this *FileStorage) initList() error {
|
|||||||
// 清理任务
|
// 清理任务
|
||||||
// TODO purge每个分区
|
// TODO purge每个分区
|
||||||
func (this *FileStorage) purgeLoop() {
|
func (this *FileStorage) purgeLoop() {
|
||||||
// 检查磁盘剩余空间
|
|
||||||
this.checkDiskSpace()
|
|
||||||
|
|
||||||
// 计算是否应该开启LFU清理
|
// 计算是否应该开启LFU清理
|
||||||
var capacityBytes = this.diskCapacityBytes()
|
var capacityBytes = this.diskCapacityBytes()
|
||||||
var startLFU = false
|
var startLFU = false
|
||||||
var lfuFreePercent = this.policy.PersistenceLFUFreePercent
|
var lfuFreePercent = this.policy.PersistenceLFUFreePercent
|
||||||
if lfuFreePercent <= 0 {
|
if lfuFreePercent <= 0 {
|
||||||
lfuFreePercent = 5
|
lfuFreePercent = 5
|
||||||
}
|
|
||||||
|
|
||||||
var hasFullDisk = this.mainDiskIsFull
|
// 2TB级别以上
|
||||||
if !hasFullDisk {
|
if capacityBytes>>30 > 2000 {
|
||||||
var subDirs = this.subDirs // copy slice
|
lfuFreePercent = 100 /** GB **/ / float32(capacityBytes>>30) * 100 /** % **/
|
||||||
for _, subDir := range subDirs {
|
if lfuFreePercent > 3 {
|
||||||
if subDir.IsFull {
|
lfuFreePercent = 3
|
||||||
hasFullDisk = true
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var hasFullDisk = this.hasFullDisk()
|
||||||
if hasFullDisk {
|
if hasFullDisk {
|
||||||
startLFU = true
|
startLFU = true
|
||||||
} else {
|
} else {
|
||||||
@@ -1044,8 +1032,15 @@ func (this *FileStorage) purgeLoop() {
|
|||||||
var times = 1
|
var times = 1
|
||||||
|
|
||||||
// 空闲时间多清理
|
// 空闲时间多清理
|
||||||
if utils.SharedFreeHoursManager.IsFreeHour() {
|
systemLoad, _ := load.Avg()
|
||||||
times = 5
|
if systemLoad != nil {
|
||||||
|
if systemLoad.Load5 < 2 {
|
||||||
|
times = 5
|
||||||
|
} else if systemLoad.Load5 < 3 {
|
||||||
|
times = 3
|
||||||
|
} else if systemLoad.Load5 < 5 {
|
||||||
|
times = 2
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处于LFU阈值时,多清理
|
// 处于LFU阈值时,多清理
|
||||||
@@ -1082,13 +1077,32 @@ func (this *FileStorage) purgeLoop() {
|
|||||||
|
|
||||||
// 磁盘空间不足时,清除老旧的缓存
|
// 磁盘空间不足时,清除老旧的缓存
|
||||||
if startLFU {
|
if startLFU {
|
||||||
|
var maxCount = 2000
|
||||||
|
var maxLoops = 5
|
||||||
|
|
||||||
|
if fsutils.DiskIsFast() {
|
||||||
|
maxCount = 5000
|
||||||
|
} else if fsutils.DiskIsExtremelyFast() {
|
||||||
|
maxCount = 10000
|
||||||
|
}
|
||||||
|
|
||||||
var total, _ = this.list.Count()
|
var total, _ = this.list.Count()
|
||||||
if total > 0 {
|
if total > 0 {
|
||||||
var count = types.Int(math.Ceil(float64(total) * float64(lfuFreePercent*2) / 100))
|
for {
|
||||||
if count > 0 {
|
maxLoops--
|
||||||
|
if maxLoops <= 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// 开始清理
|
||||||
|
var count = types.Int(math.Ceil(float64(total) * float64(lfuFreePercent*2) / 100))
|
||||||
|
if count <= 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
// 限制单次清理的条数,防止占用太多系统资源
|
// 限制单次清理的条数,防止占用太多系统资源
|
||||||
if count > 2000 {
|
if count > maxCount {
|
||||||
count = 2000
|
count = maxCount
|
||||||
}
|
}
|
||||||
|
|
||||||
remotelogs.Println("CACHE", "LFU purge policy '"+this.policy.Name+"' id: "+types.String(this.policy.Id)+", count: "+types.String(count))
|
remotelogs.Println("CACHE", "LFU purge policy '"+this.policy.Name+"' id: "+types.String(this.policy.Id)+", count: "+types.String(count))
|
||||||
@@ -1104,6 +1118,11 @@ func (this *FileStorage) purgeLoop() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
remotelogs.Warn("CACHE", "purge file storage in LFU failed: "+err.Error())
|
remotelogs.Warn("CACHE", "purge file storage in LFU failed: "+err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 检查硬盘空间状态
|
||||||
|
if !this.hasFullDisk() {
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1230,8 +1249,9 @@ func (this *FileStorage) hotLoop() {
|
|||||||
|
|
||||||
func (this *FileStorage) diskCapacityBytes() int64 {
|
func (this *FileStorage) diskCapacityBytes() int64 {
|
||||||
var c1 = this.policy.CapacityBytes()
|
var c1 = this.policy.CapacityBytes()
|
||||||
if SharedManager.MaxDiskCapacity != nil {
|
var nodeCapacity = SharedManager.MaxDiskCapacity // copy
|
||||||
var c2 = SharedManager.MaxDiskCapacity.Bytes()
|
if nodeCapacity != nil {
|
||||||
|
var c2 = nodeCapacity.Bytes()
|
||||||
if c2 > 0 {
|
if c2 > 0 {
|
||||||
return c2
|
return c2
|
||||||
}
|
}
|
||||||
@@ -1239,7 +1259,25 @@ func (this *FileStorage) diskCapacityBytes() int64 {
|
|||||||
return c1
|
return c1
|
||||||
}
|
}
|
||||||
|
|
||||||
// 清理 *-deleted 目录
|
// remove all *.trash directories under policy directory
|
||||||
|
func (this *FileStorage) cleanAllDeletedDirs() {
|
||||||
|
var rootDirs = []string{this.options.Dir}
|
||||||
|
var subDirs = this.subDirs // copy slice
|
||||||
|
if len(subDirs) > 0 {
|
||||||
|
for _, subDir := range subDirs {
|
||||||
|
rootDirs = append(rootDirs, subDir.Path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, rootDir := range rootDirs {
|
||||||
|
var dir = rootDir + "/p" + types.String(this.policy.Id)
|
||||||
|
goman.New(func() {
|
||||||
|
_ = this.cleanDeletedDirs(dir)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清理 *.trash 目录
|
||||||
// 由于在很多硬盘上耗时非常久,所以应该放在后台运行
|
// 由于在很多硬盘上耗时非常久,所以应该放在后台运行
|
||||||
func (this *FileStorage) cleanDeletedDirs(dir string) error {
|
func (this *FileStorage) cleanDeletedDirs(dir string) error {
|
||||||
fp, err := os.Open(dir)
|
fp, err := os.Open(dir)
|
||||||
@@ -1254,8 +1292,8 @@ func (this *FileStorage) cleanDeletedDirs(dir string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, info := range subDirs {
|
for _, info := range subDirs {
|
||||||
subDir := info.Name()
|
var subDir = info.Name()
|
||||||
if !strings.HasSuffix(subDir, "-deleted") {
|
if !strings.HasSuffix(subDir, ".trash") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1295,13 +1333,6 @@ func (this *FileStorage) increaseHit(key string, hash string, reader Reader) {
|
|||||||
this.hotMapLocker.Lock()
|
this.hotMapLocker.Lock()
|
||||||
hotItem, ok := this.hotMap[key]
|
hotItem, ok := this.hotMap[key]
|
||||||
|
|
||||||
// 限制热点数据存活时间
|
|
||||||
var unixTime = time.Now().Unix()
|
|
||||||
var expiresAt = reader.ExpiresAt()
|
|
||||||
if expiresAt <= 0 || expiresAt > unixTime+HotItemLifeSeconds {
|
|
||||||
expiresAt = unixTime + HotItemLifeSeconds
|
|
||||||
}
|
|
||||||
|
|
||||||
if ok {
|
if ok {
|
||||||
hotItem.Hits++
|
hotItem.Hits++
|
||||||
} else if len(this.hotMap) < HotItemSize { // 控制数量
|
} else if len(this.hotMap) < HotItemSize { // 控制数量
|
||||||
@@ -1327,7 +1358,7 @@ func (this *FileStorage) removeCacheFile(path string) error {
|
|||||||
err = nil
|
err = nil
|
||||||
|
|
||||||
// 删除Partial相关
|
// 删除Partial相关
|
||||||
var partialPath = partialRangesFilePath(path)
|
var partialPath = PartialRangesFilePath(path)
|
||||||
if openFileCache != nil {
|
if openFileCache != nil {
|
||||||
openFileCache.Close(partialPath)
|
openFileCache.Close(partialPath)
|
||||||
}
|
}
|
||||||
@@ -1410,7 +1441,7 @@ func (this *FileStorage) initOpenFileCache() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *FileStorage) runMemoryStorageSafety(f func(memoryStorage *MemoryStorage)) {
|
func (this *FileStorage) runMemoryStorageSafety(f func(memoryStorage *MemoryStorage)) {
|
||||||
var memoryStorage = this.memoryStorage
|
var memoryStorage = this.memoryStorage // copy
|
||||||
if memoryStorage != nil {
|
if memoryStorage != nil {
|
||||||
f(memoryStorage)
|
f(memoryStorage)
|
||||||
}
|
}
|
||||||
@@ -1418,21 +1449,63 @@ func (this *FileStorage) runMemoryStorageSafety(f func(memoryStorage *MemoryStor
|
|||||||
|
|
||||||
// 检查磁盘剩余空间
|
// 检查磁盘剩余空间
|
||||||
func (this *FileStorage) checkDiskSpace() {
|
func (this *FileStorage) checkDiskSpace() {
|
||||||
if this.options != nil && len(this.options.Dir) > 0 {
|
var minFreeSize = DefaultMinDiskFreeSpace
|
||||||
stat, err := fsutils.Stat(this.options.Dir)
|
|
||||||
|
var options = this.options // copy
|
||||||
|
if options != nil && options.MinFreeSize != nil && options.MinFreeSize.Bytes() > 0 {
|
||||||
|
minFreeSize = uint64(options.MinFreeSize.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
if options != nil && len(options.Dir) > 0 {
|
||||||
|
stat, err := fsutils.StatDevice(options.Dir)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
this.mainDiskIsFull = stat.FreeSize() < MinDiskSpace
|
this.mainDiskIsFull = stat.FreeSize() < minFreeSize
|
||||||
|
|
||||||
|
// check capacity (only on main directory) when node capacity had not been set
|
||||||
|
if !this.mainDiskIsFull {
|
||||||
|
var capacityBytes int64
|
||||||
|
var maxDiskCapacity = SharedManager.MaxDiskCapacity // copy
|
||||||
|
if maxDiskCapacity != nil && maxDiskCapacity.Bytes() > 0 {
|
||||||
|
capacityBytes = SharedManager.MaxDiskCapacity.Bytes()
|
||||||
|
} else {
|
||||||
|
var policy = this.policy // copy
|
||||||
|
if policy != nil {
|
||||||
|
capacityBytes = policy.CapacityBytes() // copy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if capacityBytes > 0 && stat.UsedSize() >= uint64(capacityBytes) {
|
||||||
|
this.mainDiskIsFull = true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var subDirs = this.subDirs // copy slice
|
var subDirs = this.subDirs // copy slice
|
||||||
for _, subDir := range subDirs {
|
for _, subDir := range subDirs {
|
||||||
stat, err := fsutils.Stat(subDir.Path)
|
stat, err := fsutils.StatDevice(subDir.Path)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
subDir.IsFull = stat.FreeSize() < MinDiskSpace
|
subDir.IsFull = stat.FreeSize() < minFreeSize
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 检查是否有已满的磁盘分区
|
||||||
|
func (this *FileStorage) hasFullDisk() bool {
|
||||||
|
this.checkDiskSpace()
|
||||||
|
|
||||||
|
var hasFullDisk = this.mainDiskIsFull
|
||||||
|
if !hasFullDisk {
|
||||||
|
var subDirs = this.subDirs // copy slice
|
||||||
|
for _, subDir := range subDirs {
|
||||||
|
if subDir.IsFull {
|
||||||
|
hasFullDisk = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hasFullDisk
|
||||||
|
}
|
||||||
|
|
||||||
// 获取目录
|
// 获取目录
|
||||||
func (this *FileStorage) subDir(hash string) (dirPath string, dirIsFull bool) {
|
func (this *FileStorage) subDir(hash string) (dirPath string, dirIsFull bool) {
|
||||||
var suffix = "/p" + types.String(this.policy.Id) + "/" + hash[:2] + "/" + hash[2:4]
|
var suffix = "/p" + types.String(this.policy.Id) + "/" + hash[:2] + "/" + hash[2:4]
|
||||||
@@ -1462,6 +1535,89 @@ func (this *FileStorage) subDir(hash string) (dirPath string, dirIsFull bool) {
|
|||||||
return subDir.Path + suffix, subDir.IsFull
|
return subDir.Path + suffix, subDir.IsFull
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ScanGarbageCaches 清理目录中“失联”的缓存文件
|
||||||
|
// “失联”为不在HashMap中的文件
|
||||||
|
func (this *FileStorage) ScanGarbageCaches(fileCallback func(path string) error) error {
|
||||||
|
if !this.list.(*FileList).HashMapIsLoaded() {
|
||||||
|
return errors.New("cache list is loading")
|
||||||
|
}
|
||||||
|
|
||||||
|
var mainDir = this.options.Dir
|
||||||
|
var allDirs = []string{mainDir}
|
||||||
|
var subDirs = this.subDirs // copy
|
||||||
|
for _, subDir := range subDirs {
|
||||||
|
allDirs = append(allDirs, subDir.Path)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, subDir := range allDirs {
|
||||||
|
var dir0 = subDir + "/p" + types.String(this.policy.Id)
|
||||||
|
dir1Matches, err := filepath.Glob(dir0 + "/*")
|
||||||
|
if err != nil {
|
||||||
|
// ignore error
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, dir1 := range dir1Matches {
|
||||||
|
if len(filepath.Base(dir1)) != 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
dir2Matches, err := filepath.Glob(dir1 + "/*")
|
||||||
|
if err != nil {
|
||||||
|
// ignore error
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, dir2 := range dir2Matches {
|
||||||
|
if len(filepath.Base(dir2)) != 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fileMatches, err := filepath.Glob(dir2 + "/*.cache")
|
||||||
|
if err != nil {
|
||||||
|
// ignore error
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, file := range fileMatches {
|
||||||
|
var filename = filepath.Base(file)
|
||||||
|
var hash = strings.TrimSuffix(filename, ".cache")
|
||||||
|
if len(hash) != HashKeyLength {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
isReady, found := this.list.(*FileList).ExistQuick(hash)
|
||||||
|
if !isReady {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if found {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查文件正在被写入
|
||||||
|
stat, err := os.Stat(file)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if fasttime.Now().Unix()-stat.ModTime().Unix() < 300 /** 5 minutes **/ {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if fileCallback != nil {
|
||||||
|
err = fileCallback(file)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算字节数字代号
|
||||||
func (this *FileStorage) charCode(r byte) uint8 {
|
func (this *FileStorage) charCode(r byte) uint8 {
|
||||||
if r >= '0' && r <= '9' {
|
if r >= '0' && r <= '9' {
|
||||||
return r - '0'
|
return r - '0'
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/utils/testutils"
|
||||||
"github.com/iwind/TeaGo/Tea"
|
"github.com/iwind/TeaGo/Tea"
|
||||||
_ "github.com/iwind/TeaGo/bootstrap"
|
_ "github.com/iwind/TeaGo/bootstrap"
|
||||||
"github.com/iwind/TeaGo/logs"
|
"github.com/iwind/TeaGo/logs"
|
||||||
@@ -159,7 +160,7 @@ func TestFileStorage_OpenWriter_HTTP(t *testing.T) {
|
|||||||
t.Log(writer)
|
t.Log(writer)
|
||||||
|
|
||||||
resp := &http.Response{
|
resp := &http.Response{
|
||||||
StatusCode: 200,
|
StatusCode: http.StatusOK,
|
||||||
Header: http.Header{
|
Header: http.Header{
|
||||||
"Content-Type": []string{"text/html; charset=utf-8"},
|
"Content-Type": []string{"text/html; charset=utf-8"},
|
||||||
"Last-Modified": []string{"Wed, 06 Jan 2021 10:03:29 GMT"},
|
"Last-Modified": []string{"Wed, 06 Jan 2021 10:03:29 GMT"},
|
||||||
@@ -577,6 +578,31 @@ func TestFileStorage_RemoveCacheFile(t *testing.T) {
|
|||||||
t.Log(storage.removeCacheFile("/Users/WorkSpace/EdgeProject/EdgeCache/p43/15/7e/157eba0dfc6dfb6fbbf20b1f9e584674.cache"))
|
t.Log(storage.removeCacheFile("/Users/WorkSpace/EdgeProject/EdgeCache/p43/15/7e/157eba0dfc6dfb6fbbf20b1f9e584674.cache"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFileStorage_ScanGarbageCaches(t *testing.T) {
|
||||||
|
if !testutils.IsSingleTesting() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var storage = NewFileStorage(&serverconfigs.HTTPCachePolicy{
|
||||||
|
Id: 43,
|
||||||
|
Options: map[string]any{"dir": "/Users/WorkSpace/EdgeProject/EdgeCache"},
|
||||||
|
})
|
||||||
|
err := storage.Init()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = storage.ScanGarbageCaches(func(path string) {
|
||||||
|
t.Log(path)
|
||||||
|
}, func(path string) error {
|
||||||
|
t.Log(path, PartialRangesFilePath(path))
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func BenchmarkFileStorage_Read(b *testing.B) {
|
func BenchmarkFileStorage_Read(b *testing.B) {
|
||||||
runtime.GOMAXPROCS(1)
|
runtime.GOMAXPROCS(1)
|
||||||
|
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeNode/internal/trackers"
|
"github.com/TeaOSLab/EdgeNode/internal/trackers"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils/fasttime"
|
"github.com/TeaOSLab/EdgeNode/internal/utils/fasttime"
|
||||||
|
fsutils "github.com/TeaOSLab/EdgeNode/internal/utils/fs"
|
||||||
setutils "github.com/TeaOSLab/EdgeNode/internal/utils/sets"
|
setutils "github.com/TeaOSLab/EdgeNode/internal/utils/sets"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils/sizes"
|
"github.com/TeaOSLab/EdgeNode/internal/utils/sizes"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/zero"
|
"github.com/TeaOSLab/EdgeNode/internal/zero"
|
||||||
"github.com/cespare/xxhash"
|
"github.com/cespare/xxhash"
|
||||||
"github.com/iwind/TeaGo/rands"
|
|
||||||
"github.com/iwind/TeaGo/types"
|
"github.com/iwind/TeaGo/types"
|
||||||
"github.com/shirou/gopsutil/v3/load"
|
"github.com/shirou/gopsutil/v3/load"
|
||||||
"math"
|
"math"
|
||||||
@@ -136,17 +136,6 @@ func (this *MemoryStorage) OpenReader(key string, useStale bool, isPartial bool)
|
|||||||
}
|
}
|
||||||
this.locker.RUnlock()
|
this.locker.RUnlock()
|
||||||
|
|
||||||
// 增加点击量
|
|
||||||
// 1/1000采样
|
|
||||||
// TODO 考虑是否在缓存策略里设置
|
|
||||||
if rands.Int(0, 1000) == 0 {
|
|
||||||
var hitErr = this.list.IncreaseHit(types.String(hash))
|
|
||||||
if hitErr != nil {
|
|
||||||
// 此错误可以忽略
|
|
||||||
remotelogs.Error("CACHE", "increase hit failed: "+hitErr.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return reader, nil
|
return reader, nil
|
||||||
}
|
}
|
||||||
this.locker.RUnlock()
|
this.locker.RUnlock()
|
||||||
@@ -483,15 +472,14 @@ func (this *MemoryStorage) startFlush() {
|
|||||||
if statCount == 100 {
|
if statCount == 100 {
|
||||||
statCount = 0
|
statCount = 0
|
||||||
|
|
||||||
if protectingLoadWhenDump {
|
// delay some time to reduce load if needed
|
||||||
|
if !fsutils.DiskIsFast() {
|
||||||
loadStat, err := load.Avg()
|
loadStat, err := load.Avg()
|
||||||
if err == nil && loadStat != nil {
|
if err == nil && loadStat != nil {
|
||||||
if loadStat.Load1 > 10 {
|
if loadStat.Load1 > 10 {
|
||||||
writeDelayMS = 100
|
writeDelayMS = 100
|
||||||
} else if loadStat.Load1 > 3 {
|
} else if loadStat.Load1 > 5 {
|
||||||
writeDelayMS = 50
|
writeDelayMS = 50
|
||||||
} else if loadStat.Load1 > 2 {
|
|
||||||
writeDelayMS = 10
|
|
||||||
} else {
|
} else {
|
||||||
writeDelayMS = 0
|
writeDelayMS = 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ package caches
|
|||||||
|
|
||||||
import "strings"
|
import "strings"
|
||||||
|
|
||||||
// 获取 ranges 文件路径
|
// PartialRangesFilePath 获取 ranges 文件路径
|
||||||
func partialRangesFilePath(path string) string {
|
func PartialRangesFilePath(path string) string {
|
||||||
// ranges路径
|
// ranges路径
|
||||||
var dotIndex = strings.LastIndex(path, ".")
|
var dotIndex = strings.LastIndex(path, ".")
|
||||||
var rangePath = ""
|
var rangePath string
|
||||||
if dotIndex < 0 {
|
if dotIndex < 0 {
|
||||||
rangePath = path + "@ranges.cache"
|
rangePath = path + "@ranges.cache"
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ func TestParseHost(t *testing.T) {
|
|||||||
func TestUintString(t *testing.T) {
|
func TestUintString(t *testing.T) {
|
||||||
t.Log(strconv.FormatUint(xxhash.Sum64String("https://goedge.cn/"), 10))
|
t.Log(strconv.FormatUint(xxhash.Sum64String("https://goedge.cn/"), 10))
|
||||||
t.Log(strconv.FormatUint(123456789, 10))
|
t.Log(strconv.FormatUint(123456789, 10))
|
||||||
t.Log(fmt.Sprintf("%d", 1234567890123))
|
t.Logf("%d", 1234567890123)
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkUint_String(b *testing.B) {
|
func BenchmarkUint_String(b *testing.B) {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package caches
|
|||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
|
fsutils "github.com/TeaOSLab/EdgeNode/internal/utils/fs"
|
||||||
"github.com/iwind/TeaGo/types"
|
"github.com/iwind/TeaGo/types"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
@@ -42,7 +43,9 @@ func NewFileWriter(storage StorageInterface, rawWriter *os.File, key string, exp
|
|||||||
|
|
||||||
// WriteHeader 写入数据
|
// WriteHeader 写入数据
|
||||||
func (this *FileWriter) WriteHeader(data []byte) (n int, err error) {
|
func (this *FileWriter) WriteHeader(data []byte) (n int, err error) {
|
||||||
|
fsutils.WriteBegin()
|
||||||
n, err = this.rawWriter.Write(data)
|
n, err = this.rawWriter.Write(data)
|
||||||
|
fsutils.WriteEnd()
|
||||||
this.headerSize += int64(n)
|
this.headerSize += int64(n)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = this.Discard()
|
_ = this.Discard()
|
||||||
@@ -72,21 +75,30 @@ func (this *FileWriter) WriteHeaderLength(headerLength int) error {
|
|||||||
|
|
||||||
// Write 写入数据
|
// Write 写入数据
|
||||||
func (this *FileWriter) Write(data []byte) (n int, err error) {
|
func (this *FileWriter) Write(data []byte) (n int, err error) {
|
||||||
n, err = this.rawWriter.Write(data)
|
// split LARGE data
|
||||||
this.bodySize += int64(n)
|
var l = len(data)
|
||||||
|
if l > (2 << 20) {
|
||||||
if this.maxSize > 0 && this.bodySize > this.maxSize {
|
var offset = 0
|
||||||
err = ErrEntityTooLarge
|
const bufferSize = 256 << 10
|
||||||
|
for {
|
||||||
if this.storage != nil {
|
var end = offset + bufferSize
|
||||||
this.storage.IgnoreKey(this.key, this.maxSize)
|
if end > l {
|
||||||
|
end = l
|
||||||
|
}
|
||||||
|
n1, err1 := this.write(data[offset:end])
|
||||||
|
n += n1
|
||||||
|
if err1 != nil {
|
||||||
|
return n, err1
|
||||||
|
}
|
||||||
|
if end >= l {
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
offset = end
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
// write NORMAL size data
|
||||||
_ = this.Discard()
|
return this.write(data)
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteAt 在指定位置写入数据
|
// WriteAt 在指定位置写入数据
|
||||||
@@ -126,18 +138,24 @@ func (this *FileWriter) Close() error {
|
|||||||
|
|
||||||
err := this.WriteHeaderLength(types.Int(this.headerSize))
|
err := this.WriteHeaderLength(types.Int(this.headerSize))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
fsutils.WriteBegin()
|
||||||
_ = this.rawWriter.Close()
|
_ = this.rawWriter.Close()
|
||||||
|
fsutils.WriteEnd()
|
||||||
_ = os.Remove(path)
|
_ = os.Remove(path)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = this.WriteBodyLength(this.bodySize)
|
err = this.WriteBodyLength(this.bodySize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
fsutils.WriteBegin()
|
||||||
_ = this.rawWriter.Close()
|
_ = this.rawWriter.Close()
|
||||||
|
fsutils.WriteEnd()
|
||||||
_ = os.Remove(path)
|
_ = os.Remove(path)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fsutils.WriteBegin()
|
||||||
err = this.rawWriter.Close()
|
err = this.rawWriter.Close()
|
||||||
|
fsutils.WriteEnd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = os.Remove(path)
|
_ = os.Remove(path)
|
||||||
} else if strings.HasSuffix(path, FileTmpSuffix) {
|
} else if strings.HasSuffix(path, FileTmpSuffix) {
|
||||||
@@ -156,7 +174,9 @@ func (this *FileWriter) Discard() error {
|
|||||||
this.endFunc()
|
this.endFunc()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
fsutils.WriteBegin()
|
||||||
_ = this.rawWriter.Close()
|
_ = this.rawWriter.Close()
|
||||||
|
fsutils.WriteEnd()
|
||||||
|
|
||||||
err := os.Remove(this.rawWriter.Name())
|
err := os.Remove(this.rawWriter.Name())
|
||||||
return err
|
return err
|
||||||
@@ -182,3 +202,23 @@ func (this *FileWriter) Key() string {
|
|||||||
func (this *FileWriter) ItemType() ItemType {
|
func (this *FileWriter) ItemType() ItemType {
|
||||||
return ItemTypeFile
|
return ItemTypeFile
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *FileWriter) write(data []byte) (n int, err error) {
|
||||||
|
fsutils.WriteBegin()
|
||||||
|
n, err = this.rawWriter.Write(data)
|
||||||
|
fsutils.WriteEnd()
|
||||||
|
this.bodySize += int64(n)
|
||||||
|
|
||||||
|
if this.maxSize > 0 && this.bodySize > this.maxSize {
|
||||||
|
err = ErrEntityTooLarge
|
||||||
|
|
||||||
|
if this.storage != nil {
|
||||||
|
this.storage.IgnoreKey(this.key, this.maxSize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
_ = this.Discard()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ package caches
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
fsutils "github.com/TeaOSLab/EdgeNode/internal/utils/fs"
|
||||||
"github.com/iwind/TeaGo/types"
|
"github.com/iwind/TeaGo/types"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
@@ -42,7 +43,7 @@ func NewPartialFileWriter(rawWriter *os.File, key string, expiredAt int64, metaH
|
|||||||
isPartial: isPartial,
|
isPartial: isPartial,
|
||||||
bodyOffset: bodyOffset,
|
bodyOffset: bodyOffset,
|
||||||
ranges: ranges,
|
ranges: ranges,
|
||||||
rangePath: partialRangesFilePath(rawWriter.Name()),
|
rangePath: PartialRangesFilePath(rawWriter.Name()),
|
||||||
metaHeaderSize: metaHeaderSize,
|
metaHeaderSize: metaHeaderSize,
|
||||||
metaBodySize: metaBodySize,
|
metaBodySize: metaBodySize,
|
||||||
}
|
}
|
||||||
@@ -53,7 +54,9 @@ func (this *PartialFileWriter) WriteHeader(data []byte) (n int, err error) {
|
|||||||
if !this.isNew {
|
if !this.isNew {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
fsutils.WriteBegin()
|
||||||
n, err = this.rawWriter.Write(data)
|
n, err = this.rawWriter.Write(data)
|
||||||
|
fsutils.WriteEnd()
|
||||||
this.headerSize += int64(n)
|
this.headerSize += int64(n)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = this.Discard()
|
_ = this.Discard()
|
||||||
@@ -62,7 +65,9 @@ func (this *PartialFileWriter) WriteHeader(data []byte) (n int, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *PartialFileWriter) AppendHeader(data []byte) error {
|
func (this *PartialFileWriter) AppendHeader(data []byte) error {
|
||||||
|
fsutils.WriteBegin()
|
||||||
_, err := this.rawWriter.Write(data)
|
_, err := this.rawWriter.Write(data)
|
||||||
|
fsutils.WriteEnd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = this.Discard()
|
_ = this.Discard()
|
||||||
} else {
|
} else {
|
||||||
@@ -99,7 +104,9 @@ func (this *PartialFileWriter) WriteHeaderLength(headerLength int) error {
|
|||||||
|
|
||||||
// Write 写入数据
|
// Write 写入数据
|
||||||
func (this *PartialFileWriter) Write(data []byte) (n int, err error) {
|
func (this *PartialFileWriter) Write(data []byte) (n int, err error) {
|
||||||
|
fsutils.WriteBegin()
|
||||||
n, err = this.rawWriter.Write(data)
|
n, err = this.rawWriter.Write(data)
|
||||||
|
fsutils.WriteEnd()
|
||||||
this.bodySize += int64(n)
|
this.bodySize += int64(n)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = this.Discard()
|
_ = this.Discard()
|
||||||
@@ -128,7 +135,9 @@ func (this *PartialFileWriter) WriteAt(offset int64, data []byte) error {
|
|||||||
this.bodyOffset = SizeMeta + int64(keyLength) + this.headerSize
|
this.bodyOffset = SizeMeta + int64(keyLength) + this.headerSize
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fsutils.WriteBegin()
|
||||||
_, err := this.rawWriter.WriteAt(data, this.bodyOffset+offset)
|
_, err := this.rawWriter.WriteAt(data, this.bodyOffset+offset)
|
||||||
|
fsutils.WriteEnd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -172,7 +181,9 @@ func (this *PartialFileWriter) Close() error {
|
|||||||
this.ranges.BodySize = this.bodySize
|
this.ranges.BodySize = this.bodySize
|
||||||
err := this.ranges.WriteToFile(this.rangePath)
|
err := this.ranges.WriteToFile(this.rangePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
fsutils.WriteBegin()
|
||||||
_ = this.rawWriter.Close()
|
_ = this.rawWriter.Close()
|
||||||
|
fsutils.WriteEnd()
|
||||||
this.remove()
|
this.remove()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -181,19 +192,25 @@ func (this *PartialFileWriter) Close() error {
|
|||||||
if this.isNew {
|
if this.isNew {
|
||||||
err = this.WriteHeaderLength(types.Int(this.headerSize))
|
err = this.WriteHeaderLength(types.Int(this.headerSize))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
fsutils.WriteBegin()
|
||||||
_ = this.rawWriter.Close()
|
_ = this.rawWriter.Close()
|
||||||
|
fsutils.WriteEnd()
|
||||||
this.remove()
|
this.remove()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = this.WriteBodyLength(this.bodySize)
|
err = this.WriteBodyLength(this.bodySize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
fsutils.WriteBegin()
|
||||||
_ = this.rawWriter.Close()
|
_ = this.rawWriter.Close()
|
||||||
|
fsutils.WriteEnd()
|
||||||
this.remove()
|
this.remove()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fsutils.WriteBegin()
|
||||||
err = this.rawWriter.Close()
|
err = this.rawWriter.Close()
|
||||||
|
fsutils.WriteEnd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.remove()
|
this.remove()
|
||||||
}
|
}
|
||||||
@@ -207,7 +224,9 @@ func (this *PartialFileWriter) Discard() error {
|
|||||||
this.endFunc()
|
this.endFunc()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
fsutils.WriteBegin()
|
||||||
_ = this.rawWriter.Close()
|
_ = this.rawWriter.Close()
|
||||||
|
fsutils.WriteEnd()
|
||||||
|
|
||||||
_ = os.Remove(this.rangePath)
|
_ = os.Remove(this.rangePath)
|
||||||
|
|
||||||
|
|||||||
@@ -1,38 +1,81 @@
|
|||||||
package configs
|
package configs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"github.com/iwind/TeaGo/Tea"
|
"github.com/iwind/TeaGo/Tea"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
// APIConfig 节点API配置
|
const ConfigFileName = "api_node.yaml"
|
||||||
|
const oldConfigFileName = "api.yaml"
|
||||||
|
|
||||||
type APIConfig struct {
|
type APIConfig struct {
|
||||||
RPC struct {
|
OldRPC struct {
|
||||||
Endpoints []string `yaml:"endpoints" json:"endpoints"`
|
Endpoints []string `yaml:"endpoints" json:"endpoints"`
|
||||||
DisableUpdate bool `yaml:"disableUpdate" json:"disableUpdate"`
|
DisableUpdate bool `yaml:"disableUpdate" json:"disableUpdate"`
|
||||||
} `yaml:"rpc" json:"rpc"`
|
} `yaml:"rpc,omitempty" json:"rpc"`
|
||||||
NodeId string `yaml:"nodeId" json:"nodeId"`
|
|
||||||
Secret string `yaml:"secret" json:"secret"`
|
RPCEndpoints []string `yaml:"rpc.endpoints,flow" json:"rpc.endpoints"`
|
||||||
|
RPCDisableUpdate bool `yaml:"rpc.disableUpdate" json:"rpc.disableUpdate"`
|
||||||
|
NodeId string `yaml:"nodeId" json:"nodeId"`
|
||||||
|
Secret string `yaml:"secret" json:"secret"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAPIConfig() *APIConfig {
|
func NewAPIConfig() *APIConfig {
|
||||||
return &APIConfig{}
|
return &APIConfig{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *APIConfig) Init() error {
|
||||||
|
// compatible with old
|
||||||
|
if len(this.RPCEndpoints) == 0 && len(this.OldRPC.Endpoints) > 0 {
|
||||||
|
this.RPCEndpoints = this.OldRPC.Endpoints
|
||||||
|
this.RPCDisableUpdate = this.OldRPC.DisableUpdate
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(this.RPCEndpoints) == 0 {
|
||||||
|
return errors.New("no valid 'rpc.endpoints'")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(this.NodeId) == 0 {
|
||||||
|
return errors.New("'nodeId' required")
|
||||||
|
}
|
||||||
|
if len(this.Secret) == 0 {
|
||||||
|
return errors.New("'secret' required")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func LoadAPIConfig() (*APIConfig, error) {
|
func LoadAPIConfig() (*APIConfig, error) {
|
||||||
data, err := os.ReadFile(Tea.ConfigFile("api.yaml"))
|
for _, filename := range []string{ConfigFileName, oldConfigFileName} {
|
||||||
if err != nil {
|
data, err := os.ReadFile(Tea.ConfigFile(filename))
|
||||||
return nil, err
|
if err != nil {
|
||||||
}
|
if os.IsNotExist(err) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
config := &APIConfig{}
|
var config = &APIConfig{}
|
||||||
err = yaml.Unmarshal(data, config)
|
err = yaml.Unmarshal(data, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return config, nil
|
err = config.Init()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("init error: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// 自动生成新的配置文件
|
||||||
|
if filename == oldConfigFileName {
|
||||||
|
config.OldRPC.Endpoints = nil
|
||||||
|
_ = config.WriteFile(Tea.ConfigFile(ConfigFileName))
|
||||||
|
}
|
||||||
|
|
||||||
|
return config, nil
|
||||||
|
}
|
||||||
|
return nil, errors.New("no config file '" + ConfigFileName + "' found")
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteFile 保存到文件
|
// WriteFile 保存到文件
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package configs_test
|
|||||||
import (
|
import (
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/configs"
|
"github.com/TeaOSLab/EdgeNode/internal/configs"
|
||||||
_ "github.com/iwind/TeaGo/bootstrap"
|
_ "github.com/iwind/TeaGo/bootstrap"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -12,4 +13,10 @@ func TestLoadAPIConfig(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
t.Logf("%+v", config)
|
t.Logf("%+v", config)
|
||||||
|
|
||||||
|
configData, err := yaml.Marshal(config)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log(string(configData))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,57 @@
|
|||||||
package configs
|
package configs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/iwind/TeaGo/Tea"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
// ClusterConfig 集群配置
|
// ClusterConfig 集群配置
|
||||||
type ClusterConfig struct {
|
type ClusterConfig struct {
|
||||||
RPC struct {
|
OldRPC struct {
|
||||||
Endpoints []string `yaml:"endpoints" json:"endpoints"`
|
Endpoints []string `yaml:"endpoints" json:"endpoints"`
|
||||||
DisableUpdate bool `yaml:"disableUpdate" json:"disableUpdate"`
|
DisableUpdate bool `yaml:"disableUpdate" json:"disableUpdate"`
|
||||||
} `yaml:"rpc" json:"rpc"`
|
} `yaml:"rpc,omitempty" json:"rpc"`
|
||||||
|
|
||||||
|
RPCEndpoints []string `yaml:"rpc.endpoints,flow" json:"rpc.endpoints"`
|
||||||
|
RPCDisableUpdate bool `yaml:"rpc.disableUpdate" json:"rpc.disableUpdate"`
|
||||||
|
|
||||||
ClusterId string `yaml:"clusterId" json:"clusterId"`
|
ClusterId string `yaml:"clusterId" json:"clusterId"`
|
||||||
Secret string `yaml:"secret" json:"secret"`
|
Secret string `yaml:"secret" json:"secret"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *ClusterConfig) Init() error {
|
||||||
|
// compatible with old
|
||||||
|
if len(this.RPCEndpoints) == 0 && len(this.OldRPC.Endpoints) > 0 {
|
||||||
|
this.RPCEndpoints = this.OldRPC.Endpoints
|
||||||
|
this.RPCDisableUpdate = this.OldRPC.DisableUpdate
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadClusterConfig() (*ClusterConfig, error) {
|
||||||
|
for _, filename := range []string{"api_cluster.yaml", "cluster.yaml"} {
|
||||||
|
data, err := os.ReadFile(Tea.ConfigFile(filename))
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var config = &ClusterConfig{}
|
||||||
|
err = yaml.Unmarshal(data, config)
|
||||||
|
if err != nil {
|
||||||
|
return config, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = config.Init()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return config, nil
|
||||||
|
}
|
||||||
|
return nil, os.ErrNotExist
|
||||||
|
}
|
||||||
|
|||||||
23
internal/configs/cluster_config_test.go
Normal file
23
internal/configs/cluster_config_test.go
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||||
|
|
||||||
|
package configs_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/configs"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLoadClusterConfig(t *testing.T) {
|
||||||
|
config, err := configs.LoadClusterConfig()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Logf("%+v", config)
|
||||||
|
|
||||||
|
configData, err := yaml.Marshal(config)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log(string(configData))
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package teaconst
|
package teaconst
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Version = "1.2.2"
|
Version = "1.2.9"
|
||||||
|
|
||||||
ProductName = "Edge Node"
|
ProductName = "Edge Node"
|
||||||
ProcessName = "edge-node"
|
ProcessName = "edge-node"
|
||||||
|
|||||||
@@ -22,8 +22,6 @@ var (
|
|||||||
|
|
||||||
IsQuiting = false // 是否正在退出
|
IsQuiting = false // 是否正在退出
|
||||||
EnableDBStat = false // 是否开启本地数据库统计
|
EnableDBStat = false // 是否开启本地数据库统计
|
||||||
|
|
||||||
DiskIsFast = false // 是否为高速硬盘
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// 检查是否为主程序
|
// 检查是否为主程序
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ func TestAES128CFBMethod_Encrypt(t *testing.T) {
|
|||||||
dst = dst[:len(src)]
|
dst = dst[:len(src)]
|
||||||
t.Log("dst:", string(dst))
|
t.Log("dst:", string(dst))
|
||||||
|
|
||||||
src = make([]byte, len(src))
|
|
||||||
src, err = method.Decrypt(dst)
|
src, err = method.Decrypt(dst)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -54,7 +53,6 @@ func TestAES128CFBMethod_Encrypt2(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
||||||
a := []byte{1}
|
a := []byte{1}
|
||||||
_, err = method.Decrypt(a)
|
_, err = method.Decrypt(a)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -64,7 +62,6 @@ func TestAES128CFBMethod_Encrypt2(t *testing.T) {
|
|||||||
|
|
||||||
for _, dst := range sources {
|
for _, dst := range sources {
|
||||||
dst2 := append([]byte{}, dst...)
|
dst2 := append([]byte{}, dst...)
|
||||||
src2 := make([]byte, len(dst2))
|
|
||||||
src2, err := method.Decrypt(dst2)
|
src2, err := method.Decrypt(dst2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|||||||
@@ -3,10 +3,11 @@ package events
|
|||||||
type Event = string
|
type Event = string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
EventStart Event = "start" // start loading
|
EventStart Event = "start" // start loading
|
||||||
EventLoaded Event = "loaded" // first load
|
EventLoaded Event = "loaded" // first load
|
||||||
EventQuit Event = "quit" // quit node gracefully
|
EventQuit Event = "quit" // quit node gracefully
|
||||||
EventReload Event = "reload" // reload config
|
EventReload Event = "reload" // reload config
|
||||||
EventTerminated Event = "terminated" // process terminated
|
EventTerminated Event = "terminated" // process terminated
|
||||||
EventNFTablesReady Event = "nftablesReady" // nftables ready
|
EventNFTablesReady Event = "nftablesReady" // nftables ready
|
||||||
|
EventReloadSomeServers Event = "reloadSomeServers" // reload some servers
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -24,6 +24,12 @@ func On(event Event, callback func()) {
|
|||||||
OnKey(event, nil, callback)
|
OnKey(event, nil, callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func OnEvents(events []Event, callback func()) {
|
||||||
|
for _, event := range events {
|
||||||
|
On(event, callback)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func OnClose(callback func()) {
|
func OnClose(callback func()) {
|
||||||
On(EventQuit, callback)
|
On(EventQuit, callback)
|
||||||
On(EventTerminated, callback)
|
On(EventTerminated, callback)
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ func TestOn(t *testing.T) {
|
|||||||
type User struct {
|
type User struct {
|
||||||
name string
|
name string
|
||||||
}
|
}
|
||||||
var u = &User{}
|
var u = &User{name: "lily"}
|
||||||
var u2 = &User{}
|
var u2 = &User{name: "lucy"}
|
||||||
|
|
||||||
events.On("hello", func() {
|
events.On("hello", func() {
|
||||||
t.Log("world")
|
t.Log("world")
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/ddosconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/ddosconfigs"
|
||||||
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
||||||
@@ -92,7 +93,7 @@ func (this *DDoSProtectionManager) Apply(config *ddosconfigs.ProtectionConfig) e
|
|||||||
// 对比配置
|
// 对比配置
|
||||||
configJSON, err := json.Marshal(config)
|
configJSON, err := json.Marshal(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("encode config to json failed: " + err.Error())
|
return fmt.Errorf("encode config to json failed: %w", err)
|
||||||
}
|
}
|
||||||
if !allowIPListChanged && bytes.Equal(this.lastConfig, configJSON) {
|
if !allowIPListChanged && bytes.Equal(this.lastConfig, configJSON) {
|
||||||
return nil
|
return nil
|
||||||
@@ -188,7 +189,7 @@ func (this *DDoSProtectionManager) addTCPRules(tcpConfig *ddosconfigs.TCPConfig)
|
|||||||
for _, filter := range nftablesFilters {
|
for _, filter := range nftablesFilters {
|
||||||
chain, oldRules, err := this.getRules(filter)
|
chain, oldRules, err := this.getRules(filter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("get old rules failed: " + err.Error())
|
return fmt.Errorf("get old rules failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var protocol = filter.protocol()
|
var protocol = filter.protocol()
|
||||||
@@ -273,7 +274,7 @@ func (this *DDoSProtectionManager) addTCPRules(tcpConfig *ddosconfigs.TCPConfig)
|
|||||||
// 先清空所有相关规则
|
// 先清空所有相关规则
|
||||||
err = this.removeOldTCPRules(chain, oldRules)
|
err = this.removeOldTCPRules(chain, oldRules)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("delete old rules failed: " + err.Error())
|
return fmt.Errorf("delete old rules failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加新规则
|
// 添加新规则
|
||||||
@@ -281,9 +282,9 @@ func (this *DDoSProtectionManager) addTCPRules(tcpConfig *ddosconfigs.TCPConfig)
|
|||||||
if maxConnections > 0 {
|
if maxConnections > 0 {
|
||||||
var cmd = executils.NewTimeoutCmd(10*time.Second, nftExe, "add", "rule", protocol, filter.Name, nftablesChainName, "tcp", "dport", types.String(port), "ct", "count", "over", types.String(maxConnections), "counter", "drop", "comment", this.encodeUserData([]string{"tcp", types.String(port), "maxConnections", types.String(maxConnections)}))
|
var cmd = executils.NewTimeoutCmd(10*time.Second, nftExe, "add", "rule", protocol, filter.Name, nftablesChainName, "tcp", "dport", types.String(port), "ct", "count", "over", types.String(maxConnections), "counter", "drop", "comment", this.encodeUserData([]string{"tcp", types.String(port), "maxConnections", types.String(maxConnections)}))
|
||||||
cmd.WithStderr()
|
cmd.WithStderr()
|
||||||
err := cmd.Run()
|
err = cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("add nftables rule '" + cmd.String() + "' failed: " + err.Error() + " (" + cmd.Stderr() + ")")
|
return fmt.Errorf("add nftables rule '%s' failed: %w (%s)", cmd.String(), err, cmd.Stderr())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,7 +294,7 @@ func (this *DDoSProtectionManager) addTCPRules(tcpConfig *ddosconfigs.TCPConfig)
|
|||||||
cmd.WithStderr()
|
cmd.WithStderr()
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("add nftables rule '" + cmd.String() + "' failed: " + err.Error() + " (" + cmd.Stderr() + ")")
|
return fmt.Errorf("add nftables rule '%s' failed: %w (%s)", cmd.String(), err, cmd.Stderr())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -305,14 +306,14 @@ func (this *DDoSProtectionManager) addTCPRules(tcpConfig *ddosconfigs.TCPConfig)
|
|||||||
cmd.WithStderr()
|
cmd.WithStderr()
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("add nftables rule '" + cmd.String() + "' failed: " + err.Error() + " (" + cmd.Stderr() + ")")
|
return fmt.Errorf("add nftables rule '%s' failed: %w (%s)", cmd.String(), err, cmd.Stderr())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var cmd = executils.NewTimeoutCmd(10*time.Second, nftExe, "add", "rule", protocol, filter.Name, nftablesChainName, "tcp", "dport", types.String(port), "ct", "state", "new", "meter", "meter-"+protocol+"-"+types.String(port)+"-new-connections-rate", "{ "+protocol+" saddr limit rate over "+types.String(newConnectionsMinutelyRate)+"/minute burst "+types.String(newConnectionsMinutelyRate+3)+" packets }" /**"add", "@deny_set", "{"+protocol+" saddr}",**/, "counter", "drop", "comment", this.encodeUserData([]string{"tcp", types.String(port), "newConnectionsRate", "0"}))
|
var cmd = executils.NewTimeoutCmd(10*time.Second, nftExe, "add", "rule", protocol, filter.Name, nftablesChainName, "tcp", "dport", types.String(port), "ct", "state", "new", "meter", "meter-"+protocol+"-"+types.String(port)+"-new-connections-rate", "{ "+protocol+" saddr limit rate over "+types.String(newConnectionsMinutelyRate)+"/minute burst "+types.String(newConnectionsMinutelyRate+3)+" packets }" /**"add", "@deny_set", "{"+protocol+" saddr}",**/, "counter", "drop", "comment", this.encodeUserData([]string{"tcp", types.String(port), "newConnectionsRate", "0"}))
|
||||||
cmd.WithStderr()
|
cmd.WithStderr()
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("add nftables rule '" + cmd.String() + "' failed: " + err.Error() + " (" + cmd.Stderr() + ")")
|
return fmt.Errorf("add nftables rule '%s' failed: %w (%s)", cmd.String(), err, cmd.Stderr())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -325,14 +326,14 @@ func (this *DDoSProtectionManager) addTCPRules(tcpConfig *ddosconfigs.TCPConfig)
|
|||||||
cmd.WithStderr()
|
cmd.WithStderr()
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("add nftables rule '" + cmd.String() + "' failed: " + err.Error() + " (" + cmd.Stderr() + ")")
|
return fmt.Errorf("add nftables rule '%s' failed: %w (%s)", cmd.String(), err, cmd.Stderr())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var cmd = executils.NewTimeoutCmd(10*time.Second, nftExe, "add", "rule", protocol, filter.Name, nftablesChainName, "tcp", "dport", types.String(port), "ct", "state", "new", "meter", "meter-"+protocol+"-"+types.String(port)+"-new-connections-secondly-rate", "{ "+protocol+" saddr limit rate over "+types.String(newConnectionsSecondlyRate)+"/second burst "+types.String(newConnectionsSecondlyRate+3)+" packets }" /**"add", "@deny_set", "{"+protocol+" saddr}",**/, "counter", "drop", "comment", this.encodeUserData([]string{"tcp", types.String(port), "newConnectionsSecondlyRate", "0"}))
|
var cmd = executils.NewTimeoutCmd(10*time.Second, nftExe, "add", "rule", protocol, filter.Name, nftablesChainName, "tcp", "dport", types.String(port), "ct", "state", "new", "meter", "meter-"+protocol+"-"+types.String(port)+"-new-connections-secondly-rate", "{ "+protocol+" saddr limit rate over "+types.String(newConnectionsSecondlyRate)+"/second burst "+types.String(newConnectionsSecondlyRate+3)+" packets }" /**"add", "@deny_set", "{"+protocol+" saddr}",**/, "counter", "drop", "comment", this.encodeUserData([]string{"tcp", types.String(port), "newConnectionsSecondlyRate", "0"}))
|
||||||
cmd.WithStderr()
|
cmd.WithStderr()
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("add nftables rule '" + cmd.String() + "' failed: " + err.Error() + " (" + cmd.Stderr() + ")")
|
return fmt.Errorf("add nftables rule '%s' failed: %w (%s)", cmd.String(), err, cmd.Stderr())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -498,11 +499,11 @@ func (this *DDoSProtectionManager) getTable(filter *nftablesTableDefinition) (*n
|
|||||||
func (this *DDoSProtectionManager) getRules(filter *nftablesTableDefinition) (*nftables.Chain, []*nftables.Rule, error) {
|
func (this *DDoSProtectionManager) getRules(filter *nftablesTableDefinition) (*nftables.Chain, []*nftables.Rule, error) {
|
||||||
table, err := this.getTable(filter)
|
table, err := this.getTable(filter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, errors.New("get table failed: " + err.Error())
|
return nil, nil, fmt.Errorf("get table failed: %w", err)
|
||||||
}
|
}
|
||||||
chain, err := table.GetChain(nftablesChainName)
|
chain, err := table.GetChain(nftablesChainName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, errors.New("get chain failed: " + err.Error())
|
return nil, nil, fmt.Errorf("get chain failed: %w", err)
|
||||||
}
|
}
|
||||||
rules, err := chain.GetRules()
|
rules, err := chain.GetRules()
|
||||||
return chain, rules, err
|
return chain, rules, err
|
||||||
@@ -538,7 +539,7 @@ func (this *DDoSProtectionManager) updateAllowIPList(allIPList []string) error {
|
|||||||
// 不存在则删除
|
// 不存在则删除
|
||||||
err = set.DeleteIPElement(ip)
|
err = set.DeleteIPElement(ip)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("delete ip element '" + ip + "' failed: " + err.Error())
|
return fmt.Errorf("delete ip element '%s' failed: %w", ip, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -556,7 +557,7 @@ func (this *DDoSProtectionManager) updateAllowIPList(allIPList []string) error {
|
|||||||
// 不存在则添加
|
// 不存在则添加
|
||||||
err = set.AddIPElement(ip, nil, false)
|
err = set.AddIPElement(ip, nil, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("add ip '" + ip + "' failed: " + err.Error())
|
return fmt.Errorf("add ip '%s' failed: %w", ip, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
package firewalls
|
package firewalls
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"fmt"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/conns"
|
"github.com/TeaOSLab/EdgeNode/internal/conns"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
@@ -194,7 +194,7 @@ func (this *Firewalld) DropSourceIP(ip string, timeoutSeconds int, async bool) e
|
|||||||
|
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("run command failed '" + cmd.String() + "': " + err.Error())
|
return fmt.Errorf("run command failed '%s': %w", cmd.String(), err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ package firewalls
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
|
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/conns"
|
"github.com/TeaOSLab/EdgeNode/internal/conns"
|
||||||
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
||||||
@@ -149,10 +150,10 @@ func (this *NFTablesFirewall) init() error {
|
|||||||
table, err = this.conn.AddIPv6Table(tableDef.Name)
|
table, err = this.conn.AddIPv6Table(tableDef.Name)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("create table '" + tableDef.Name + "' failed: " + err.Error())
|
return fmt.Errorf("create table '%s' failed: %w", tableDef.Name, err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return errors.New("get table '" + tableDef.Name + "' failed: " + err.Error())
|
return fmt.Errorf("get table '%s' failed: %w", tableDef.Name, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if table == nil {
|
if table == nil {
|
||||||
@@ -166,10 +167,10 @@ func (this *NFTablesFirewall) init() error {
|
|||||||
if nftables.IsNotFound(err) {
|
if nftables.IsNotFound(err) {
|
||||||
chain, err = table.AddAcceptChain(chainName)
|
chain, err = table.AddAcceptChain(chainName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("create chain '" + chainName + "' failed: " + err.Error())
|
return fmt.Errorf("create chain '%s' failed: %w", chainName, err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return errors.New("get chain '" + chainName + "' failed: " + err.Error())
|
return fmt.Errorf("get chain '%s' failed: %w", chainName, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if chain == nil {
|
if chain == nil {
|
||||||
@@ -184,7 +185,7 @@ func (this *NFTablesFirewall) init() error {
|
|||||||
_, err = chain.AddAcceptInterfaceRule("lo", loRuleName)
|
_, err = chain.AddAcceptInterfaceRule("lo", loRuleName)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("add 'lo' rule failed: " + err.Error())
|
return fmt.Errorf("add 'lo' rule failed: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,10 +208,10 @@ func (this *NFTablesFirewall) init() error {
|
|||||||
HasTimeout: true,
|
HasTimeout: true,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("create set '" + setName + "' failed: " + err.Error())
|
return fmt.Errorf("create set '%s' failed: %w", setName, err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return errors.New("get set '" + setName + "' failed: " + err.Error())
|
return fmt.Errorf("get set '%s' failed: %w", setName, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if set == nil {
|
if set == nil {
|
||||||
@@ -259,10 +260,10 @@ func (this *NFTablesFirewall) init() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("add rule failed: " + err.Error())
|
return fmt.Errorf("add rule failed: %w", err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return errors.New("get rule failed: " + err.Error())
|
return fmt.Errorf("get rule failed: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if rule == nil {
|
if rule == nil {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
package nftables
|
package nftables
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"fmt"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||||
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/events"
|
"github.com/TeaOSLab/EdgeNode/internal/events"
|
||||||
@@ -85,7 +85,7 @@ func (this *Installer) Install() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否已经存在
|
// 检查是否已经存在
|
||||||
if len(NftExePath()) > 0 {
|
if len(NftExePath()) > 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,7 +121,7 @@ func (this *Installer) Install() error {
|
|||||||
cmd.WithStderr()
|
cmd.WithStderr()
|
||||||
err = cmd.Run()
|
err = cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New(err.Error() + ": " + cmd.Stderr())
|
return fmt.Errorf("%w: %s", err, cmd.Stderr())
|
||||||
}
|
}
|
||||||
|
|
||||||
remotelogs.Println("NFTABLES", "installed nftables with command '"+cmd.String()+"' successfully")
|
remotelogs.Println("NFTABLES", "installed nftables with command '"+cmd.String()+"' successfully")
|
||||||
|
|||||||
@@ -1,27 +1,28 @@
|
|||||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||||
|
|
||||||
package goman
|
package goman_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNew(t *testing.T) {
|
func TestNew(t *testing.T) {
|
||||||
New(func() {
|
goman.New(func() {
|
||||||
t.Log("Hello")
|
t.Log("Hello")
|
||||||
|
|
||||||
t.Log(List())
|
t.Log(goman.List())
|
||||||
})
|
})
|
||||||
|
|
||||||
time.Sleep(1 * time.Second)
|
time.Sleep(1 * time.Second)
|
||||||
t.Log(List())
|
t.Log(goman.List())
|
||||||
|
|
||||||
time.Sleep(1 * time.Second)
|
time.Sleep(1 * time.Second)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewWithArgs(t *testing.T) {
|
func TestNewWithArgs(t *testing.T) {
|
||||||
NewWithArgs(func(args ...interface{}) {
|
goman.NewWithArgs(func(args ...interface{}) {
|
||||||
t.Log(args[0], args[1])
|
t.Log(args[0], args[1])
|
||||||
}, 1, 2)
|
}, 1, 2)
|
||||||
time.Sleep(1 * time.Second)
|
time.Sleep(1 * time.Second)
|
||||||
|
|||||||
52
internal/goman/task_group.go
Normal file
52
internal/goman/task_group.go
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||||
|
|
||||||
|
package goman
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/zero"
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TaskGroup struct {
|
||||||
|
semi chan zero.Zero
|
||||||
|
wg *sync.WaitGroup
|
||||||
|
locker *sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTaskGroup() *TaskGroup {
|
||||||
|
var concurrent = runtime.NumCPU()
|
||||||
|
if concurrent <= 1 {
|
||||||
|
concurrent = 2
|
||||||
|
}
|
||||||
|
return &TaskGroup{
|
||||||
|
semi: make(chan zero.Zero, concurrent),
|
||||||
|
wg: &sync.WaitGroup{},
|
||||||
|
locker: &sync.RWMutex{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *TaskGroup) Run(f func()) {
|
||||||
|
this.wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer this.wg.Done()
|
||||||
|
|
||||||
|
this.semi <- zero.Zero{}
|
||||||
|
|
||||||
|
f()
|
||||||
|
|
||||||
|
<-this.semi
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *TaskGroup) Wait() {
|
||||||
|
this.wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *TaskGroup) Lock() {
|
||||||
|
this.locker.Lock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *TaskGroup) Unlock() {
|
||||||
|
this.locker.Unlock()
|
||||||
|
}
|
||||||
30
internal/goman/task_group_test.go
Normal file
30
internal/goman/task_group_test.go
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||||
|
|
||||||
|
package goman_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||||
|
"runtime"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewTaskGroup(t *testing.T) {
|
||||||
|
var group = goman.NewTaskGroup()
|
||||||
|
var m = map[int]bool{}
|
||||||
|
|
||||||
|
for i := 0; i < runtime.NumCPU()*2; i++ {
|
||||||
|
var index = i
|
||||||
|
group.Run(func() {
|
||||||
|
t.Log("task", index)
|
||||||
|
|
||||||
|
group.Lock()
|
||||||
|
_, ok := m[index]
|
||||||
|
if ok {
|
||||||
|
t.Error("duplicated:", index)
|
||||||
|
}
|
||||||
|
m[index] = true
|
||||||
|
group.Unlock()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
group.Wait()
|
||||||
|
}
|
||||||
@@ -147,7 +147,7 @@ func (this *FirewalldAction) runActionSingleIP(action string, listType IPListTyp
|
|||||||
cmd.WithStderr()
|
cmd.WithStderr()
|
||||||
err = cmd.Run()
|
err = cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New(err.Error() + ", output: " + cmd.Stderr())
|
return fmt.Errorf("%w, output: %s", err, cmd.Stderr())
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package iplibrary
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
||||||
executils "github.com/TeaOSLab/EdgeNode/internal/utils/exec"
|
executils "github.com/TeaOSLab/EdgeNode/internal/utils/exec"
|
||||||
@@ -70,7 +71,7 @@ func (this *IPSetAction) Init(config *firewallconfigs.FirewallActionConfig) erro
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
var output = cmd.Stderr()
|
var output = cmd.Stderr()
|
||||||
if !strings.Contains(output, "already exists") {
|
if !strings.Contains(output, "already exists") {
|
||||||
return errors.New("create ipset '" + listName + "': " + err.Error() + ", output: " + output)
|
return fmt.Errorf("create ipset '%s': %w, output: %s", listName, err, output)
|
||||||
} else {
|
} else {
|
||||||
err = nil
|
err = nil
|
||||||
}
|
}
|
||||||
@@ -88,7 +89,7 @@ func (this *IPSetAction) Init(config *firewallconfigs.FirewallActionConfig) erro
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
var output = cmd.Stderr()
|
var output = cmd.Stderr()
|
||||||
if !strings.Contains(output, "already exists") {
|
if !strings.Contains(output, "already exists") {
|
||||||
return errors.New("create ipset '" + listName + "': " + err.Error() + ", output: " + output)
|
return fmt.Errorf("create ipset '%s': %w, output: %s", listName, err, output)
|
||||||
} else {
|
} else {
|
||||||
err = nil
|
err = nil
|
||||||
}
|
}
|
||||||
@@ -116,7 +117,7 @@ func (this *IPSetAction) Init(config *firewallconfigs.FirewallActionConfig) erro
|
|||||||
if strings.Contains(output, "NAME_CONFLICT") {
|
if strings.Contains(output, "NAME_CONFLICT") {
|
||||||
err = nil
|
err = nil
|
||||||
} else {
|
} else {
|
||||||
return errors.New("firewall-cmd add ipset '" + listName + "': " + err.Error() + ", output: " + output)
|
return fmt.Errorf("firewall-cmd add ipset '%s': %w, output: %s", listName, err, output)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -134,7 +135,7 @@ func (this *IPSetAction) Init(config *firewallconfigs.FirewallActionConfig) erro
|
|||||||
if strings.Contains(output, "NAME_CONFLICT") {
|
if strings.Contains(output, "NAME_CONFLICT") {
|
||||||
err = nil
|
err = nil
|
||||||
} else {
|
} else {
|
||||||
return errors.New("firewall-cmd add ipset '" + listName + "': " + err.Error() + ", output: " + output)
|
return fmt.Errorf("firewall-cmd add ipset '%s': %w, output: %s", listName, err, output)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -148,7 +149,7 @@ func (this *IPSetAction) Init(config *firewallconfigs.FirewallActionConfig) erro
|
|||||||
cmd.WithStderr()
|
cmd.WithStderr()
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("firewall-cmd add rich rule '" + listName + "': " + err.Error() + ", output: " + cmd.Stderr())
|
return fmt.Errorf("firewall-cmd add rich rule '%s': %w, output: %s", listName, err, cmd.Stderr())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,7 +162,7 @@ func (this *IPSetAction) Init(config *firewallconfigs.FirewallActionConfig) erro
|
|||||||
cmd.WithStderr()
|
cmd.WithStderr()
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("firewall-cmd add rich rule '" + listName + "': " + err.Error() + ", output: " + cmd.Stderr())
|
return fmt.Errorf("firewall-cmd add rich rule '%s': %w, output: %s", listName, err, cmd.Stderr())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,7 +172,7 @@ func (this *IPSetAction) Init(config *firewallconfigs.FirewallActionConfig) erro
|
|||||||
cmd.WithStderr()
|
cmd.WithStderr()
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("firewall-cmd reload: " + err.Error() + ", output: " + cmd.Stderr())
|
return fmt.Errorf("firewall-cmd reload: %w, output: %s", err, cmd.Stderr())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -200,7 +201,7 @@ func (this *IPSetAction) Init(config *firewallconfigs.FirewallActionConfig) erro
|
|||||||
cmd.WithStderr()
|
cmd.WithStderr()
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("iptables add rule: " + err.Error() + ", output: " + cmd.Stderr())
|
return fmt.Errorf("iptables add rule: %w, output: %s", err, cmd.Stderr())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -221,7 +222,7 @@ func (this *IPSetAction) Init(config *firewallconfigs.FirewallActionConfig) erro
|
|||||||
cmd.WithStderr()
|
cmd.WithStderr()
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("iptables add rule: " + err.Error() + ", output: " + cmd.Stderr())
|
return fmt.Errorf("iptables add rule: %w, output: %s", err, cmd.Stderr())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -283,7 +284,7 @@ func (this *IPSetAction) runActionSingleIP(action string, listType IPListType, i
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var listName = ""
|
var listName string
|
||||||
var isIPv6 = strings.Contains(item.IpFrom, ":")
|
var isIPv6 = strings.Contains(item.IpFrom, ":")
|
||||||
|
|
||||||
switch listType {
|
switch listType {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package iplibrary
|
package iplibrary
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"fmt"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
@@ -128,7 +128,7 @@ func (this *IPTablesAction) runActionSingleIP(action string, listType IPListType
|
|||||||
if strings.Contains(output, "No chain/target/match") {
|
if strings.Contains(output, "No chain/target/match") {
|
||||||
err = nil
|
err = nil
|
||||||
} else {
|
} else {
|
||||||
return errors.New(err.Error() + ", output: " + output)
|
return fmt.Errorf("%w, output: %s", err, output)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ func (this *ActionManager) UpdateActions(actions []*firewallconfigs.FirewallActi
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
instances, _ := this.eventMap[action.EventLevel]
|
var instances = this.eventMap[action.EventLevel]
|
||||||
instances = append(instances, instance)
|
instances = append(instances, instance)
|
||||||
this.eventMap[action.EventLevel] = instances
|
this.eventMap[action.EventLevel] = instances
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package iplibrary
|
package iplibrary
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
||||||
@@ -62,7 +61,7 @@ func (this *ScriptAction) runAction(action string, listType IPListType, item *pb
|
|||||||
cmd.WithStderr()
|
cmd.WithStderr()
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New(err.Error() + ", output: " + cmd.Stderr())
|
return fmt.Errorf("%w, output: %s", err, cmd.Stderr())
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils/expires"
|
"github.com/TeaOSLab/EdgeNode/internal/utils/expires"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils/fasttime"
|
"github.com/TeaOSLab/EdgeNode/internal/utils/fasttime"
|
||||||
|
"github.com/iwind/TeaGo/logs"
|
||||||
"sort"
|
"sort"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
@@ -14,6 +15,8 @@ var GlobalWhiteIPList = NewIPList()
|
|||||||
// IPList IP名单
|
// IPList IP名单
|
||||||
// TODO IP名单可以分片关闭,这样让每一片的数据量减少,查询更快
|
// TODO IP名单可以分片关闭,这样让每一片的数据量减少,查询更快
|
||||||
type IPList struct {
|
type IPList struct {
|
||||||
|
isDeleted bool
|
||||||
|
|
||||||
itemsMap map[uint64]*IPItem // id => item
|
itemsMap map[uint64]*IPItem // id => item
|
||||||
sortedItems []*IPItem
|
sortedItems []*IPItem
|
||||||
allItemsMap map[uint64]*IPItem // id => item
|
allItemsMap map[uint64]*IPItem // id => item
|
||||||
@@ -38,11 +41,19 @@ func NewIPList() *IPList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *IPList) Add(item *IPItem) {
|
func (this *IPList) Add(item *IPItem) {
|
||||||
|
if this.isDeleted {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
this.addItem(item, true)
|
this.addItem(item, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddDelay 延迟添加,需要手工调用Sort()函数
|
// AddDelay 延迟添加,需要手工调用Sort()函数
|
||||||
func (this *IPList) AddDelay(item *IPItem) {
|
func (this *IPList) AddDelay(item *IPItem) {
|
||||||
|
if this.isDeleted {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
this.addItem(item, false)
|
this.addItem(item, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,6 +71,10 @@ func (this *IPList) Delete(itemId uint64) {
|
|||||||
|
|
||||||
// Contains 判断是否包含某个IP
|
// Contains 判断是否包含某个IP
|
||||||
func (this *IPList) Contains(ip uint64) bool {
|
func (this *IPList) Contains(ip uint64) bool {
|
||||||
|
if this.isDeleted {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
this.locker.RLock()
|
this.locker.RLock()
|
||||||
if len(this.allItemsMap) > 0 {
|
if len(this.allItemsMap) > 0 {
|
||||||
this.locker.RUnlock()
|
this.locker.RUnlock()
|
||||||
@@ -75,6 +90,10 @@ func (this *IPList) Contains(ip uint64) bool {
|
|||||||
|
|
||||||
// ContainsExpires 判断是否包含某个IP
|
// ContainsExpires 判断是否包含某个IP
|
||||||
func (this *IPList) ContainsExpires(ip uint64) (expiresAt int64, ok bool) {
|
func (this *IPList) ContainsExpires(ip uint64) (expiresAt int64, ok bool) {
|
||||||
|
if this.isDeleted {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
this.locker.RLock()
|
this.locker.RLock()
|
||||||
if len(this.allItemsMap) > 0 {
|
if len(this.allItemsMap) > 0 {
|
||||||
this.locker.RUnlock()
|
this.locker.RUnlock()
|
||||||
@@ -94,6 +113,10 @@ func (this *IPList) ContainsExpires(ip uint64) (expiresAt int64, ok bool) {
|
|||||||
|
|
||||||
// ContainsIPStrings 是否包含一组IP中的任意一个,并返回匹配的第一个Item
|
// ContainsIPStrings 是否包含一组IP中的任意一个,并返回匹配的第一个Item
|
||||||
func (this *IPList) ContainsIPStrings(ipStrings []string) (item *IPItem, found bool) {
|
func (this *IPList) ContainsIPStrings(ipStrings []string) (item *IPItem, found bool) {
|
||||||
|
if this.isDeleted {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if len(ipStrings) == 0 {
|
if len(ipStrings) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -125,6 +148,11 @@ func (this *IPList) ContainsIPStrings(ipStrings []string) (item *IPItem, found b
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *IPList) SetDeleted() {
|
||||||
|
this.isDeleted = true
|
||||||
|
logs.Println("set deleted:", this.isDeleted) // TODO
|
||||||
|
}
|
||||||
|
|
||||||
func (this *IPList) addItem(item *IPItem, sortable bool) {
|
func (this *IPList) addItem(item *IPItem, sortable bool) {
|
||||||
if item == nil {
|
if item == nil {
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -22,10 +22,10 @@ func TestIPListDB_AddItem(t *testing.T) {
|
|||||||
IpFrom: "192.168.1.101",
|
IpFrom: "192.168.1.101",
|
||||||
IpTo: "",
|
IpTo: "",
|
||||||
Version: 1024,
|
Version: 1024,
|
||||||
ExpiredAt: time.Now().Unix(),
|
ExpiredAt: time.Now().Unix() + 3600,
|
||||||
Reason: "",
|
Reason: "",
|
||||||
ListId: 2,
|
ListId: 2,
|
||||||
IsDeleted: true,
|
IsDeleted: false,
|
||||||
Type: "ipv4",
|
Type: "ipv4",
|
||||||
EventLevel: "error",
|
EventLevel: "error",
|
||||||
ListType: "black",
|
ListType: "black",
|
||||||
|
|||||||
@@ -187,7 +187,7 @@ func (this *IPListManager) fetch() (hasNext bool, err error) {
|
|||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if rpc.IsConnError(err) {
|
if rpc.IsConnError(err) {
|
||||||
remotelogs.Warn("IP_LIST_MANAGER", "rpc connection error: "+err.Error())
|
remotelogs.Debug("IP_LIST_MANAGER", "rpc connection error: "+err.Error())
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
return false, err
|
return false, err
|
||||||
@@ -214,7 +214,7 @@ func (this *IPListManager) fetch() (hasNext bool, err error) {
|
|||||||
|
|
||||||
func (this *IPListManager) FindList(listId int64) *IPList {
|
func (this *IPListManager) FindList(listId int64) *IPList {
|
||||||
this.locker.Lock()
|
this.locker.Lock()
|
||||||
list, _ := this.listMap[listId]
|
var list = this.listMap[listId]
|
||||||
this.locker.Unlock()
|
this.locker.Unlock()
|
||||||
return list
|
return list
|
||||||
}
|
}
|
||||||
@@ -229,6 +229,11 @@ func (this *IPListManager) DeleteExpiredItems() {
|
|||||||
func (this *IPListManager) processItems(items []*pb.IPItem, fromRemote bool) {
|
func (this *IPListManager) processItems(items []*pb.IPItem, fromRemote bool) {
|
||||||
var changedLists = map[*IPList]zero.Zero{}
|
var changedLists = map[*IPList]zero.Zero{}
|
||||||
for _, item := range items {
|
for _, item := range items {
|
||||||
|
// 调试
|
||||||
|
if Tea.IsTesting() {
|
||||||
|
this.debugItem(item)
|
||||||
|
}
|
||||||
|
|
||||||
var list *IPList
|
var list *IPList
|
||||||
// TODO 实现节点专有List
|
// TODO 实现节点专有List
|
||||||
if item.ServerId > 0 { // 服务专有List
|
if item.ServerId > 0 { // 服务专有List
|
||||||
@@ -300,3 +305,12 @@ func (this *IPListManager) processItems(items []*pb.IPItem, fromRemote bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 调试IP信息
|
||||||
|
func (this *IPListManager) debugItem(item *pb.IPItem) {
|
||||||
|
if item.IsDeleted {
|
||||||
|
remotelogs.Debug("IP_ITEM_DEBUG", "delete '"+item.IpFrom+"'")
|
||||||
|
} else {
|
||||||
|
remotelogs.Debug("IP_ITEM_DEBUG", "add '"+item.IpFrom+"'")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -26,9 +26,9 @@ func init() {
|
|||||||
type Manager struct {
|
type Manager struct {
|
||||||
isQuiting bool
|
isQuiting bool
|
||||||
|
|
||||||
tasks map[int64]*Task // itemId => *Task
|
taskMap map[int64]*Task // itemId => *Task
|
||||||
categoryTasks map[string][]*Task // category => []*Task
|
categoryTaskMap map[string][]*Task // category => []*Task
|
||||||
locker sync.RWMutex
|
locker sync.RWMutex
|
||||||
|
|
||||||
hasHTTPMetrics bool
|
hasHTTPMetrics bool
|
||||||
hasTCPMetrics bool
|
hasTCPMetrics bool
|
||||||
@@ -37,8 +37,8 @@ type Manager struct {
|
|||||||
|
|
||||||
func NewManager() *Manager {
|
func NewManager() *Manager {
|
||||||
return &Manager{
|
return &Manager{
|
||||||
tasks: map[int64]*Task{},
|
taskMap: map[int64]*Task{},
|
||||||
categoryTasks: map[string][]*Task{},
|
categoryTaskMap: map[string][]*Task{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,7 +56,7 @@ func (this *Manager) Update(items []*serverconfigs.MetricItemConfig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 停用以前的 或 修改现在的
|
// 停用以前的 或 修改现在的
|
||||||
for itemId, task := range this.tasks {
|
for itemId, task := range this.taskMap {
|
||||||
newItem, ok := newMap[itemId]
|
newItem, ok := newMap[itemId]
|
||||||
if !ok || !newItem.IsOn { // 停用以前的
|
if !ok || !newItem.IsOn { // 停用以前的
|
||||||
remotelogs.Println("METRIC_MANAGER", "stop task '"+strconv.FormatInt(itemId, 10)+"'")
|
remotelogs.Println("METRIC_MANAGER", "stop task '"+strconv.FormatInt(itemId, 10)+"'")
|
||||||
@@ -64,7 +64,7 @@ func (this *Manager) Update(items []*serverconfigs.MetricItemConfig) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
remotelogs.Error("METRIC_MANAGER", "stop task '"+strconv.FormatInt(itemId, 10)+"' failed: "+err.Error())
|
remotelogs.Error("METRIC_MANAGER", "stop task '"+strconv.FormatInt(itemId, 10)+"' failed: "+err.Error())
|
||||||
}
|
}
|
||||||
delete(this.tasks, itemId)
|
delete(this.taskMap, itemId)
|
||||||
} else { // 更新已存在的
|
} else { // 更新已存在的
|
||||||
if newItem.Version != task.item.Version {
|
if newItem.Version != task.item.Version {
|
||||||
remotelogs.Println("METRIC_MANAGER", "update task '"+strconv.FormatInt(itemId, 10)+"'")
|
remotelogs.Println("METRIC_MANAGER", "update task '"+strconv.FormatInt(itemId, 10)+"'")
|
||||||
@@ -78,7 +78,7 @@ func (this *Manager) Update(items []*serverconfigs.MetricItemConfig) {
|
|||||||
if !newItem.IsOn {
|
if !newItem.IsOn {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
_, ok := this.tasks[newItem.Id]
|
_, ok := this.taskMap[newItem.Id]
|
||||||
if !ok {
|
if !ok {
|
||||||
remotelogs.Println("METRIC_MANAGER", "start task '"+strconv.FormatInt(newItem.Id, 10)+"'")
|
remotelogs.Println("METRIC_MANAGER", "start task '"+strconv.FormatInt(newItem.Id, 10)+"'")
|
||||||
task := NewTask(newItem)
|
task := NewTask(newItem)
|
||||||
@@ -92,7 +92,7 @@ func (this *Manager) Update(items []*serverconfigs.MetricItemConfig) {
|
|||||||
remotelogs.Error("METRIC_MANAGER", "start task failed: "+err.Error())
|
remotelogs.Error("METRIC_MANAGER", "start task failed: "+err.Error())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
this.tasks[newItem.Id] = task
|
this.taskMap[newItem.Id] = task
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,11 +100,11 @@ func (this *Manager) Update(items []*serverconfigs.MetricItemConfig) {
|
|||||||
this.hasHTTPMetrics = false
|
this.hasHTTPMetrics = false
|
||||||
this.hasTCPMetrics = false
|
this.hasTCPMetrics = false
|
||||||
this.hasUDPMetrics = false
|
this.hasUDPMetrics = false
|
||||||
this.categoryTasks = map[string][]*Task{}
|
this.categoryTaskMap = map[string][]*Task{}
|
||||||
for _, task := range this.tasks {
|
for _, task := range this.taskMap {
|
||||||
tasks := this.categoryTasks[task.item.Category]
|
var tasks = this.categoryTaskMap[task.item.Category]
|
||||||
tasks = append(tasks, task)
|
tasks = append(tasks, task)
|
||||||
this.categoryTasks[task.item.Category] = tasks
|
this.categoryTaskMap[task.item.Category] = tasks
|
||||||
|
|
||||||
switch task.item.Category {
|
switch task.item.Category {
|
||||||
case serverconfigs.MetricItemCategoryHTTP:
|
case serverconfigs.MetricItemCategoryHTTP:
|
||||||
@@ -124,10 +124,12 @@ func (this *Manager) Add(obj MetricInterface) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.locker.RLock()
|
this.locker.RLock()
|
||||||
for _, task := range this.categoryTasks[obj.MetricCategory()] {
|
var tasks = this.categoryTaskMap[obj.MetricCategory()]
|
||||||
|
this.locker.RUnlock()
|
||||||
|
|
||||||
|
for _, task := range tasks {
|
||||||
task.Add(obj)
|
task.Add(obj)
|
||||||
}
|
}
|
||||||
this.locker.RUnlock()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *Manager) HasHTTPMetrics() bool {
|
func (this *Manager) HasHTTPMetrics() bool {
|
||||||
@@ -149,9 +151,9 @@ func (this *Manager) Quit() {
|
|||||||
remotelogs.Println("METRIC_MANAGER", "quit")
|
remotelogs.Println("METRIC_MANAGER", "quit")
|
||||||
|
|
||||||
this.locker.Lock()
|
this.locker.Lock()
|
||||||
for _, task := range this.tasks {
|
for _, task := range this.taskMap {
|
||||||
_ = task.Stop()
|
_ = task.Stop()
|
||||||
}
|
}
|
||||||
this.tasks = map[int64]*Task{}
|
this.taskMap = map[int64]*Task{}
|
||||||
this.locker.Unlock()
|
this.locker.Unlock()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ func TestNewManager(t *testing.T) {
|
|||||||
var manager = NewManager()
|
var manager = NewManager()
|
||||||
{
|
{
|
||||||
manager.Update([]*serverconfigs.MetricItemConfig{})
|
manager.Update([]*serverconfigs.MetricItemConfig{})
|
||||||
for _, task := range manager.tasks {
|
for _, task := range manager.taskMap {
|
||||||
t.Log(task.item.Id)
|
t.Log(task.item.Id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -28,7 +28,7 @@ func TestNewManager(t *testing.T) {
|
|||||||
Id: 3,
|
Id: 3,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
for _, task := range manager.tasks {
|
for _, task := range manager.taskMap {
|
||||||
t.Log("task:", task.item.Id)
|
t.Log("task:", task.item.Id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -43,7 +43,7 @@ func TestNewManager(t *testing.T) {
|
|||||||
Id: 2,
|
Id: 2,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
for _, task := range manager.tasks {
|
for _, task := range manager.taskMap {
|
||||||
t.Log("task:", task.item.Id)
|
t.Log("task:", task.item.Id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -56,7 +56,7 @@ func TestNewManager(t *testing.T) {
|
|||||||
Version: 1,
|
Version: 1,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
for _, task := range manager.tasks {
|
for _, task := range manager.taskMap {
|
||||||
t.Log("task:", task.item.Id)
|
t.Log("task:", task.item.Id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,6 @@ type Stat struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func SumStat(serverId int64, keys []string, time string, version int32, itemId int64) string {
|
func SumStat(serverId int64, keys []string, time string, version int32, itemId int64) string {
|
||||||
keysData := strings.Join(keys, "$EDGE$")
|
var keysData = strings.Join(keys, "$EDGE$")
|
||||||
return strconv.FormatUint(fnv.HashString(strconv.FormatInt(serverId, 10)+"@"+keysData+"@"+time+"@"+strconv.Itoa(int(version))+"@"+strconv.FormatInt(itemId, 10)), 10)
|
return strconv.FormatUint(fnv.HashString(strconv.FormatInt(serverId, 10)+"@"+keysData+"@"+time+"@"+strconv.Itoa(int(version))+"@"+strconv.FormatInt(itemId, 10)), 10)
|
||||||
}
|
}
|
||||||
|
|||||||
20
internal/metrics/sum_test.go
Normal file
20
internal/metrics/sum_test.go
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||||
|
|
||||||
|
package metrics_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/metrics"
|
||||||
|
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||||
|
"runtime"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func BenchmarkSumStat(b *testing.B) {
|
||||||
|
runtime.GOMAXPROCS(2)
|
||||||
|
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
for pb.Next() {
|
||||||
|
metrics.SumStat(1, []string{"1.2.3.4"}, timeutil.Format("Ymd"), 1, 1)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -19,6 +19,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -59,7 +60,7 @@ type Task struct {
|
|||||||
serverIdMapLocker sync.Mutex
|
serverIdMapLocker sync.Mutex
|
||||||
|
|
||||||
statsMap map[string]*Stat // 待写入队列,hash => *Stat
|
statsMap map[string]*Stat // 待写入队列,hash => *Stat
|
||||||
statsLocker sync.Mutex
|
statsLocker sync.RWMutex
|
||||||
statsTicker *utils.Ticker
|
statsTicker *utils.Ticker
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,7 +238,7 @@ func (this *Task) Add(obj MetricInterface) {
|
|||||||
|
|
||||||
var keys = []string{}
|
var keys = []string{}
|
||||||
for _, key := range this.item.Keys {
|
for _, key := range this.item.Keys {
|
||||||
k := obj.MetricKey(key)
|
var k = obj.MetricKey(key)
|
||||||
|
|
||||||
// 忽略499状态
|
// 忽略499状态
|
||||||
if key == "${status}" && k == "499" {
|
if key == "${status}" && k == "499" {
|
||||||
@@ -253,14 +254,19 @@ func (this *Task) Add(obj MetricInterface) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var hash = SumStat(obj.MetricServerId(), keys, this.item.CurrentTime(), this.item.Version, this.item.Id)
|
var hash = SumStat(obj.MetricServerId(), keys, this.item.CurrentTime(), this.item.Version, this.item.Id)
|
||||||
this.statsLocker.Lock()
|
var countItems int
|
||||||
|
this.statsLocker.RLock()
|
||||||
oldStat, ok := this.statsMap[hash]
|
oldStat, ok := this.statsMap[hash]
|
||||||
|
if !ok {
|
||||||
|
countItems = len(this.statsMap)
|
||||||
|
}
|
||||||
|
this.statsLocker.RUnlock()
|
||||||
if ok {
|
if ok {
|
||||||
oldStat.Value += v
|
atomic.AddInt64(&oldStat.Value, 1)
|
||||||
oldStat.Hash = hash
|
|
||||||
} else {
|
} else {
|
||||||
// 防止过载
|
// 防止过载
|
||||||
if len(this.statsMap) < MaxQueueSize {
|
if countItems < MaxQueueSize {
|
||||||
|
this.statsLocker.Lock()
|
||||||
this.statsMap[hash] = &Stat{
|
this.statsMap[hash] = &Stat{
|
||||||
ServerId: obj.MetricServerId(),
|
ServerId: obj.MetricServerId(),
|
||||||
Keys: keys,
|
Keys: keys,
|
||||||
@@ -268,9 +274,9 @@ func (this *Task) Add(obj MetricInterface) {
|
|||||||
Time: this.item.CurrentTime(),
|
Time: this.item.CurrentTime(),
|
||||||
Hash: hash,
|
Hash: hash,
|
||||||
}
|
}
|
||||||
|
this.statsLocker.Unlock()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.statsLocker.Unlock()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop 停止任务
|
// Stop 停止任务
|
||||||
|
|||||||
@@ -4,11 +4,15 @@ package metrics_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/metrics"
|
"github.com/TeaOSLab/EdgeNode/internal/metrics"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils/testutils"
|
"github.com/TeaOSLab/EdgeNode/internal/utils/testutils"
|
||||||
_ "github.com/iwind/TeaGo/bootstrap"
|
_ "github.com/iwind/TeaGo/bootstrap"
|
||||||
"github.com/iwind/TeaGo/rands"
|
"github.com/iwind/TeaGo/rands"
|
||||||
|
"log"
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -213,3 +217,65 @@ func TestTask_Upload(t *testing.T) {
|
|||||||
}
|
}
|
||||||
t.Log("ok")
|
t.Log("ok")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var testingTask *metrics.Task
|
||||||
|
var testingTaskInitOnce = &sync.Once{}
|
||||||
|
|
||||||
|
func initTestingTask() {
|
||||||
|
testingTask = metrics.NewTask(&serverconfigs.MetricItemConfig{
|
||||||
|
Id: 1,
|
||||||
|
IsOn: false,
|
||||||
|
Category: "tcp",
|
||||||
|
Period: 1,
|
||||||
|
PeriodUnit: serverconfigs.MetricItemPeriodUnitDay,
|
||||||
|
Keys: []string{"${remoteAddr}"},
|
||||||
|
Value: "${countRequest}",
|
||||||
|
})
|
||||||
|
|
||||||
|
err := testingTask.Init()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = testingTask.Start()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkTask_Add(b *testing.B) {
|
||||||
|
runtime.GOMAXPROCS(1)
|
||||||
|
|
||||||
|
testingTaskInitOnce.Do(func() {
|
||||||
|
initTestingTask()
|
||||||
|
})
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
for pb.Next() {
|
||||||
|
testingTask.Add(&taskRequest{})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type taskRequest struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *taskRequest) MetricKey(key string) string {
|
||||||
|
return configutils.ParseVariables(key, func(varName string) (value string) {
|
||||||
|
return "1.2.3.4"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *taskRequest) MetricValue(value string) (result int64, ok bool) {
|
||||||
|
return 1, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *taskRequest) MetricServerId() int64 {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *taskRequest) MetricCategory() string {
|
||||||
|
return "http"
|
||||||
|
}
|
||||||
|
|||||||
@@ -50,7 +50,11 @@ func (this *APIStream) Start() {
|
|||||||
}
|
}
|
||||||
err := this.loop()
|
err := this.loop()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
remotelogs.Warn("API_STREAM", err.Error())
|
if rpc.IsConnError(err) {
|
||||||
|
remotelogs.Debug("API_STREAM", err.Error())
|
||||||
|
} else {
|
||||||
|
remotelogs.Warn("API_STREAM", err.Error())
|
||||||
|
}
|
||||||
time.Sleep(10 * time.Second)
|
time.Sleep(10 * time.Second)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -76,7 +80,7 @@ func (this *APIStream) loop() error {
|
|||||||
if this.isQuiting {
|
if this.isQuiting {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return errors.Wrap(err)
|
return err
|
||||||
}
|
}
|
||||||
this.stream = nodeStream
|
this.stream = nodeStream
|
||||||
|
|
||||||
@@ -92,7 +96,7 @@ func (this *APIStream) loop() error {
|
|||||||
remotelogs.Println("API_STREAM", "quit")
|
remotelogs.Println("API_STREAM", "quit")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return errors.Wrap(err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理消息
|
// 处理消息
|
||||||
@@ -438,10 +442,10 @@ func (this *APIStream) handleChangeAPINode(message *pb.NodeStreamMessage) error
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
config.RPC.Endpoints = []string{messageData.Addr}
|
config.RPCEndpoints = []string{messageData.Addr}
|
||||||
|
|
||||||
// 保存到文件
|
// 保存到文件
|
||||||
err = config.WriteFile(Tea.ConfigFile("api.yaml"))
|
err = config.WriteFile(Tea.ConfigFile(configs.ConfigFileName))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.replyFail(message.RequestId, "save config file failed: "+err.Error())
|
this.replyFail(message.RequestId, "save config file failed: "+err.Error())
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
package nodes
|
package nodes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"fmt"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/conns"
|
"github.com/TeaOSLab/EdgeNode/internal/conns"
|
||||||
@@ -98,7 +98,7 @@ func (this *ClientConn) Read(b []byte) (n int, err error) {
|
|||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.lastErr = errors.New("read error: " + err.Error())
|
this.lastErr = fmt.Errorf("read error: %w", err)
|
||||||
} else {
|
} else {
|
||||||
this.lastErr = nil
|
this.lastErr = nil
|
||||||
}
|
}
|
||||||
@@ -129,9 +129,8 @@ func (this *ClientConn) Read(b []byte) (n int, err error) {
|
|||||||
// 检测是否为超时错误
|
// 检测是否为超时错误
|
||||||
var isTimeout = err != nil && os.IsTimeout(err)
|
var isTimeout = err != nil && os.IsTimeout(err)
|
||||||
var isHandshakeError = isTimeout && !this.hasRead
|
var isHandshakeError = isTimeout && !this.hasRead
|
||||||
if isTimeout {
|
|
||||||
_ = this.SetLinger(0)
|
if err != nil {
|
||||||
} else {
|
|
||||||
_ = this.SetLinger(nodeconfigs.DefaultTCPLinger)
|
_ = this.SetLinger(nodeconfigs.DefaultTCPLinger)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,7 +163,7 @@ func (this *ClientConn) Write(b []byte) (n int, err error) {
|
|||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.lastErr = errors.New("write error: " + err.Error())
|
this.lastErr = fmt.Errorf("write error: %w", err)
|
||||||
} else {
|
} else {
|
||||||
this.lastErr = nil
|
this.lastErr = nil
|
||||||
}
|
}
|
||||||
@@ -189,6 +188,8 @@ func (this *ClientConn) Write(b []byte) (n int, err error) {
|
|||||||
var before = time.Now()
|
var before = time.Now()
|
||||||
n, err = this.rawConn.Write(b)
|
n, err = this.rawConn.Write(b)
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
|
atomic.AddInt64(&this.totalSentBytes, int64(n))
|
||||||
|
|
||||||
// 统计当前服务带宽
|
// 统计当前服务带宽
|
||||||
if this.serverId > 0 {
|
if this.serverId > 0 {
|
||||||
// TODO 需要加入在serverId绑定之前的带宽
|
// TODO 需要加入在serverId绑定之前的带宽
|
||||||
@@ -197,9 +198,9 @@ func (this *ClientConn) Write(b []byte) (n int, err error) {
|
|||||||
|
|
||||||
var cost = time.Since(before).Seconds()
|
var cost = time.Since(before).Seconds()
|
||||||
if cost > 1 {
|
if cost > 1 {
|
||||||
stats.SharedBandwidthStatManager.AddBandwidth(this.userId, this.serverId, int64(float64(n)/cost), int64(n))
|
stats.SharedBandwidthStatManager.AddBandwidth(this.userId, this.userPlanId, this.serverId, int64(float64(n)/cost), int64(n))
|
||||||
} else {
|
} else {
|
||||||
stats.SharedBandwidthStatManager.AddBandwidth(this.userId, this.serverId, int64(n), int64(n))
|
stats.SharedBandwidthStatManager.AddBandwidth(this.userId, this.userPlanId, this.serverId, int64(n), int64(n))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeNode/internal/firewalls"
|
"github.com/TeaOSLab/EdgeNode/internal/firewalls"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/iplibrary"
|
"github.com/TeaOSLab/EdgeNode/internal/iplibrary"
|
||||||
"net"
|
"net"
|
||||||
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -15,6 +16,7 @@ type BaseClientConn struct {
|
|||||||
|
|
||||||
isBound bool
|
isBound bool
|
||||||
userId int64
|
userId int64
|
||||||
|
userPlanId int64
|
||||||
serverId int64
|
serverId int64
|
||||||
remoteAddr string
|
remoteAddr string
|
||||||
hasLimit bool
|
hasLimit bool
|
||||||
@@ -25,6 +27,8 @@ type BaseClientConn struct {
|
|||||||
isClosed bool
|
isClosed bool
|
||||||
|
|
||||||
rawIP string
|
rawIP string
|
||||||
|
|
||||||
|
totalSentBytes int64
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *BaseClientConn) IsClosed() bool {
|
func (this *BaseClientConn) IsClosed() bool {
|
||||||
@@ -103,11 +107,31 @@ func (this *BaseClientConn) SetUserId(userId int64) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *BaseClientConn) SetUserPlanId(userPlanId int64) {
|
||||||
|
this.userPlanId = userPlanId
|
||||||
|
|
||||||
|
// 设置包装前连接
|
||||||
|
switch conn := this.rawConn.(type) {
|
||||||
|
case *tls.Conn:
|
||||||
|
nativeConn, ok := conn.NetConn().(ClientConnInterface)
|
||||||
|
if ok {
|
||||||
|
nativeConn.SetUserPlanId(userPlanId)
|
||||||
|
}
|
||||||
|
case *ClientConn:
|
||||||
|
conn.SetUserPlanId(userPlanId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// UserId 获取当前连接所属服务的用户ID
|
// UserId 获取当前连接所属服务的用户ID
|
||||||
func (this *BaseClientConn) UserId() int64 {
|
func (this *BaseClientConn) UserId() int64 {
|
||||||
return this.userId
|
return this.userId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UserPlanId 用户套餐ID
|
||||||
|
func (this *BaseClientConn) UserPlanId() int64 {
|
||||||
|
return this.userPlanId
|
||||||
|
}
|
||||||
|
|
||||||
// RawIP 原本IP
|
// RawIP 原本IP
|
||||||
func (this *BaseClientConn) RawIP() string {
|
func (this *BaseClientConn) RawIP() string {
|
||||||
if len(this.rawIP) > 0 {
|
if len(this.rawIP) > 0 {
|
||||||
@@ -160,3 +184,10 @@ func (this *BaseClientConn) SetFingerprint(fingerprint []byte) {
|
|||||||
func (this *BaseClientConn) Fingerprint() []byte {
|
func (this *BaseClientConn) Fingerprint() []byte {
|
||||||
return this.fingerprint
|
return this.fingerprint
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LastRequestBytes 读取上一次请求发送的字节数
|
||||||
|
func (this *BaseClientConn) LastRequestBytes() int64 {
|
||||||
|
var result = atomic.LoadInt64(&this.totalSentBytes)
|
||||||
|
atomic.StoreInt64(&this.totalSentBytes, 0)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|||||||
@@ -18,9 +18,12 @@ type ClientConnInterface interface {
|
|||||||
// SetServerId 设置服务ID
|
// SetServerId 设置服务ID
|
||||||
SetServerId(serverId int64) (goNext bool)
|
SetServerId(serverId int64) (goNext bool)
|
||||||
|
|
||||||
// SetUserId 设置所属服务的用户ID
|
// SetUserId 设置所属网站的用户ID
|
||||||
SetUserId(userId int64)
|
SetUserId(userId int64)
|
||||||
|
|
||||||
|
// SetUserPlanId 设置
|
||||||
|
SetUserPlanId(userPlanId int64)
|
||||||
|
|
||||||
// UserId 获取当前连接所属服务的用户ID
|
// UserId 获取当前连接所属服务的用户ID
|
||||||
UserId() int64
|
UserId() int64
|
||||||
|
|
||||||
@@ -32,4 +35,7 @@ type ClientConnInterface interface {
|
|||||||
|
|
||||||
// Fingerprint 读取指纹信息
|
// Fingerprint 读取指纹信息
|
||||||
Fingerprint() []byte
|
Fingerprint() []byte
|
||||||
|
|
||||||
|
// LastRequestBytes 读取上一次请求发送的字节数
|
||||||
|
LastRequestBytes() int64
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ func (this *ClientListener) Accept() (net.Conn, error) {
|
|||||||
firewalls.DropTemporaryTo(ip, expiresAt)
|
firewalls.DropTemporaryTo(ip, expiresAt)
|
||||||
} else {
|
} else {
|
||||||
if !waf.SharedIPWhiteList.Contains(waf.IPTypeAll, firewallconfigs.FirewallScopeGlobal, 0, ip) {
|
if !waf.SharedIPWhiteList.Contains(waf.IPTypeAll, firewallconfigs.FirewallScopeGlobal, 0, ip) {
|
||||||
var ok = false
|
var ok bool
|
||||||
expiresAt, ok = waf.SharedIPBlackList.ContainsExpires(waf.IPTypeAll, firewallconfigs.FirewallScopeGlobal, 0, ip)
|
expiresAt, ok = waf.SharedIPBlackList.ContainsExpires(waf.IPTypeAll, firewallconfigs.FirewallScopeGlobal, 0, ip)
|
||||||
if ok {
|
if ok {
|
||||||
canGoNext = false
|
canGoNext = false
|
||||||
|
|||||||
@@ -82,3 +82,18 @@ func (this *ClientTLSConn) Fingerprint() []byte {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LastRequestBytes 读取上一次请求发送的字节数
|
||||||
|
func (this *ClientTLSConn) LastRequestBytes() int64 {
|
||||||
|
tlsConn, ok := this.rawConn.(*tls.Conn)
|
||||||
|
if ok {
|
||||||
|
var rawConn = tlsConn.NetConn()
|
||||||
|
if rawConn != nil {
|
||||||
|
clientConn, ok := rawConn.(*ClientConn)
|
||||||
|
if ok {
|
||||||
|
return clientConn.LastRequestBytes()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|||||||
@@ -122,6 +122,7 @@ func TestHTTPAccessLogQueue_Memory(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
runtime.GC()
|
runtime.GC()
|
||||||
|
_ = accessLogs
|
||||||
|
|
||||||
// will not release automatically
|
// will not release automatically
|
||||||
func() {
|
func() {
|
||||||
@@ -131,6 +132,7 @@ func TestHTTPAccessLogQueue_Memory(t *testing.T) {
|
|||||||
RequestPath: "https://goedge.cn/hello/world",
|
RequestPath: "https://goedge.cn/hello/world",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
_ = accessLogs1
|
||||||
}()
|
}()
|
||||||
|
|
||||||
time.Sleep(5 * time.Second)
|
time.Sleep(5 * time.Second)
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/caches"
|
"github.com/TeaOSLab/EdgeNode/internal/caches"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/compressions"
|
"github.com/TeaOSLab/EdgeNode/internal/compressions"
|
||||||
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
||||||
@@ -19,8 +21,10 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -41,9 +45,11 @@ var SharedHTTPCacheTaskManager = NewHTTPCacheTaskManager()
|
|||||||
// HTTPCacheTaskManager 缓存任务管理
|
// HTTPCacheTaskManager 缓存任务管理
|
||||||
type HTTPCacheTaskManager struct {
|
type HTTPCacheTaskManager struct {
|
||||||
ticker *time.Ticker
|
ticker *time.Ticker
|
||||||
httpClient *http.Client
|
|
||||||
protocolReg *regexp.Regexp
|
protocolReg *regexp.Regexp
|
||||||
|
|
||||||
|
timeoutClientMap map[time.Duration]*http.Client // timeout seconds=> *http.Client
|
||||||
|
locker sync.Mutex
|
||||||
|
|
||||||
taskQueue chan *pb.PurgeServerCacheRequest
|
taskQueue chan *pb.PurgeServerCacheRequest
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,36 +58,12 @@ func NewHTTPCacheTaskManager() *HTTPCacheTaskManager {
|
|||||||
if Tea.IsTesting() {
|
if Tea.IsTesting() {
|
||||||
duration = 10 * time.Second
|
duration = 10 * time.Second
|
||||||
}
|
}
|
||||||
return &HTTPCacheTaskManager{
|
|
||||||
ticker: time.NewTicker(duration),
|
|
||||||
httpClient: &http.Client{
|
|
||||||
Timeout: 10 * time.Minute, // TODO 可以设置请求超时时间
|
|
||||||
Transport: &http.Transport{
|
|
||||||
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
|
||||||
_, port, err := net.SplitHostPort(addr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
conn, err := net.Dial(network, "127.0.0.1:"+port)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return connutils.NewNoStat(conn), nil
|
return &HTTPCacheTaskManager{
|
||||||
},
|
ticker: time.NewTicker(duration),
|
||||||
MaxIdleConns: 128,
|
protocolReg: regexp.MustCompile(`^(?i)(http|https)://`),
|
||||||
MaxIdleConnsPerHost: 32,
|
taskQueue: make(chan *pb.PurgeServerCacheRequest, 1024),
|
||||||
MaxConnsPerHost: 32,
|
timeoutClientMap: make(map[time.Duration]*http.Client),
|
||||||
IdleConnTimeout: 2 * time.Minute,
|
|
||||||
ExpectContinueTimeout: 1 * time.Second,
|
|
||||||
TLSHandshakeTimeout: 0,
|
|
||||||
TLSClientConfig: &tls.Config{
|
|
||||||
InsecureSkipVerify: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
protocolReg: regexp.MustCompile(`^(?i)(http|https)://`),
|
|
||||||
taskQueue: make(chan *pb.PurgeServerCacheRequest, 1024),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,21 +113,29 @@ func (this *HTTPCacheTaskManager) Loop() error {
|
|||||||
|
|
||||||
var pbResults = []*pb.UpdateHTTPCacheTaskKeysStatusRequest_KeyResult{}
|
var pbResults = []*pb.UpdateHTTPCacheTaskKeysStatusRequest_KeyResult{}
|
||||||
|
|
||||||
|
var taskGroup = goman.NewTaskGroup()
|
||||||
for _, key := range keys {
|
for _, key := range keys {
|
||||||
err = this.processKey(key)
|
var taskKey = key
|
||||||
|
taskGroup.Run(func() {
|
||||||
|
processErr := this.processKey(taskKey)
|
||||||
|
var pbResult = &pb.UpdateHTTPCacheTaskKeysStatusRequest_KeyResult{
|
||||||
|
Id: taskKey.Id,
|
||||||
|
NodeClusterId: taskKey.NodeClusterId,
|
||||||
|
Error: "",
|
||||||
|
}
|
||||||
|
|
||||||
var pbResult = &pb.UpdateHTTPCacheTaskKeysStatusRequest_KeyResult{
|
if processErr != nil {
|
||||||
Id: key.Id,
|
pbResult.Error = processErr.Error()
|
||||||
NodeClusterId: key.NodeClusterId,
|
}
|
||||||
Error: "",
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
taskGroup.Lock()
|
||||||
pbResult.Error = err.Error()
|
pbResults = append(pbResults, pbResult)
|
||||||
}
|
taskGroup.Unlock()
|
||||||
pbResults = append(pbResults, pbResult)
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
taskGroup.Wait()
|
||||||
|
|
||||||
_, err = rpcClient.HTTPCacheTaskKeyRPC.UpdateHTTPCacheTaskKeysStatus(rpcClient.Context(), &pb.UpdateHTTPCacheTaskKeysStatusRequest{KeyResults: pbResults})
|
_, err = rpcClient.HTTPCacheTaskKeyRPC.UpdateHTTPCacheTaskKeysStatus(rpcClient.Context(), &pb.UpdateHTTPCacheTaskKeysStatusRequest{KeyResults: pbResults})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -224,7 +214,6 @@ func (this *HTTPCacheTaskManager) processKey(key *pb.HTTPCacheTaskKey) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO 增加失败重试
|
// TODO 增加失败重试
|
||||||
// TODO 使用并发操作
|
|
||||||
func (this *HTTPCacheTaskManager) fetchKey(key *pb.HTTPCacheTaskKey) error {
|
func (this *HTTPCacheTaskManager) fetchKey(key *pb.HTTPCacheTaskKey) error {
|
||||||
var fullKey = key.Key
|
var fullKey = key.Key
|
||||||
if !this.protocolReg.MatchString(fullKey) {
|
if !this.protocolReg.MatchString(fullKey) {
|
||||||
@@ -233,29 +222,103 @@ func (this *HTTPCacheTaskManager) fetchKey(key *pb.HTTPCacheTaskKey) error {
|
|||||||
|
|
||||||
req, err := http.NewRequest(http.MethodGet, fullKey, nil)
|
req, err := http.NewRequest(http.MethodGet, fullKey, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("invalid url: " + fullKey + ": " + err.Error())
|
return fmt.Errorf("invalid url: '%s': %w", fullKey, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO 可以在管理界面自定义Header
|
// TODO 可以在管理界面自定义Header
|
||||||
req.Header.Set("X-Edge-Cache-Action", "fetch")
|
req.Header.Set("X-Edge-Cache-Action", "fetch")
|
||||||
req.Header.Set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36") // TODO 可以定义
|
req.Header.Set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36") // TODO 可以定义
|
||||||
req.Header.Set("Accept-Encoding", "gzip, deflate, br")
|
req.Header.Set("Accept-Encoding", "gzip, deflate, br")
|
||||||
resp, err := this.httpClient.Do(req)
|
resp, err := this.httpClient().Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("request failed: " + fullKey + ": " + err.Error())
|
err = this.simplifyErr(err)
|
||||||
|
return fmt.Errorf("request failed: '%s': %w", fullKey, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
_ = resp.Body.Close()
|
_ = resp.Body.Close()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// 读取内容,以便于生成缓存
|
|
||||||
_, _ = io.Copy(io.Discard, resp.Body)
|
|
||||||
|
|
||||||
// 处理502
|
// 处理502
|
||||||
if resp.StatusCode == http.StatusBadGateway {
|
if resp.StatusCode == http.StatusBadGateway {
|
||||||
return errors.New("read origin site timeout")
|
return errors.New("read origin site timeout")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 读取内容,以便于生成缓存
|
||||||
|
_, err = io.Copy(io.Discard, resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
if err != io.EOF {
|
||||||
|
err = this.simplifyErr(err)
|
||||||
|
return fmt.Errorf("request failed: '%s': %w", fullKey, err)
|
||||||
|
} else {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *HTTPCacheTaskManager) simplifyErr(err error) error {
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if os.IsTimeout(err) {
|
||||||
|
return errors.New("timeout to read origin site")
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *HTTPCacheTaskManager) httpClient() *http.Client {
|
||||||
|
var timeout = serverconfigs.DefaultHTTPCachePolicyFetchTimeout
|
||||||
|
|
||||||
|
var nodeConfig = sharedNodeConfig // copy
|
||||||
|
if nodeConfig != nil {
|
||||||
|
var cachePolicies = nodeConfig.HTTPCachePolicies // copy
|
||||||
|
if len(cachePolicies) > 0 && cachePolicies[0].FetchTimeout != nil && cachePolicies[0].FetchTimeout.Count > 0 {
|
||||||
|
var fetchTimeout = cachePolicies[0].FetchTimeout.Duration()
|
||||||
|
if fetchTimeout > 0 {
|
||||||
|
timeout = fetchTimeout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.locker.Lock()
|
||||||
|
defer this.locker.Unlock()
|
||||||
|
|
||||||
|
client, ok := this.timeoutClientMap[timeout]
|
||||||
|
if ok {
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
|
||||||
|
client = &http.Client{
|
||||||
|
Timeout: timeout,
|
||||||
|
Transport: &http.Transport{
|
||||||
|
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||||
|
_, port, err := net.SplitHostPort(addr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
conn, err := net.Dial(network, "127.0.0.1:"+port)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return connutils.NewNoStat(conn), nil
|
||||||
|
},
|
||||||
|
MaxIdleConns: 128,
|
||||||
|
MaxIdleConnsPerHost: 32,
|
||||||
|
MaxConnsPerHost: 32,
|
||||||
|
IdleConnTimeout: 2 * time.Minute,
|
||||||
|
ExpectContinueTimeout: 1 * time.Second,
|
||||||
|
TLSHandshakeTimeout: 0,
|
||||||
|
TLSClientConfig: &tls.Config{
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
this.timeoutClientMap[timeout] = client
|
||||||
|
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/utils/fasttime"
|
||||||
"github.com/pires/go-proxyproto"
|
"github.com/pires/go-proxyproto"
|
||||||
"golang.org/x/net/http2"
|
"golang.org/x/net/http2"
|
||||||
"net"
|
"net"
|
||||||
@@ -21,6 +21,8 @@ import (
|
|||||||
// SharedHTTPClientPool HTTP客户端池单例
|
// SharedHTTPClientPool HTTP客户端池单例
|
||||||
var SharedHTTPClientPool = NewHTTPClientPool()
|
var SharedHTTPClientPool = NewHTTPClientPool()
|
||||||
|
|
||||||
|
const httpClientProxyProtocolTag = "@ProxyProtocol@"
|
||||||
|
|
||||||
// HTTPClientPool 客户端池
|
// HTTPClientPool 客户端池
|
||||||
type HTTPClientPool struct {
|
type HTTPClientPool struct {
|
||||||
clientsMap map[string]*HTTPClient // backend key => client
|
clientsMap map[string]*HTTPClient // backend key => client
|
||||||
@@ -55,6 +57,14 @@ func (this *HTTPClientPool) Client(req *HTTPRequest,
|
|||||||
}
|
}
|
||||||
|
|
||||||
var key = origin.UniqueKey() + "@" + originAddr
|
var key = origin.UniqueKey() + "@" + originAddr
|
||||||
|
|
||||||
|
// if we are under available ProxyProtocol, we add client ip to key to make every client unique
|
||||||
|
var isProxyProtocol = false
|
||||||
|
if proxyProtocol != nil && proxyProtocol.IsOn {
|
||||||
|
key += httpClientProxyProtocolTag + req.requestRemoteAddr(true)
|
||||||
|
isProxyProtocol = true
|
||||||
|
}
|
||||||
|
|
||||||
var isLnRequest = origin.Id == 0
|
var isLnRequest = origin.Id == 0
|
||||||
|
|
||||||
this.locker.RLock()
|
this.locker.RLock()
|
||||||
@@ -103,8 +113,9 @@ func (this *HTTPClientPool) Client(req *HTTPRequest,
|
|||||||
idleConns = numberCPU * 16
|
idleConns = numberCPU * 16
|
||||||
}
|
}
|
||||||
|
|
||||||
// 可以判断为Ln节点请求
|
if isProxyProtocol { // ProxyProtocol无需保持太多空闲连接
|
||||||
if isLnRequest {
|
idleConns = 3
|
||||||
|
} else if isLnRequest { // 可以判断为Ln节点请求
|
||||||
maxConnections *= 8
|
maxConnections *= 8
|
||||||
idleConns *= 8
|
idleConns *= 8
|
||||||
idleTimeout *= 4
|
idleTimeout *= 4
|
||||||
@@ -132,14 +143,8 @@ func (this *HTTPClientPool) Client(req *HTTPRequest,
|
|||||||
var transport = &HTTPClientTransport{
|
var transport = &HTTPClientTransport{
|
||||||
Transport: &http.Transport{
|
Transport: &http.Transport{
|
||||||
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||||
// 支持TOA的连接
|
|
||||||
conn, err := this.handleTOA(req, ctx, network, originAddr, connectionTimeout)
|
|
||||||
if conn != nil || err != nil {
|
|
||||||
return conn, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// 普通的连接
|
// 普通的连接
|
||||||
conn, err = (&net.Dialer{
|
conn, err := (&net.Dialer{
|
||||||
Timeout: connectionTimeout,
|
Timeout: connectionTimeout,
|
||||||
KeepAlive: 1 * time.Minute,
|
KeepAlive: 1 * time.Minute,
|
||||||
}).DialContext(ctx, network, originAddr)
|
}).DialContext(ctx, network, originAddr)
|
||||||
@@ -153,7 +158,7 @@ func (this *HTTPClientPool) Client(req *HTTPRequest,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return conn, nil
|
return NewOriginConn(conn), nil
|
||||||
},
|
},
|
||||||
MaxIdleConns: 0,
|
MaxIdleConns: 0,
|
||||||
MaxIdleConnsPerHost: idleConns,
|
MaxIdleConnsPerHost: idleConns,
|
||||||
@@ -202,54 +207,45 @@ func (this *HTTPClientPool) Client(req *HTTPRequest,
|
|||||||
// 清理不使用的Client
|
// 清理不使用的Client
|
||||||
func (this *HTTPClientPool) cleanClients() {
|
func (this *HTTPClientPool) cleanClients() {
|
||||||
for range this.cleanTicker.C {
|
for range this.cleanTicker.C {
|
||||||
var nowTime = time.Now().Unix()
|
var nowTime = fasttime.Now().Unix()
|
||||||
|
|
||||||
this.locker.Lock()
|
var expiredKeys = []string{}
|
||||||
|
var expiredClients = []*HTTPClient{}
|
||||||
|
|
||||||
|
// lookup expired clients
|
||||||
|
this.locker.RLock()
|
||||||
for k, client := range this.clientsMap {
|
for k, client := range this.clientsMap {
|
||||||
if client.AccessTime() < nowTime+86400 { // 超过 N 秒没有调用就关闭
|
if client.AccessTime() < nowTime-86400 ||
|
||||||
|
(strings.Contains(k, httpClientProxyProtocolTag) && client.AccessTime() < nowTime-3600) { // 超过 N 秒没有调用就关闭
|
||||||
|
expiredKeys = append(expiredKeys, k)
|
||||||
|
expiredClients = append(expiredClients, client)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.locker.RUnlock()
|
||||||
|
|
||||||
|
// remove expired keys
|
||||||
|
if len(expiredKeys) > 0 {
|
||||||
|
this.locker.Lock()
|
||||||
|
for _, k := range expiredKeys {
|
||||||
delete(this.clientsMap, k)
|
delete(this.clientsMap, k)
|
||||||
|
}
|
||||||
|
this.locker.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// close expired clients
|
||||||
|
if len(expiredClients) > 0 {
|
||||||
|
for _, client := range expiredClients {
|
||||||
client.Close()
|
client.Close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.locker.Unlock()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 支持TOA
|
|
||||||
func (this *HTTPClientPool) handleTOA(req *HTTPRequest, ctx context.Context, network string, originAddr string, connectionTimeout time.Duration) (net.Conn, error) {
|
|
||||||
// TODO 每个服务读取自身所属集群的TOA设置
|
|
||||||
var toaConfig = sharedTOAManager.Config()
|
|
||||||
if toaConfig != nil && toaConfig.IsOn {
|
|
||||||
var retries = 3
|
|
||||||
for i := 1; i <= retries; i++ {
|
|
||||||
var port = int(toaConfig.RandLocalPort())
|
|
||||||
// TODO 思考是否支持X-Real-IP/X-Forwarded-IP
|
|
||||||
err := sharedTOAManager.SendMsg("add:" + strconv.Itoa(port) + ":" + req.requestRemoteAddr(true))
|
|
||||||
if err != nil {
|
|
||||||
remotelogs.Error("TOA", "add failed: "+err.Error())
|
|
||||||
} else {
|
|
||||||
dialer := net.Dialer{
|
|
||||||
Timeout: connectionTimeout,
|
|
||||||
KeepAlive: 1 * time.Minute,
|
|
||||||
LocalAddr: &net.TCPAddr{
|
|
||||||
Port: port,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
conn, err := dialer.DialContext(ctx, network, originAddr)
|
|
||||||
// TODO 需要在合适的时机删除TOA记录
|
|
||||||
if err == nil || i == retries {
|
|
||||||
return conn, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 支持PROXY Protocol
|
// 支持PROXY Protocol
|
||||||
func (this *HTTPClientPool) handlePROXYProtocol(conn net.Conn, req *HTTPRequest, proxyProtocol *serverconfigs.ProxyProtocolConfig) error {
|
func (this *HTTPClientPool) handlePROXYProtocol(conn net.Conn, req *HTTPRequest, proxyProtocol *serverconfigs.ProxyProtocolConfig) error {
|
||||||
if proxyProtocol != nil && proxyProtocol.IsOn && (proxyProtocol.Version == serverconfigs.ProxyProtocolVersion1 || proxyProtocol.Version == serverconfigs.ProxyProtocolVersion2) {
|
if proxyProtocol != nil &&
|
||||||
|
proxyProtocol.IsOn &&
|
||||||
|
(proxyProtocol.Version == serverconfigs.ProxyProtocolVersion1 || proxyProtocol.Version == serverconfigs.ProxyProtocolVersion2) {
|
||||||
var remoteAddr = req.requestRemoteAddr(true)
|
var remoteAddr = req.requestRemoteAddr(true)
|
||||||
var transportProtocol = proxyproto.TCPv4
|
var transportProtocol = proxyproto.TCPv4
|
||||||
if strings.Contains(remoteAddr, ":") {
|
if strings.Contains(remoteAddr, ":") {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package nodes
|
package nodes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||||
"runtime"
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -16,7 +17,7 @@ func TestHTTPClientPool_Client(t *testing.T) {
|
|||||||
Version: 2,
|
Version: 2,
|
||||||
Addr: &serverconfigs.NetworkAddressConfig{Host: "127.0.0.1", PortRange: "1234"},
|
Addr: &serverconfigs.NetworkAddressConfig{Host: "127.0.0.1", PortRange: "1234"},
|
||||||
}
|
}
|
||||||
err := origin.Init(nil)
|
err := origin.Init(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -43,7 +44,7 @@ func TestHTTPClientPool_cleanClients(t *testing.T) {
|
|||||||
Version: 2,
|
Version: 2,
|
||||||
Addr: &serverconfigs.NetworkAddressConfig{Host: "127.0.0.1", PortRange: "1234"},
|
Addr: &serverconfigs.NetworkAddressConfig{Host: "127.0.0.1", PortRange: "1234"},
|
||||||
}
|
}
|
||||||
err := origin.Init(nil)
|
err := origin.Init(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -65,7 +66,7 @@ func BenchmarkHTTPClientPool_Client(b *testing.B) {
|
|||||||
Version: 2,
|
Version: 2,
|
||||||
Addr: &serverconfigs.NetworkAddressConfig{Host: "127.0.0.1", PortRange: "1234"},
|
Addr: &serverconfigs.NetworkAddressConfig{Host: "127.0.0.1", PortRange: "1234"},
|
||||||
}
|
}
|
||||||
err := origin.Init(nil)
|
err := origin.Init(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,15 +38,16 @@ type HTTPRequest struct {
|
|||||||
requestId string
|
requestId string
|
||||||
|
|
||||||
// 外部参数
|
// 外部参数
|
||||||
RawReq *http.Request
|
RawReq *http.Request
|
||||||
RawWriter http.ResponseWriter
|
RawWriter http.ResponseWriter
|
||||||
ReqServer *serverconfigs.ServerConfig
|
ReqServer *serverconfigs.ServerConfig
|
||||||
ReqHost string // 请求的Host
|
ReqHost string // 请求的Host
|
||||||
ServerName string // 实际匹配到的Host
|
ServerName string // 实际匹配到的Host
|
||||||
ServerAddr string // 实际启动的服务器监听地址
|
ServerAddr string // 实际启动的服务器监听地址
|
||||||
IsHTTP bool
|
IsHTTP bool
|
||||||
IsHTTPS bool
|
IsHTTPS bool
|
||||||
IsHTTP3 bool
|
IsHTTP3 bool
|
||||||
|
isHealthCheck bool
|
||||||
|
|
||||||
// 共享参数
|
// 共享参数
|
||||||
nodeConfig *nodeconfigs.NodeConfig
|
nodeConfig *nodeconfigs.NodeConfig
|
||||||
@@ -168,10 +169,9 @@ func (this *HTTPRequest) Do() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 处理健康检查
|
// 处理健康检查
|
||||||
var isHealthCheck = false
|
|
||||||
var healthCheckKey = this.RawReq.Header.Get(serverconfigs.HealthCheckHeaderName)
|
var healthCheckKey = this.RawReq.Header.Get(serverconfigs.HealthCheckHeaderName)
|
||||||
if len(healthCheckKey) > 0 {
|
if len(healthCheckKey) > 0 {
|
||||||
if this.doHealthCheck(healthCheckKey, &isHealthCheck) {
|
if this.doHealthCheck(healthCheckKey, &this.isHealthCheck) {
|
||||||
this.doEnd()
|
this.doEnd()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -198,14 +198,14 @@ func (this *HTTPRequest) Do() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 流量限制
|
// 流量限制
|
||||||
if this.ReqServer.TrafficLimit != nil && this.ReqServer.TrafficLimit.IsOn && !this.ReqServer.TrafficLimit.IsEmpty() && this.ReqServer.TrafficLimitStatus != nil && this.ReqServer.TrafficLimitStatus.IsValid() {
|
if this.ReqServer.TrafficLimitStatus != nil && this.ReqServer.TrafficLimitStatus.IsValid() {
|
||||||
this.doTrafficLimit()
|
this.doTrafficLimit()
|
||||||
this.doEnd()
|
this.doEnd()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// UAM
|
// UAM
|
||||||
if !isHealthCheck {
|
if !this.isHealthCheck {
|
||||||
if this.web.UAM != nil {
|
if this.web.UAM != nil {
|
||||||
if this.web.UAM.IsOn {
|
if this.web.UAM.IsOn {
|
||||||
if this.doUAM() {
|
if this.doUAM() {
|
||||||
@@ -223,7 +223,7 @@ func (this *HTTPRequest) Do() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CC
|
// CC
|
||||||
if !isHealthCheck {
|
if !this.isHealthCheck {
|
||||||
if this.web.CC != nil {
|
if this.web.CC != nil {
|
||||||
if this.web.CC.IsOn {
|
if this.web.CC.IsOn {
|
||||||
if this.doCC() {
|
if this.doCC() {
|
||||||
@@ -388,7 +388,22 @@ func (this *HTTPRequest) doEnd() {
|
|||||||
|
|
||||||
// 流量统计
|
// 流量统计
|
||||||
// TODO 增加是否开启开关
|
// TODO 增加是否开启开关
|
||||||
if this.ReqServer != nil && this.ReqServer.Id > 0 {
|
if this.ReqServer != nil && this.ReqServer.Id > 0 && !this.isHealthCheck /** 健康检查时不统计 **/ {
|
||||||
|
var totalBytes int64 = 0
|
||||||
|
|
||||||
|
var requestConn = this.RawReq.Context().Value(HTTPConnContextKey)
|
||||||
|
if requestConn != nil {
|
||||||
|
requestClientConn, ok := requestConn.(ClientConnInterface)
|
||||||
|
if ok {
|
||||||
|
// 这里读取的其实是上一个请求消耗的流量,不是当前请求消耗的流量,只不过单个请求的流量统计不需要特别精确,整体趋于一致即可
|
||||||
|
totalBytes = requestClientConn.LastRequestBytes()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if totalBytes == 0 {
|
||||||
|
totalBytes = this.writer.SentBodyBytes() + this.writer.SentHeaderBytes()
|
||||||
|
}
|
||||||
|
|
||||||
var countCached int64 = 0
|
var countCached int64 = 0
|
||||||
var cachedBytes int64 = 0
|
var cachedBytes int64 = 0
|
||||||
|
|
||||||
@@ -397,14 +412,17 @@ func (this *HTTPRequest) doEnd() {
|
|||||||
|
|
||||||
if this.isCached {
|
if this.isCached {
|
||||||
countCached = 1
|
countCached = 1
|
||||||
cachedBytes = this.writer.SentBodyBytes() + this.writer.SentHeaderBytes()
|
cachedBytes = totalBytes
|
||||||
}
|
}
|
||||||
if this.isAttack {
|
if this.isAttack {
|
||||||
countAttacks = 1
|
countAttacks = 1
|
||||||
attackBytes = this.CalculateSize()
|
attackBytes = this.CalculateSize()
|
||||||
|
if attackBytes < totalBytes {
|
||||||
|
attackBytes = totalBytes
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
stats.SharedTrafficStatManager.Add(this.ReqServer.UserId, 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.UserId, this.ReqServer.Id, this.ReqHost, totalBytes, cachedBytes, 1, countCached, countAttacks, attackBytes, this.ReqServer.ShouldCheckTrafficLimit(), this.ReqServer.PlanId())
|
||||||
|
|
||||||
// 指标
|
// 指标
|
||||||
if metrics.SharedManager.HasHTTPMetrics() {
|
if metrics.SharedManager.HasHTTPMetrics() {
|
||||||
@@ -836,6 +854,24 @@ func (this *HTTPRequest) Format(source string) string {
|
|||||||
return this.requestHeadersString()
|
return this.requestHeadersString()
|
||||||
case "serverName":
|
case "serverName":
|
||||||
return this.ServerName
|
return this.ServerName
|
||||||
|
case "serverAddr":
|
||||||
|
var nodeConfig = this.nodeConfig
|
||||||
|
if nodeConfig != nil && nodeConfig.GlobalServerConfig != nil && nodeConfig.GlobalServerConfig.HTTPAll.EnableServerAddrVariable {
|
||||||
|
if len(this.requestRemoteAddrs()) > 1 {
|
||||||
|
return "" // hidden for security
|
||||||
|
}
|
||||||
|
var requestConn = this.RawReq.Context().Value(HTTPConnContextKey)
|
||||||
|
if requestConn != nil {
|
||||||
|
conn, ok := requestConn.(net.Conn)
|
||||||
|
if ok {
|
||||||
|
host, _, _ := net.SplitHostPort(conn.LocalAddr().String())
|
||||||
|
if len(host) > 0 {
|
||||||
|
return host
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
case "serverPort":
|
case "serverPort":
|
||||||
return strconv.Itoa(this.requestServerPort())
|
return strconv.Itoa(this.requestServerPort())
|
||||||
case "hostname":
|
case "hostname":
|
||||||
@@ -1135,10 +1171,32 @@ func (this *HTTPRequest) requestRemoteAddr(supportVar bool) string {
|
|||||||
this.web.RemoteAddr != nil &&
|
this.web.RemoteAddr != nil &&
|
||||||
this.web.RemoteAddr.IsOn &&
|
this.web.RemoteAddr.IsOn &&
|
||||||
!this.web.RemoteAddr.IsEmpty() {
|
!this.web.RemoteAddr.IsEmpty() {
|
||||||
var remoteAddr = this.Format(this.web.RemoteAddr.Value)
|
if this.web.RemoteAddr.HasValues() { // multiple values
|
||||||
if net.ParseIP(remoteAddr) != nil {
|
for _, value := range this.web.RemoteAddr.Values() {
|
||||||
this.remoteAddr = remoteAddr
|
var remoteAddr = this.Format(value)
|
||||||
return remoteAddr
|
if len(remoteAddr) > 0 && net.ParseIP(remoteAddr) != nil {
|
||||||
|
this.remoteAddr = remoteAddr
|
||||||
|
return remoteAddr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else { // single value
|
||||||
|
var remoteAddr = this.Format(this.web.RemoteAddr.Value)
|
||||||
|
if len(remoteAddr) > 0 && net.ParseIP(remoteAddr) != nil {
|
||||||
|
this.remoteAddr = remoteAddr
|
||||||
|
return remoteAddr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果是从Header中读取,则直接返回原始IP
|
||||||
|
if this.web.RemoteAddr.Type == serverconfigs.HTTPRemoteAddrTypeRequestHeader {
|
||||||
|
var remoteAddr = this.RawReq.RemoteAddr
|
||||||
|
host, _, err := net.SplitHostPort(remoteAddr)
|
||||||
|
if err == nil {
|
||||||
|
this.remoteAddr = host
|
||||||
|
return host
|
||||||
|
} else {
|
||||||
|
return remoteAddr
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1204,7 +1262,7 @@ func (this *HTTPRequest) requestRemoteAddrs() (result []string) {
|
|||||||
var 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 && !lists.ContainsString(result, forwardedFor[:commaIndex]) {
|
||||||
result = append(result, forwardedFor[:commaIndex])
|
result = append(result, forwardedFor[:commaIndex])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1212,7 +1270,7 @@ func (this *HTTPRequest) requestRemoteAddrs() (result []string) {
|
|||||||
// Real-IP
|
// Real-IP
|
||||||
{
|
{
|
||||||
realIP, ok := this.RawReq.Header["X-Real-IP"]
|
realIP, ok := this.RawReq.Header["X-Real-IP"]
|
||||||
if ok && len(realIP) > 0 {
|
if ok && len(realIP) > 0 && !lists.ContainsString(result, realIP[0]) {
|
||||||
result = append(result, realIP[0])
|
result = append(result, realIP[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1220,7 +1278,7 @@ func (this *HTTPRequest) requestRemoteAddrs() (result []string) {
|
|||||||
// Real-Ip
|
// Real-Ip
|
||||||
{
|
{
|
||||||
realIP, ok := this.RawReq.Header["X-Real-Ip"]
|
realIP, ok := this.RawReq.Header["X-Real-Ip"]
|
||||||
if ok && len(realIP) > 0 {
|
if ok && len(realIP) > 0 && !lists.ContainsString(result, realIP[0]) {
|
||||||
result = append(result, realIP[0])
|
result = append(result, realIP[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1230,7 +1288,9 @@ func (this *HTTPRequest) requestRemoteAddrs() (result []string) {
|
|||||||
var remoteAddr = this.RawReq.RemoteAddr
|
var remoteAddr = this.RawReq.RemoteAddr
|
||||||
host, _, err := net.SplitHostPort(remoteAddr)
|
host, _, err := net.SplitHostPort(remoteAddr)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
result = append(result, host)
|
if !lists.ContainsString(result, host) {
|
||||||
|
result = append(result, host)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
result = append(result, remoteAddr)
|
result = append(result, remoteAddr)
|
||||||
}
|
}
|
||||||
@@ -1547,10 +1607,10 @@ func (this *HTTPRequest) setForwardHeaders(header http.Header) {
|
|||||||
this.RawReq.Header.Set("Connection", "keep-alive")
|
this.RawReq.Header.Set("Connection", "keep-alive")
|
||||||
}
|
}
|
||||||
|
|
||||||
remoteAddr := this.RawReq.RemoteAddr
|
var rawRemoteAddr = this.RawReq.RemoteAddr
|
||||||
host, _, err := net.SplitHostPort(remoteAddr)
|
host, _, err := net.SplitHostPort(rawRemoteAddr)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
remoteAddr = host
|
rawRemoteAddr = host
|
||||||
}
|
}
|
||||||
|
|
||||||
// x-real-ip
|
// x-real-ip
|
||||||
@@ -1558,24 +1618,24 @@ func (this *HTTPRequest) setForwardHeaders(header http.Header) {
|
|||||||
_, ok1 := header["X-Real-IP"]
|
_, ok1 := header["X-Real-IP"]
|
||||||
_, ok2 := header["X-Real-Ip"]
|
_, ok2 := header["X-Real-Ip"]
|
||||||
if !ok1 && !ok2 {
|
if !ok1 && !ok2 {
|
||||||
header["X-Real-IP"] = []string{remoteAddr}
|
header["X-Real-IP"] = []string{this.requestRemoteAddr(true)}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// X-Forwarded-For
|
// X-Forwarded-For
|
||||||
if this.reverseProxy != nil && this.reverseProxy.ShouldAddXForwardedForHeader() {
|
if this.reverseProxy != nil && this.reverseProxy.ShouldAddXForwardedForHeader() {
|
||||||
forwardedFor, ok := header["X-Forwarded-For"]
|
forwardedFor, ok := header["X-Forwarded-For"]
|
||||||
if ok {
|
if ok && len(forwardedFor) > 0 { // already exists
|
||||||
_, hasForwardHeader := this.RawReq.Header["X-Forwarded-For"]
|
_, hasForwardHeader := this.RawReq.Header["X-Forwarded-For"]
|
||||||
if hasForwardHeader {
|
if hasForwardHeader {
|
||||||
header["X-Forwarded-For"] = []string{strings.Join(forwardedFor, ", ") + ", " + remoteAddr}
|
header["X-Forwarded-For"] = []string{strings.Join(forwardedFor, ", ") + ", " + rawRemoteAddr}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var clientRemoteAddr = this.requestRemoteAddr(true)
|
var clientRemoteAddr = this.requestRemoteAddr(true)
|
||||||
if len(clientRemoteAddr) > 0 && clientRemoteAddr != remoteAddr {
|
if len(clientRemoteAddr) > 0 && clientRemoteAddr != rawRemoteAddr {
|
||||||
header["X-Forwarded-For"] = []string{clientRemoteAddr + ", " + remoteAddr}
|
header["X-Forwarded-For"] = []string{clientRemoteAddr + ", " + rawRemoteAddr}
|
||||||
} else {
|
} else {
|
||||||
header["X-Forwarded-For"] = []string{remoteAddr}
|
header["X-Forwarded-For"] = []string{rawRemoteAddr}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,8 +38,13 @@ func (this *HTTPRequest) doCacheRead(useStale bool) (shouldStop bool) {
|
|||||||
|
|
||||||
// 添加 X-Cache Header
|
// 添加 X-Cache Header
|
||||||
var addStatusHeader = this.web.Cache.AddStatusHeader
|
var addStatusHeader = this.web.Cache.AddStatusHeader
|
||||||
|
var cacheBypassDescription = ""
|
||||||
if addStatusHeader {
|
if addStatusHeader {
|
||||||
defer func() {
|
defer func() {
|
||||||
|
if len(cacheBypassDescription) > 0 {
|
||||||
|
this.writer.Header().Set("X-Cache", cacheBypassDescription)
|
||||||
|
return
|
||||||
|
}
|
||||||
var cacheStatus = this.varMapping["cache.status"]
|
var cacheStatus = this.varMapping["cache.status"]
|
||||||
if cacheStatus != "HIT" {
|
if cacheStatus != "HIT" {
|
||||||
this.writer.Header().Set("X-Cache", cacheStatus)
|
this.writer.Header().Set("X-Cache", cacheStatus)
|
||||||
@@ -85,6 +90,13 @@ func (this *HTTPRequest) doCacheRead(useStale bool) (shouldStop bool) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 是否强制Range回源
|
||||||
|
if this.cacheRef.AlwaysForwardRangeRequest && len(this.RawReq.Header.Get("Range")) > 0 {
|
||||||
|
this.cacheRef = nil
|
||||||
|
cacheBypassDescription = "BYPASS, forward range"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// 是否正在Purge
|
// 是否正在Purge
|
||||||
var isPurging = this.web.Cache.PurgeIsOn && strings.ToUpper(this.RawReq.Method) == "PURGE" && this.RawReq.Header.Get("X-Edge-Purge-Key") == this.web.Cache.PurgeKey
|
var isPurging = this.web.Cache.PurgeIsOn && strings.ToUpper(this.RawReq.Method) == "PURGE" && this.RawReq.Header.Get("X-Edge-Purge-Key") == this.web.Cache.PurgeKey
|
||||||
if isPurging {
|
if isPurging {
|
||||||
@@ -94,6 +106,7 @@ func (this *HTTPRequest) doCacheRead(useStale bool) (shouldStop bool) {
|
|||||||
// 校验请求
|
// 校验请求
|
||||||
if !this.cacheRef.MatchRequest(this.RawReq) {
|
if !this.cacheRef.MatchRequest(this.RawReq) {
|
||||||
this.cacheRef = nil
|
this.cacheRef = nil
|
||||||
|
cacheBypassDescription = "BYPASS, not match"
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,6 +119,7 @@ func (this *HTTPRequest) doCacheRead(useStale bool) (shouldStop bool) {
|
|||||||
if this.cacheRef.EnableRequestCachePragma {
|
if this.cacheRef.EnableRequestCachePragma {
|
||||||
if this.RawReq.Header.Get("Cache-Control") == "no-cache" || this.RawReq.Header.Get("Pragma") == "no-cache" {
|
if this.RawReq.Header.Get("Cache-Control") == "no-cache" || this.RawReq.Header.Get("Pragma") == "no-cache" {
|
||||||
this.cacheRef = nil
|
this.cacheRef = nil
|
||||||
|
cacheBypassDescription = "BYPASS, Cache-Control or Pragma"
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -119,6 +133,7 @@ func (this *HTTPRequest) doCacheRead(useStale bool) (shouldStop bool) {
|
|||||||
var key = this.Format(this.cacheRef.Key)
|
var key = this.Format(this.cacheRef.Key)
|
||||||
if len(key) == 0 {
|
if len(key) == 0 {
|
||||||
this.cacheRef = nil
|
this.cacheRef = nil
|
||||||
|
cacheBypassDescription = "BYPASS, empty key"
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var method = this.Method()
|
var method = this.Method()
|
||||||
@@ -134,6 +149,7 @@ func (this *HTTPRequest) doCacheRead(useStale bool) (shouldStop bool) {
|
|||||||
var storage = caches.SharedManager.FindStorageWithPolicy(cachePolicy.Id)
|
var storage = caches.SharedManager.FindStorageWithPolicy(cachePolicy.Id)
|
||||||
if storage == nil {
|
if storage == nil {
|
||||||
this.cacheRef = nil
|
this.cacheRef = nil
|
||||||
|
cacheBypassDescription = "BYPASS, no policy found"
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.writer.cacheStorage = storage
|
this.writer.cacheStorage = storage
|
||||||
|
|||||||
@@ -9,16 +9,20 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const httpStatusPageTemplate = `<!DOCTYPE html>
|
const httpStatusPageTemplate = `<!DOCTYPE html>
|
||||||
<html>
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<title>${status} ${statusMessage}</title>
|
<title>${status} ${statusMessage}</title>
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
||||||
|
<style>
|
||||||
|
address { line-height: 1.8; }
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<h1>${status} ${statusMessage}</h1>
|
<h1>${status} ${statusMessage}</h1>
|
||||||
<p>${message}</p>
|
<p>${message}</p>
|
||||||
|
|
||||||
|
<address>Connection: ${remoteAddr} (Client) -> ${serverAddr} (Server)</address>
|
||||||
<address>Request ID: ${requestId}.</address>
|
<address>Request ID: ${requestId}.</address>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
@@ -39,8 +43,6 @@ func (this *HTTPRequest) writeCode(statusCode int, enMessage string, zhMessage s
|
|||||||
return types.String(statusCode)
|
return types.String(statusCode)
|
||||||
case "statusMessage":
|
case "statusMessage":
|
||||||
return http.StatusText(statusCode)
|
return http.StatusText(statusCode)
|
||||||
case "requestId":
|
|
||||||
return this.requestId
|
|
||||||
case "message":
|
case "message":
|
||||||
var acceptLanguages = this.RawReq.Header.Get("Accept-Language")
|
var acceptLanguages = this.RawReq.Header.Get("Accept-Language")
|
||||||
if len(acceptLanguages) > 0 {
|
if len(acceptLanguages) > 0 {
|
||||||
@@ -54,7 +56,7 @@ func (this *HTTPRequest) writeCode(statusCode int, enMessage string, zhMessage s
|
|||||||
}
|
}
|
||||||
return enMessage
|
return enMessage
|
||||||
}
|
}
|
||||||
return "${" + varName + "}"
|
return this.Format("${" + varName + "}")
|
||||||
})
|
})
|
||||||
|
|
||||||
this.ProcessResponseHeaders(this.writer.Header(), statusCode)
|
this.ProcessResponseHeaders(this.writer.Header(), statusCode)
|
||||||
@@ -107,7 +109,7 @@ func (this *HTTPRequest) write50x(err error, statusCode int, enMessage string, z
|
|||||||
}
|
}
|
||||||
return "The site is unavailable now, cause: " + enMessage + "."
|
return "The site is unavailable now, cause: " + enMessage + "."
|
||||||
}
|
}
|
||||||
return "${" + varName + "}"
|
return this.Format("${" + varName + "}")
|
||||||
})
|
})
|
||||||
|
|
||||||
this.ProcessResponseHeaders(this.writer.Header(), statusCode)
|
this.ProcessResponseHeaders(this.writer.Header(), statusCode)
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ func (this *HTTPRequest) doFastcgi() (shouldStop bool) {
|
|||||||
|
|
||||||
// 处理SCRIPT_FILENAME
|
// 处理SCRIPT_FILENAME
|
||||||
scriptPath := env.GetString("SCRIPT_FILENAME")
|
scriptPath := env.GetString("SCRIPT_FILENAME")
|
||||||
if len(scriptPath) > 0 && (strings.Index(scriptPath, "/") < 0 && strings.Index(scriptPath, "\\") < 0) {
|
if len(scriptPath) > 0 && !strings.Contains(scriptPath, "/") && !strings.Contains(scriptPath, "\\") {
|
||||||
env["SCRIPT_FILENAME"] = env.GetString("DOCUMENT_ROOT") + Tea.DS + scriptPath
|
env["SCRIPT_FILENAME"] = env.GetString("DOCUMENT_ROOT") + Tea.DS + scriptPath
|
||||||
}
|
}
|
||||||
scriptFilename := filepath.Base(this.RawReq.URL.Path)
|
scriptFilename := filepath.Base(this.RawReq.URL.Path)
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ func (this *HTTPRequest) doHostRedirect() (blocked bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var fullURL = ""
|
var fullURL string
|
||||||
if u.BeforeHasQuery() {
|
if u.BeforeHasQuery() {
|
||||||
fullURL = this.URL()
|
fullURL = this.URL()
|
||||||
} else {
|
} else {
|
||||||
@@ -55,7 +55,7 @@ func (this *HTTPRequest) doHostRedirect() (blocked bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.ProcessResponseHeaders(this.writer.Header(), status)
|
this.ProcessResponseHeaders(this.writer.Header(), status)
|
||||||
http.Redirect(this.RawWriter, this.RawReq, afterURL, status)
|
httpRedirect(this.writer, this.RawReq, afterURL, status)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
} else if u.MatchRegexp { // 正则匹配
|
} else if u.MatchRegexp { // 正则匹配
|
||||||
@@ -97,7 +97,7 @@ func (this *HTTPRequest) doHostRedirect() (blocked bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.ProcessResponseHeaders(this.writer.Header(), status)
|
this.ProcessResponseHeaders(this.writer.Header(), status)
|
||||||
http.Redirect(this.RawWriter, this.RawReq, afterURL, status)
|
httpRedirect(this.writer, this.RawReq, afterURL, status)
|
||||||
return true
|
return true
|
||||||
} else { // 精准匹配
|
} else { // 精准匹配
|
||||||
if fullURL == u.RealBeforeURL() {
|
if fullURL == u.RealBeforeURL() {
|
||||||
@@ -120,7 +120,7 @@ func (this *HTTPRequest) doHostRedirect() (blocked bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.ProcessResponseHeaders(this.writer.Header(), status)
|
this.ProcessResponseHeaders(this.writer.Header(), status)
|
||||||
http.Redirect(this.RawWriter, this.RawReq, afterURL, status)
|
httpRedirect(this.writer, this.RawReq, afterURL, status)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -163,7 +163,7 @@ func (this *HTTPRequest) doHostRedirect() (blocked bool) {
|
|||||||
afterURL += this.uri[qIndex:]
|
afterURL += this.uri[qIndex:]
|
||||||
}
|
}
|
||||||
|
|
||||||
http.Redirect(this.RawWriter, this.RawReq, afterURL, status)
|
httpRedirect(this.writer, this.RawReq, afterURL, status)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
} else if u.Type == serverconfigs.HTTPHostRedirectTypePort {
|
} else if u.Type == serverconfigs.HTTPHostRedirectTypePort {
|
||||||
@@ -194,7 +194,7 @@ func (this *HTTPRequest) doHostRedirect() (blocked bool) {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
var containsPort = false
|
var containsPort bool
|
||||||
if u.PortsAll {
|
if u.PortsAll {
|
||||||
containsPort = true
|
containsPort = true
|
||||||
} else {
|
} else {
|
||||||
@@ -212,7 +212,7 @@ func (this *HTTPRequest) doHostRedirect() (blocked bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.ProcessResponseHeaders(this.writer.Header(), status)
|
this.ProcessResponseHeaders(this.writer.Header(), status)
|
||||||
http.Redirect(this.RawWriter, this.RawReq, afterURL, status)
|
httpRedirect(this.writer, this.RawReq, afterURL, status)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,8 +7,9 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/ttlcache"
|
"github.com/TeaOSLab/EdgeNode/internal/ttlcache"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/waf"
|
"github.com/TeaOSLab/EdgeNode/internal/waf"
|
||||||
"net"
|
"github.com/iwind/TeaGo/types"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -35,9 +36,25 @@ func (this *HTTPRequest) doMismatch() {
|
|||||||
// 根据配置进行相应的处理
|
// 根据配置进行相应的处理
|
||||||
var globalServerConfig = sharedNodeConfig.GlobalServerConfig
|
var globalServerConfig = sharedNodeConfig.GlobalServerConfig
|
||||||
if globalServerConfig != nil && globalServerConfig.HTTPAll.MatchDomainStrictly {
|
if globalServerConfig != nil && globalServerConfig.HTTPAll.MatchDomainStrictly {
|
||||||
|
var statusCode = 404
|
||||||
|
var httpAllConfig = globalServerConfig.HTTPAll
|
||||||
|
var mismatchAction = httpAllConfig.DomainMismatchAction
|
||||||
|
|
||||||
|
if mismatchAction != nil && mismatchAction.Options != nil {
|
||||||
|
var mismatchStatusCode = mismatchAction.Options.GetInt("statusCode")
|
||||||
|
if mismatchStatusCode > 0 && mismatchStatusCode >= 100 && mismatchStatusCode < 1000 {
|
||||||
|
statusCode = mismatchStatusCode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 是否正在访问IP
|
// 是否正在访问IP
|
||||||
if globalServerConfig.HTTPAll.NodeIPShowPage && net.ParseIP(this.ReqHost) != nil {
|
if globalServerConfig.HTTPAll.NodeIPShowPage && utils.IsWildIP(this.ReqHost) {
|
||||||
_, _ = this.writer.WriteString(globalServerConfig.HTTPAll.NodeIPPageHTML)
|
this.writer.statusCode = statusCode
|
||||||
|
var contentHTML = this.Format(globalServerConfig.HTTPAll.NodeIPPageHTML)
|
||||||
|
this.writer.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||||
|
this.writer.Header().Set("Content-Length", types.String(len(contentHTML)))
|
||||||
|
this.writer.WriteHeader(statusCode)
|
||||||
|
_, _ = this.writer.WriteString(contentHTML)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,13 +72,14 @@ func (this *HTTPRequest) doMismatch() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 处理当前连接
|
// 处理当前连接
|
||||||
var httpAllConfig = globalServerConfig.HTTPAll
|
if mismatchAction != nil && mismatchAction.Code == serverconfigs.DomainMismatchActionPage {
|
||||||
var mismatchAction = httpAllConfig.DomainMismatchAction
|
|
||||||
if mismatchAction != nil && mismatchAction.Code == "page" {
|
|
||||||
if mismatchAction.Options != nil {
|
if mismatchAction.Options != nil {
|
||||||
|
this.writer.statusCode = statusCode
|
||||||
|
var contentHTML = this.Format(mismatchAction.Options.GetString("contentHTML"))
|
||||||
this.writer.Header().Set("Content-Type", "text/html; charset=utf-8")
|
this.writer.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||||
this.writer.WriteHeader(mismatchAction.Options.GetInt("statusCode"))
|
this.writer.Header().Set("Content-Length", types.String(len(contentHTML)))
|
||||||
_, _ = this.writer.Write([]byte(mismatchAction.Options.GetString("contentHTML")))
|
this.writer.WriteHeader(statusCode)
|
||||||
|
_, _ = this.writer.Write([]byte(contentHTML))
|
||||||
} else {
|
} else {
|
||||||
http.Error(this.writer, "404 page not found: '"+this.URL()+"'", http.StatusNotFound)
|
http.Error(this.writer, "404 page not found: '"+this.URL()+"'", http.StatusNotFound)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const defaultPageContentType = "text/html; charset=utf-8"
|
||||||
|
|
||||||
// 请求特殊页面
|
// 请求特殊页面
|
||||||
func (this *HTTPRequest) doPage(status int) (shouldStop bool) {
|
func (this *HTTPRequest) doPage(status int) (shouldStop bool) {
|
||||||
if len(this.web.Pages) == 0 {
|
if len(this.web.Pages) == 0 {
|
||||||
@@ -58,6 +60,7 @@ func (this *HTTPRequest) doPageLookup(pages []*serverconfigs.HTTPPageConfig, sta
|
|||||||
var realpath = path.Clean(page.URL)
|
var realpath = path.Clean(page.URL)
|
||||||
if !strings.HasPrefix(realpath, "/pages/") && !strings.HasPrefix(realpath, "pages/") { // only files under "/pages/" can be used
|
if !strings.HasPrefix(realpath, "/pages/") && !strings.HasPrefix(realpath, "pages/") { // only files under "/pages/" can be used
|
||||||
var msg = "404 page not found: '" + page.URL + "'"
|
var msg = "404 page not found: '" + page.URL + "'"
|
||||||
|
this.writer.Header().Set("Content-Type", defaultPageContentType)
|
||||||
this.writer.WriteHeader(http.StatusNotFound)
|
this.writer.WriteHeader(http.StatusNotFound)
|
||||||
_, _ = this.writer.Write([]byte(msg))
|
_, _ = this.writer.Write([]byte(msg))
|
||||||
return true
|
return true
|
||||||
@@ -66,6 +69,7 @@ func (this *HTTPRequest) doPageLookup(pages []*serverconfigs.HTTPPageConfig, sta
|
|||||||
fp, err := os.Open(file)
|
fp, err := os.Open(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var msg = "404 page not found: '" + page.URL + "'"
|
var msg = "404 page not found: '" + page.URL + "'"
|
||||||
|
this.writer.Header().Set("Content-Type", defaultPageContentType)
|
||||||
this.writer.WriteHeader(http.StatusNotFound)
|
this.writer.WriteHeader(http.StatusNotFound)
|
||||||
_, _ = this.writer.Write([]byte(msg))
|
_, _ = this.writer.Write([]byte(msg))
|
||||||
return true
|
return true
|
||||||
@@ -77,6 +81,7 @@ func (this *HTTPRequest) doPageLookup(pages []*serverconfigs.HTTPPageConfig, sta
|
|||||||
stat, err := fp.Stat()
|
stat, err := fp.Stat()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var msg = "404 could not read page content: '" + page.URL + "'"
|
var msg = "404 could not read page content: '" + page.URL + "'"
|
||||||
|
this.writer.Header().Set("Content-Type", defaultPageContentType)
|
||||||
this.writer.WriteHeader(http.StatusNotFound)
|
this.writer.WriteHeader(http.StatusNotFound)
|
||||||
_, _ = this.writer.Write([]byte(msg))
|
_, _ = this.writer.Write([]byte(msg))
|
||||||
return true
|
return true
|
||||||
@@ -85,10 +90,12 @@ func (this *HTTPRequest) doPageLookup(pages []*serverconfigs.HTTPPageConfig, sta
|
|||||||
// 修改状态码
|
// 修改状态码
|
||||||
if page.NewStatus > 0 {
|
if page.NewStatus > 0 {
|
||||||
// 自定义响应Headers
|
// 自定义响应Headers
|
||||||
|
this.writer.Header().Set("Content-Type", defaultPageContentType)
|
||||||
this.ProcessResponseHeaders(this.writer.Header(), page.NewStatus)
|
this.ProcessResponseHeaders(this.writer.Header(), page.NewStatus)
|
||||||
this.writer.Prepare(nil, stat.Size(), page.NewStatus, true)
|
this.writer.Prepare(nil, stat.Size(), page.NewStatus, true)
|
||||||
this.writer.WriteHeader(page.NewStatus)
|
this.writer.WriteHeader(page.NewStatus)
|
||||||
} else {
|
} else {
|
||||||
|
this.writer.Header().Set("Content-Type", defaultPageContentType)
|
||||||
this.ProcessResponseHeaders(this.writer.Header(), status)
|
this.ProcessResponseHeaders(this.writer.Header(), status)
|
||||||
this.writer.Prepare(nil, stat.Size(), status, true)
|
this.writer.Prepare(nil, stat.Size(), status, true)
|
||||||
this.writer.WriteHeader(status)
|
this.writer.WriteHeader(status)
|
||||||
@@ -120,10 +127,12 @@ func (this *HTTPRequest) doPageLookup(pages []*serverconfigs.HTTPPageConfig, sta
|
|||||||
// 修改状态码
|
// 修改状态码
|
||||||
if page.NewStatus > 0 {
|
if page.NewStatus > 0 {
|
||||||
// 自定义响应Headers
|
// 自定义响应Headers
|
||||||
|
this.writer.Header().Set("Content-Type", defaultPageContentType)
|
||||||
this.ProcessResponseHeaders(this.writer.Header(), page.NewStatus)
|
this.ProcessResponseHeaders(this.writer.Header(), page.NewStatus)
|
||||||
this.writer.Prepare(nil, int64(len(content)), page.NewStatus, true)
|
this.writer.Prepare(nil, int64(len(content)), page.NewStatus, true)
|
||||||
this.writer.WriteHeader(page.NewStatus)
|
this.writer.WriteHeader(page.NewStatus)
|
||||||
} else {
|
} else {
|
||||||
|
this.writer.Header().Set("Content-Type", defaultPageContentType)
|
||||||
this.ProcessResponseHeaders(this.writer.Header(), status)
|
this.ProcessResponseHeaders(this.writer.Header(), status)
|
||||||
this.writer.Prepare(nil, int64(len(content)), status, true)
|
this.writer.Prepare(nil, int64(len(content)), status, true)
|
||||||
this.writer.WriteHeader(status)
|
this.writer.WriteHeader(status)
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ func (this *HTTPRequest) doRedirectToHTTPS(redirectToHTTPSConfig *serverconfigs.
|
|||||||
|
|
||||||
var newURL = "https://" + host + this.RawReq.RequestURI
|
var newURL = "https://" + host + this.RawReq.RequestURI
|
||||||
this.ProcessResponseHeaders(this.writer.Header(), statusCode)
|
this.ProcessResponseHeaders(this.writer.Header(), statusCode)
|
||||||
http.Redirect(this.writer, this.RawReq, newURL, statusCode)
|
httpRedirect(this.writer, this.RawReq, newURL, statusCode)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ 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/TeaOSLab/EdgeNode/internal/utils/fnv"
|
||||||
|
"github.com/iwind/TeaGo/lists"
|
||||||
"github.com/iwind/TeaGo/types"
|
"github.com/iwind/TeaGo/types"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -21,15 +23,13 @@ func (this *HTTPRequest) doReverseProxy() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var isLowVersionHTTP = this.RawReq.ProtoMajor < 1 /** 0.x **/ || (this.RawReq.ProtoMajor == 1 && this.RawReq.ProtoMinor == 0 /** 1.0 **/)
|
|
||||||
|
|
||||||
var retries = 3
|
var retries = 3
|
||||||
|
|
||||||
var failedOriginIds []int64
|
var failedOriginIds []int64
|
||||||
var failedLnNodeIds []int64
|
var failedLnNodeIds []int64
|
||||||
|
|
||||||
for i := 0; i < retries; i++ {
|
for i := 0; i < retries; i++ {
|
||||||
originId, lnNodeId, shouldRetry := this.doOriginRequest(failedOriginIds, failedLnNodeIds, i == 0, i == retries-1, isLowVersionHTTP)
|
originId, lnNodeId, shouldRetry := this.doOriginRequest(failedOriginIds, failedLnNodeIds, i == 0, i == retries-1)
|
||||||
if !shouldRetry {
|
if !shouldRetry {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -43,7 +43,7 @@ func (this *HTTPRequest) doReverseProxy() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 请求源站
|
// 请求源站
|
||||||
func (this *HTTPRequest) doOriginRequest(failedOriginIds []int64, failedLnNodeIds []int64, isFirstTry bool, isLastRetry bool, isLowVersionHTTP bool) (originId int64, lnNodeId int64, shouldRetry bool) {
|
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
|
||||||
@@ -67,7 +67,7 @@ func (this *HTTPRequest) doOriginRequest(failedOriginIds []int64, failedLnNodeId
|
|||||||
// 二级节点
|
// 二级节点
|
||||||
var hasMultipleLnNodes = false
|
var hasMultipleLnNodes = false
|
||||||
if this.cacheRef != nil || (this.nodeConfig != nil && this.nodeConfig.GlobalServerConfig != nil && this.nodeConfig.GlobalServerConfig.HTTPAll.ForceLnRequest) {
|
if this.cacheRef != nil || (this.nodeConfig != nil && this.nodeConfig.GlobalServerConfig != nil && this.nodeConfig.GlobalServerConfig.HTTPAll.ForceLnRequest) {
|
||||||
origin, lnNodeId, hasMultipleLnNodes = this.getLnOrigin(failedLnNodeIds)
|
origin, lnNodeId, hasMultipleLnNodes = this.getLnOrigin(failedLnNodeIds, fnv.HashString(this.URL()))
|
||||||
if origin != nil {
|
if origin != nil {
|
||||||
// 强制变更原来访问的域名
|
// 强制变更原来访问的域名
|
||||||
requestHost = this.ReqHost
|
requestHost = this.ReqHost
|
||||||
@@ -258,6 +258,7 @@ func (this *HTTPRequest) doOriginRequest(failedOriginIds []int64, failedLnNodeId
|
|||||||
}
|
}
|
||||||
|
|
||||||
var resp *http.Response
|
var resp *http.Response
|
||||||
|
var respBodyIsClosed bool
|
||||||
var requestErr error
|
var requestErr error
|
||||||
var requestErrCode string
|
var requestErrCode string
|
||||||
if isHTTPOrigin { // 普通HTTP(S)源站
|
if isHTTPOrigin { // 普通HTTP(S)源站
|
||||||
@@ -296,9 +297,19 @@ func (this *HTTPRequest) doOriginRequest(failedOriginIds []int64, failedLnNodeId
|
|||||||
this.writeCode(http.StatusBadGateway, "The type of origin site has not been supported", "设置的源站类型尚未支持")
|
this.writeCode(http.StatusBadGateway, "The type of origin site has not been supported", "设置的源站类型尚未支持")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if resp != nil && resp.Body != nil {
|
||||||
|
defer func() {
|
||||||
|
if !respBodyIsClosed {
|
||||||
|
_ = resp.Body.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
if requestErr != nil {
|
if requestErr != nil {
|
||||||
// 客户端取消请求,则不提示
|
// 客户端取消请求,则不提示
|
||||||
httpErr, ok := requestErr.(*url.Error)
|
var httpErr *url.Error
|
||||||
|
ok := errors.As(requestErr, &httpErr)
|
||||||
if !ok {
|
if !ok {
|
||||||
if isHTTPOrigin {
|
if isHTTPOrigin {
|
||||||
SharedOriginStateManager.Fail(origin, requestHost, this.reverseProxy, func() {
|
SharedOriginStateManager.Fail(origin, requestHost, this.reverseProxy, func() {
|
||||||
@@ -312,7 +323,7 @@ func (this *HTTPRequest) doOriginRequest(failedOriginIds []int64, failedLnNodeId
|
|||||||
this.write50x(requestErr, http.StatusBadGateway, "Failed to read origin site", "源站读取失败", true)
|
this.write50x(requestErr, http.StatusBadGateway, "Failed to read origin site", "源站读取失败", true)
|
||||||
}
|
}
|
||||||
remotelogs.WarnServer("HTTP_REQUEST_REVERSE_PROXY", this.RawReq.URL.String()+": Request origin server failed: "+requestErr.Error())
|
remotelogs.WarnServer("HTTP_REQUEST_REVERSE_PROXY", this.RawReq.URL.String()+": Request origin server failed: "+requestErr.Error())
|
||||||
} else if httpErr.Err != context.Canceled {
|
} else if !errors.Is(httpErr, context.Canceled) {
|
||||||
if isHTTPOrigin {
|
if isHTTPOrigin {
|
||||||
SharedOriginStateManager.Fail(origin, requestHost, this.reverseProxy, func() {
|
SharedOriginStateManager.Fail(origin, requestHost, this.reverseProxy, func() {
|
||||||
this.reverseProxy.ResetScheduling()
|
this.reverseProxy.ResetScheduling()
|
||||||
@@ -324,10 +335,6 @@ func (this *HTTPRequest) doOriginRequest(failedOriginIds []int64, failedLnNodeId
|
|||||||
shouldRetry = true
|
shouldRetry = true
|
||||||
this.uri = oldURI // 恢复备份
|
this.uri = oldURI // 恢复备份
|
||||||
|
|
||||||
if resp != nil && resp.Body != nil {
|
|
||||||
_ = resp.Body.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
if httpErr.Err != io.EOF {
|
if httpErr.Err != io.EOF {
|
||||||
remotelogs.WarnServer("HTTP_REQUEST_REVERSE_PROXY", this.URL()+": Request origin server failed: "+requestErr.Error())
|
remotelogs.WarnServer("HTTP_REQUEST_REVERSE_PROXY", this.URL()+": Request origin server failed: "+requestErr.Error())
|
||||||
}
|
}
|
||||||
@@ -349,7 +356,7 @@ func (this *HTTPRequest) doOriginRequest(failedOriginIds []int64, failedLnNodeId
|
|||||||
// 是否为客户端方面的错误
|
// 是否为客户端方面的错误
|
||||||
var isClientError = false
|
var isClientError = false
|
||||||
if ok {
|
if ok {
|
||||||
if httpErr.Err == context.Canceled {
|
if errors.Is(httpErr, context.Canceled) {
|
||||||
// 如果是服务器端主动关闭,则无需提示
|
// 如果是服务器端主动关闭,则无需提示
|
||||||
if this.isConnClosed() {
|
if this.isConnClosed() {
|
||||||
this.disableLog = true
|
this.disableLog = true
|
||||||
@@ -366,22 +373,38 @@ func (this *HTTPRequest) doOriginRequest(failedOriginIds []int64, failedLnNodeId
|
|||||||
this.write50x(requestErr, http.StatusBadGateway, "Failed to read origin site", "源站读取失败", true)
|
this.write50x(requestErr, http.StatusBadGateway, "Failed to read origin site", "源站读取失败", true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if resp != nil && resp.Body != nil {
|
|
||||||
_ = resp.Body.Close()
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 是否为1.1以下
|
// 50x
|
||||||
if isLowVersionHTTP && resp.ContentLength < 0 {
|
if resp != nil &&
|
||||||
this.writer.WriteHeader(http.StatusBadRequest)
|
resp.StatusCode >= 500 &&
|
||||||
_, _ = this.writer.WriteString("The content does not support " + this.RawReq.Proto + " request.")
|
resp.StatusCode < 510 &&
|
||||||
|
this.reverseProxy.Retry50X &&
|
||||||
|
(originId > 0 || (lnNodeId > 0 && hasMultipleLnNodes)) &&
|
||||||
|
!isLastRetry {
|
||||||
if resp.Body != nil {
|
if resp.Body != nil {
|
||||||
_ = resp.Body.Close()
|
_ = resp.Body.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
shouldRetry = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 尝试从缓存中恢复
|
||||||
|
if resp != nil &&
|
||||||
|
resp.StatusCode >= 500 && // support 50X only
|
||||||
|
resp.StatusCode < 510 &&
|
||||||
|
this.cacheCanTryStale &&
|
||||||
|
this.web.Cache.Stale != nil &&
|
||||||
|
this.web.Cache.Stale.IsOn &&
|
||||||
|
(len(this.web.Cache.Stale.Status) == 0 || lists.ContainsInt(this.web.Cache.Stale.Status, resp.StatusCode)) {
|
||||||
|
var ok = this.doCacheRead(true)
|
||||||
|
if ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 记录相关数据
|
// 记录相关数据
|
||||||
this.originStatus = int32(resp.StatusCode)
|
this.originStatus = int32(resp.StatusCode)
|
||||||
|
|
||||||
@@ -395,20 +418,12 @@ func (this *HTTPRequest) doOriginRequest(failedOriginIds []int64, failedLnNodeId
|
|||||||
// WAF对出站进行检查
|
// WAF对出站进行检查
|
||||||
if this.web.FirewallRef != nil && this.web.FirewallRef.IsOn {
|
if this.web.FirewallRef != nil && this.web.FirewallRef.IsOn {
|
||||||
if this.doWAFResponse(resp) {
|
if this.doWAFResponse(resp) {
|
||||||
err := resp.Body.Close()
|
|
||||||
if err != nil {
|
|
||||||
remotelogs.WarnServer("HTTP_REQUEST_REVERSE_PROXY", this.URL()+": Closing Error (WAF): "+err.Error())
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 特殊页面
|
// 特殊页面
|
||||||
if len(this.web.Pages) > 0 && this.doPage(resp.StatusCode) {
|
if this.doPage(resp.StatusCode) {
|
||||||
err := resp.Body.Close()
|
|
||||||
if err != nil {
|
|
||||||
remotelogs.WarnServer("HTTP_REQUEST_REVERSE_PROXY", this.URL()+": Closing error (Page): "+err.Error())
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -499,6 +514,7 @@ func (this *HTTPRequest) doOriginRequest(failedOriginIds []int64, failedLnNodeId
|
|||||||
_, _ = io.CopyBuffer(this.writer, resp.Body, buf)
|
_, _ = io.CopyBuffer(this.writer, resp.Body, buf)
|
||||||
utils.BytePool4k.Put(buf)
|
utils.BytePool4k.Put(buf)
|
||||||
_ = resp.Body.Close()
|
_ = resp.Body.Close()
|
||||||
|
respBodyIsClosed = true
|
||||||
|
|
||||||
this.writer.SetOk()
|
this.writer.SetOk()
|
||||||
return
|
return
|
||||||
@@ -524,11 +540,33 @@ func (this *HTTPRequest) doOriginRequest(failedOriginIds []int64, failedLnNodeId
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_, err = io.CopyBuffer(this.writer, resp.Body, buf)
|
if this.cacheRef != nil &&
|
||||||
|
this.cacheRef.EnableReadingOriginAsync &&
|
||||||
|
resp.ContentLength > 0 &&
|
||||||
|
resp.ContentLength < (128<<20) { // TODO configure max content-length in cache policy OR CacheRef
|
||||||
|
var requestIsCanceled = false
|
||||||
|
for {
|
||||||
|
n, readErr := resp.Body.Read(buf)
|
||||||
|
|
||||||
|
if n > 0 && !requestIsCanceled {
|
||||||
|
_, err = this.writer.Write(buf[:n])
|
||||||
|
if err != nil {
|
||||||
|
requestIsCanceled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if readErr != nil {
|
||||||
|
err = readErr
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_, err = io.CopyBuffer(this.writer, resp.Body, buf)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
pool.Put(buf)
|
pool.Put(buf)
|
||||||
|
|
||||||
var closeErr = resp.Body.Close()
|
var closeErr = resp.Body.Close()
|
||||||
|
respBodyIsClosed = true
|
||||||
if closeErr != nil {
|
if closeErr != nil {
|
||||||
if !this.canIgnore(closeErr) {
|
if !this.canIgnore(closeErr) {
|
||||||
remotelogs.WarnServer("HTTP_REQUEST_REVERSE_PROXY", this.URL()+": Closing error: "+closeErr.Error())
|
remotelogs.WarnServer("HTTP_REQUEST_REVERSE_PROXY", this.URL()+": Closing error: "+closeErr.Error())
|
||||||
|
|||||||
@@ -31,10 +31,10 @@ func (this *HTTPRequest) doRewrite() (shouldShop bool) {
|
|||||||
if this.rewriteRule.Mode == serverconfigs.HTTPRewriteModeRedirect {
|
if this.rewriteRule.Mode == serverconfigs.HTTPRewriteModeRedirect {
|
||||||
if this.rewriteRule.RedirectStatus > 0 {
|
if this.rewriteRule.RedirectStatus > 0 {
|
||||||
this.ProcessResponseHeaders(this.writer.Header(), this.rewriteRule.RedirectStatus)
|
this.ProcessResponseHeaders(this.writer.Header(), this.rewriteRule.RedirectStatus)
|
||||||
http.Redirect(this.writer, this.RawReq, this.rewriteReplace, this.rewriteRule.RedirectStatus)
|
httpRedirect(this.writer, this.RawReq, this.rewriteReplace, this.rewriteRule.RedirectStatus)
|
||||||
} else {
|
} else {
|
||||||
this.ProcessResponseHeaders(this.writer.Header(), http.StatusTemporaryRedirect)
|
this.ProcessResponseHeaders(this.writer.Header(), http.StatusTemporaryRedirect)
|
||||||
http.Redirect(this.writer, this.RawReq, this.rewriteReplace, http.StatusTemporaryRedirect)
|
httpRedirect(this.writer, this.RawReq, this.rewriteReplace, http.StatusTemporaryRedirect)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,6 +66,19 @@ func (this *HTTPRequest) doRoot() (isBreak bool) {
|
|||||||
requestPath = this.uri[:questionMarkIndex]
|
requestPath = this.uri[:questionMarkIndex]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// except hidden files
|
||||||
|
if this.web.Root.ExceptHiddenFiles &&
|
||||||
|
(strings.Contains(requestPath, "/.") || strings.Contains(requestPath, "\\.")) {
|
||||||
|
this.write404()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// except and only files
|
||||||
|
if !this.web.Root.MatchURL(this.URL()) {
|
||||||
|
this.write404()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// 去掉其中的奇怪的路径
|
// 去掉其中的奇怪的路径
|
||||||
requestPath = strings.Replace(requestPath, "..\\", "", -1)
|
requestPath = strings.Replace(requestPath, "..\\", "", -1)
|
||||||
|
|
||||||
@@ -95,7 +108,7 @@ func (this *HTTPRequest) doRoot() (isBreak bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var filename = strings.Replace(requestPath, "/", Tea.DS, -1)
|
var filename = strings.Replace(requestPath, "/", Tea.DS, -1)
|
||||||
var filePath = ""
|
var filePath string
|
||||||
if len(filename) > 0 && filename[0:1] == Tea.DS {
|
if len(filename) > 0 && filename[0:1] == Tea.DS {
|
||||||
filePath = rootDir + filename
|
filePath = rootDir + filename
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -8,15 +8,17 @@ import (
|
|||||||
|
|
||||||
// 流量限制
|
// 流量限制
|
||||||
func (this *HTTPRequest) doTrafficLimit() {
|
func (this *HTTPRequest) doTrafficLimit() {
|
||||||
var config = this.ReqServer.TrafficLimit
|
this.tags = append(this.tags, "trafficLimit")
|
||||||
|
|
||||||
this.tags = append(this.tags, "bandwidth")
|
|
||||||
|
|
||||||
var statusCode = 509
|
var statusCode = 509
|
||||||
|
this.writer.statusCode = statusCode
|
||||||
this.ProcessResponseHeaders(this.writer.Header(), statusCode)
|
this.ProcessResponseHeaders(this.writer.Header(), statusCode)
|
||||||
|
|
||||||
|
this.writer.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||||
this.writer.WriteHeader(statusCode)
|
this.writer.WriteHeader(statusCode)
|
||||||
if len(config.NoticePageBody) != 0 {
|
|
||||||
|
var config = this.ReqServer.TrafficLimit
|
||||||
|
if config != nil && len(config.NoticePageBody) != 0 {
|
||||||
_, _ = this.writer.WriteString(this.Format(config.NoticePageBody))
|
_, _ = this.writer.WriteString(this.Format(config.NoticePageBody))
|
||||||
} else {
|
} else {
|
||||||
_, _ = this.writer.WriteString(this.Format(serverconfigs.DefaultTrafficLimitNoticePageBody))
|
_, _ = this.writer.WriteString(this.Format(serverconfigs.DefaultTrafficLimitNoticePageBody))
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (this *HTTPRequest) doCheckUserAgent() (shouldStop bool) {
|
func (this *HTTPRequest) doCheckUserAgent() (shouldStop bool) {
|
||||||
if this.web.UserAgent == nil {
|
if this.web.UserAgent == nil || !this.web.UserAgent.IsOn {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -174,11 +174,19 @@ func (this *HTTPRequest) checkWAFRequest(firewallPolicy *firewallconfigs.HTTPFir
|
|||||||
if !regionConfig.IsAllowedCountry(result.CountryId(), result.ProvinceId()) {
|
if !regionConfig.IsAllowedCountry(result.CountryId(), result.ProvinceId()) {
|
||||||
this.firewallPolicyId = firewallPolicy.Id
|
this.firewallPolicyId = firewallPolicy.Id
|
||||||
|
|
||||||
|
var promptHTML string
|
||||||
if len(regionConfig.CountryHTML) > 0 {
|
if len(regionConfig.CountryHTML) > 0 {
|
||||||
|
promptHTML = regionConfig.CountryHTML
|
||||||
|
} else if this.ReqServer != nil && this.ReqServer.HTTPFirewallPolicy != nil && len(this.ReqServer.HTTPFirewallPolicy.DenyCountryHTML) > 0 {
|
||||||
|
promptHTML = this.ReqServer.HTTPFirewallPolicy.DenyCountryHTML
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(promptHTML) > 0 {
|
||||||
|
var formattedHTML = this.Format(promptHTML)
|
||||||
this.writer.Header().Set("Content-Type", "text/html; charset=utf-8")
|
this.writer.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||||
this.writer.Header().Set("Content-Length", types.String(len(regionConfig.CountryHTML)))
|
this.writer.Header().Set("Content-Length", types.String(len(formattedHTML)))
|
||||||
this.writer.WriteHeader(http.StatusForbidden)
|
this.writer.WriteHeader(http.StatusForbidden)
|
||||||
_, _ = this.writer.Write([]byte(regionConfig.CountryHTML))
|
_, _ = this.writer.Write([]byte(formattedHTML))
|
||||||
} else {
|
} else {
|
||||||
this.writeCode(http.StatusForbidden, "The region has been denied.", "当前区域禁止访问")
|
this.writeCode(http.StatusForbidden, "The region has been denied.", "当前区域禁止访问")
|
||||||
}
|
}
|
||||||
@@ -202,11 +210,19 @@ func (this *HTTPRequest) checkWAFRequest(firewallPolicy *firewallconfigs.HTTPFir
|
|||||||
if !regionConfig.IsAllowedProvince(result.CountryId(), result.ProvinceId()) {
|
if !regionConfig.IsAllowedProvince(result.CountryId(), result.ProvinceId()) {
|
||||||
this.firewallPolicyId = firewallPolicy.Id
|
this.firewallPolicyId = firewallPolicy.Id
|
||||||
|
|
||||||
|
var promptHTML string
|
||||||
if len(regionConfig.ProvinceHTML) > 0 {
|
if len(regionConfig.ProvinceHTML) > 0 {
|
||||||
|
promptHTML = regionConfig.ProvinceHTML
|
||||||
|
} else if this.ReqServer != nil && this.ReqServer.HTTPFirewallPolicy != nil && len(this.ReqServer.HTTPFirewallPolicy.DenyProvinceHTML) > 0 {
|
||||||
|
promptHTML = this.ReqServer.HTTPFirewallPolicy.DenyProvinceHTML
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(promptHTML) > 0 {
|
||||||
|
var formattedHTML = this.Format(promptHTML)
|
||||||
this.writer.Header().Set("Content-Type", "text/html; charset=utf-8")
|
this.writer.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||||
this.writer.Header().Set("Content-Length", types.String(len(regionConfig.ProvinceHTML)))
|
this.writer.Header().Set("Content-Length", types.String(len(formattedHTML)))
|
||||||
this.writer.WriteHeader(http.StatusForbidden)
|
this.writer.WriteHeader(http.StatusForbidden)
|
||||||
_, _ = this.writer.Write([]byte(regionConfig.ProvinceHTML))
|
_, _ = this.writer.Write([]byte(formattedHTML))
|
||||||
} else {
|
} else {
|
||||||
this.writeCode(http.StatusForbidden, "The region has been denied.", "当前区域禁止访问")
|
this.writeCode(http.StatusForbidden, "The region has been denied.", "当前区域禁止访问")
|
||||||
}
|
}
|
||||||
@@ -441,6 +457,14 @@ func (this *HTTPRequest) WAFFingerprint() []byte {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *HTTPRequest) WAFMaxRequestSize() int64 {
|
||||||
|
var maxRequestSize = firewallconfigs.DefaultMaxRequestBodySize
|
||||||
|
if this.ReqServer.HTTPFirewallPolicy != nil && this.ReqServer.HTTPFirewallPolicy.MaxRequestBodySize > 0 {
|
||||||
|
maxRequestSize = this.ReqServer.HTTPFirewallPolicy.MaxRequestBodySize
|
||||||
|
}
|
||||||
|
return maxRequestSize
|
||||||
|
}
|
||||||
|
|
||||||
// DisableAccessLog 在当前请求中不使用访问日志
|
// DisableAccessLog 在当前请求中不使用访问日志
|
||||||
func (this *HTTPRequest) DisableAccessLog() {
|
func (this *HTTPRequest) DisableAccessLog() {
|
||||||
this.disableLog = true
|
this.disableLog = true
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"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/caches"
|
"github.com/TeaOSLab/EdgeNode/internal/caches"
|
||||||
@@ -15,6 +16,7 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils/fasttime"
|
"github.com/TeaOSLab/EdgeNode/internal/utils/fasttime"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils/readers"
|
"github.com/TeaOSLab/EdgeNode/internal/utils/readers"
|
||||||
|
setutils "github.com/TeaOSLab/EdgeNode/internal/utils/sets"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils/writers"
|
"github.com/TeaOSLab/EdgeNode/internal/utils/writers"
|
||||||
_ "github.com/biessek/golang-ico"
|
_ "github.com/biessek/golang-ico"
|
||||||
"github.com/iwind/TeaGo/lists"
|
"github.com/iwind/TeaGo/lists"
|
||||||
@@ -24,7 +26,6 @@ import (
|
|||||||
_ "golang.org/x/image/webp"
|
_ "golang.org/x/image/webp"
|
||||||
"image"
|
"image"
|
||||||
"image/gif"
|
"image/gif"
|
||||||
_ "image/gif"
|
|
||||||
_ "image/jpeg"
|
_ "image/jpeg"
|
||||||
_ "image/png"
|
_ "image/png"
|
||||||
"io"
|
"io"
|
||||||
@@ -39,6 +40,7 @@ import (
|
|||||||
|
|
||||||
var webpMaxBufferSize int64 = 1_000_000_000
|
var webpMaxBufferSize int64 = 1_000_000_000
|
||||||
var webpTotalBufferSize int64 = 0
|
var webpTotalBufferSize int64 = 0
|
||||||
|
var webpIgnoreURLSet = setutils.NewFixedSet(131072)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
if !teaconst.IsMain {
|
if !teaconst.IsMain {
|
||||||
@@ -47,7 +49,7 @@ func init() {
|
|||||||
|
|
||||||
var systemMemory = utils.SystemMemoryGB() / 8
|
var systemMemory = utils.SystemMemoryGB() / 8
|
||||||
if systemMemory > 0 {
|
if systemMemory > 0 {
|
||||||
webpMaxBufferSize = int64(systemMemory) * 1024 * 1024 * 1024
|
webpMaxBufferSize = int64(systemMemory) << 30
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -324,11 +326,11 @@ func (this *HTTPWriter) PrepareCache(resp *http.Response, size int64) {
|
|||||||
|
|
||||||
// 待写入尺寸
|
// 待写入尺寸
|
||||||
var totalSize = size
|
var totalSize = size
|
||||||
if totalSize < 0 && this.isPartial {
|
if this.isPartial {
|
||||||
var contentRange = resp.Header.Get("Content-Range")
|
var contentRange = resp.Header.Get("Content-Range")
|
||||||
if len(contentRange) > 0 {
|
if len(contentRange) > 0 {
|
||||||
_, partialTotalSize := httpRequestParseContentRangeHeader(contentRange)
|
_, partialTotalSize := httpRequestParseContentRangeHeader(contentRange)
|
||||||
if partialTotalSize > 0 {
|
if partialTotalSize > 0 && partialTotalSize > totalSize {
|
||||||
totalSize = partialTotalSize
|
totalSize = partialTotalSize
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -362,10 +364,7 @@ func (this *HTTPWriter) PrepareCache(resp *http.Response, size int64) {
|
|||||||
// 写入Header
|
// 写入Header
|
||||||
var headerBuf = utils.SharedBufferPool.Get()
|
var headerBuf = utils.SharedBufferPool.Get()
|
||||||
for k, v := range this.Header() {
|
for k, v := range this.Header() {
|
||||||
if k == "Set-Cookie" ||
|
if this.shouldIgnoreHeader(k) {
|
||||||
k == "Strict-Transport-Security" ||
|
|
||||||
k == "Alt-Svc" ||
|
|
||||||
(this.isPartial && k == "Content-Range") {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, v1 := range v {
|
for _, v1 := range v {
|
||||||
@@ -410,7 +409,9 @@ func (this *HTTPWriter) PrepareCache(resp *http.Response, size int64) {
|
|||||||
var filterReader = readers.NewFilterReaderCloser(resp.Body)
|
var filterReader = readers.NewFilterReaderCloser(resp.Body)
|
||||||
this.cacheIsFinished = true
|
this.cacheIsFinished = true
|
||||||
var hasError = false
|
var hasError = false
|
||||||
filterReader.Add(func(p []byte, err error) error {
|
filterReader.Add(func(p []byte, readErr error) error {
|
||||||
|
// 这里不用处理readErr,因为只要把成功读取的部分写入缓存即可
|
||||||
|
|
||||||
if hasError {
|
if hasError {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -548,6 +549,11 @@ func (this *HTTPWriter) PrepareWebP(resp *http.Response, size int64) {
|
|||||||
this.req.web.WebP.IsOn &&
|
this.req.web.WebP.IsOn &&
|
||||||
this.req.web.WebP.MatchResponse(contentType, size, filepath.Ext(this.req.Path()), this.req.Format) &&
|
this.req.web.WebP.MatchResponse(contentType, size, filepath.Ext(this.req.Path()), this.req.Format) &&
|
||||||
this.req.web.WebP.MatchAccept(this.req.requestHeader("Accept")) {
|
this.req.web.WebP.MatchAccept(this.req.requestHeader("Accept")) {
|
||||||
|
// 检查是否已经因为尺寸过大而忽略
|
||||||
|
if webpIgnoreURLSet.Has(this.req.URL()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// 如果已经是WebP不再重复处理
|
// 如果已经是WebP不再重复处理
|
||||||
// TODO 考虑是否需要很严格的匹配
|
// TODO 考虑是否需要很严格的匹配
|
||||||
if strings.Contains(contentType, "image/webp") {
|
if strings.Contains(contentType, "image/webp") {
|
||||||
@@ -693,10 +699,7 @@ func (this *HTTPWriter) PrepareCompression(resp *http.Response, size int64) {
|
|||||||
// 写入Header
|
// 写入Header
|
||||||
var headerBuffer = utils.SharedBufferPool.Get()
|
var headerBuffer = utils.SharedBufferPool.Get()
|
||||||
for k, v := range this.Header() {
|
for k, v := range this.Header() {
|
||||||
if k == "Set-Cookie" ||
|
if this.shouldIgnoreHeader(k) {
|
||||||
k == "Strict-Transport-Security" ||
|
|
||||||
k == "Alt-Svc" ||
|
|
||||||
(this.isPartial && k == "Content-Range") {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, v1 := range v {
|
for _, v1 := range v {
|
||||||
@@ -735,7 +738,6 @@ func (this *HTTPWriter) PrepareCompression(resp *http.Response, size int64) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// compression writer
|
// compression writer
|
||||||
var err error = nil
|
|
||||||
compressionWriter, err := compressions.NewWriter(this.writer, compressionType, int(this.compressionConfig.Level))
|
compressionWriter, err := compressions.NewWriter(this.writer, compressionType, int(this.compressionConfig.Level))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
remotelogs.Error("HTTP_WRITER", err.Error())
|
remotelogs.Error("HTTP_WRITER", err.Error())
|
||||||
@@ -792,12 +794,10 @@ func (this *HTTPWriter) AddHeaders(header http.Header) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
switch key {
|
switch key {
|
||||||
case "ETag":
|
case "Accept-CH", "ETag", "Content-MD5", "IM", "P3P", "WWW-Authenticate", "X-Request-ID":
|
||||||
newHeaders[key] = value
|
newHeaders[key] = value
|
||||||
default:
|
default:
|
||||||
for _, v := range value {
|
newHeaders[http.CanonicalHeaderKey(key)] = value
|
||||||
newHeaders.Add(key, v)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -869,7 +869,7 @@ func (this *HTTPWriter) SendFile(status int, path string) (int64, error) {
|
|||||||
|
|
||||||
fp, err := os.OpenFile(path, os.O_RDONLY, 0444)
|
fp, err := os.OpenFile(path, os.O_RDONLY, 0444)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, errors.New("open file '" + path + "' failed: " + err.Error())
|
return 0, fmt.Errorf("open file '%s' failed: %w", path, err)
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
_ = fp.Close()
|
_ = fp.Close()
|
||||||
@@ -913,7 +913,7 @@ func (this *HTTPWriter) SendResp(resp *http.Response) (int64, error) {
|
|||||||
|
|
||||||
// Redirect 跳转
|
// Redirect 跳转
|
||||||
func (this *HTTPWriter) Redirect(status int, url string) {
|
func (this *HTTPWriter) Redirect(status int, url string) {
|
||||||
http.Redirect(this.rawWriter, this.req.RawReq, url, status)
|
httpRedirect(this, this.req.RawReq, url, status)
|
||||||
this.isFinished = true
|
this.isFinished = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -989,7 +989,7 @@ func (this *HTTPWriter) DelayRead() bool {
|
|||||||
|
|
||||||
// 计算stale时长
|
// 计算stale时长
|
||||||
func (this *HTTPWriter) calculateStaleLife() int {
|
func (this *HTTPWriter) calculateStaleLife() int {
|
||||||
var staleLife = 600 // TODO 可以在缓存策略里设置此时间
|
var staleLife = caches.DefaultStaleCacheSeconds
|
||||||
var staleConfig = this.req.web.Cache.Stale
|
var staleConfig = this.req.web.Cache.Stale
|
||||||
if staleConfig != nil && staleConfig.IsOn {
|
if staleConfig != nil && staleConfig.IsOn {
|
||||||
// 从Header中读取stale-if-error
|
// 从Header中读取stale-if-error
|
||||||
@@ -1039,9 +1039,7 @@ func (this *HTTPWriter) finishWebP() {
|
|||||||
if webpCacheWriter != nil {
|
if webpCacheWriter != nil {
|
||||||
// 写入Header
|
// 写入Header
|
||||||
for k, v := range this.Header() {
|
for k, v := range this.Header() {
|
||||||
if k == "Set-Cookie" ||
|
if this.shouldIgnoreHeader(k) {
|
||||||
k == "Strict-Transport-Security" ||
|
|
||||||
k == "Alt-Svc" {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1063,7 +1061,9 @@ func (this *HTTPWriter) finishWebP() {
|
|||||||
if webpCacheWriter != nil {
|
if webpCacheWriter != nil {
|
||||||
var teeWriter = writers.NewTeeWriterCloser(this.writer, webpCacheWriter)
|
var teeWriter = writers.NewTeeWriterCloser(this.writer, webpCacheWriter)
|
||||||
teeWriter.OnFail(func(err error) {
|
teeWriter.OnFail(func(err error) {
|
||||||
_ = webpCacheWriter.Discard()
|
if webpCacheWriter != nil {
|
||||||
|
_ = webpCacheWriter.Discard()
|
||||||
|
}
|
||||||
webpCacheWriter = nil
|
webpCacheWriter = nil
|
||||||
})
|
})
|
||||||
this.writer = teeWriter
|
this.writer = teeWriter
|
||||||
@@ -1079,12 +1079,24 @@ func (this *HTTPWriter) finishWebP() {
|
|||||||
var err error
|
var err error
|
||||||
if isGif {
|
if isGif {
|
||||||
gifImage, err = gif.DecodeAll(reader)
|
gifImage, err = gif.DecodeAll(reader)
|
||||||
|
if gifImage != nil && (gifImage.Config.Width > gowebp.WebPMaxDimension || gifImage.Config.Height > gowebp.WebPMaxDimension) {
|
||||||
|
webpIgnoreURLSet.Push(this.req.URL())
|
||||||
|
return
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
imageData, _, err = image.Decode(reader)
|
imageData, _, err = image.Decode(reader)
|
||||||
|
if imageData != nil {
|
||||||
|
var bound = imageData.Bounds()
|
||||||
|
if bound.Max.X > gowebp.WebPMaxDimension || bound.Max.Y > gowebp.WebPMaxDimension {
|
||||||
|
webpIgnoreURLSet.Push(this.req.URL())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// 发生了错误终止处理
|
// 发生了错误终止处理
|
||||||
|
webpIgnoreURLSet.Push(this.req.URL())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1260,10 +1272,7 @@ func (this *HTTPWriter) finishRequest() {
|
|||||||
// 计算Header长度
|
// 计算Header长度
|
||||||
func (this *HTTPWriter) calculateHeaderLength() (result int) {
|
func (this *HTTPWriter) calculateHeaderLength() (result int) {
|
||||||
for k, v := range this.Header() {
|
for k, v := range this.Header() {
|
||||||
if k == "Set-Cookie" ||
|
if this.shouldIgnoreHeader(k) {
|
||||||
k == "Strict-Transport-Security" ||
|
|
||||||
k == "Alt-Svc" ||
|
|
||||||
(this.isPartial && k == "Content-Range") {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, v1 := range v {
|
for _, v1 := range v {
|
||||||
@@ -1272,3 +1281,12 @@ func (this *HTTPWriter) calculateHeaderLength() (result int) {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *HTTPWriter) shouldIgnoreHeader(name string) bool {
|
||||||
|
switch name {
|
||||||
|
case "Set-Cookie", "Strict-Transport-Security", "Alt-Svc", "Upgrade", "X-Cache":
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return (this.isPartial && name == "Content-Range")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
"github.com/iwind/TeaGo/types"
|
"github.com/iwind/TeaGo/types"
|
||||||
"net"
|
"net"
|
||||||
)
|
)
|
||||||
@@ -38,7 +39,7 @@ func (this *BaseListener) buildTLSConfig() *tls.Config {
|
|||||||
GetConfigForClient: func(clientInfo *tls.ClientHelloInfo) (config *tls.Config, e error) {
|
GetConfigForClient: func(clientInfo *tls.ClientHelloInfo) (config *tls.Config, e error) {
|
||||||
// 指纹信息
|
// 指纹信息
|
||||||
var fingerprint = this.calculateFingerprint(clientInfo)
|
var fingerprint = this.calculateFingerprint(clientInfo)
|
||||||
if len(fingerprint) > 0 {
|
if len(fingerprint) > 0 && clientInfo.Conn != nil {
|
||||||
clientConn, ok := clientInfo.Conn.(ClientConnInterface)
|
clientConn, ok := clientInfo.Conn.(ClientConnInterface)
|
||||||
if ok {
|
if ok {
|
||||||
clientConn.SetFingerprint(fingerprint)
|
clientConn.SetFingerprint(fingerprint)
|
||||||
@@ -61,7 +62,7 @@ func (this *BaseListener) buildTLSConfig() *tls.Config {
|
|||||||
GetCertificate: func(clientInfo *tls.ClientHelloInfo) (certificate *tls.Certificate, e error) {
|
GetCertificate: func(clientInfo *tls.ClientHelloInfo) (certificate *tls.Certificate, e error) {
|
||||||
// 指纹信息
|
// 指纹信息
|
||||||
var fingerprint = this.calculateFingerprint(clientInfo)
|
var fingerprint = this.calculateFingerprint(clientInfo)
|
||||||
if len(fingerprint) > 0 {
|
if len(fingerprint) > 0 && clientInfo.Conn != nil {
|
||||||
clientConn, ok := clientInfo.Conn.(ClientConnInterface)
|
clientConn, ok := clientInfo.Conn.(ClientConnInterface)
|
||||||
if ok {
|
if ok {
|
||||||
clientConn.SetFingerprint(fingerprint)
|
clientConn.SetFingerprint(fingerprint)
|
||||||
@@ -179,10 +180,10 @@ func (this *BaseListener) findNamedServer(name string) (serverConfig *serverconf
|
|||||||
|
|
||||||
if globalServerConfig != nil &&
|
if globalServerConfig != nil &&
|
||||||
len(globalServerConfig.HTTPAll.DefaultDomain) > 0 &&
|
len(globalServerConfig.HTTPAll.DefaultDomain) > 0 &&
|
||||||
(!matchDomainStrictly || configutils.MatchDomains(globalServerConfig.HTTPAll.AllowMismatchDomains, name) || (globalServerConfig.HTTPAll.AllowNodeIP && net.ParseIP(name) != nil)) {
|
(!matchDomainStrictly || configutils.MatchDomains(globalServerConfig.HTTPAll.AllowMismatchDomains, name) || (globalServerConfig.HTTPAll.AllowNodeIP && utils.IsWildIP(name))) {
|
||||||
if globalServerConfig.HTTPAll.AllowNodeIP &&
|
if globalServerConfig.HTTPAll.AllowNodeIP &&
|
||||||
globalServerConfig.HTTPAll.NodeIPShowPage &&
|
globalServerConfig.HTTPAll.NodeIPShowPage &&
|
||||||
net.ParseIP(name) != nil {
|
utils.IsWildIP(name) {
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
var defaultDomain = globalServerConfig.HTTPAll.DefaultDomain
|
var defaultDomain = globalServerConfig.HTTPAll.DefaultDomain
|
||||||
@@ -193,7 +194,7 @@ func (this *BaseListener) findNamedServer(name string) (serverConfig *serverconf
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if matchDomainStrictly && !configutils.MatchDomains(globalServerConfig.HTTPAll.AllowMismatchDomains, name) && (!globalServerConfig.HTTPAll.AllowNodeIP || net.ParseIP(name) == nil) {
|
if matchDomainStrictly && !configutils.MatchDomains(globalServerConfig.HTTPAll.AllowMismatchDomains, name) && (!globalServerConfig.HTTPAll.AllowNodeIP || !utils.IsWildIP(name)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -235,7 +236,7 @@ func (this *BaseListener) findNamedServerMatched(name string) (serverConfig *ser
|
|||||||
// 从Hello信息中获取服务名称
|
// 从Hello信息中获取服务名称
|
||||||
func (this *BaseListener) helloServerName(clientInfo *tls.ClientHelloInfo) string {
|
func (this *BaseListener) helloServerName(clientInfo *tls.ClientHelloInfo) string {
|
||||||
var serverName = clientInfo.ServerName
|
var serverName = clientInfo.ServerName
|
||||||
if len(serverName) == 0 {
|
if len(serverName) == 0 && clientInfo.Conn != nil {
|
||||||
var localAddr = clientInfo.Conn.LocalAddr()
|
var localAddr = clientInfo.Conn.LocalAddr()
|
||||||
if localAddr != nil {
|
if localAddr != nil {
|
||||||
tcpAddr, ok := localAddr.(*net.TCPAddr)
|
tcpAddr, ok := localAddr.(*net.TCPAddr)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
package nodes
|
package nodes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"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/iwind/TeaGo/types"
|
"github.com/iwind/TeaGo/types"
|
||||||
@@ -23,7 +24,7 @@ func TestBaseListener_FindServer(t *testing.T) {
|
|||||||
{Name: types.String(i) + ".hello.com"},
|
{Name: types.String(i) + ".hello.com"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
_ = server.Init(nil)
|
_ = server.Init(context.Background())
|
||||||
listener.Group.Add(server)
|
listener.Group.Add(server)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -177,6 +177,12 @@ func (this *HTTPListener) ServeHTTP(rawWriter http.ResponseWriter, rawReq *http.
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
clientConn.SetUserId(server.UserId)
|
clientConn.SetUserId(server.UserId)
|
||||||
|
|
||||||
|
var userPlanId int64
|
||||||
|
if server.UserPlan != nil && server.UserPlan.Id > 0 {
|
||||||
|
userPlanId = server.UserPlan.Id
|
||||||
|
}
|
||||||
|
clientConn.SetUserPlanId(userPlanId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -210,7 +216,7 @@ func (this *HTTPListener) ServeHTTP(rawWriter http.ResponseWriter, rawReq *http.
|
|||||||
// 检查host是否为IP
|
// 检查host是否为IP
|
||||||
func (this *HTTPListener) isIP(host string) bool {
|
func (this *HTTPListener) isIP(host string) bool {
|
||||||
// IPv6
|
// IPv6
|
||||||
if strings.Index(host, "[") > -1 {
|
if strings.Contains(host, "[") {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package nodes
|
package nodes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"fmt"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||||
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
||||||
@@ -141,7 +141,7 @@ func (this *ListenerManager) Start(nodeConfig *nodeconfigs.NodeConfig) error {
|
|||||||
var port = addr[portIndex+1:]
|
var port = addr[portIndex+1:]
|
||||||
var processName = this.findProcessNameWithPort(group.IsUDP(), port)
|
var processName = this.findProcessNameWithPort(group.IsUDP(), port)
|
||||||
if len(processName) > 0 {
|
if len(processName) > 0 {
|
||||||
err = errors.New(err.Error() + " (the process using port: '" + processName + "')")
|
err = fmt.Errorf("%w (the process using port: '%s')", err, processName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,6 +80,12 @@ func (this *TCPListener) handleConn(conn net.Conn) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
clientConn.SetUserId(server.UserId)
|
clientConn.SetUserId(server.UserId)
|
||||||
|
|
||||||
|
var userPlanId int64
|
||||||
|
if server.UserPlan != nil && server.UserPlan.Id > 0 {
|
||||||
|
userPlanId = server.UserPlan.Id
|
||||||
|
}
|
||||||
|
clientConn.SetUserPlanId(userPlanId)
|
||||||
} else {
|
} else {
|
||||||
tlsConn, ok := conn.(*tls.Conn)
|
tlsConn, ok := conn.(*tls.Conn)
|
||||||
if ok {
|
if ok {
|
||||||
@@ -92,6 +98,12 @@ func (this *TCPListener) handleConn(conn net.Conn) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
clientConn.SetUserId(server.UserId)
|
clientConn.SetUserId(server.UserId)
|
||||||
|
|
||||||
|
var userPlanId int64
|
||||||
|
if server.UserPlan != nil && server.UserPlan.Id > 0 {
|
||||||
|
userPlanId = server.UserPlan.Id
|
||||||
|
}
|
||||||
|
clientConn.SetUserPlanId(userPlanId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -404,7 +404,11 @@ func NewUDPConn(server *serverconfigs.ServerConfig, addr net.Addr, proxyListener
|
|||||||
stats.SharedTrafficStatManager.Add(server.UserId, server.Id, "", int64(n), 0, 0, 0, 0, 0, server.ShouldCheckTrafficLimit(), server.PlanId())
|
stats.SharedTrafficStatManager.Add(server.UserId, server.Id, "", int64(n), 0, 0, 0, 0, 0, server.ShouldCheckTrafficLimit(), server.PlanId())
|
||||||
|
|
||||||
// 带宽
|
// 带宽
|
||||||
stats.SharedBandwidthStatManager.AddBandwidth(server.UserId, server.Id, int64(n), int64(n))
|
var userPlanId int64
|
||||||
|
if server.UserPlan != nil && server.UserPlan.Id > 0 {
|
||||||
|
userPlanId = server.UserPlan.Id
|
||||||
|
}
|
||||||
|
stats.SharedBandwidthStatManager.AddBandwidth(server.UserId, userPlanId, server.Id, int64(n), int64(n))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -2,8 +2,10 @@ package nodes
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
|
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
|
||||||
iplib "github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
|
iplib "github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||||
@@ -34,7 +36,6 @@ import (
|
|||||||
"github.com/iwind/TeaGo/maps"
|
"github.com/iwind/TeaGo/maps"
|
||||||
"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"
|
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
@@ -95,11 +96,11 @@ func (this *Node) Test() error {
|
|||||||
// 检查是否能连接API
|
// 检查是否能连接API
|
||||||
rpcClient, err := rpc.SharedRPC()
|
rpcClient, err := rpc.SharedRPC()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("test rpc failed: " + err.Error())
|
return fmt.Errorf("test rpc failed: %w", err)
|
||||||
}
|
}
|
||||||
_, err = rpcClient.APINodeRPC.FindCurrentAPINodeVersion(rpcClient.Context(), &pb.FindCurrentAPINodeVersionRequest{})
|
_, err = rpcClient.APINodeRPC.FindCurrentAPINodeVersion(rpcClient.Context(), &pb.FindCurrentAPINodeVersionRequest{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("test rpc failed: " + err.Error())
|
return fmt.Errorf("test rpc failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -141,9 +142,6 @@ func (this *Node) Start() {
|
|||||||
// 调整系统参数
|
// 调整系统参数
|
||||||
this.checkSystem()
|
this.checkSystem()
|
||||||
|
|
||||||
// 检查硬盘类型
|
|
||||||
this.checkDisk()
|
|
||||||
|
|
||||||
// 启动事件
|
// 启动事件
|
||||||
events.Notify(events.EventStart)
|
events.Notify(events.EventStart)
|
||||||
|
|
||||||
@@ -197,7 +195,7 @@ func (this *Node) Start() {
|
|||||||
}
|
}
|
||||||
teaconst.NodeId = nodeConfig.Id
|
teaconst.NodeId = nodeConfig.Id
|
||||||
teaconst.NodeIdString = types.String(teaconst.NodeId)
|
teaconst.NodeIdString = types.String(teaconst.NodeId)
|
||||||
err, serverErrors := nodeConfig.Init(nil)
|
err, serverErrors := nodeConfig.Init(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
remotelogs.Error("NODE", "init node config failed: "+err.Error())
|
remotelogs.Error("NODE", "init node config failed: "+err.Error())
|
||||||
return
|
return
|
||||||
@@ -315,17 +313,17 @@ func (this *Node) syncConfig(taskVersion int64) error {
|
|||||||
this.locker.Lock()
|
this.locker.Lock()
|
||||||
defer this.locker.Unlock()
|
defer this.locker.Unlock()
|
||||||
|
|
||||||
// 检查api.yaml是否存在
|
// 检查api_node.yaml是否存在
|
||||||
apiConfigFile := Tea.ConfigFile("api.yaml")
|
var apiConfigFile = Tea.ConfigFile(configs.ConfigFileName)
|
||||||
_, err := os.Stat(apiConfigFile)
|
_, err := os.Stat(apiConfigFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
clusterErr := this.checkClusterConfig()
|
clusterErr := this.checkClusterConfig()
|
||||||
if clusterErr != nil {
|
if clusterErr != nil {
|
||||||
if os.IsNotExist(clusterErr) {
|
if os.IsNotExist(clusterErr) {
|
||||||
return errors.New("can not find config file 'configs/api.yaml'")
|
return fmt.Errorf("can not find config file 'configs/%s'", configs.ConfigFileName)
|
||||||
}
|
}
|
||||||
return errors.New("check cluster config failed: " + clusterErr.Error())
|
return fmt.Errorf("check cluster config failed: %w", clusterErr)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return err
|
return err
|
||||||
@@ -334,7 +332,7 @@ func (this *Node) syncConfig(taskVersion int64) error {
|
|||||||
|
|
||||||
rpcClient, err := rpc.SharedRPC()
|
rpcClient, err := rpc.SharedRPC()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("create rpc client failed: " + err.Error())
|
return fmt.Errorf("create rpc client failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取同步任务
|
// 获取同步任务
|
||||||
@@ -346,7 +344,7 @@ func (this *Node) syncConfig(taskVersion int64) error {
|
|||||||
UseDataMap: true,
|
UseDataMap: true,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("read config from rpc failed: " + err.Error())
|
return fmt.Errorf("read config from rpc failed: %w", err)
|
||||||
}
|
}
|
||||||
if !configResp.IsChanged {
|
if !configResp.IsChanged {
|
||||||
return nil
|
return nil
|
||||||
@@ -374,7 +372,7 @@ func (this *Node) syncConfig(taskVersion int64) error {
|
|||||||
var nodeConfig = &nodeconfigs.NodeConfig{}
|
var nodeConfig = &nodeconfigs.NodeConfig{}
|
||||||
err = json.Unmarshal(configJSON, nodeConfig)
|
err = json.Unmarshal(configJSON, nodeConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("decode config failed: " + err.Error())
|
return fmt.Errorf("decode config failed: %w", err)
|
||||||
}
|
}
|
||||||
teaconst.NodeId = nodeConfig.Id
|
teaconst.NodeId = nodeConfig.Id
|
||||||
teaconst.NodeIdString = types.String(teaconst.NodeId)
|
teaconst.NodeIdString = types.String(teaconst.NodeId)
|
||||||
@@ -394,7 +392,7 @@ func (this *Node) syncConfig(taskVersion int64) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err, serverErrors := nodeConfig.Init(nil)
|
err, serverErrors := nodeConfig.Init(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -525,21 +523,15 @@ func (this *Node) startSyncTimer() {
|
|||||||
|
|
||||||
// 检查集群设置
|
// 检查集群设置
|
||||||
func (this *Node) checkClusterConfig() error {
|
func (this *Node) checkClusterConfig() error {
|
||||||
configFile := Tea.ConfigFile("cluster.yaml")
|
config, err := configs.LoadClusterConfig()
|
||||||
data, err := os.ReadFile(configFile)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
config := &configs.ClusterConfig{}
|
|
||||||
err = yaml.Unmarshal(data, config)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
rpcClient, err := rpc.NewRPCClient(&configs.APIConfig{
|
rpcClient, err := rpc.NewRPCClient(&configs.APIConfig{
|
||||||
RPC: config.RPC,
|
RPCEndpoints: config.RPCEndpoints,
|
||||||
NodeId: config.ClusterId,
|
RPCDisableUpdate: config.RPCDisableUpdate,
|
||||||
Secret: config.Secret,
|
NodeId: config.ClusterId,
|
||||||
|
Secret: config.Secret,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -557,22 +549,17 @@ func (this *Node) checkClusterConfig() error {
|
|||||||
resp.Endpoints = []string{}
|
resp.Endpoints = []string{}
|
||||||
}
|
}
|
||||||
var apiConfig = &configs.APIConfig{
|
var apiConfig = &configs.APIConfig{
|
||||||
RPC: struct {
|
RPCEndpoints: resp.Endpoints,
|
||||||
Endpoints []string `yaml:"endpoints" json:"endpoints"`
|
RPCDisableUpdate: false,
|
||||||
DisableUpdate bool `yaml:"disableUpdate" json:"disableUpdate"`
|
NodeId: resp.UniqueId,
|
||||||
}{
|
Secret: resp.Secret,
|
||||||
Endpoints: resp.Endpoints,
|
|
||||||
DisableUpdate: false,
|
|
||||||
},
|
|
||||||
NodeId: resp.UniqueId,
|
|
||||||
Secret: resp.Secret,
|
|
||||||
}
|
}
|
||||||
remotelogs.Debug("NODE", "writing 'configs/api.yaml' ...")
|
remotelogs.Debug("NODE", "writing 'configs/"+configs.ConfigFileName+"' ...")
|
||||||
err = apiConfig.WriteFile(Tea.ConfigFile("api.yaml"))
|
err = apiConfig.WriteFile(Tea.ConfigFile(configs.ConfigFileName))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
remotelogs.Debug("NODE", "wrote 'configs/api.yaml' successfully")
|
remotelogs.Debug("NODE", "wrote 'configs/"+configs.ConfigFileName+"' successfully")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -821,6 +808,36 @@ func (this *Node) listenSock() error {
|
|||||||
_ = cmd.Reply(&gosock.Command{Params: maps.Map{
|
_ = cmd.Reply(&gosock.Command{Params: maps.Map{
|
||||||
"stats": m,
|
"stats": m,
|
||||||
}})
|
}})
|
||||||
|
case "cache.garbage":
|
||||||
|
var shouldDelete = maps.NewMap(cmd.Params).GetBool("delete")
|
||||||
|
|
||||||
|
var count = 0
|
||||||
|
var sampleFiles = []string{}
|
||||||
|
err := caches.SharedManager.ScanGarbageCaches(func(path string) error {
|
||||||
|
count++
|
||||||
|
if len(sampleFiles) < 10 {
|
||||||
|
sampleFiles = append(sampleFiles, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
if shouldDelete {
|
||||||
|
_ = os.Remove(path) // .cache
|
||||||
|
_ = os.Remove(caches.PartialRangesFilePath(path)) // @range.cache
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
_ = cmd.Reply(&gosock.Command{Params: maps.Map{
|
||||||
|
"isOk": false,
|
||||||
|
"error": err.Error(),
|
||||||
|
}})
|
||||||
|
} else {
|
||||||
|
_ = cmd.Reply(&gosock.Command{Params: maps.Map{
|
||||||
|
"isOk": true,
|
||||||
|
"count": count,
|
||||||
|
"sampleFiles": sampleFiles,
|
||||||
|
}})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -1037,7 +1054,7 @@ func (this *Node) reloadServer() {
|
|||||||
remotelogs.Debug("NODE", "reload "+types.String(countUpdatingServers)+" servers")
|
remotelogs.Debug("NODE", "reload "+types.String(countUpdatingServers)+" servers")
|
||||||
}
|
}
|
||||||
|
|
||||||
err, serverErrors := newNodeConfig.Init(nil)
|
err, serverErrors := newNodeConfig.Init(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
remotelogs.Error("NODE", "apply server config error: "+err.Error())
|
remotelogs.Error("NODE", "apply server config error: "+err.Error())
|
||||||
return
|
return
|
||||||
@@ -1054,6 +1071,9 @@ func (this *Node) reloadServer() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
remotelogs.Error("NODE", "apply server config error: "+err.Error())
|
remotelogs.Error("NODE", "apply server config error: "+err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// notify event
|
||||||
|
events.Notify(events.EventReloadSomeServers)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1107,28 +1127,6 @@ func (this *Node) checkSystem() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查硬盘
|
|
||||||
func (this *Node) checkDisk() {
|
|
||||||
if runtime.GOOS != "linux" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for n := 'a'; n <= 'z'; n++ {
|
|
||||||
for _, path := range []string{
|
|
||||||
"/sys/block/vd" + string(n) + "/queue/rotational",
|
|
||||||
"/sys/block/sd" + string(n) + "/queue/rotational",
|
|
||||||
} {
|
|
||||||
data, err := os.ReadFile(path)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if string(data) == "0" {
|
|
||||||
teaconst.DiskIsFast = true
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查API节点地址
|
// 检查API节点地址
|
||||||
func (this *Node) changeAPINodeAddrs(apiNodeAddrs []*serverconfigs.NetworkAddressConfig) {
|
func (this *Node) changeAPINodeAddrs(apiNodeAddrs []*serverconfigs.NetworkAddressConfig) {
|
||||||
var addrs = []string{}
|
var addrs = []string{}
|
||||||
@@ -1156,7 +1154,7 @@ func (this *Node) changeAPINodeAddrs(apiNodeAddrs []*serverconfigs.NetworkAddres
|
|||||||
if config == nil {
|
if config == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var oldEndpoints = config.RPC.Endpoints
|
var oldEndpoints = config.RPCEndpoints
|
||||||
|
|
||||||
rpcClient, err := rpc.SharedRPC()
|
rpcClient, err := rpc.SharedRPC()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -1170,9 +1168,9 @@ func (this *Node) changeAPINodeAddrs(apiNodeAddrs []*serverconfigs.NetworkAddres
|
|||||||
go func(v int64) {
|
go func(v int64) {
|
||||||
// 测试新的API节点地址
|
// 测试新的API节点地址
|
||||||
if rpcClient.TestEndpoints(addrs) {
|
if rpcClient.TestEndpoints(addrs) {
|
||||||
config.RPC.Endpoints = addrs
|
config.RPCEndpoints = addrs
|
||||||
} else {
|
} else {
|
||||||
config.RPC.Endpoints = oldEndpoints
|
config.RPCEndpoints = oldEndpoints
|
||||||
this.lastAPINodeAddrs = nil // 恢复为空,以便于下次更新重试
|
this.lastAPINodeAddrs = nil // 恢复为空,以便于下次更新重试
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,3 +15,7 @@ func (this *Node) reloadIPLibrary() {
|
|||||||
func (this *Node) notifyPlusChange() error {
|
func (this *Node) notifyPlusChange() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *Node) execTOAChangedTask() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||||
//go:build arm64
|
//go:build arm64
|
||||||
// +build arm64
|
|
||||||
|
|
||||||
package nodes
|
package nodes
|
||||||
|
|
||||||
// 处理异常
|
// 处理异常
|
||||||
func (this *Node) handlePanic() {
|
func (this *Node) handlePanic() {
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,8 +82,7 @@ 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, _ = 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()
|
||||||
@@ -208,6 +207,8 @@ func (this *NodeStatusExecutor) updateCPU(status *nodeconfigs.NodeStatus) {
|
|||||||
|
|
||||||
// 更新硬盘
|
// 更新硬盘
|
||||||
func (this *NodeStatusExecutor) updateDisk(status *nodeconfigs.NodeStatus) {
|
func (this *NodeStatusExecutor) updateDisk(status *nodeconfigs.NodeStatus) {
|
||||||
|
status.DiskWritingSpeedMB = int(fsutils.DiskSpeedMB)
|
||||||
|
|
||||||
partitions, err := disk.Partitions(false)
|
partitions, err := disk.Partitions(false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
remotelogs.Error("NODE_STATUS", err.Error())
|
remotelogs.Error("NODE_STATUS", err.Error())
|
||||||
@@ -263,7 +264,9 @@ func (this *NodeStatusExecutor) updateDisk(status *nodeconfigs.NodeStatus) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
status.DiskTotal = total
|
status.DiskTotal = total
|
||||||
status.DiskUsage = float64(totalUsage) / float64(total)
|
if total > 0 {
|
||||||
|
status.DiskUsage = float64(totalUsage) / float64(total)
|
||||||
|
}
|
||||||
status.DiskMaxUsage = maxUsage / 100
|
status.DiskMaxUsage = maxUsage / 100
|
||||||
|
|
||||||
// 记录监控数据
|
// 记录监控数据
|
||||||
@@ -279,7 +282,7 @@ func (this *NodeStatusExecutor) updateCacheSpace(status *nodeconfigs.NodeStatus)
|
|||||||
var result = []maps.Map{}
|
var result = []maps.Map{}
|
||||||
var cachePaths = caches.SharedManager.FindAllCachePaths()
|
var cachePaths = caches.SharedManager.FindAllCachePaths()
|
||||||
for _, path := range cachePaths {
|
for _, path := range cachePaths {
|
||||||
stat, err := fsutils.Stat(path)
|
stat, err := fsutils.StatDevice(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,19 +5,24 @@ package nodes
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/ddosconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/ddosconfigs"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/configs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/firewalls"
|
"github.com/TeaOSLab/EdgeNode/internal/firewalls"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/iplibrary"
|
"github.com/TeaOSLab/EdgeNode/internal/iplibrary"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/trackers"
|
"github.com/TeaOSLab/EdgeNode/internal/trackers"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/waf"
|
||||||
"github.com/iwind/TeaGo/Tea"
|
"github.com/iwind/TeaGo/Tea"
|
||||||
|
"github.com/iwind/TeaGo/maps"
|
||||||
"github.com/iwind/TeaGo/types"
|
"github.com/iwind/TeaGo/types"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -26,8 +31,8 @@ func (this *Node) loopTasks() error {
|
|||||||
var tr = trackers.Begin("CHECK_NODE_CONFIG_CHANGES")
|
var tr = trackers.Begin("CHECK_NODE_CONFIG_CHANGES")
|
||||||
defer tr.End()
|
defer tr.End()
|
||||||
|
|
||||||
// 检查api.yaml是否存在
|
// 检查api_node.yaml是否存在
|
||||||
var apiConfigFile = Tea.ConfigFile("api.yaml")
|
var apiConfigFile = Tea.ConfigFile(configs.ConfigFileName)
|
||||||
_, err := os.Stat(apiConfigFile)
|
_, err := os.Stat(apiConfigFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
@@ -35,7 +40,7 @@ func (this *Node) loopTasks() error {
|
|||||||
|
|
||||||
rpcClient, err := rpc.SharedRPC()
|
rpcClient, err := rpc.SharedRPC()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("create rpc client failed: " + err.Error())
|
return fmt.Errorf("create rpc client failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
tasksResp, err := rpcClient.NodeTaskRPC.FindNodeTasks(rpcClient.Context(), &pb.FindNodeTasksRequest{
|
tasksResp, err := rpcClient.NodeTaskRPC.FindNodeTasks(rpcClient.Context(), &pb.FindNodeTasksRequest{
|
||||||
@@ -45,7 +50,7 @@ func (this *Node) loopTasks() error {
|
|||||||
if rpc.IsConnError(err) && !Tea.IsTesting() {
|
if rpc.IsConnError(err) && !Tea.IsTesting() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return errors.New("read node tasks failed: " + err.Error())
|
return fmt.Errorf("read node tasks failed: %w", err)
|
||||||
}
|
}
|
||||||
for _, task := range tasksResp.NodeTasks {
|
for _, task := range tasksResp.NodeTasks {
|
||||||
err := this.execTask(rpcClient, task)
|
err := this.execTask(rpcClient, task)
|
||||||
@@ -90,8 +95,15 @@ func (this *Node) execTask(rpcClient *rpc.RPCClient, task *pb.NodeTask) error {
|
|||||||
err = this.execUpdatingServersTask(rpcClient)
|
err = this.execUpdatingServersTask(rpcClient)
|
||||||
case "plusChanged":
|
case "plusChanged":
|
||||||
err = this.notifyPlusChange()
|
err = this.notifyPlusChange()
|
||||||
|
case "toaChanged":
|
||||||
|
err = this.execTOAChangedTask()
|
||||||
default:
|
default:
|
||||||
remotelogs.Error("NODE", "task '"+types.String(task.Id)+"', type '"+task.Type+"' has not been handled")
|
// 特殊任务
|
||||||
|
if strings.HasPrefix(task.Type, "ipListDeleted") { // 删除IP名单
|
||||||
|
err = this.execDeleteIPList(task.Type)
|
||||||
|
} else { // 未处理的任务
|
||||||
|
remotelogs.Error("NODE", "task '"+types.String(task.Id)+"', type '"+task.Type+"' has not been handled")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
@@ -145,7 +157,7 @@ func (this *Node) execNodeLevelChangedTask(rpcClient *rpc.RPCClient) error {
|
|||||||
if len(levelInfoResp.ParentNodesMapJSON) > 0 {
|
if len(levelInfoResp.ParentNodesMapJSON) > 0 {
|
||||||
err = json.Unmarshal(levelInfoResp.ParentNodesMapJSON, &parentNodes)
|
err = json.Unmarshal(levelInfoResp.ParentNodesMapJSON, &parentNodes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("decode level info failed: " + err.Error())
|
return fmt.Errorf("decode level info failed: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,7 +184,7 @@ func (this *Node) execDDoSProtectionChangedTask(rpcClient *rpc.RPCClient) error
|
|||||||
var ddosProtectionConfig = &ddosconfigs.ProtectionConfig{}
|
var ddosProtectionConfig = &ddosconfigs.ProtectionConfig{}
|
||||||
err = json.Unmarshal(resp.DdosProtectionJSON, ddosProtectionConfig)
|
err = json.Unmarshal(resp.DdosProtectionJSON, ddosProtectionConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("decode DDoS protection config failed: " + err.Error())
|
return fmt.Errorf("decode DDoS protection config failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ddosProtectionConfig != nil && sharedNodeConfig != nil {
|
if ddosProtectionConfig != nil && sharedNodeConfig != nil {
|
||||||
@@ -200,13 +212,13 @@ func (this *Node) execGlobalServerConfigChangedTask(rpcClient *rpc.RPCClient) er
|
|||||||
var globalServerConfig = serverconfigs.NewGlobalServerConfig()
|
var globalServerConfig = serverconfigs.NewGlobalServerConfig()
|
||||||
err = json.Unmarshal(resp.GlobalServerConfigJSON, globalServerConfig)
|
err = json.Unmarshal(resp.GlobalServerConfigJSON, globalServerConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("decode global server config failed: " + err.Error())
|
return fmt.Errorf("decode global server config failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if globalServerConfig != nil {
|
if globalServerConfig != nil {
|
||||||
err = globalServerConfig.Init()
|
err = globalServerConfig.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("validate global server config failed: " + err.Error())
|
return fmt.Errorf("validate global server config failed: %w", err)
|
||||||
}
|
}
|
||||||
if sharedNodeConfig != nil {
|
if sharedNodeConfig != nil {
|
||||||
sharedNodeConfig.GlobalServerConfig = globalServerConfig
|
sharedNodeConfig.GlobalServerConfig = globalServerConfig
|
||||||
@@ -254,7 +266,7 @@ func (this *Node) execUpdatingServersTask(rpcClient *rpc.RPCClient) error {
|
|||||||
var serverConfigs = []*serverconfigs.ServerConfig{}
|
var serverConfigs = []*serverconfigs.ServerConfig{}
|
||||||
err = json.Unmarshal(resp.ServersJSON, &serverConfigs)
|
err = json.Unmarshal(resp.ServersJSON, &serverConfigs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("decode server configs failed: " + err.Error())
|
return fmt.Errorf("decode server configs failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if resp.MaxId > this.lastUpdatingServerListId {
|
if resp.MaxId > this.lastUpdatingServerListId {
|
||||||
@@ -282,6 +294,34 @@ func (this *Node) execUpdatingServersTask(rpcClient *rpc.RPCClient) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 删除IP名单
|
||||||
|
func (this *Node) execDeleteIPList(taskType string) error {
|
||||||
|
optionsString, ok := strings.CutPrefix(taskType, "ipListDeleted@")
|
||||||
|
if !ok {
|
||||||
|
return errors.New("invalid task type '" + taskType + "'")
|
||||||
|
}
|
||||||
|
var optionMap = maps.Map{}
|
||||||
|
err := json.Unmarshal([]byte(optionsString), &optionMap)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("decode options failed: %w, options: %s", err, optionsString)
|
||||||
|
}
|
||||||
|
var listId = optionMap.GetInt64("listId")
|
||||||
|
if listId <= 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 标记已被删除
|
||||||
|
waf.AddDeletedIPList(listId)
|
||||||
|
|
||||||
|
var list = iplibrary.SharedIPListManager.FindList(listId)
|
||||||
|
|
||||||
|
if list != nil {
|
||||||
|
list.SetDeleted()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// 标记任务完成
|
// 标记任务完成
|
||||||
func (this *Node) finishTask(taskId int64, taskVersion int64, taskErr error) (success bool) {
|
func (this *Node) finishTask(taskId int64, taskVersion int64, taskErr error) (success bool) {
|
||||||
if taskId <= 0 {
|
if taskId <= 0 {
|
||||||
|
|||||||
@@ -1,11 +1,7 @@
|
|||||||
package nodes
|
package nodes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
|
||||||
"github.com/golang/protobuf/proto"
|
|
||||||
_ "github.com/iwind/TeaGo/bootstrap"
|
_ "github.com/iwind/TeaGo/bootstrap"
|
||||||
"io"
|
|
||||||
"strconv"
|
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -22,29 +18,3 @@ func TestNode_Test(t *testing.T) {
|
|||||||
}
|
}
|
||||||
t.Log("ok")
|
t.Log("ok")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNode_Proto_Buffer(t *testing.T) {
|
|
||||||
buff := proto.NewBuffer([]byte{})
|
|
||||||
for i := 0; i < 10; i++ {
|
|
||||||
err := buff.EncodeMessage(&pb.NodeStreamMessage{
|
|
||||||
RequestId: int64(i),
|
|
||||||
Code: "msg" + strconv.Itoa(i),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < 11; i++ {
|
|
||||||
msg := &pb.NodeStreamMessage{}
|
|
||||||
err := buff.DecodeMessage(msg)
|
|
||||||
if err != nil {
|
|
||||||
if err == io.EOF || err == io.ErrUnexpectedEOF {
|
|
||||||
break
|
|
||||||
} else {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
t.Log(msg.Code, msg.RequestId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
102
internal/nodes/origin_conn.go
Normal file
102
internal/nodes/origin_conn.go
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||||
|
|
||||||
|
package nodes
|
||||||
|
|
||||||
|
import (
|
||||||
|
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/utils/fasttime"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/zero"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const originConnCloseDelaySeconds = 3
|
||||||
|
|
||||||
|
var closingOriginConnMap = map[*OriginConn]zero.Zero{}
|
||||||
|
var closingOriginConnLocker = &sync.RWMutex{}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
if !teaconst.IsMain {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
goman.New(func() {
|
||||||
|
var ticker = time.NewTicker(originConnCloseDelaySeconds * time.Second)
|
||||||
|
for range ticker.C {
|
||||||
|
CleanOriginConnsTask()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func CleanOriginConnsTask() {
|
||||||
|
var closingConns = []*OriginConn{}
|
||||||
|
|
||||||
|
closingOriginConnLocker.RLock()
|
||||||
|
for conn := range closingOriginConnMap {
|
||||||
|
if conn.IsExpired() {
|
||||||
|
closingConns = append(closingConns, conn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closingOriginConnLocker.RUnlock()
|
||||||
|
|
||||||
|
if len(closingConns) > 0 {
|
||||||
|
for _, conn := range closingConns {
|
||||||
|
_ = conn.ForceClose()
|
||||||
|
closingOriginConnLocker.Lock()
|
||||||
|
delete(closingOriginConnMap, conn)
|
||||||
|
closingOriginConnLocker.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OriginConn connection with origin site
|
||||||
|
type OriginConn struct {
|
||||||
|
net.Conn
|
||||||
|
|
||||||
|
lastReadOk bool
|
||||||
|
lastReadAt int64
|
||||||
|
isClosed bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewOriginConn create new origin connection
|
||||||
|
func NewOriginConn(rawConn net.Conn) net.Conn {
|
||||||
|
return &OriginConn{Conn: rawConn}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read implement Read() for net.Conn interface
|
||||||
|
func (this *OriginConn) Read(b []byte) (n int, err error) {
|
||||||
|
n, err = this.Conn.Read(b)
|
||||||
|
this.lastReadOk = err == nil
|
||||||
|
if this.lastReadOk {
|
||||||
|
this.lastReadAt = fasttime.Now().Unix()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close implement Close() for net.Conn interface
|
||||||
|
func (this *OriginConn) Close() error {
|
||||||
|
if this.lastReadOk && fasttime.Now().Unix()-this.lastReadAt <= originConnCloseDelaySeconds {
|
||||||
|
closingOriginConnLocker.Lock()
|
||||||
|
closingOriginConnMap[this] = zero.Zero{}
|
||||||
|
closingOriginConnLocker.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isClosed = true
|
||||||
|
return this.Conn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *OriginConn) ForceClose() error {
|
||||||
|
if this.isClosed {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isClosed = true
|
||||||
|
return this.Conn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *OriginConn) IsExpired() bool {
|
||||||
|
return fasttime.Now().Unix()-this.lastReadAt > originConnCloseDelaySeconds
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user