Compare commits
85 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a16d8f1afa | ||
|
|
1bab7bfcba | ||
|
|
5928875623 | ||
|
|
734cf81ff0 | ||
|
|
de8c2e13f1 | ||
|
|
0742dc963d | ||
|
|
1fdce3ef7e | ||
|
|
2079b0ebee | ||
|
|
c706f2fdf1 | ||
|
|
bd3247668d | ||
|
|
73024fe38c | ||
|
|
db520858b3 | ||
|
|
84c8a351a9 | ||
|
|
c6c0823d30 | ||
|
|
1be64adb6a | ||
|
|
d0610a5001 | ||
|
|
a2f7511a46 | ||
|
|
6e8c886cd6 | ||
|
|
03f2130827 | ||
|
|
9fea0749a0 | ||
|
|
71e0d9ce07 | ||
|
|
24e69028f8 | ||
|
|
34521dbc5c | ||
|
|
2c59ae4a5b | ||
|
|
d0bd7bb88d | ||
|
|
47c24b4aa8 | ||
|
|
4eb58a3082 | ||
|
|
c10c7cc157 | ||
|
|
7fd8d7756b | ||
|
|
032c118f49 | ||
|
|
b6cab3919a | ||
|
|
8edd30bdd8 | ||
|
|
a8c8d80e3b | ||
|
|
c43b6b37ea | ||
|
|
ac2d57d2f1 | ||
|
|
83ac62cda3 | ||
|
|
c0909a2cd0 | ||
|
|
a73b9f2674 | ||
|
|
3e79b71afc | ||
|
|
d3caccbb55 | ||
|
|
f95bac8d38 | ||
|
|
41d2ab728b | ||
|
|
b319061e85 | ||
|
|
99b8686a49 | ||
|
|
fe8c5b505a | ||
|
|
f88d0982ed | ||
|
|
a9389d53e1 | ||
|
|
fc4b45fec7 | ||
|
|
9b22e6cf69 | ||
|
|
7bd7f7da45 | ||
|
|
b68e6517df | ||
|
|
bbae229d08 | ||
|
|
c73a6cbfe8 | ||
|
|
e869e8e4d6 | ||
|
|
bde4e8507f | ||
|
|
5ae25cffa0 | ||
|
|
44b721d0d3 | ||
|
|
a2d6b7e0a8 | ||
|
|
95d65481e3 | ||
|
|
71522243b5 | ||
|
|
953533d9a3 | ||
|
|
31509392c9 | ||
|
|
dc30469b2c | ||
|
|
fa2903ebb9 | ||
|
|
c43387bf6a | ||
|
|
47f5cbeac9 | ||
|
|
2aea68fac4 | ||
|
|
9f7e6559d3 | ||
|
|
e352e3125c | ||
|
|
6b5e93f5ad | ||
|
|
cea5ae9c6c | ||
|
|
80b2bbf1eb | ||
|
|
c87f25d7bf | ||
|
|
82aff804a6 | ||
|
|
973e67acdb | ||
|
|
93bd9daf76 | ||
|
|
d8c121662c | ||
|
|
5f33249c5d | ||
|
|
1beafc9976 | ||
|
|
b3857adc0f | ||
|
|
e75010692c | ||
|
|
f2df4a1560 | ||
|
|
7a4b68de97 | ||
|
|
6fce430469 | ||
|
|
8856094dd1 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,2 +1,3 @@
|
|||||||
*_plus.go
|
*_plus.go
|
||||||
|
*_plus_test.go
|
||||||
*-plus.sh
|
*-plus.sh
|
||||||
@@ -55,7 +55,7 @@ function build() {
|
|||||||
cp -R "$ROOT"/pages "$DIST"/
|
cp -R "$ROOT"/pages "$DIST"/
|
||||||
|
|
||||||
# we support TOA on linux/amd64 only
|
# we support TOA on linux/amd64 only
|
||||||
if [ "$OS" == "linux" -a "$ARCH" == "amd64" ]
|
if [ "$OS" == "linux" ] && [ "$ARCH" == "amd64" ]
|
||||||
then
|
then
|
||||||
cp -R "$ROOT"/edge-toa "$DIST"
|
cp -R "$ROOT"/edge-toa "$DIST"
|
||||||
fi
|
fi
|
||||||
@@ -114,7 +114,10 @@ function build() {
|
|||||||
if [ ! -z $CC_PATH ]; then
|
if [ ! -z $CC_PATH ]; then
|
||||||
env CC=$MUSL_DIR/$CC_PATH CXX=$MUSL_DIR/$CXX_PATH GOOS="${OS}" GOARCH="${ARCH}" CGO_ENABLED=1 go build -trimpath -tags $BUILD_TAG -o "$DIST"/bin/${NAME} -ldflags "-linkmode external -extldflags -static -s -w" "$ROOT"/../cmd/edge-node/main.go
|
env CC=$MUSL_DIR/$CC_PATH CXX=$MUSL_DIR/$CXX_PATH GOOS="${OS}" GOARCH="${ARCH}" CGO_ENABLED=1 go build -trimpath -tags $BUILD_TAG -o "$DIST"/bin/${NAME} -ldflags "-linkmode external -extldflags -static -s -w" "$ROOT"/../cmd/edge-node/main.go
|
||||||
else
|
else
|
||||||
env GOOS="${OS}" GOARCH="${ARCH}" CGO_ENABLED=1 go build -trimpath -tags $TAG -o "$DIST"/bin/${NAME} -ldflags="-s -w" "$ROOT"/../cmd/edge-node/main.go
|
if [[ `uname` == *"Linux"* ]] && [ "$OS" == "linux" ] && [[ "$ARCH" == "amd64" || "$ARCH" == "arm64" ]] && [ "$TAG" == "plus" ]; then
|
||||||
|
BUILD_TAG="plus,script"
|
||||||
|
fi
|
||||||
|
env GOOS="${OS}" GOARCH="${ARCH}" CGO_ENABLED=1 go build -trimpath -tags $BUILD_TAG -o "$DIST"/bin/${NAME} -ldflags="-s -w" "$ROOT"/../cmd/edge-node/main.go
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# delete hidden files
|
# delete hidden files
|
||||||
|
|||||||
9
build/test.sh
Executable file
9
build/test.sh
Executable file
@@ -0,0 +1,9 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
TAG=${1}
|
||||||
|
|
||||||
|
if [ -z "$TAG" ]; then
|
||||||
|
TAG="community"
|
||||||
|
fi
|
||||||
|
|
||||||
|
go test -v ../... -tags=${TAG}
|
||||||
76
go.mod
76
go.mod
@@ -5,60 +5,78 @@ go 1.18
|
|||||||
replace (
|
replace (
|
||||||
github.com/TeaOSLab/EdgeCommon => ../EdgeCommon
|
github.com/TeaOSLab/EdgeCommon => ../EdgeCommon
|
||||||
github.com/fsnotify/fsnotify => github.com/iwind/fsnotify v1.5.2-0.20220817040843-193be2051ff4
|
github.com/fsnotify/fsnotify => github.com/iwind/fsnotify v1.5.2-0.20220817040843-193be2051ff4
|
||||||
|
github.com/google/nftables => github.com/iwind/nftables v0.0.0-20230419014751-9f023a644ad4
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/TeaOSLab/EdgeCommon v0.0.0-00010101000000-000000000000
|
github.com/TeaOSLab/EdgeCommon v0.0.0-00010101000000-000000000000
|
||||||
github.com/andybalholm/brotli v1.0.4
|
github.com/aliyun/aliyun-oss-go-sdk v2.2.7+incompatible
|
||||||
|
github.com/andybalholm/brotli v1.0.5
|
||||||
|
github.com/aws/aws-sdk-go v1.44.279
|
||||||
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.5.1
|
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.2
|
github.com/golang/protobuf v1.5.3
|
||||||
github.com/google/nftables v0.0.0-20220407195405-950e408d48c6
|
github.com/google/nftables v0.1.0
|
||||||
github.com/iwind/TeaGo v0.0.0-20220807030847-31de8e1cbe55
|
github.com/huaweicloud/huaweicloud-sdk-go-obs v3.23.4+incompatible
|
||||||
|
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-20220819053541-c235395277b5
|
github.com/iwind/gowebp v0.0.0-20230615040911-5013dbb9d508
|
||||||
github.com/klauspost/compress v1.15.8
|
github.com/klauspost/compress v1.16.5
|
||||||
github.com/mattn/go-sqlite3 v1.14.9
|
github.com/mattn/go-sqlite3 v1.14.9
|
||||||
github.com/mdlayher/netlink v1.4.2
|
github.com/mdlayher/netlink v1.7.1
|
||||||
github.com/miekg/dns v1.1.43
|
github.com/miekg/dns v1.1.43
|
||||||
github.com/mssola/user_agent v0.5.3
|
github.com/mssola/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/quic-go/quic-go v0.36.1
|
||||||
github.com/shirou/gopsutil/v3 v3.22.2
|
github.com/shirou/gopsutil/v3 v3.22.2
|
||||||
golang.org/x/image v0.0.0-20220722155232-062f8c9fd539
|
github.com/tencentyun/cos-go-sdk-v5 v0.7.41
|
||||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f
|
golang.org/x/image v0.7.0
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab
|
golang.org/x/net v0.12.0
|
||||||
golang.org/x/text v0.3.7
|
golang.org/x/sys v0.10.0
|
||||||
google.golang.org/grpc v1.45.0
|
google.golang.org/grpc v1.55.0
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
rogchap.com/v8go v0.7.0
|
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/BurntSushi/toml v0.4.1 // indirect
|
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
|
||||||
github.com/chai2010/webp v1.1.1 // indirect
|
github.com/chai2010/webp v1.1.1 // indirect
|
||||||
|
github.com/clbanning/mxj v1.8.4 // indirect
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||||
github.com/go-sql-driver/mysql v1.5.0 // indirect
|
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
||||||
github.com/google/go-cmp v0.5.7 // indirect
|
github.com/golang/mock v1.6.0 // indirect
|
||||||
github.com/josharian/native v0.0.0-20200817173448-b6b71def0850 // indirect
|
github.com/google/go-cmp v0.5.9 // indirect
|
||||||
|
github.com/google/go-querystring v1.0.0 // indirect
|
||||||
|
github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8 // indirect
|
||||||
|
github.com/jmespath/go-jmespath v0.4.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
|
||||||
github.com/kr/text v0.2.0 // indirect
|
|
||||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||||
github.com/mdlayher/socket v0.0.0-20211102153432-57e3fa563ecb // indirect
|
github.com/mdlayher/socket v0.4.0 // indirect
|
||||||
|
github.com/mitchellh/mapstructure v1.4.3 // indirect
|
||||||
|
github.com/mozillazg/go-httpheader v0.2.1 // indirect
|
||||||
|
github.com/onsi/ginkgo/v2 v2.11.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/qtls-go1-19 v0.3.2 // indirect
|
||||||
|
github.com/quic-go/qtls-go1-20 v0.2.2 // indirect
|
||||||
|
github.com/tdewolff/minify/v2 v2.12.7 // 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/mod v0.5.1 // indirect
|
golang.org/x/crypto v0.11.0 // indirect
|
||||||
golang.org/x/tools v0.1.8 // indirect
|
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df // indirect
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
golang.org/x/mod v0.12.0 // indirect
|
||||||
google.golang.org/genproto v0.0.0-20220317150908-0efb43f6373e // indirect
|
golang.org/x/sync v0.3.0 // indirect
|
||||||
google.golang.org/protobuf v1.27.1 // indirect
|
golang.org/x/text v0.11.0 // indirect
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
golang.org/x/time v0.3.0 // indirect
|
||||||
honnef.co/go/tools v0.2.2 // indirect
|
golang.org/x/tools v0.11.0 // indirect
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect
|
||||||
|
google.golang.org/protobuf v1.30.0 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
383
go.sum
383
go.sum
@@ -1,32 +1,23 @@
|
|||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
|
||||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
|
||||||
github.com/BurntSushi/toml v0.4.1 h1:GaI7EiDXDRfa8VshkTj7Fym7ha+y8/XxIgD2okUIjLw=
|
|
||||||
github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
|
||||||
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
|
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
|
||||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||||
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
|
github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM=
|
||||||
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
github.com/aliyun/aliyun-oss-go-sdk v2.2.7+incompatible h1:KpbJFXwhVeuxNtBJ74MCGbIoaBok2uZvkD7QXp2+Wis=
|
||||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
github.com/aliyun/aliyun-oss-go-sdk v2.2.7+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
|
||||||
|
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
|
||||||
|
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/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
|
||||||
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/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
|
||||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||||
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
|
||||||
github.com/chai2010/webp v1.1.1 h1:jTRmEccAJ4MGrhFOrPMpNGIJ/eybIgwKpcACsrTEapk=
|
github.com/chai2010/webp v1.1.1 h1:jTRmEccAJ4MGrhFOrPMpNGIJ/eybIgwKpcACsrTEapk=
|
||||||
github.com/chai2010/webp v1.1.1/go.mod h1:0XVwvZWdjjdxpUEIf7b9g9VkHFnInUSYujwqTLEuldU=
|
github.com/chai2010/webp v1.1.1/go.mod h1:0XVwvZWdjjdxpUEIf7b9g9VkHFnInUSYujwqTLEuldU=
|
||||||
github.com/cilium/ebpf v0.5.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=
|
github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgkuj+NQRlZcDbAbM1ORAbXjXX77sX7T289U=
|
||||||
github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA=
|
github.com/clbanning/mxj v1.8.4 h1:HuhwZtbyvyOw+3Z1AowPkU87JkJUSv751ELWaiTpj8I=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng=
|
||||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
|
||||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
|
||||||
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
|
|
||||||
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
|
||||||
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
|
||||||
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
@@ -35,296 +26,240 @@ github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f h1:q/DpyjJjZs94bziQ
|
|||||||
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f/go.mod h1:QGrK8vMWWHQYQ3QU9bw9Y9OPNfxccGzfb41qjvVeXtY=
|
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f/go.mod h1:QGrK8vMWWHQYQ3QU9bw9Y9OPNfxccGzfb41qjvVeXtY=
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/djherbis/atime v1.1.0/go.mod h1:28OF6Y8s3NQWwacXc5eZTsEsiMzp7LF8MbXE+XJPdBE=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
|
||||||
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
|
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
|
||||||
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
|
|
||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
|
||||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||||
|
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||||
|
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||||
|
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
|
||||||
|
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||||
|
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
|
||||||
|
github.com/go-playground/validator/v10 v10.8.0/go.mod h1:9JhgTzTaE31GZDpH/HSvHiRJrJ3iKAgqqH0Bl/Ocjdk=
|
||||||
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
|
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
|
||||||
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
|
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
|
||||||
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
|
||||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
|
||||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
|
||||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
|
||||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
|
||||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
|
||||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
|
||||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
|
||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
|
||||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
|
||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
|
|
||||||
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
||||||
github.com/google/nftables v0.0.0-20220407195405-950e408d48c6 h1:btadZscaRmsi/+fOhkyUguRpSnrf6dykNEWxDeUCj9I=
|
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||||
github.com/google/nftables v0.0.0-20220407195405-950e408d48c6/go.mod h1:0F8on3JWMkm+xahTHItkiu/E1SPqMd0TOxNweQv8ptE=
|
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
|
||||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||||
github.com/iwind/TeaGo v0.0.0-20220807030847-31de8e1cbe55 h1:shQNx0flJFBwKsGE7Hs3bI2bDz+YF0zl/4qE8B2KRiY=
|
github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8 h1:n6vlPhxsA+BW/XsS5+uqi7GyzaLa5MH7qlSLBZtRdiA=
|
||||||
github.com/iwind/TeaGo v0.0.0-20220807030847-31de8e1cbe55/go.mod h1:fi/Pq+/5m2HZoseM+39dMF57ANXRt6w4PkGu3NXPc5s=
|
github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8/go.mod h1:Jh3hGz2jkYak8qXPD19ryItVnUgpgeqzdkY/D0EaeuA=
|
||||||
|
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/huaweicloud/huaweicloud-sdk-go-obs v3.23.4+incompatible h1:XRAk4HBDLCYEdPLWtKf5iZhOi7lfx17aY0oSO9+mcg8=
|
||||||
|
github.com/huaweicloud/huaweicloud-sdk-go-obs v3.23.4+incompatible/go.mod h1:l7VUhRbTKCzdOacdT4oWCwATKyvZqUOlOqr0Ous3k4s=
|
||||||
|
github.com/iwind/TeaGo v0.0.0-20230630104525-161f0b32996d h1:XnTIj781NdSipts60fVqbgZorVAKVSRaA6nqVNfBQ1g=
|
||||||
|
github.com/iwind/TeaGo v0.0.0-20230630104525-161f0b32996d/go.mod h1:fi/Pq+/5m2HZoseM+39dMF57ANXRt6w4PkGu3NXPc5s=
|
||||||
github.com/iwind/fsnotify v1.5.2-0.20220817040843-193be2051ff4 h1:PKtXlgNHJhdwl5ozio7KRV3n0SckMw+8ZC2NCpRSv8U=
|
github.com/iwind/fsnotify v1.5.2-0.20220817040843-193be2051ff4 h1:PKtXlgNHJhdwl5ozio7KRV3n0SckMw+8ZC2NCpRSv8U=
|
||||||
github.com/iwind/fsnotify v1.5.2-0.20220817040843-193be2051ff4/go.mod h1:DmAukmDY25inGlriLn0B2jidmvaDR1REOcPXwvzvDPI=
|
github.com/iwind/fsnotify v1.5.2-0.20220817040843-193be2051ff4/go.mod h1:DmAukmDY25inGlriLn0B2jidmvaDR1REOcPXwvzvDPI=
|
||||||
github.com/iwind/gofcgi v0.0.0-20210528023741-a92711d45f11 h1:DaQjoWZhLNxjhIXedVg4/vFEtHkZhK4IjIwsWdyzBLg=
|
github.com/iwind/gofcgi v0.0.0-20210528023741-a92711d45f11 h1:DaQjoWZhLNxjhIXedVg4/vFEtHkZhK4IjIwsWdyzBLg=
|
||||||
github.com/iwind/gofcgi v0.0.0-20210528023741-a92711d45f11/go.mod h1:JtbX20untAjUVjZs1ZBtq80f5rJWvwtQNRL6EnuYRnY=
|
github.com/iwind/gofcgi v0.0.0-20210528023741-a92711d45f11/go.mod h1:JtbX20untAjUVjZs1ZBtq80f5rJWvwtQNRL6EnuYRnY=
|
||||||
github.com/iwind/gosock v0.0.0-20211103081026-ee4652210ca4 h1:VWGsCqTzObdlbf7UUE3oceIpcEKi4C/YBUszQXk118A=
|
github.com/iwind/gosock v0.0.0-20211103081026-ee4652210ca4 h1:VWGsCqTzObdlbf7UUE3oceIpcEKi4C/YBUszQXk118A=
|
||||||
github.com/iwind/gosock v0.0.0-20211103081026-ee4652210ca4/go.mod h1:H5Q7SXwbx3a97ecJkaS2sD77gspzE7HFUafBO0peEyA=
|
github.com/iwind/gosock v0.0.0-20211103081026-ee4652210ca4/go.mod h1:H5Q7SXwbx3a97ecJkaS2sD77gspzE7HFUafBO0peEyA=
|
||||||
github.com/iwind/gowebp v0.0.0-20220819053541-c235395277b5 h1:SgKhrqRhWVpu0ZKxQM8MGjdhy7hlH3Xax8snwsZWSic=
|
github.com/iwind/gowebp v0.0.0-20230615040911-5013dbb9d508 h1:fjKiHAyPQmdwuw1DQ2BI1JTbhUWAtI3Kr9wIZQBdRgQ=
|
||||||
github.com/iwind/gowebp v0.0.0-20220819053541-c235395277b5/go.mod h1:Re7TEhwL+ygnxFg52fC0PWy01ULAIZp2QR0q5WwEOQA=
|
github.com/iwind/gowebp v0.0.0-20230615040911-5013dbb9d508/go.mod h1:Re7TEhwL+ygnxFg52fC0PWy01ULAIZp2QR0q5WwEOQA=
|
||||||
github.com/josharian/native v0.0.0-20200817173448-b6b71def0850 h1:uhL5Gw7BINiiPAo24A2sxkcDI0Jt/sqp1v5xQCniEFA=
|
github.com/iwind/nftables v0.0.0-20230419014751-9f023a644ad4 h1:RPAH9Sj9l/20zH5zU5/iJGszfwPq6eLjoiC/n/asulA=
|
||||||
github.com/josharian/native v0.0.0-20200817173448-b6b71def0850/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
github.com/iwind/nftables v0.0.0-20230419014751-9f023a644ad4/go.mod h1:7OLL+86wZKfBnAJxNxmdcZ0ebbgdp/A28fcagx9oJqA=
|
||||||
github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw=
|
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
||||||
github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ=
|
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||||
github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok=
|
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
||||||
github.com/jsimonetti/rtnetlink v0.0.0-20201216134343-bde56ed16391/go.mod h1:cR77jAZG3Y3bsb8hF6fHJbFoyFukLFOkQ98S0pQz3xw=
|
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||||
github.com/jsimonetti/rtnetlink v0.0.0-20201220180245-69540ac93943/go.mod h1:z4c53zj6Eex712ROyh8WI0ihysb5j2ROyV42iNogmAs=
|
github.com/josharian/native v1.0.0 h1:Ts/E8zCSEsG17dUqv7joXJFybuMLjQfWE04tsBODTxk=
|
||||||
github.com/jsimonetti/rtnetlink v0.0.0-20210122163228-8d122574c736/go.mod h1:ZXpIyOK59ZnN7J0BV99cZUPmsqDRZ3eq5X+st7u/oSA=
|
github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||||
github.com/jsimonetti/rtnetlink v0.0.0-20210212075122-66c871082f2b/go.mod h1:8w9Rh8m+aHZIG69YPGGem1i5VzoyRC8nw2kA8B+ik5U=
|
|
||||||
github.com/jsimonetti/rtnetlink v0.0.0-20210525051524-4cc836578190/go.mod h1:NmKSdU4VGSiv1bMsdqNALI4RSvvjtz65tTMCnD05qLo=
|
|
||||||
github.com/jsimonetti/rtnetlink v0.0.0-20211022192332-93da33804786 h1:N527AHMa793TP5z5GNAn/VLPzlc0ewzWdeP/25gDfgQ=
|
|
||||||
github.com/jsimonetti/rtnetlink v0.0.0-20211022192332-93da33804786/go.mod h1:v4hqbTdfQngbVSZJVWUhGE/lbTFf9jb+ygmNUDQMuOs=
|
|
||||||
github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e h1:LvL4XsI70QxOGHed6yhQtAU34Kx3Qq2wwBzGFKY8zKk=
|
github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e h1:LvL4XsI70QxOGHed6yhQtAU34Kx3Qq2wwBzGFKY8zKk=
|
||||||
github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e/go.mod h1:kLgvv7o6UM+0QSf0QjAse3wReFDsb9qbZJdfexWlrQw=
|
github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e/go.mod h1:kLgvv7o6UM+0QSf0QjAse3wReFDsb9qbZJdfexWlrQw=
|
||||||
github.com/klauspost/compress v1.15.8 h1:JahtItbkWjf2jzm/T+qgMxkP9EMHsqEUA6vCMGmXvhA=
|
github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI=
|
||||||
github.com/klauspost/compress v1.15.8/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
|
github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
||||||
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
|
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||||
|
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
|
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
||||||
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/mattn/go-sqlite3 v1.14.9 h1:10HX2Td0ocZpYEjhilsuo6WWtUqttj2Kb0KtD86/KYA=
|
github.com/mattn/go-sqlite3 v1.14.9 h1:10HX2Td0ocZpYEjhilsuo6WWtUqttj2Kb0KtD86/KYA=
|
||||||
github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||||
github.com/mdlayher/ethtool v0.0.0-20210210192532-2b88debcdd43/go.mod h1:+t7E0lkKfbBsebllff1xdTmyJt8lH37niI6kwFk9OTo=
|
github.com/mdlayher/netlink v1.7.1 h1:FdUaT/e33HjEXagwELR8R3/KL1Fq5x3G5jgHLp/BTmg=
|
||||||
github.com/mdlayher/ethtool v0.0.0-20211028163843-288d040e9d60 h1:tHdB+hQRHU10CfcK0furo6rSNgZ38JT8uPh70c/pFD8=
|
github.com/mdlayher/netlink v1.7.1/go.mod h1:nKO5CSjE/DJjVhk/TNp6vCE1ktVxEA8VEh8drhZzxsQ=
|
||||||
github.com/mdlayher/ethtool v0.0.0-20211028163843-288d040e9d60/go.mod h1:aYbhishWc4Ai3I2U4Gaa2n3kHWSwzme6EsG/46HRQbE=
|
github.com/mdlayher/socket v0.4.0 h1:280wsy40IC9M9q1uPGcLBwXpcTQDtoGwVt+BNoITxIw=
|
||||||
github.com/mdlayher/genetlink v1.0.0 h1:OoHN1OdyEIkScEmRgxLEe2M9U8ClMytqA5niynLtfj0=
|
github.com/mdlayher/socket v0.4.0/go.mod h1:xxFqz5GRCUN3UEOm9CZqEJsAbe1C8OwSK46NlmWuVoc=
|
||||||
github.com/mdlayher/genetlink v1.0.0/go.mod h1:0rJ0h4itni50A86M2kHcgS85ttZazNt7a8H2a2cw0Gc=
|
|
||||||
github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA=
|
|
||||||
github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M=
|
|
||||||
github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY=
|
|
||||||
github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o=
|
|
||||||
github.com/mdlayher/netlink v1.2.0/go.mod h1:kwVW1io0AZy9A1E2YYgaD4Cj+C+GPkU6klXCMzIJ9p8=
|
|
||||||
github.com/mdlayher/netlink v1.2.1/go.mod h1:bacnNlfhqHqqLo4WsYeXSqfyXkInQ9JneWI68v1KwSU=
|
|
||||||
github.com/mdlayher/netlink v1.2.2-0.20210123213345-5cc92139ae3e/go.mod h1:bacnNlfhqHqqLo4WsYeXSqfyXkInQ9JneWI68v1KwSU=
|
|
||||||
github.com/mdlayher/netlink v1.3.0/go.mod h1:xK/BssKuwcRXHrtN04UBkwQ6dY9VviGGuriDdoPSWys=
|
|
||||||
github.com/mdlayher/netlink v1.4.0/go.mod h1:dRJi5IABcZpBD2A3D0Mv/AiX8I9uDEu5oGkAVrekmf8=
|
|
||||||
github.com/mdlayher/netlink v1.4.1/go.mod h1:e4/KuJ+s8UhfUpO9z00/fDZZmhSrs+oxyqAS9cNgn6Q=
|
|
||||||
github.com/mdlayher/netlink v1.4.2 h1:3sbnJWe/LETovA7yRZIX3f9McVOWV3OySH6iIBxiFfI=
|
|
||||||
github.com/mdlayher/netlink v1.4.2/go.mod h1:13VaingaArGUTUxFLf/iEovKxXji32JAtF858jZYEug=
|
|
||||||
github.com/mdlayher/socket v0.0.0-20210307095302-262dc9984e00/go.mod h1:GAFlyu4/XV68LkQKYzKhIo/WW7j3Zi0YRAz/BOoanUc=
|
|
||||||
github.com/mdlayher/socket v0.0.0-20211007213009-516dcbdf0267/go.mod h1:nFZ1EtZYK8Gi/k6QNu7z7CgO20i/4ExeQswwWuPmG/g=
|
|
||||||
github.com/mdlayher/socket v0.0.0-20211102153432-57e3fa563ecb h1:2dC7L10LmTqlyMVzFJ00qM25lqESg9Z4u3GuEXN5iHY=
|
|
||||||
github.com/mdlayher/socket v0.0.0-20211102153432-57e3fa563ecb/go.mod h1:nFZ1EtZYK8Gi/k6QNu7z7CgO20i/4ExeQswwWuPmG/g=
|
|
||||||
github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg=
|
github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg=
|
||||||
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
|
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
|
||||||
github.com/mssola/user_agent v0.5.3 h1:lBRPML9mdFuIZgI2cmlQ+atbpJdLdeVl2IDodjBR578=
|
github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs=
|
||||||
github.com/mssola/user_agent v0.5.3/go.mod h1:TTPno8LPY3wAIEKRpAtkdMT0f8SE24pLRGPahjCH4uw=
|
github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
|
github.com/mozillazg/go-httpheader v0.2.1 h1:geV7TrjbL8KXSyvghnFm+NyTux/hxwueTSrwhe88TQQ=
|
||||||
|
github.com/mozillazg/go-httpheader v0.2.1/go.mod h1:jJ8xECTlalr6ValeXYdOF8fFUISeBAdw6E61aqQma60=
|
||||||
|
github.com/mssola/useragent v1.0.0 h1:WRlDpXyxHDNfvZaPEut5Biveq86Ze4o4EMffyMxmH5o=
|
||||||
|
github.com/mssola/useragent v1.0.0/go.mod h1:hz9Cqz4RXusgg1EdI4Al0INR62kP7aPSRNHnpU+b85Y=
|
||||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||||
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
|
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/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc=
|
||||||
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/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
|
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
|
||||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/qiniu/dyn v1.3.0/go.mod h1:E8oERcm8TtwJiZvkQPbcAh0RL8jO1G0VXJMW3FAWdkk=
|
||||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
github.com/qiniu/go-sdk/v7 v7.16.0 h1:Jt4YOMLuaDfgb/KdVg0O1fYLpv5MDkYe/zV+Ri7gWRs=
|
||||||
|
github.com/qiniu/go-sdk/v7 v7.16.0/go.mod h1:nqoYCNo53ZlGA521RvRethvxUDvXKt4gtYXOwye868w=
|
||||||
|
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/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
|
||||||
|
github.com/quic-go/qtls-go1-19 v0.3.2 h1:tFxjCFcTQzK+oMxG6Zcvp4Dq8dx4yD3dDiIiyc86Z5U=
|
||||||
|
github.com/quic-go/qtls-go1-19 v0.3.2/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI=
|
||||||
|
github.com/quic-go/qtls-go1-20 v0.2.2 h1:WLOPx6OY/hxtTxKV1Zrq20FtXtDEkeY00CGQm8GEa3E=
|
||||||
|
github.com/quic-go/qtls-go1-20 v0.2.2/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
|
||||||
|
github.com/quic-go/quic-go v0.36.1 h1:WsG73nVtnDy1TiACxFxhQ3TqaW+DipmqzLEtNlAwZyY=
|
||||||
|
github.com/quic-go/quic-go v0.36.1/go.mod h1:zPetvwDlILVxt15n3hr3Gf/I3mDf7LpLKPhR4Ez0AZQ=
|
||||||
|
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/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||||
github.com/shirou/gopsutil/v3 v3.22.2 h1:wCrArWFkHYIdDxx/FSfF5RB4dpJYW6t7rcp3+zL8uks=
|
github.com/shirou/gopsutil/v3 v3.22.2 h1:wCrArWFkHYIdDxx/FSfF5RB4dpJYW6t7rcp3+zL8uks=
|
||||||
github.com/shirou/gopsutil/v3 v3.22.2/go.mod h1:WapW1AOOPlHyXr+yOyw3uYx36enocrtSoSBy0L5vUHY=
|
github.com/shirou/gopsutil/v3 v3.22.2/go.mod h1:WapW1AOOPlHyXr+yOyw3uYx36enocrtSoSBy0L5vUHY=
|
||||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ=
|
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ=
|
||||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/tdewolff/minify/v2 v2.12.7 h1:pBzz2tAfz5VghOXiQIsSta6srhmTeinQPjRDHWoumCA=
|
||||||
|
github.com/tdewolff/minify/v2 v2.12.7/go.mod h1:ZRKTheiOGyLSK8hOZWWv+YoJAECzDivNgAlVYDHp/Ws=
|
||||||
|
github.com/tdewolff/parse/v2 v2.6.6 h1:Yld+0CrKUJaCV78DL1G2nk3C9lKrxyRTux5aaK/AkDo=
|
||||||
|
github.com/tdewolff/parse/v2 v2.6.6/go.mod h1:woz0cgbLwFdtbjJu8PIKxhW05KplTFQkOdX78o+Jgrs=
|
||||||
|
github.com/tdewolff/test v1.0.7/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
|
||||||
|
github.com/tdewolff/test v1.0.9 h1:SswqJCmeN4B+9gEAi/5uqT0qpi1y2/2O47V/1hhGZT0=
|
||||||
|
github.com/tdewolff/test v1.0.9/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
|
||||||
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.194/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y=
|
||||||
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/kms v1.0.194/go.mod h1:yrBKWhChnDqNz1xuXdSbWXG56XawEq0G5j1lg4VwBD4=
|
||||||
|
github.com/tencentyun/cos-go-sdk-v5 v0.7.41 h1:iU0Li/Np78H4SBna0ECQoF3mpgi6ImLXU+doGzPFXGc=
|
||||||
|
github.com/tencentyun/cos-go-sdk-v5 v0.7.41/go.mod h1:4dCEtLHGh8QPxHEkgq+nFaky7yZxQuYwgSJM87icDaw=
|
||||||
github.com/tklauser/go-sysconf v0.3.9 h1:JeUVdAOWhhxVcU6Eqr/ATFHgXk/mmiItdKeJPev3vTo=
|
github.com/tklauser/go-sysconf v0.3.9 h1:JeUVdAOWhhxVcU6Eqr/ATFHgXk/mmiItdKeJPev3vTo=
|
||||||
github.com/tklauser/go-sysconf v0.3.9/go.mod h1:11DU/5sG7UexIrp/O6g35hrWzu0JxlwQ3LSFUzyeuhs=
|
github.com/tklauser/go-sysconf v0.3.9/go.mod h1:11DU/5sG7UexIrp/O6g35hrWzu0JxlwQ3LSFUzyeuhs=
|
||||||
github.com/tklauser/numcpus v0.3.0 h1:ILuRUQBtssgnxw0XXIjKUC56fgnOrFoQQ/4+DeU2biQ=
|
github.com/tklauser/numcpus v0.3.0 h1:ILuRUQBtssgnxw0XXIjKUC56fgnOrFoQQ/4+DeU2biQ=
|
||||||
github.com/tklauser/numcpus v0.3.0/go.mod h1:yFGUr7TUHQRAhyqBcEg0Ge34zDBAsIvJJcyE6boqnA8=
|
github.com/tklauser/numcpus v0.3.0/go.mod h1:yFGUr7TUHQRAhyqBcEg0Ge34zDBAsIvJJcyE6boqnA8=
|
||||||
github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc h1:R83G5ikgLMxrBvLh22JhdfI8K6YXEPHx5P03Uu3DRs4=
|
github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc h1:R83G5ikgLMxrBvLh22JhdfI8K6YXEPHx5P03Uu3DRs4=
|
||||||
github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI=
|
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
|
||||||
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
|
||||||
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
|
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
|
||||||
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/image v0.0.0-20220722155232-062f8c9fd539 h1:/eM0PCrQI2xd471rI+snWuu251/+/jpBpZqir2mPdnU=
|
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
|
||||||
golang.org/x/image v0.0.0-20220722155232-062f8c9fd539/go.mod h1:doUCurBvlfPMKfmIpRIywoHmhN3VyhnoFDbvIEWF4hY=
|
golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA=
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
|
||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME=
|
||||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
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/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
|
||||||
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
|
||||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
|
||||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
|
||||||
golang.org/x/net v0.0.0-20201216054612-986b41b23924/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
|
||||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
|
||||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||||
golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50=
|
||||||
golang.org/x/net v0.0.0-20211020060615-d418f374d309/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
|
||||||
golang.org/x/net v0.0.0-20211201190559-0a0e4e1bb54c/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
|
||||||
golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
|
||||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc=
|
|
||||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/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-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
|
||||||
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/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
||||||
|
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20201118182958-a01c418693c7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201218084310-7d0127a74742/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210110051926-789bb1bd4061/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210123111255-9b0068b26619/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210216163648-f7da38b97c65/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU=
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.5.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.10.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.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
|
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
|
golang.org/x/text v0.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.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
|
||||||
|
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
|
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/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=
|
||||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
|
||||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
|
||||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
golang.org/x/tools v0.1.8 h1:P1HhGGuLW4aAclzjtmJdf0mJOjVUZUzOTqkAkWL+l6w=
|
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
golang.org/x/tools v0.11.0 h1:EMCa6U9S2LtZXLAMoWiR/R8dAQFRqbAitmbJ2UKhoi8=
|
||||||
|
golang.org/x/tools v0.11.0/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8=
|
||||||
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=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc h1:XSJ8Vk1SWuNr8S18z1NZSziL0CPIXLCCMDOEFtHBOFc=
|
||||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA=
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag=
|
||||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8=
|
||||||
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
|
||||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
|
||||||
google.golang.org/genproto v0.0.0-20220317150908-0efb43f6373e h1:fNKDNuUyC4WH+inqDMpfXDdfvwfYILbsX+oskGZ8hxg=
|
|
||||||
google.golang.org/genproto v0.0.0-20220317150908-0efb43f6373e/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E=
|
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
|
||||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
|
||||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
|
||||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
|
||||||
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
|
||||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
|
||||||
google.golang.org/grpc v1.45.0 h1:NEpgUqV3Z+ZjkqMsxMg11IaDrXY4RY6CQukSGK0uI1M=
|
|
||||||
google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
|
||||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
|
||||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
|
||||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
|
||||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
|
||||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
|
||||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
|
||||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
|
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
||||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/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=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
|
||||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
|
||||||
honnef.co/go/tools v0.2.1/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY=
|
|
||||||
honnef.co/go/tools v0.2.2 h1:MNh1AVMyVX23VUHE2O27jm6lNj3vjO5DexS4A1xvnzk=
|
|
||||||
honnef.co/go/tools v0.2.2/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY=
|
|
||||||
rogchap.com/v8go v0.7.0 h1:kgjbiO4zE5itA962ze6Hqmbs4HgZbGzmueCXsZtremg=
|
|
||||||
rogchap.com/v8go v0.7.0/go.mod h1:MxgP3pL2MW4dpme/72QRs8sgNMmM0pRc8DPhcuLWPAs=
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package apps
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
||||||
|
executils "github.com/TeaOSLab/EdgeNode/internal/utils/exec"
|
||||||
"github.com/iwind/TeaGo/logs"
|
"github.com/iwind/TeaGo/logs"
|
||||||
"github.com/iwind/TeaGo/maps"
|
"github.com/iwind/TeaGo/maps"
|
||||||
"github.com/iwind/TeaGo/types"
|
"github.com/iwind/TeaGo/types"
|
||||||
@@ -215,7 +216,7 @@ func (this *AppCmd) runStop() {
|
|||||||
|
|
||||||
// 从systemd中停止
|
// 从systemd中停止
|
||||||
if runtime.GOOS == "linux" {
|
if runtime.GOOS == "linux" {
|
||||||
systemctl, _ := exec.LookPath("systemctl")
|
systemctl, _ := executils.LookPath("systemctl")
|
||||||
if len(systemctl) > 0 {
|
if len(systemctl) > 0 {
|
||||||
go func() {
|
go func() {
|
||||||
// 有可能会长时间执行,这里不阻塞进程
|
// 有可能会长时间执行,这里不阻塞进程
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeNode/internal/utils/fnv"
|
"github.com/TeaOSLab/EdgeNode/internal/utils/fnv"
|
||||||
"github.com/iwind/TeaGo/types"
|
"github.com/iwind/TeaGo/types"
|
||||||
"os"
|
"os"
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -21,7 +20,6 @@ const CountFileDB = 20
|
|||||||
type FileList struct {
|
type FileList struct {
|
||||||
dir string
|
dir string
|
||||||
dbList [CountFileDB]*FileListDB
|
dbList [CountFileDB]*FileListDB
|
||||||
total int64
|
|
||||||
|
|
||||||
onAdd func(item *Item)
|
onAdd func(item *Item)
|
||||||
onRemove func(item *Item)
|
onRemove func(item *Item)
|
||||||
@@ -76,12 +74,6 @@ func (this *FileList) Init() error {
|
|||||||
this.dbList[i] = db
|
this.dbList[i] = db
|
||||||
}
|
}
|
||||||
|
|
||||||
// 读取总数量
|
|
||||||
this.total = 0
|
|
||||||
for _, db := range this.dbList {
|
|
||||||
this.total += db.total
|
|
||||||
}
|
|
||||||
|
|
||||||
// 升级老版本数据库
|
// 升级老版本数据库
|
||||||
goman.New(func() {
|
goman.New(func() {
|
||||||
this.upgradeOldDB()
|
this.upgradeOldDB()
|
||||||
@@ -102,13 +94,11 @@ func (this *FileList) Add(hash string, item *Item) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
err := db.AddAsync(hash, item)
|
err := db.AddSync(hash, item)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
atomic.AddInt64(&this.total, 1)
|
|
||||||
|
|
||||||
// 这里不增加点击量,以减少对数据库的操作次数
|
// 这里不增加点击量,以减少对数据库的操作次数
|
||||||
|
|
||||||
this.memoryCache.Write(hash, 1, item.ExpiredAt)
|
this.memoryCache.Write(hash, 1, item.ExpiredAt)
|
||||||
@@ -296,8 +286,6 @@ func (this *FileList) CleanAll() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
atomic.StoreInt64(&this.total, 0)
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -332,7 +320,15 @@ func (this *FileList) Stat(check func(hash string) bool) (*Stat, error) {
|
|||||||
// Count 总数量
|
// Count 总数量
|
||||||
// 常用的方法,所以避免直接查询数据库
|
// 常用的方法,所以避免直接查询数据库
|
||||||
func (this *FileList) Count() (int64, error) {
|
func (this *FileList) Count() (int64, error) {
|
||||||
return atomic.LoadInt64(&this.total), nil
|
var total int64
|
||||||
|
for _, db := range this.dbList {
|
||||||
|
count, err := db.Total()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
total += count
|
||||||
|
}
|
||||||
|
return total, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// IncreaseHit 增加点击量
|
// IncreaseHit 增加点击量
|
||||||
@@ -392,37 +388,19 @@ func (this *FileList) remove(hash string) (notFound bool, err error) {
|
|||||||
// 从缓存中删除
|
// 从缓存中删除
|
||||||
this.memoryCache.Delete(hash)
|
this.memoryCache.Delete(hash)
|
||||||
|
|
||||||
var row = db.selectByHashStmt.QueryRow(hash)
|
err = db.DeleteSync(hash)
|
||||||
if row.Err() != nil {
|
|
||||||
if row.Err() == sql.ErrNoRows {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
return false, row.Err()
|
|
||||||
}
|
|
||||||
|
|
||||||
var item = &Item{Type: ItemTypeFile}
|
|
||||||
err = row.Scan(&item.Key, &item.HeaderSize, &item.BodySize, &item.MetaSize, &item.ExpiredAt)
|
|
||||||
if err != nil {
|
|
||||||
if err == sql.ErrNoRows {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = db.DeleteAsync(hash)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, db.WrapError(err)
|
return false, db.WrapError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
atomic.AddInt64(&this.total, -1)
|
|
||||||
|
|
||||||
err = db.DeleteHitAsync(hash)
|
err = db.DeleteHitAsync(hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, db.WrapError(err)
|
return false, db.WrapError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if this.onRemove != nil {
|
if this.onRemove != nil {
|
||||||
this.onRemove(item)
|
// when remove file item, no any extra information needed
|
||||||
|
this.onRemove(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
return false, nil
|
return false, nil
|
||||||
|
|||||||
@@ -35,8 +35,6 @@ type FileListDB struct {
|
|||||||
itemsTableName string
|
itemsTableName string
|
||||||
hitsTableName string
|
hitsTableName string
|
||||||
|
|
||||||
total int64
|
|
||||||
|
|
||||||
isClosed bool
|
isClosed bool
|
||||||
isReady bool
|
isReady bool
|
||||||
|
|
||||||
@@ -110,7 +108,7 @@ func (this *FileListDB) Open(dbPath string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.writeBatch = dbs.NewBatch(writeDB.RawDB(), 4)
|
this.writeBatch = dbs.NewBatch(writeDB, 4)
|
||||||
this.writeBatch.OnFail(func(err error) {
|
this.writeBatch.OnFail(func(err error) {
|
||||||
remotelogs.Warn("LIST_FILE_DB", "run batch failed: "+err.Error()+" ("+filepath.Base(this.dbPath)+")")
|
remotelogs.Warn("LIST_FILE_DB", "run batch failed: "+err.Error()+" ("+filepath.Base(this.dbPath)+")")
|
||||||
})
|
})
|
||||||
@@ -151,18 +149,6 @@ func (this *FileListDB) Init() error {
|
|||||||
return errors.New("init tables failed: " + err.Error())
|
return errors.New("init tables failed: " + err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// 读取总数量
|
|
||||||
row := this.readDB.QueryRow(`SELECT COUNT(*) FROM "` + this.itemsTableName + `"`)
|
|
||||||
if row.Err() != nil {
|
|
||||||
return row.Err()
|
|
||||||
}
|
|
||||||
var total int64
|
|
||||||
err = row.Scan(&total)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
this.total = total
|
|
||||||
|
|
||||||
// 常用语句
|
// 常用语句
|
||||||
this.existsByHashStmt, err = this.readDB.Prepare(`SELECT "expiredAt" FROM "` + this.itemsTableName + `" INDEXED BY "hash" WHERE "hash"=? AND expiredAt>? LIMIT 1`)
|
this.existsByHashStmt, err = this.readDB.Prepare(`SELECT "expiredAt" FROM "` + this.itemsTableName + `" INDEXED BY "hash" WHERE "hash"=? AND expiredAt>? LIMIT 1`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -250,20 +236,15 @@ func (this *FileListDB) IsReady() bool {
|
|||||||
return this.isReady
|
return this.isReady
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *FileListDB) Total() int64 {
|
func (this *FileListDB) Total() (int64, error) {
|
||||||
return this.total
|
// 读取总数量
|
||||||
}
|
var row = this.readDB.QueryRow(`SELECT COUNT(*) FROM "` + this.itemsTableName + `"`)
|
||||||
|
if row.Err() != nil {
|
||||||
func (this *FileListDB) AddAsync(hash string, item *Item) error {
|
return 0, row.Err()
|
||||||
this.hashMap.Add(hash)
|
|
||||||
|
|
||||||
if item.StaleAt == 0 {
|
|
||||||
item.StaleAt = item.ExpiredAt
|
|
||||||
}
|
}
|
||||||
|
var total int64
|
||||||
this.writeBatch.Add(this.insertSQL, hash, item.Key, item.HeaderSize, item.BodySize, item.MetaSize, item.ExpiredAt, item.StaleAt, item.Host, item.ServerId, fasttime.Now().Unix(), timeutil.Format("YW"))
|
err := row.Scan(&total)
|
||||||
return nil
|
return total, err
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *FileListDB) AddSync(hash string, item *Item) error {
|
func (this *FileListDB) AddSync(hash string, item *Item) error {
|
||||||
@@ -281,13 +262,6 @@ func (this *FileListDB) AddSync(hash string, item *Item) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *FileListDB) DeleteAsync(hash string) error {
|
|
||||||
this.hashMap.Delete(hash)
|
|
||||||
|
|
||||||
this.writeBatch.Add(this.deleteByHashSQL, hash)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *FileListDB) DeleteSync(hash string) error {
|
func (this *FileListDB) DeleteSync(hash string) error {
|
||||||
this.hashMap.Delete(hash)
|
this.hashMap.Delete(hash)
|
||||||
|
|
||||||
@@ -504,6 +478,10 @@ func (this *FileListDB) CleanAll() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *FileListDB) Close() error {
|
func (this *FileListDB) Close() error {
|
||||||
|
if this.isClosed {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
this.isClosed = true
|
this.isClosed = true
|
||||||
this.isReady = false
|
this.isReady = false
|
||||||
|
|
||||||
@@ -535,10 +513,6 @@ func (this *FileListDB) Close() error {
|
|||||||
_ = this.listOlderItemsStmt.Close()
|
_ = this.listOlderItemsStmt.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
if this.writeBatch != nil {
|
|
||||||
this.writeBatch.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
var errStrings []string
|
var errStrings []string
|
||||||
|
|
||||||
if this.readDB != nil {
|
if this.readDB != nil {
|
||||||
|
|||||||
@@ -12,6 +12,11 @@ import (
|
|||||||
|
|
||||||
func TestFileListDB_ListLFUItems(t *testing.T) {
|
func TestFileListDB_ListLFUItems(t *testing.T) {
|
||||||
var db = caches.NewFileListDB()
|
var db = caches.NewFileListDB()
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
_ = db.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
err := db.Open(Tea.Root + "/data/cache-db-large.db")
|
err := db.Open(Tea.Root + "/data/cache-db-large.db")
|
||||||
//err := db.Open(Tea.Root + "/data/cache-index/p1/db-0.db")
|
//err := db.Open(Tea.Root + "/data/cache-index/p1/db-0.db")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -22,10 +27,6 @@ func TestFileListDB_ListLFUItems(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() {
|
|
||||||
_ = db.Close()
|
|
||||||
}()
|
|
||||||
|
|
||||||
hashList, err := db.ListLFUItems(100)
|
hashList, err := db.ListLFUItems(100)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -35,25 +36,38 @@ func TestFileListDB_ListLFUItems(t *testing.T) {
|
|||||||
|
|
||||||
func TestFileListDB_IncreaseHitAsync(t *testing.T) {
|
func TestFileListDB_IncreaseHitAsync(t *testing.T) {
|
||||||
var db = caches.NewFileListDB()
|
var db = caches.NewFileListDB()
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
_ = db.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
err := db.Open(Tea.Root + "/data/cache-db-large.db")
|
err := db.Open(Tea.Root + "/data/cache-db-large.db")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = db.Init()
|
err = db.Init()
|
||||||
err = db.IncreaseHitAsync("4598e5231ba47d6ec7aa9ea640ff2eaf")
|
err = db.IncreaseHitAsync("4598e5231ba47d6ec7aa9ea640ff2eaf")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// wait transaction
|
// wait transaction
|
||||||
time.Sleep(1 * time.Second)
|
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()
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
_ = db.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
err := db.Open(Tea.Root + "/data/cache-db-large.db")
|
err := db.Open(Tea.Root + "/data/cache-db-large.db")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = db.Init()
|
err = db.Init()
|
||||||
|
|
||||||
err = db.CleanMatchKey("https://*.goedge.cn/large-text")
|
err = db.CleanMatchKey("https://*.goedge.cn/large-text")
|
||||||
@@ -69,10 +83,16 @@ func TestFileListDB_CleanMatchKey(t *testing.T) {
|
|||||||
|
|
||||||
func TestFileListDB_CleanMatchPrefix(t *testing.T) {
|
func TestFileListDB_CleanMatchPrefix(t *testing.T) {
|
||||||
var db = caches.NewFileListDB()
|
var db = caches.NewFileListDB()
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
_ = db.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
err := db.Open(Tea.Root + "/data/cache-db-large.db")
|
err := db.Open(Tea.Root + "/data/cache-db-large.db")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = db.Init()
|
err = db.Init()
|
||||||
|
|
||||||
err = db.CleanMatchPrefix("https://*.goedge.cn/large-text")
|
err = db.CleanMatchPrefix("https://*.goedge.cn/large-text")
|
||||||
|
|||||||
@@ -59,15 +59,16 @@ func TestFileListHashMap_BigInt(t *testing.T) {
|
|||||||
|
|
||||||
func TestFileListHashMap_Load(t *testing.T) {
|
func TestFileListHashMap_Load(t *testing.T) {
|
||||||
var list = caches.NewFileList(Tea.Root + "/data/cache-index/p1").(*caches.FileList)
|
var list = caches.NewFileList(Tea.Root + "/data/cache-index/p1").(*caches.FileList)
|
||||||
err := list.Init()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
_ = list.Close()
|
_ = list.Close()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
err := list.Init()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
var m = caches.NewFileListHashMap()
|
var m = caches.NewFileListHashMap()
|
||||||
var before = time.Now()
|
var before = time.Now()
|
||||||
var db = list.GetDB("abc")
|
var db = list.GetDB("abc")
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ package caches_test
|
|||||||
import (
|
import (
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/caches"
|
"github.com/TeaOSLab/EdgeNode/internal/caches"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/utils/testutils"
|
||||||
"github.com/iwind/TeaGo/Tea"
|
"github.com/iwind/TeaGo/Tea"
|
||||||
"github.com/iwind/TeaGo/rands"
|
"github.com/iwind/TeaGo/rands"
|
||||||
"github.com/iwind/TeaGo/types"
|
"github.com/iwind/TeaGo/types"
|
||||||
@@ -17,6 +18,11 @@ import (
|
|||||||
|
|
||||||
func TestFileList_Init(t *testing.T) {
|
func TestFileList_Init(t *testing.T) {
|
||||||
var list = caches.NewFileList(Tea.Root + "/data/cache-index/p1")
|
var list = caches.NewFileList(Tea.Root + "/data/cache-index/p1")
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
_ = list.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
err := list.Init()
|
err := list.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -29,6 +35,11 @@ func TestFileList_Init(t *testing.T) {
|
|||||||
|
|
||||||
func TestFileList_Add(t *testing.T) {
|
func TestFileList_Add(t *testing.T) {
|
||||||
var list = caches.NewFileList(Tea.Root + "/data/cache-index/p1").(*caches.FileList)
|
var list = caches.NewFileList(Tea.Root + "/data/cache-index/p1").(*caches.FileList)
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
_ = list.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
err := list.Init()
|
err := list.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -59,16 +70,21 @@ func TestFileList_Add(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFileList_Add_Many(t *testing.T) {
|
func TestFileList_Add_Many(t *testing.T) {
|
||||||
var list = caches.NewFileList(Tea.Root + "/data/cache-index/p1")
|
if !testutils.IsSingleTesting() {
|
||||||
err := list.Init()
|
return
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var list = caches.NewFileList(Tea.Root + "/data/cache-index/p1")
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
_ = list.Close()
|
_ = list.Close()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
err := list.Init()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
var before = time.Now()
|
var before = time.Now()
|
||||||
for i := 0; i < 10_000_000; i++ {
|
for i := 0; i < 10_000_000; i++ {
|
||||||
u := "https://edge.teaos.cn/123456" + strconv.Itoa(i)
|
u := "https://edge.teaos.cn/123456" + strconv.Itoa(i)
|
||||||
@@ -92,15 +108,15 @@ func TestFileList_Add_Many(t *testing.T) {
|
|||||||
|
|
||||||
func TestFileList_Exist(t *testing.T) {
|
func TestFileList_Exist(t *testing.T) {
|
||||||
var list = caches.NewFileList(Tea.Root + "/data/cache-index/p1").(*caches.FileList)
|
var list = caches.NewFileList(Tea.Root + "/data/cache-index/p1").(*caches.FileList)
|
||||||
|
defer func() {
|
||||||
|
_ = list.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
err := list.Init()
|
err := list.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() {
|
|
||||||
_ = list.Close()
|
|
||||||
}()
|
|
||||||
|
|
||||||
total, _ := list.Count()
|
total, _ := list.Count()
|
||||||
t.Log("total:", total)
|
t.Log("total:", total)
|
||||||
|
|
||||||
@@ -130,7 +146,7 @@ func TestFileList_Exist_Many_DB(t *testing.T) {
|
|||||||
// 测试在多个数据库下的性能
|
// 测试在多个数据库下的性能
|
||||||
var listSlice = []caches.ListInterface{}
|
var listSlice = []caches.ListInterface{}
|
||||||
for i := 1; i <= 10; i++ {
|
for i := 1; i <= 10; i++ {
|
||||||
list := caches.NewFileList(Tea.Root + "/data/data" + strconv.Itoa(i))
|
var list = caches.NewFileList(Tea.Root + "/data/data" + strconv.Itoa(i))
|
||||||
err := list.Init()
|
err := list.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -138,6 +154,12 @@ func TestFileList_Exist_Many_DB(t *testing.T) {
|
|||||||
listSlice = append(listSlice, list)
|
listSlice = append(listSlice, list)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
for _, list := range listSlice {
|
||||||
|
_ = list.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
var wg = sync.WaitGroup{}
|
var wg = sync.WaitGroup{}
|
||||||
var threads = 8
|
var threads = 8
|
||||||
wg.Add(threads)
|
wg.Add(threads)
|
||||||
@@ -181,15 +203,16 @@ func TestFileList_Exist_Many_DB(t *testing.T) {
|
|||||||
|
|
||||||
func TestFileList_CleanPrefix(t *testing.T) {
|
func TestFileList_CleanPrefix(t *testing.T) {
|
||||||
var list = caches.NewFileList(Tea.Root + "/data/cache-index/p1")
|
var list = caches.NewFileList(Tea.Root + "/data/cache-index/p1")
|
||||||
err := list.Init()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
_ = list.Close()
|
_ = list.Close()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
err := list.Init()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
before := time.Now()
|
before := time.Now()
|
||||||
err = list.CleanPrefix("123")
|
err = list.CleanPrefix("123")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -200,15 +223,15 @@ func TestFileList_CleanPrefix(t *testing.T) {
|
|||||||
|
|
||||||
func TestFileList_Remove(t *testing.T) {
|
func TestFileList_Remove(t *testing.T) {
|
||||||
var list = caches.NewFileList(Tea.Root + "/data/cache-index/p1").(*caches.FileList)
|
var list = caches.NewFileList(Tea.Root + "/data/cache-index/p1").(*caches.FileList)
|
||||||
|
defer func() {
|
||||||
|
_ = list.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
err := list.Init()
|
err := list.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() {
|
|
||||||
_ = list.Close()
|
|
||||||
}()
|
|
||||||
|
|
||||||
list.OnRemove(func(item *caches.Item) {
|
list.OnRemove(func(item *caches.Item) {
|
||||||
t.Logf("remove %#v", item)
|
t.Logf("remove %#v", item)
|
||||||
})
|
})
|
||||||
@@ -224,13 +247,15 @@ func TestFileList_Remove(t *testing.T) {
|
|||||||
|
|
||||||
func TestFileList_Purge(t *testing.T) {
|
func TestFileList_Purge(t *testing.T) {
|
||||||
var list = caches.NewFileList(Tea.Root + "/data/cache-index/p1")
|
var list = caches.NewFileList(Tea.Root + "/data/cache-index/p1")
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
_ = list.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
err := list.Init()
|
err := list.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer func() {
|
|
||||||
_ = list.Close()
|
|
||||||
}()
|
|
||||||
|
|
||||||
var count = 0
|
var count = 0
|
||||||
_, err = list.Purge(caches.CountFileDB*2, func(hash string) error {
|
_, err = list.Purge(caches.CountFileDB*2, func(hash string) error {
|
||||||
@@ -246,13 +271,15 @@ func TestFileList_Purge(t *testing.T) {
|
|||||||
|
|
||||||
func TestFileList_PurgeLFU(t *testing.T) {
|
func TestFileList_PurgeLFU(t *testing.T) {
|
||||||
var list = caches.NewFileList(Tea.Root + "/data/cache-index/p1")
|
var list = caches.NewFileList(Tea.Root + "/data/cache-index/p1")
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
_ = list.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
err := list.Init()
|
err := list.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer func() {
|
|
||||||
_ = list.Close()
|
|
||||||
}()
|
|
||||||
|
|
||||||
err = list.IncreaseHit(stringutil.Md5("123456"))
|
err = list.IncreaseHit(stringutil.Md5("123456"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -273,15 +300,16 @@ func TestFileList_PurgeLFU(t *testing.T) {
|
|||||||
|
|
||||||
func TestFileList_Stat(t *testing.T) {
|
func TestFileList_Stat(t *testing.T) {
|
||||||
var list = caches.NewFileList(Tea.Root + "/data/cache-index/p1")
|
var list = caches.NewFileList(Tea.Root + "/data/cache-index/p1")
|
||||||
err := list.Init()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
_ = list.Close()
|
_ = list.Close()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
err := list.Init()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
stat, err := list.Stat(nil)
|
stat, err := list.Stat(nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -291,6 +319,11 @@ func TestFileList_Stat(t *testing.T) {
|
|||||||
|
|
||||||
func TestFileList_Count(t *testing.T) {
|
func TestFileList_Count(t *testing.T) {
|
||||||
var list = caches.NewFileList(Tea.Root + "/data")
|
var list = caches.NewFileList(Tea.Root + "/data")
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
_ = list.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
err := list.Init()
|
err := list.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -305,7 +338,12 @@ func TestFileList_Count(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFileList_CleanAll(t *testing.T) {
|
func TestFileList_CleanAll(t *testing.T) {
|
||||||
list := caches.NewFileList(Tea.Root + "/data")
|
var list = caches.NewFileList(Tea.Root + "/data")
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
_ = list.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
err := list.Init()
|
err := list.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -320,6 +358,11 @@ func TestFileList_CleanAll(t *testing.T) {
|
|||||||
|
|
||||||
func TestFileList_IncreaseHit(t *testing.T) {
|
func TestFileList_IncreaseHit(t *testing.T) {
|
||||||
var list = caches.NewFileList(Tea.Root + "/data/cache-index/p1")
|
var list = caches.NewFileList(Tea.Root + "/data/cache-index/p1")
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
_ = list.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
err := list.Init()
|
err := list.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -333,7 +376,13 @@ func TestFileList_IncreaseHit(t *testing.T) {
|
|||||||
defer func() {
|
defer func() {
|
||||||
t.Log(time.Since(before).Seconds()*1000, "ms")
|
t.Log(time.Since(before).Seconds()*1000, "ms")
|
||||||
}()
|
}()
|
||||||
for i := 0; i < 1000_000; i++ {
|
|
||||||
|
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)))
|
err = list.IncreaseHit(stringutil.Md5("abc" + types.String(i)))
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -344,6 +393,11 @@ func TestFileList_IncreaseHit(t *testing.T) {
|
|||||||
|
|
||||||
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)
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
_ = list.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
err := list.Init()
|
err := list.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -363,6 +417,11 @@ func TestFileList_UpgradeV3(t *testing.T) {
|
|||||||
|
|
||||||
func BenchmarkFileList_Exist(b *testing.B) {
|
func BenchmarkFileList_Exist(b *testing.B) {
|
||||||
var list = caches.NewFileList(Tea.Root + "/data/cache-index/p1")
|
var list = caches.NewFileList(Tea.Root + "/data/cache-index/p1")
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
_ = list.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
err := list.Init()
|
err := list.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package caches
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/utils/testutils"
|
||||||
"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"
|
||||||
@@ -107,7 +108,9 @@ func TestMemoryList_Purge_Large_List(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
time.Sleep(1 * time.Hour)
|
if testutils.IsSingleTesting() {
|
||||||
|
time.Sleep(1 * time.Hour)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMemoryList_Stat(t *testing.T) {
|
func TestMemoryList_Stat(t *testing.T) {
|
||||||
@@ -255,9 +258,11 @@ func TestMemoryList_GC(t *testing.T) {
|
|||||||
//runtime.GC()
|
//runtime.GC()
|
||||||
t.Log("gc cost:", time.Since(before).Seconds()*1000, "ms")
|
t.Log("gc cost:", time.Since(before).Seconds()*1000, "ms")
|
||||||
|
|
||||||
timeout := time.NewTimer(2 * time.Minute)
|
if testutils.IsSingleTesting() {
|
||||||
<-timeout.C
|
timeout := time.NewTimer(2 * time.Minute)
|
||||||
t.Log("2 minutes passed")
|
<-timeout.C
|
||||||
|
t.Log("2 minutes passed")
|
||||||
|
|
||||||
time.Sleep(30 * time.Minute)
|
time.Sleep(30 * time.Minute)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ func init() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
events.On(events.EventQuit, func() {
|
events.OnClose(func() {
|
||||||
remotelogs.Println("CACHE", "quiting cache manager")
|
remotelogs.Println("CACHE", "quiting cache manager")
|
||||||
SharedManager.UpdatePolicies([]*serverconfigs.HTTPCachePolicy{})
|
SharedManager.UpdatePolicies([]*serverconfigs.HTTPCachePolicy{})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -52,9 +52,8 @@ func TestManager_UpdatePolicies(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Id: 2,
|
Id: 2,
|
||||||
Type: serverconfigs.CachePolicyStorageFile,
|
Type: serverconfigs.CachePolicyStorageFile,
|
||||||
MaxKeys: 1,
|
|
||||||
Options: map[string]interface{}{
|
Options: map[string]interface{}{
|
||||||
"dir": Tea.Root + "/caches",
|
"dir": Tea.Root + "/caches",
|
||||||
},
|
},
|
||||||
@@ -95,9 +94,9 @@ func TestManager_ChangePolicy_Memory(t *testing.T) {
|
|||||||
func TestManager_ChangePolicy_File(t *testing.T) {
|
func TestManager_ChangePolicy_File(t *testing.T) {
|
||||||
var policies = []*serverconfigs.HTTPCachePolicy{
|
var policies = []*serverconfigs.HTTPCachePolicy{
|
||||||
{
|
{
|
||||||
Id: 1,
|
Id: 1,
|
||||||
Type: serverconfigs.CachePolicyStorageFile,
|
Type: serverconfigs.CachePolicyStorageFile,
|
||||||
Options: map[string]interface{}{
|
Options: map[string]interface{}{
|
||||||
"dir": Tea.Root + "/data/cache-index/p1",
|
"dir": Tea.Root + "/data/cache-index/p1",
|
||||||
},
|
},
|
||||||
Capacity: &shared.SizeCapacity{Count: 1, Unit: shared.SizeCapacityUnitGB},
|
Capacity: &shared.SizeCapacity{Count: 1, Unit: shared.SizeCapacityUnitGB},
|
||||||
@@ -106,9 +105,9 @@ func TestManager_ChangePolicy_File(t *testing.T) {
|
|||||||
SharedManager.UpdatePolicies(policies)
|
SharedManager.UpdatePolicies(policies)
|
||||||
SharedManager.UpdatePolicies([]*serverconfigs.HTTPCachePolicy{
|
SharedManager.UpdatePolicies([]*serverconfigs.HTTPCachePolicy{
|
||||||
{
|
{
|
||||||
Id: 1,
|
Id: 1,
|
||||||
Type: serverconfigs.CachePolicyStorageFile,
|
Type: serverconfigs.CachePolicyStorageFile,
|
||||||
Options: map[string]interface{}{
|
Options: map[string]interface{}{
|
||||||
"dir": Tea.Root + "/data/cache-index/p1",
|
"dir": Tea.Root + "/data/cache-index/p1",
|
||||||
},
|
},
|
||||||
Capacity: &shared.SizeCapacity{Count: 2, Unit: shared.SizeCapacityUnitGB},
|
Capacity: &shared.SizeCapacity{Count: 2, Unit: shared.SizeCapacityUnitGB},
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ package caches_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/caches"
|
"github.com/TeaOSLab/EdgeNode/internal/caches"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/utils/testutils"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -23,7 +24,9 @@ func TestNewOpenFileCache_Close(t *testing.T) {
|
|||||||
cache.Get("d.txt")
|
cache.Get("d.txt")
|
||||||
cache.Close("a.txt")
|
cache.Close("a.txt")
|
||||||
|
|
||||||
time.Sleep(100 * time.Second)
|
if testutils.IsSingleTesting() {
|
||||||
|
time.Sleep(100 * time.Second)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewOpenFileCache_CloseAll(t *testing.T) {
|
func TestNewOpenFileCache_CloseAll(t *testing.T) {
|
||||||
|
|||||||
@@ -8,21 +8,29 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestFileReader(t *testing.T) {
|
func TestFileReader(t *testing.T) {
|
||||||
storage := NewFileStorage(&serverconfigs.HTTPCachePolicy{
|
var storage = NewFileStorage(&serverconfigs.HTTPCachePolicy{
|
||||||
Id: 1,
|
Id: 1,
|
||||||
IsOn: true,
|
IsOn: true,
|
||||||
Options: map[string]interface{}{
|
Options: map[string]interface{}{
|
||||||
"dir": Tea.Root + "/caches",
|
"dir": Tea.Root + "/caches",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
defer storage.Stop()
|
||||||
|
|
||||||
err := storage.Init()
|
err := storage.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, path, _ := storage.keyPath("my-key")
|
_, path, _ := storage.keyPath("my-key")
|
||||||
|
|
||||||
fp, err := os.Open(path)
|
fp, err := os.Open(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
t.Log("file '" + path + "' not exists")
|
||||||
|
return
|
||||||
|
}
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
@@ -58,6 +66,10 @@ func TestFileReader_ReadHeader(t *testing.T) {
|
|||||||
var path = "/Users/WorkSpace/EdgeProject/EdgeCache/p43/12/6b/126bbed90fc80f2bdfb19558948b0d49.cache"
|
var path = "/Users/WorkSpace/EdgeProject/EdgeCache/p43/12/6b/126bbed90fc80f2bdfb19558948b0d49.cache"
|
||||||
fp, err := os.Open(path)
|
fp, err := os.Open(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
t.Log("'" + path + "' not exists")
|
||||||
|
return
|
||||||
|
}
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
@@ -66,6 +78,11 @@ func TestFileReader_ReadHeader(t *testing.T) {
|
|||||||
var reader = NewFileReader(fp)
|
var reader = NewFileReader(fp)
|
||||||
err = reader.Init()
|
err = reader.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
t.Log("file '" + path + "' not exists")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
var buf = make([]byte, 16*1024)
|
var buf = make([]byte, 16*1024)
|
||||||
@@ -79,13 +96,16 @@ func TestFileReader_ReadHeader(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFileReader_Range(t *testing.T) {
|
func TestFileReader_Range(t *testing.T) {
|
||||||
storage := NewFileStorage(&serverconfigs.HTTPCachePolicy{
|
var storage = NewFileStorage(&serverconfigs.HTTPCachePolicy{
|
||||||
Id: 1,
|
Id: 1,
|
||||||
IsOn: true,
|
IsOn: true,
|
||||||
Options: map[string]interface{}{
|
Options: map[string]interface{}{
|
||||||
"dir": Tea.Root + "/caches",
|
"dir": Tea.Root + "/caches",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
defer storage.Stop()
|
||||||
|
|
||||||
err := storage.Init()
|
err := storage.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -109,6 +129,10 @@ func TestFileReader_Range(t *testing.T) {
|
|||||||
|
|
||||||
fp, err := os.Open(path)
|
fp, err := os.Open(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
t.Log("'" + path + "' not exists")
|
||||||
|
return
|
||||||
|
}
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
|
|||||||
@@ -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"
|
||||||
|
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"
|
||||||
@@ -21,9 +22,6 @@ 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"
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
"golang.org/x/text/language"
|
|
||||||
"golang.org/x/text/message"
|
|
||||||
"math"
|
"math"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@@ -32,7 +30,6 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -53,12 +50,12 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
FileStorageMaxIgnoreKeys = 32768 // 最大可忽略的键值数(尺寸过大的键值)
|
FileStorageMaxIgnoreKeys = 32768 // 最大可忽略的键值数(尺寸过大的键值)
|
||||||
HotItemSize = 1024 // 热点数据数量
|
HotItemSize = 1024 // 热点数据数量
|
||||||
HotItemLifeSeconds int64 = 3600 // 热点数据生命周期
|
HotItemLifeSeconds int64 = 3600 // 热点数据生命周期
|
||||||
FileToMemoryMaxSize = 32 * sizes.M // 可以从文件写入到内存的最大文件尺寸
|
FileToMemoryMaxSize = 32 * sizes.M // 可以从文件写入到内存的最大文件尺寸
|
||||||
FileTmpSuffix = ".tmp"
|
FileTmpSuffix = ".tmp"
|
||||||
MinDiskSpace = 5 << 30 // 当前磁盘最小剩余空间
|
MinDiskSpace uint64 = 5 << 30 // 当前磁盘最小剩余空间
|
||||||
)
|
)
|
||||||
|
|
||||||
var sharedWritingFileKeyMap = map[string]zero.Zero{} // key => bool
|
var sharedWritingFileKeyMap = map[string]zero.Zero{} // key => bool
|
||||||
@@ -77,7 +74,6 @@ type FileStorage struct {
|
|||||||
policy *serverconfigs.HTTPCachePolicy
|
policy *serverconfigs.HTTPCachePolicy
|
||||||
options *serverconfigs.HTTPFileCacheStorage // 二级缓存
|
options *serverconfigs.HTTPFileCacheStorage // 二级缓存
|
||||||
memoryStorage *MemoryStorage // 一级缓存
|
memoryStorage *MemoryStorage // 一级缓存
|
||||||
totalSize int64
|
|
||||||
|
|
||||||
list ListInterface
|
list ListInterface
|
||||||
locker sync.RWMutex
|
locker sync.RWMutex
|
||||||
@@ -92,7 +88,6 @@ type FileStorage struct {
|
|||||||
|
|
||||||
openFileCache *OpenFileCache
|
openFileCache *OpenFileCache
|
||||||
|
|
||||||
mainDir string
|
|
||||||
mainDiskIsFull bool
|
mainDiskIsFull bool
|
||||||
|
|
||||||
subDirs []*FileDir
|
subDirs []*FileDir
|
||||||
@@ -203,6 +198,9 @@ func (this *FileStorage) UpdatePolicy(newPolicy *serverconfigs.HTTPCachePolicy)
|
|||||||
if newPolicy.PersistenceAutoPurgeInterval != this.policy.PersistenceAutoPurgeInterval {
|
if newPolicy.PersistenceAutoPurgeInterval != this.policy.PersistenceAutoPurgeInterval {
|
||||||
this.initPurgeTicker()
|
this.initPurgeTicker()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// reset ignored keys
|
||||||
|
this.ignoreKeys.Reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init 初始化
|
// Init 初始化
|
||||||
@@ -255,19 +253,6 @@ func (this *FileStorage) Init() error {
|
|||||||
}
|
}
|
||||||
list.(*FileList).SetOldDir(dir + "/p" + types.String(this.policy.Id))
|
list.(*FileList).SetOldDir(dir + "/p" + types.String(this.policy.Id))
|
||||||
this.list = list
|
this.list = list
|
||||||
stat, err := list.Stat(func(hash string) bool {
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
this.totalSize = stat.Size
|
|
||||||
this.list.OnAdd(func(item *Item) {
|
|
||||||
atomic.AddInt64(&this.totalSize, item.TotalSize())
|
|
||||||
})
|
|
||||||
this.list.OnRemove(func(item *Item) {
|
|
||||||
atomic.AddInt64(&this.totalSize, -item.TotalSize())
|
|
||||||
})
|
|
||||||
|
|
||||||
// 检查目录是否存在
|
// 检查目录是否存在
|
||||||
_, err = os.Stat(dir)
|
_, err = os.Stat(dir)
|
||||||
@@ -284,19 +269,17 @@ func (this *FileStorage) Init() error {
|
|||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
// 统计
|
// 统计
|
||||||
var count = stat.Count
|
var totalSize = this.TotalDiskSize()
|
||||||
var size = stat.Size
|
|
||||||
|
|
||||||
var cost = time.Since(before).Seconds() * 1000
|
var cost = time.Since(before).Seconds() * 1000
|
||||||
sizeMB := strconv.FormatInt(size, 10) + " Bytes"
|
var sizeMB = types.String(totalSize) + " Bytes"
|
||||||
if size > 1*sizes.G {
|
if totalSize > 1*sizes.G {
|
||||||
sizeMB = fmt.Sprintf("%.3f G", float64(size)/float64(sizes.G))
|
sizeMB = fmt.Sprintf("%.3f G", float64(totalSize)/float64(sizes.G))
|
||||||
} else if size > 1*sizes.M {
|
} else if totalSize > 1*sizes.M {
|
||||||
sizeMB = fmt.Sprintf("%.3f M", float64(size)/float64(sizes.M))
|
sizeMB = fmt.Sprintf("%.3f M", float64(totalSize)/float64(sizes.M))
|
||||||
} else if size > 1*sizes.K {
|
} else if totalSize > 1*sizes.K {
|
||||||
sizeMB = fmt.Sprintf("%.3f K", float64(size)/float64(sizes.K))
|
sizeMB = fmt.Sprintf("%.3f K", float64(totalSize)/float64(sizes.K))
|
||||||
}
|
}
|
||||||
remotelogs.Println("CACHE", "init policy "+strconv.FormatInt(this.policy.Id, 10)+" from '"+this.options.Dir+"', cost: "+fmt.Sprintf("%.2f", cost)+" ms, count: "+message.NewPrinter(language.English).Sprintf("%d", count)+", size: "+sizeMB)
|
remotelogs.Println("CACHE", "init policy "+types.String(this.policy.Id)+" from '"+this.options.Dir+"', cost: "+fmt.Sprintf("%.2f", cost)+" ms, size: "+sizeMB)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// 初始化list
|
// 初始化list
|
||||||
@@ -430,14 +413,14 @@ func (this *FileStorage) openWriter(key string, expiredAt int64, status int, hea
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 是否已忽略
|
// 是否已忽略
|
||||||
if this.ignoreKeys.Has(key) {
|
if maxSize > 0 && this.ignoreKeys.Has(types.String(maxSize)+"$"+key) {
|
||||||
return nil, ErrEntityTooLarge
|
return nil, ErrEntityTooLarge
|
||||||
}
|
}
|
||||||
|
|
||||||
// 先尝试内存缓存
|
// 先尝试内存缓存
|
||||||
// 我们限定仅小文件优先存在内存中
|
// 我们限定仅小文件优先存在内存中
|
||||||
var maxMemorySize = FileToMemoryMaxSize
|
var maxMemorySize = FileToMemoryMaxSize
|
||||||
if maxSize > maxMemorySize {
|
if maxSize > 0 && maxSize < maxMemorySize {
|
||||||
maxMemorySize = maxSize
|
maxMemorySize = maxSize
|
||||||
}
|
}
|
||||||
var memoryStorage = this.memoryStorage
|
var memoryStorage = this.memoryStorage
|
||||||
@@ -480,17 +463,10 @@ func (this *FileStorage) openWriter(key string, expiredAt int64, status int, hea
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// 检查是否超出最大值
|
// 检查是否超出容量
|
||||||
count, err := this.list.Count()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if this.policy.MaxKeys > 0 && count > this.policy.MaxKeys {
|
|
||||||
return nil, NewCapacityError("write file cache failed: too many keys in cache storage")
|
|
||||||
}
|
|
||||||
var capacityBytes = this.diskCapacityBytes()
|
var capacityBytes = this.diskCapacityBytes()
|
||||||
if capacityBytes > 0 && capacityBytes <= this.totalSize {
|
if capacityBytes > 0 && capacityBytes <= this.TotalDiskSize() {
|
||||||
return nil, NewCapacityError("write file cache failed: over disk size, current total size: " + strconv.FormatInt(this.totalSize, 10) + " bytes, capacity: " + strconv.FormatInt(capacityBytes, 10))
|
return nil, NewCapacityError("write file cache failed: over disk size, current total size: " + types.String(this.TotalDiskSize()) + " bytes, capacity: " + types.String(capacityBytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
var hash = stringutil.Md5(key)
|
var hash = stringutil.Md5(key)
|
||||||
@@ -669,7 +645,7 @@ func (this *FileStorage) openWriter(key string, expiredAt int64, status int, hea
|
|||||||
sharedWritingFileKeyLocker.Unlock()
|
sharedWritingFileKeyLocker.Unlock()
|
||||||
}), nil
|
}), nil
|
||||||
} else {
|
} else {
|
||||||
return NewFileWriter(this, writer, key, expiredAt, metaHeaderSize, metaBodySize, -1, 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 {
|
if len(sharedWritingFileKeyMap) == 0 {
|
||||||
@@ -899,7 +875,10 @@ func (this *FileStorage) Stop() {
|
|||||||
memoryStorage.Stop()
|
memoryStorage.Stop()
|
||||||
})
|
})
|
||||||
|
|
||||||
_ = this.list.Reset()
|
if this.list != nil {
|
||||||
|
_ = this.list.Reset()
|
||||||
|
}
|
||||||
|
|
||||||
if this.purgeTicker != nil {
|
if this.purgeTicker != nil {
|
||||||
this.purgeTicker.Stop()
|
this.purgeTicker.Stop()
|
||||||
}
|
}
|
||||||
@@ -907,7 +886,9 @@ func (this *FileStorage) Stop() {
|
|||||||
this.hotTicker.Stop()
|
this.hotTicker.Stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = this.list.Close()
|
if this.list != nil {
|
||||||
|
_ = this.list.Close()
|
||||||
|
}
|
||||||
|
|
||||||
var openFileCache = this.openFileCache
|
var openFileCache = this.openFileCache
|
||||||
if openFileCache != nil {
|
if openFileCache != nil {
|
||||||
@@ -919,7 +900,11 @@ func (this *FileStorage) Stop() {
|
|||||||
|
|
||||||
// TotalDiskSize 消耗的磁盘尺寸
|
// TotalDiskSize 消耗的磁盘尺寸
|
||||||
func (this *FileStorage) TotalDiskSize() int64 {
|
func (this *FileStorage) TotalDiskSize() int64 {
|
||||||
return atomic.LoadInt64(&this.totalSize)
|
stat, err := fsutils.StatCache(this.options.Dir)
|
||||||
|
if err == nil {
|
||||||
|
return int64(stat.UsedSize())
|
||||||
|
}
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// TotalMemorySize 内存尺寸
|
// TotalMemorySize 内存尺寸
|
||||||
@@ -932,8 +917,8 @@ func (this *FileStorage) TotalMemorySize() int64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// IgnoreKey 忽略某个Key,即不缓存某个Key
|
// IgnoreKey 忽略某个Key,即不缓存某个Key
|
||||||
func (this *FileStorage) IgnoreKey(key string) {
|
func (this *FileStorage) IgnoreKey(key string, maxSize int64) {
|
||||||
this.ignoreKeys.Push(key)
|
this.ignoreKeys.Push(types.String(maxSize) + "$" + key)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CanSendfile 是否支持Sendfile
|
// CanSendfile 是否支持Sendfile
|
||||||
@@ -1359,7 +1344,6 @@ func (this *FileStorage) createMemoryStorage() error {
|
|||||||
Name: this.policy.Name,
|
Name: this.policy.Name,
|
||||||
Description: this.policy.Description,
|
Description: this.policy.Description,
|
||||||
Capacity: this.options.MemoryPolicy.Capacity,
|
Capacity: this.options.MemoryPolicy.Capacity,
|
||||||
MaxKeys: this.policy.MaxKeys,
|
|
||||||
MaxSize: &shared.SizeCapacity{Count: 128, Unit: shared.SizeCapacityUnitMB}, // TODO 将来可以修改
|
MaxSize: &shared.SizeCapacity{Count: 128, Unit: shared.SizeCapacityUnitMB}, // TODO 将来可以修改
|
||||||
Type: serverconfigs.CachePolicyStorageMemory,
|
Type: serverconfigs.CachePolicyStorageMemory,
|
||||||
Options: this.policy.Options,
|
Options: this.policy.Options,
|
||||||
@@ -1435,20 +1419,16 @@ 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 {
|
if this.options != nil && len(this.options.Dir) > 0 {
|
||||||
var stat unix.Statfs_t
|
stat, err := fsutils.Stat(this.options.Dir)
|
||||||
err := unix.Statfs(this.options.Dir, &stat)
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
var availableBytes = stat.Bavail * uint64(stat.Bsize)
|
this.mainDiskIsFull = stat.FreeSize() < MinDiskSpace
|
||||||
this.mainDiskIsFull = availableBytes < MinDiskSpace
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var subDirs = this.subDirs // copy slice
|
var subDirs = this.subDirs // copy slice
|
||||||
for _, subDir := range subDirs {
|
for _, subDir := range subDirs {
|
||||||
var stat unix.Statfs_t
|
stat, err := fsutils.Stat(subDir.Path)
|
||||||
err := unix.Statfs(subDir.Path, &stat)
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
var availableBytes = stat.Bavail * uint64(stat.Bsize)
|
subDir.IsFull = stat.FreeSize() < MinDiskSpace
|
||||||
subDir.IsFull = availableBytes < MinDiskSpace
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestFileStorage_Init(t *testing.T) {
|
func TestFileStorage_Init(t *testing.T) {
|
||||||
storage := NewFileStorage(&serverconfigs.HTTPCachePolicy{
|
var storage = NewFileStorage(&serverconfigs.HTTPCachePolicy{
|
||||||
Id: 1,
|
Id: 1,
|
||||||
IsOn: true,
|
IsOn: true,
|
||||||
Options: map[string]interface{}{
|
Options: map[string]interface{}{
|
||||||
@@ -26,6 +26,8 @@ func TestFileStorage_Init(t *testing.T) {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
defer storage.Stop()
|
||||||
|
|
||||||
err := storage.Init()
|
err := storage.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -40,17 +42,22 @@ func TestFileStorage_Init(t *testing.T) {
|
|||||||
|
|
||||||
time.Sleep(2 * time.Second)
|
time.Sleep(2 * time.Second)
|
||||||
storage.purgeLoop()
|
storage.purgeLoop()
|
||||||
t.Log(storage.list.(*FileList).total, "entries left")
|
t.Log(storage.list.(*FileList).Stat(func(hash string) bool {
|
||||||
|
return true
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFileStorage_OpenWriter(t *testing.T) {
|
func TestFileStorage_OpenWriter(t *testing.T) {
|
||||||
storage := NewFileStorage(&serverconfigs.HTTPCachePolicy{
|
var storage = NewFileStorage(&serverconfigs.HTTPCachePolicy{
|
||||||
Id: 1,
|
Id: 1,
|
||||||
IsOn: true,
|
IsOn: true,
|
||||||
Options: map[string]interface{}{
|
Options: map[string]interface{}{
|
||||||
"dir": Tea.Root + "/caches",
|
"dir": Tea.Root + "/caches",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
defer storage.Stop()
|
||||||
|
|
||||||
err := storage.Init()
|
err := storage.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -95,6 +102,9 @@ func TestFileStorage_OpenWriter_Partial(t *testing.T) {
|
|||||||
"dir": Tea.Root + "/caches",
|
"dir": Tea.Root + "/caches",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
defer storage.Stop()
|
||||||
|
|
||||||
err := storage.Init()
|
err := storage.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -123,13 +133,16 @@ func TestFileStorage_OpenWriter_Partial(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFileStorage_OpenWriter_HTTP(t *testing.T) {
|
func TestFileStorage_OpenWriter_HTTP(t *testing.T) {
|
||||||
storage := NewFileStorage(&serverconfigs.HTTPCachePolicy{
|
var storage = NewFileStorage(&serverconfigs.HTTPCachePolicy{
|
||||||
Id: 1,
|
Id: 1,
|
||||||
IsOn: true,
|
IsOn: true,
|
||||||
Options: map[string]interface{}{
|
Options: map[string]interface{}{
|
||||||
"dir": Tea.Root + "/caches",
|
"dir": Tea.Root + "/caches",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
defer storage.Stop()
|
||||||
|
|
||||||
err := storage.Init()
|
err := storage.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -188,13 +201,16 @@ func TestFileStorage_OpenWriter_HTTP(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFileStorage_Concurrent_Open_DifferentFile(t *testing.T) {
|
func TestFileStorage_Concurrent_Open_DifferentFile(t *testing.T) {
|
||||||
storage := NewFileStorage(&serverconfigs.HTTPCachePolicy{
|
var storage = NewFileStorage(&serverconfigs.HTTPCachePolicy{
|
||||||
Id: 1,
|
Id: 1,
|
||||||
IsOn: true,
|
IsOn: true,
|
||||||
Options: map[string]interface{}{
|
Options: map[string]interface{}{
|
||||||
"dir": Tea.Root + "/caches",
|
"dir": Tea.Root + "/caches",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
defer storage.Stop()
|
||||||
|
|
||||||
err := storage.Init()
|
err := storage.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -243,13 +259,16 @@ func TestFileStorage_Concurrent_Open_DifferentFile(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFileStorage_Concurrent_Open_SameFile(t *testing.T) {
|
func TestFileStorage_Concurrent_Open_SameFile(t *testing.T) {
|
||||||
storage := NewFileStorage(&serverconfigs.HTTPCachePolicy{
|
var storage = NewFileStorage(&serverconfigs.HTTPCachePolicy{
|
||||||
Id: 1,
|
Id: 1,
|
||||||
IsOn: true,
|
IsOn: true,
|
||||||
Options: map[string]interface{}{
|
Options: map[string]interface{}{
|
||||||
"dir": Tea.Root + "/caches",
|
"dir": Tea.Root + "/caches",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
defer storage.Stop()
|
||||||
|
|
||||||
err := storage.Init()
|
err := storage.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -299,13 +318,16 @@ func TestFileStorage_Concurrent_Open_SameFile(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFileStorage_Read(t *testing.T) {
|
func TestFileStorage_Read(t *testing.T) {
|
||||||
storage := NewFileStorage(&serverconfigs.HTTPCachePolicy{
|
var storage = NewFileStorage(&serverconfigs.HTTPCachePolicy{
|
||||||
Id: 1,
|
Id: 1,
|
||||||
IsOn: true,
|
IsOn: true,
|
||||||
Options: map[string]interface{}{
|
Options: map[string]interface{}{
|
||||||
"dir": Tea.Root + "/caches",
|
"dir": Tea.Root + "/caches",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
defer storage.Stop()
|
||||||
|
|
||||||
err := storage.Init()
|
err := storage.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -335,13 +357,16 @@ func TestFileStorage_Read(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFileStorage_Read_HTTP_Response(t *testing.T) {
|
func TestFileStorage_Read_HTTP_Response(t *testing.T) {
|
||||||
storage := NewFileStorage(&serverconfigs.HTTPCachePolicy{
|
var storage = NewFileStorage(&serverconfigs.HTTPCachePolicy{
|
||||||
Id: 1,
|
Id: 1,
|
||||||
IsOn: true,
|
IsOn: true,
|
||||||
Options: map[string]interface{}{
|
Options: map[string]interface{}{
|
||||||
"dir": Tea.Root + "/caches",
|
"dir": Tea.Root + "/caches",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
defer storage.Stop()
|
||||||
|
|
||||||
err := storage.Init()
|
err := storage.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -388,13 +413,16 @@ func TestFileStorage_Read_HTTP_Response(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFileStorage_Read_NotFound(t *testing.T) {
|
func TestFileStorage_Read_NotFound(t *testing.T) {
|
||||||
storage := NewFileStorage(&serverconfigs.HTTPCachePolicy{
|
var storage = NewFileStorage(&serverconfigs.HTTPCachePolicy{
|
||||||
Id: 1,
|
Id: 1,
|
||||||
IsOn: true,
|
IsOn: true,
|
||||||
Options: map[string]interface{}{
|
Options: map[string]interface{}{
|
||||||
"dir": Tea.Root + "/caches",
|
"dir": Tea.Root + "/caches",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
defer storage.Stop()
|
||||||
|
|
||||||
err := storage.Init()
|
err := storage.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -421,13 +449,16 @@ func TestFileStorage_Read_NotFound(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFileStorage_Delete(t *testing.T) {
|
func TestFileStorage_Delete(t *testing.T) {
|
||||||
storage := NewFileStorage(&serverconfigs.HTTPCachePolicy{
|
var storage = NewFileStorage(&serverconfigs.HTTPCachePolicy{
|
||||||
Id: 1,
|
Id: 1,
|
||||||
IsOn: true,
|
IsOn: true,
|
||||||
Options: map[string]interface{}{
|
Options: map[string]interface{}{
|
||||||
"dir": Tea.Root + "/caches",
|
"dir": Tea.Root + "/caches",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
defer storage.Stop()
|
||||||
|
|
||||||
err := storage.Init()
|
err := storage.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -440,13 +471,16 @@ func TestFileStorage_Delete(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFileStorage_Stat(t *testing.T) {
|
func TestFileStorage_Stat(t *testing.T) {
|
||||||
storage := NewFileStorage(&serverconfigs.HTTPCachePolicy{
|
var storage = NewFileStorage(&serverconfigs.HTTPCachePolicy{
|
||||||
Id: 1,
|
Id: 1,
|
||||||
IsOn: true,
|
IsOn: true,
|
||||||
Options: map[string]interface{}{
|
Options: map[string]interface{}{
|
||||||
"dir": Tea.Root + "/caches",
|
"dir": Tea.Root + "/caches",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
defer storage.Stop()
|
||||||
|
|
||||||
err := storage.Init()
|
err := storage.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -465,13 +499,16 @@ func TestFileStorage_Stat(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFileStorage_CleanAll(t *testing.T) {
|
func TestFileStorage_CleanAll(t *testing.T) {
|
||||||
storage := NewFileStorage(&serverconfigs.HTTPCachePolicy{
|
var storage = NewFileStorage(&serverconfigs.HTTPCachePolicy{
|
||||||
Id: 1,
|
Id: 1,
|
||||||
IsOn: true,
|
IsOn: true,
|
||||||
Options: map[string]interface{}{
|
Options: map[string]interface{}{
|
||||||
"dir": Tea.Root + "/caches",
|
"dir": Tea.Root + "/caches",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
defer storage.Stop()
|
||||||
|
|
||||||
err := storage.Init()
|
err := storage.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -496,13 +533,16 @@ func TestFileStorage_CleanAll(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFileStorage_Stop(t *testing.T) {
|
func TestFileStorage_Stop(t *testing.T) {
|
||||||
storage := NewFileStorage(&serverconfigs.HTTPCachePolicy{
|
var storage = NewFileStorage(&serverconfigs.HTTPCachePolicy{
|
||||||
Id: 1,
|
Id: 1,
|
||||||
IsOn: true,
|
IsOn: true,
|
||||||
Options: map[string]interface{}{
|
Options: map[string]interface{}{
|
||||||
"dir": Tea.Root + "/caches",
|
"dir": Tea.Root + "/caches",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
defer storage.Stop()
|
||||||
|
|
||||||
err := storage.Init()
|
err := storage.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -518,6 +558,9 @@ func TestFileStorage_DecodeFile(t *testing.T) {
|
|||||||
"dir": Tea.Root + "/caches",
|
"dir": Tea.Root + "/caches",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
defer storage.Stop()
|
||||||
|
|
||||||
err := storage.Init()
|
err := storage.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -528,6 +571,9 @@ func TestFileStorage_DecodeFile(t *testing.T) {
|
|||||||
|
|
||||||
func TestFileStorage_RemoveCacheFile(t *testing.T) {
|
func TestFileStorage_RemoveCacheFile(t *testing.T) {
|
||||||
var storage = NewFileStorage(nil)
|
var storage = NewFileStorage(nil)
|
||||||
|
|
||||||
|
defer storage.Stop()
|
||||||
|
|
||||||
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"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -536,13 +582,16 @@ func BenchmarkFileStorage_Read(b *testing.B) {
|
|||||||
|
|
||||||
_ = utils.SetRLimit(1024 * 1024)
|
_ = utils.SetRLimit(1024 * 1024)
|
||||||
|
|
||||||
storage := NewFileStorage(&serverconfigs.HTTPCachePolicy{
|
var storage = NewFileStorage(&serverconfigs.HTTPCachePolicy{
|
||||||
Id: 1,
|
Id: 1,
|
||||||
IsOn: true,
|
IsOn: true,
|
||||||
Options: map[string]interface{}{
|
Options: map[string]interface{}{
|
||||||
"dir": Tea.Root + "/caches",
|
"dir": Tea.Root + "/caches",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
defer storage.Stop()
|
||||||
|
|
||||||
err := storage.Init()
|
err := storage.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ type StorageInterface interface {
|
|||||||
AddToList(item *Item)
|
AddToList(item *Item)
|
||||||
|
|
||||||
// IgnoreKey 忽略某个Key,即不缓存某个Key
|
// IgnoreKey 忽略某个Key,即不缓存某个Key
|
||||||
IgnoreKey(key string)
|
IgnoreKey(key string, maxSize int64)
|
||||||
|
|
||||||
// CanSendfile 是否支持Sendfile
|
// CanSendfile 是否支持Sendfile
|
||||||
CanSendfile() bool
|
CanSendfile() bool
|
||||||
|
|||||||
@@ -111,8 +111,15 @@ func (this *MemoryStorage) Init() error {
|
|||||||
|
|
||||||
// OpenReader 读取缓存
|
// OpenReader 读取缓存
|
||||||
func (this *MemoryStorage) OpenReader(key string, useStale bool, isPartial bool) (Reader, error) {
|
func (this *MemoryStorage) OpenReader(key string, useStale bool, isPartial bool) (Reader, error) {
|
||||||
hash := this.hash(key)
|
var hash = this.hash(key)
|
||||||
|
|
||||||
|
// check if exists in list
|
||||||
|
exists, _ := this.list.Exist(types.String(hash))
|
||||||
|
if !exists {
|
||||||
|
return nil, ErrNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
// read from valuesMap
|
||||||
this.locker.RLock()
|
this.locker.RLock()
|
||||||
item := this.valuesMap[hash]
|
item := this.valuesMap[hash]
|
||||||
if item == nil || !item.IsDone {
|
if item == nil || !item.IsDone {
|
||||||
@@ -121,7 +128,7 @@ func (this *MemoryStorage) OpenReader(key string, useStale bool, isPartial bool)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if useStale || (item.ExpiresAt > fasttime.Now().Unix()) {
|
if useStale || (item.ExpiresAt > fasttime.Now().Unix()) {
|
||||||
reader := NewMemoryReader(item)
|
var reader = NewMemoryReader(item)
|
||||||
err := reader.Init()
|
err := reader.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.locker.RUnlock()
|
this.locker.RUnlock()
|
||||||
@@ -151,7 +158,7 @@ func (this *MemoryStorage) OpenReader(key string, useStale bool, isPartial bool)
|
|||||||
|
|
||||||
// OpenWriter 打开缓存写入器等待写入
|
// OpenWriter 打开缓存写入器等待写入
|
||||||
func (this *MemoryStorage) OpenWriter(key string, expiredAt int64, status int, headerSize int, bodySize int64, maxSize int64, isPartial bool) (Writer, error) {
|
func (this *MemoryStorage) OpenWriter(key string, expiredAt int64, status int, headerSize int, bodySize int64, maxSize int64, isPartial bool) (Writer, error) {
|
||||||
if this.ignoreKeys.Has(key) {
|
if maxSize > 0 && this.ignoreKeys.Has(types.String(maxSize)+"$"+key) {
|
||||||
return nil, ErrEntityTooLarge
|
return nil, ErrEntityTooLarge
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,20 +200,22 @@ func (this *MemoryStorage) openWriter(key string, expiresAt int64, status int, h
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
// 检查是否过期
|
// 检查是否过期
|
||||||
hash := this.hash(key)
|
var hash = this.hash(key)
|
||||||
item, ok := this.valuesMap[hash]
|
item, ok := this.valuesMap[hash]
|
||||||
if ok && !item.IsExpired() {
|
if ok && !item.IsExpired() {
|
||||||
return nil, ErrFileIsWriting
|
var hashString = types.String(hash)
|
||||||
|
exists, _ := this.list.Exist(hashString)
|
||||||
|
if !exists {
|
||||||
|
// remove from values map
|
||||||
|
delete(this.valuesMap, hash)
|
||||||
|
_ = this.list.Remove(hashString)
|
||||||
|
item = nil
|
||||||
|
} else {
|
||||||
|
return nil, ErrFileIsWriting
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否超出最大值
|
// 检查是否超出最大值
|
||||||
totalKeys, err := this.list.Count()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if this.policy.MaxKeys > 0 && totalKeys > this.policy.MaxKeys {
|
|
||||||
return nil, NewCapacityError("write memory cache failed: too many keys in cache storage")
|
|
||||||
}
|
|
||||||
capacityBytes := this.memoryCapacityBytes()
|
capacityBytes := this.memoryCapacityBytes()
|
||||||
if bodySize < 0 {
|
if bodySize < 0 {
|
||||||
bodySize = 0
|
bodySize = 0
|
||||||
@@ -216,7 +225,7 @@ func (this *MemoryStorage) openWriter(key string, expiresAt int64, status int, h
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 先删除
|
// 先删除
|
||||||
err = this.deleteWithoutLocker(key)
|
err := this.deleteWithoutLocker(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -353,6 +362,9 @@ func (this *MemoryStorage) UpdatePolicy(newPolicy *serverconfigs.HTTPCachePolicy
|
|||||||
if newPolicy.CapacityBytes() == 0 {
|
if newPolicy.CapacityBytes() == 0 {
|
||||||
_ = this.CleanAll()
|
_ = this.CleanAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// reset ignored keys
|
||||||
|
this.ignoreKeys.Reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
// CanUpdatePolicy 检查策略是否可以更新
|
// CanUpdatePolicy 检查策略是否可以更新
|
||||||
@@ -383,8 +395,8 @@ func (this *MemoryStorage) TotalMemorySize() int64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// IgnoreKey 忽略某个Key,即不缓存某个Key
|
// IgnoreKey 忽略某个Key,即不缓存某个Key
|
||||||
func (this *MemoryStorage) IgnoreKey(key string) {
|
func (this *MemoryStorage) IgnoreKey(key string, maxSize int64) {
|
||||||
this.ignoreKeys.Push(key)
|
this.ignoreKeys.Push(types.String(maxSize) + "$" + key)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CanSendfile 是否支持Sendfile
|
// CanSendfile 是否支持Sendfile
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ func (this *FileWriter) Write(data []byte) (n int, err error) {
|
|||||||
err = ErrEntityTooLarge
|
err = ErrEntityTooLarge
|
||||||
|
|
||||||
if this.storage != nil {
|
if this.storage != nil {
|
||||||
this.storage.IgnoreKey(this.key)
|
this.storage.IgnoreKey(this.key, this.maxSize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ func (this *MemoryWriter) Write(data []byte) (n int, err error) {
|
|||||||
// 检查尺寸
|
// 检查尺寸
|
||||||
if this.maxSize > 0 && this.bodySize > this.maxSize {
|
if this.maxSize > 0 && this.bodySize > this.maxSize {
|
||||||
err = ErrEntityTooLarge
|
err = ErrEntityTooLarge
|
||||||
this.storage.IgnoreKey(this.key)
|
this.storage.IgnoreKey(this.key, this.maxSize)
|
||||||
return len(data), err
|
return len(data), err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
package conns
|
package conns
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/iwind/TeaGo/types"
|
||||||
"net"
|
"net"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
@@ -10,14 +11,14 @@ import (
|
|||||||
var SharedMap = NewMap()
|
var SharedMap = NewMap()
|
||||||
|
|
||||||
type Map struct {
|
type Map struct {
|
||||||
m map[string]map[int]net.Conn // ip => { port => Conn }
|
m map[string]map[string]net.Conn // ip => { network_port => Conn }
|
||||||
|
|
||||||
locker sync.RWMutex
|
locker sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMap() *Map {
|
func NewMap() *Map {
|
||||||
return &Map{
|
return &Map{
|
||||||
m: map[string]map[int]net.Conn{},
|
m: map[string]map[string]net.Conn{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -25,21 +26,19 @@ func (this *Map) Add(conn net.Conn) {
|
|||||||
if conn == nil {
|
if conn == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
tcpAddr, ok := conn.RemoteAddr().(*net.TCPAddr)
|
|
||||||
|
key, ip, ok := this.connAddr(conn)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var ip = tcpAddr.IP.String()
|
|
||||||
var port = tcpAddr.Port
|
|
||||||
|
|
||||||
this.locker.Lock()
|
this.locker.Lock()
|
||||||
defer this.locker.Unlock()
|
defer this.locker.Unlock()
|
||||||
connMap, ok := this.m[ip]
|
connMap, ok := this.m[ip]
|
||||||
if !ok {
|
if !ok {
|
||||||
this.m[ip] = map[int]net.Conn{port: conn}
|
this.m[ip] = map[string]net.Conn{key: conn}
|
||||||
} else {
|
} else {
|
||||||
connMap[port] = conn
|
connMap[key] = conn
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,14 +46,11 @@ func (this *Map) Remove(conn net.Conn) {
|
|||||||
if conn == nil {
|
if conn == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
tcpAddr, ok := conn.RemoteAddr().(*net.TCPAddr)
|
key, ip, ok := this.connAddr(conn)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var ip = tcpAddr.IP.String()
|
|
||||||
var port = tcpAddr.Port
|
|
||||||
|
|
||||||
this.locker.Lock()
|
this.locker.Lock()
|
||||||
defer this.locker.Unlock()
|
defer this.locker.Unlock()
|
||||||
|
|
||||||
@@ -62,7 +58,7 @@ func (this *Map) Remove(conn net.Conn) {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
delete(connMap, port)
|
delete(connMap, key)
|
||||||
|
|
||||||
if len(connMap) == 0 {
|
if len(connMap) == 0 {
|
||||||
delete(this.m, ip)
|
delete(this.m, ip)
|
||||||
@@ -121,3 +117,24 @@ func (this *Map) AllConns() []net.Conn {
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *Map) connAddr(conn net.Conn) (key string, ip string, ok bool) {
|
||||||
|
if conn == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var addr = conn.RemoteAddr()
|
||||||
|
switch realAddr := addr.(type) {
|
||||||
|
case *net.TCPAddr:
|
||||||
|
return addr.Network() + types.String(realAddr.Port), realAddr.IP.String(), true
|
||||||
|
case *net.UDPAddr:
|
||||||
|
return addr.Network() + types.String(realAddr.Port), realAddr.IP.String(), true
|
||||||
|
default:
|
||||||
|
var s = addr.String()
|
||||||
|
host, port, err := net.SplitHostPort(s)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return addr.Network() + port, host, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package teaconst
|
package teaconst
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Version = "1.0.4"
|
Version = "1.2.2"
|
||||||
|
|
||||||
ProductName = "Edge Node"
|
ProductName = "Edge Node"
|
||||||
ProcessName = "edge-node"
|
ProcessName = "edge-node"
|
||||||
|
|||||||
@@ -24,6 +24,11 @@ func On(event Event, callback func()) {
|
|||||||
OnKey(event, nil, callback)
|
OnKey(event, nil, callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func OnClose(callback func()) {
|
||||||
|
On(EventQuit, callback)
|
||||||
|
On(EventTerminated, callback)
|
||||||
|
}
|
||||||
|
|
||||||
// OnKey 使用Key增加事件回调
|
// OnKey 使用Key增加事件回调
|
||||||
func OnKey(event Event, key interface{}, callback func()) {
|
func OnKey(event Event, key interface{}, callback func()) {
|
||||||
if key == nil {
|
if key == nil {
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
executils "github.com/TeaOSLab/EdgeNode/internal/utils/exec"
|
executils "github.com/TeaOSLab/EdgeNode/internal/utils/exec"
|
||||||
"github.com/iwind/TeaGo/types"
|
"github.com/iwind/TeaGo/types"
|
||||||
"os/exec"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -32,7 +31,7 @@ func NewFirewalld() *Firewalld {
|
|||||||
cmdQueue: make(chan *firewalldCmd, 4096),
|
cmdQueue: make(chan *firewalldCmd, 4096),
|
||||||
}
|
}
|
||||||
|
|
||||||
path, err := exec.LookPath("firewall-cmd")
|
path, err := executils.LookPath("firewall-cmd")
|
||||||
if err == nil && len(path) > 0 {
|
if err == nil && len(path) > 0 {
|
||||||
var cmd = executils.NewTimeoutCmd(3*time.Second, path, "--state")
|
var cmd = executils.NewTimeoutCmd(3*time.Second, path, "--state")
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
|
|||||||
@@ -6,12 +6,13 @@ package nftables_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/firewalls/nftables"
|
"github.com/TeaOSLab/EdgeNode/internal/firewalls/nftables"
|
||||||
|
executils "github.com/TeaOSLab/EdgeNode/internal/utils/exec"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestConn_Test(t *testing.T) {
|
func TestConn_Test(t *testing.T) {
|
||||||
_, err := exec.LookPath("nft")
|
_, err := executils.LookPath("nft")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Log("ok")
|
t.Log("ok")
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import (
|
|||||||
executils "github.com/TeaOSLab/EdgeNode/internal/utils/exec"
|
executils "github.com/TeaOSLab/EdgeNode/internal/utils/exec"
|
||||||
"github.com/iwind/TeaGo/logs"
|
"github.com/iwind/TeaGo/logs"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -55,7 +54,7 @@ func init() {
|
|||||||
|
|
||||||
// NftExePath 查找nftables可执行文件路径
|
// NftExePath 查找nftables可执行文件路径
|
||||||
func NftExePath() string {
|
func NftExePath() string {
|
||||||
path, _ := exec.LookPath("nft")
|
path, _ := executils.LookPath("nft")
|
||||||
if len(path) > 0 {
|
if len(path) > 0 {
|
||||||
return path
|
return path
|
||||||
}
|
}
|
||||||
@@ -93,14 +92,14 @@ func (this *Installer) Install() error {
|
|||||||
var cmd *executils.Cmd
|
var cmd *executils.Cmd
|
||||||
|
|
||||||
// check dnf
|
// check dnf
|
||||||
dnfExe, err := exec.LookPath("dnf")
|
dnfExe, err := executils.LookPath("dnf")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
cmd = executils.NewCmd(dnfExe, "-y", "install", "nftables")
|
cmd = executils.NewCmd(dnfExe, "-y", "install", "nftables")
|
||||||
}
|
}
|
||||||
|
|
||||||
// check apt
|
// check apt
|
||||||
if cmd == nil {
|
if cmd == nil {
|
||||||
aptExe, err := exec.LookPath("apt")
|
aptExe, err := executils.LookPath("apt")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
cmd = executils.NewCmd(aptExe, "install", "nftables")
|
cmd = executils.NewCmd(aptExe, "install", "nftables")
|
||||||
}
|
}
|
||||||
@@ -108,7 +107,7 @@ func (this *Installer) Install() error {
|
|||||||
|
|
||||||
// check yum
|
// check yum
|
||||||
if cmd == nil {
|
if cmd == nil {
|
||||||
yumExe, err := exec.LookPath("yum")
|
yumExe, err := executils.LookPath("yum")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
cmd = executils.NewCmd(yumExe, "-y", "install", "nftables")
|
cmd = executils.NewCmd(yumExe, "-y", "install", "nftables")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import (
|
|||||||
"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"
|
||||||
"os/exec"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -82,7 +81,7 @@ func (this *FirewalldAction) runActionSingleIP(action string, listType IPListTyp
|
|||||||
path := this.config.Path
|
path := this.config.Path
|
||||||
var err error
|
var err error
|
||||||
if len(path) == 0 {
|
if len(path) == 0 {
|
||||||
path, err = exec.LookPath("firewall-cmd")
|
path, err = executils.LookPath("firewall-cmd")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if this.firewalldNotFound {
|
if this.firewalldNotFound {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import (
|
|||||||
"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"
|
||||||
"github.com/iwind/TeaGo/types"
|
"github.com/iwind/TeaGo/types"
|
||||||
"os/exec"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -55,7 +54,7 @@ func (this *IPSetAction) Init(config *firewallconfigs.FirewallActionConfig) erro
|
|||||||
|
|
||||||
// 创建ipset
|
// 创建ipset
|
||||||
{
|
{
|
||||||
path, err := exec.LookPath("ipset")
|
path, err := executils.LookPath("ipset")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -99,7 +98,7 @@ func (this *IPSetAction) Init(config *firewallconfigs.FirewallActionConfig) erro
|
|||||||
|
|
||||||
// firewalld
|
// firewalld
|
||||||
if this.config.AutoAddToFirewalld {
|
if this.config.AutoAddToFirewalld {
|
||||||
path, err := exec.LookPath("firewall-cmd")
|
path, err := executils.LookPath("firewall-cmd")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -179,7 +178,7 @@ func (this *IPSetAction) Init(config *firewallconfigs.FirewallActionConfig) erro
|
|||||||
|
|
||||||
// iptables
|
// iptables
|
||||||
if this.config.AutoAddToIPTables {
|
if this.config.AutoAddToIPTables {
|
||||||
path, err := exec.LookPath("iptables")
|
path, err := executils.LookPath("iptables")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -311,7 +310,7 @@ func (this *IPSetAction) runActionSingleIP(action string, listType IPListType, i
|
|||||||
var path = this.config.Path
|
var path = this.config.Path
|
||||||
var err error
|
var err error
|
||||||
if len(path) == 0 {
|
if len(path) == 0 {
|
||||||
path, err = exec.LookPath("ipset")
|
path, err = executils.LookPath("ipset")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// 找不到ipset命令错误只提示一次
|
// 找不到ipset命令错误只提示一次
|
||||||
if this.ipsetNotfound {
|
if this.ipsetNotfound {
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
executils "github.com/TeaOSLab/EdgeNode/internal/utils/exec"
|
executils "github.com/TeaOSLab/EdgeNode/internal/utils/exec"
|
||||||
"os/exec"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -87,7 +86,7 @@ func (this *IPTablesAction) runActionSingleIP(action string, listType IPListType
|
|||||||
var path = this.config.Path
|
var path = this.config.Path
|
||||||
var err error
|
var err error
|
||||||
if len(path) == 0 {
|
if len(path) == 0 {
|
||||||
path, err = exec.LookPath("iptables")
|
path, err = executils.LookPath("iptables")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if this.iptablesNotFound {
|
if this.iptablesNotFound {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -161,7 +161,7 @@ ON "` + this.itemTableName + `" (
|
|||||||
this.db = db
|
this.db = db
|
||||||
|
|
||||||
goman.New(func() {
|
goman.New(func() {
|
||||||
events.On(events.EventQuit, func() {
|
events.OnClose(func() {
|
||||||
_ = this.Close()
|
_ = this.Close()
|
||||||
this.cleanTicker.Stop()
|
this.cleanTicker.Stop()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ func init() {
|
|||||||
SharedIPListManager.Start()
|
SharedIPListManager.Start()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
events.On(events.EventQuit, func() {
|
events.OnClose(func() {
|
||||||
SharedIPListManager.Stop()
|
SharedIPListManager.Stop()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ func TestIPListManager_check(t *testing.T) {
|
|||||||
func TestIPListManager_loop(t *testing.T) {
|
func TestIPListManager_loop(t *testing.T) {
|
||||||
manager := NewIPListManager()
|
manager := NewIPListManager()
|
||||||
manager.Start()
|
manager.Start()
|
||||||
manager.pageSize = 10
|
|
||||||
err := manager.loop()
|
err := manager.loop()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ func init() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
events.On(events.EventQuit, func() {
|
events.OnClose(func() {
|
||||||
SharedManager.Quit()
|
SharedManager.Quit()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"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/iwind/TeaGo/bootstrap"
|
_ "github.com/iwind/TeaGo/bootstrap"
|
||||||
"github.com/iwind/TeaGo/rands"
|
"github.com/iwind/TeaGo/rands"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -79,6 +80,10 @@ func TestTask_Add(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestTask_Add_Many(t *testing.T) {
|
func TestTask_Add_Many(t *testing.T) {
|
||||||
|
if !testutils.IsSingleTesting() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var task = metrics.NewTask(&serverconfigs.MetricItemConfig{
|
var task = metrics.NewTask(&serverconfigs.MetricItemConfig{
|
||||||
Id: 1,
|
Id: 1,
|
||||||
IsOn: false,
|
IsOn: false,
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import (
|
|||||||
"github.com/iwind/TeaGo/Tea"
|
"github.com/iwind/TeaGo/Tea"
|
||||||
"github.com/iwind/TeaGo/maps"
|
"github.com/iwind/TeaGo/maps"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os/exec"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -336,7 +335,7 @@ func (this *APIStream) handleNewNodeTask(message *pb.NodeStreamMessage) error {
|
|||||||
|
|
||||||
// 检查Systemd服务
|
// 检查Systemd服务
|
||||||
func (this *APIStream) handleCheckSystemdService(message *pb.NodeStreamMessage) error {
|
func (this *APIStream) handleCheckSystemdService(message *pb.NodeStreamMessage) error {
|
||||||
systemctl, err := exec.LookPath("systemctl")
|
systemctl, err := executils.LookPath("systemctl")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.replyFail(message.RequestId, "'systemctl' not found")
|
this.replyFail(message.RequestId, "'systemctl' not found")
|
||||||
return nil
|
return nil
|
||||||
@@ -378,7 +377,7 @@ func (this *APIStream) handleCheckLocalFirewall(message *pb.NodeStreamMessage) e
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
nft, err := exec.LookPath("nft")
|
nft, err := executils.LookPath("nft")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.replyFail(message.RequestId, "'nft' not found: "+err.Error())
|
this.replyFail(message.RequestId, "'nft' not found: "+err.Error())
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ import (
|
|||||||
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/iplibrary"
|
"github.com/TeaOSLab/EdgeNode/internal/iplibrary"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/stats"
|
"github.com/TeaOSLab/EdgeNode/internal/stats"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/ttlcache"
|
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
connutils "github.com/TeaOSLab/EdgeNode/internal/utils/conns"
|
connutils "github.com/TeaOSLab/EdgeNode/internal/utils/conns"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/utils/counters"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils/fasttime"
|
"github.com/TeaOSLab/EdgeNode/internal/utils/fasttime"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/waf"
|
"github.com/TeaOSLab/EdgeNode/internal/waf"
|
||||||
"github.com/iwind/TeaGo/Tea"
|
"github.com/iwind/TeaGo/Tea"
|
||||||
@@ -24,6 +24,8 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var synFloodCounter = counters.NewCounter().WithGC()
|
||||||
|
|
||||||
// ClientConn 客户端连接
|
// ClientConn 客户端连接
|
||||||
type ClientConn struct {
|
type ClientConn struct {
|
||||||
BaseClientConn
|
BaseClientConn
|
||||||
@@ -61,11 +63,16 @@ func NewClientConn(rawConn net.Conn, isHTTP bool, isTLS bool, isInAllowList bool
|
|||||||
isTLS: isTLS,
|
isTLS: isTLS,
|
||||||
isHTTP: isHTTP,
|
isHTTP: isHTTP,
|
||||||
isLO: strings.HasPrefix(remoteAddr, "127.0.0.1:") || strings.HasPrefix(remoteAddr, "[::1]:"),
|
isLO: strings.HasPrefix(remoteAddr, "127.0.0.1:") || strings.HasPrefix(remoteAddr, "[::1]:"),
|
||||||
isNoStat: connutils.IsNoStatConn(rawConn.RemoteAddr().String()),
|
isNoStat: connutils.IsNoStatConn(remoteAddr),
|
||||||
isInAllowList: isInAllowList,
|
isInAllowList: isInAllowList,
|
||||||
createdAt: fasttime.Now().Unix(),
|
createdAt: fasttime.Now().Unix(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if existsLnNodeIP(conn.RawIP()) {
|
||||||
|
conn.SetIsPersistent(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 超时等设置
|
||||||
var globalServerConfig = sharedNodeConfig.GlobalServerConfig
|
var globalServerConfig = sharedNodeConfig.GlobalServerConfig
|
||||||
if globalServerConfig != nil {
|
if globalServerConfig != nil {
|
||||||
var performanceConfig = globalServerConfig.Performance
|
var performanceConfig = globalServerConfig.Performance
|
||||||
@@ -129,7 +136,7 @@ func (this *ClientConn) Read(b []byte) (n int, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 忽略白名单和局域网
|
// 忽略白名单和局域网
|
||||||
if this.isHTTP && !this.isInAllowList && !utils.IsLocalIP(this.RawIP()) {
|
if !this.isPersistent && this.isHTTP && !this.isInAllowList && !utils.IsLocalIP(this.RawIP()) {
|
||||||
// SYN Flood检测
|
// SYN Flood检测
|
||||||
if this.serverId == 0 || !this.hasResetSYNFlood {
|
if this.serverId == 0 || !this.hasResetSYNFlood {
|
||||||
var synFloodConfig = sharedNodeConfig.SYNFloodConfig()
|
var synFloodConfig = sharedNodeConfig.SYNFloodConfig()
|
||||||
@@ -165,8 +172,7 @@ func (this *ClientConn) Write(b []byte) (n int, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 设置写超时时间
|
// 设置写超时时间
|
||||||
if this.autoWriteTimeout {
|
if !this.isPersistent && this.autoWriteTimeout {
|
||||||
// TODO L2 -> L1 写入时不限制时间
|
|
||||||
var timeoutSeconds = len(b) / 1024
|
var timeoutSeconds = len(b) / 1024
|
||||||
if timeoutSeconds < 3 {
|
if timeoutSeconds < 3 {
|
||||||
timeoutSeconds = 3
|
timeoutSeconds = 3
|
||||||
@@ -285,14 +291,13 @@ func (this *ClientConn) LastErr() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *ClientConn) resetSYNFlood() {
|
func (this *ClientConn) resetSYNFlood() {
|
||||||
ttlcache.SharedCache.Delete("SYN_FLOOD:" + this.RawIP())
|
synFloodCounter.ResetKey("SYN_FLOOD:" + this.RawIP())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *ClientConn) increaseSYNFlood(synFloodConfig *firewallconfigs.SYNFloodConfig) {
|
func (this *ClientConn) increaseSYNFlood(synFloodConfig *firewallconfigs.SYNFloodConfig) {
|
||||||
var ip = this.RawIP()
|
var ip = this.RawIP()
|
||||||
if len(ip) > 0 && !iplibrary.IsInWhiteList(ip) && (!synFloodConfig.IgnoreLocal || !utils.IsLocalIP(ip)) {
|
if len(ip) > 0 && !iplibrary.IsInWhiteList(ip) && (!synFloodConfig.IgnoreLocal || !utils.IsLocalIP(ip)) {
|
||||||
var timestamp = fasttime.Now().UnixNextMinute()
|
var result = synFloodCounter.IncreaseKey("SYN_FLOOD:"+ip, 60)
|
||||||
var result = ttlcache.SharedCache.IncreaseInt64("SYN_FLOOD:"+ip, 1, timestamp, true)
|
|
||||||
var minAttempts = synFloodConfig.MinAttempts
|
var minAttempts = synFloodConfig.MinAttempts
|
||||||
if minAttempts < 5 {
|
if minAttempts < 5 {
|
||||||
minAttempts = 5
|
minAttempts = 5
|
||||||
@@ -301,7 +306,7 @@ func (this *ClientConn) increaseSYNFlood(synFloodConfig *firewallconfigs.SYNFloo
|
|||||||
// 非TLS,设置为两倍,防止误封
|
// 非TLS,设置为两倍,防止误封
|
||||||
minAttempts = 2 * minAttempts
|
minAttempts = 2 * minAttempts
|
||||||
}
|
}
|
||||||
if result >= int64(minAttempts) {
|
if result >= types.Uint64(minAttempts) {
|
||||||
var timeout = synFloodConfig.TimeoutSeconds
|
var timeout = synFloodConfig.TimeoutSeconds
|
||||||
if timeout <= 0 {
|
if timeout <= 0 {
|
||||||
timeout = 600
|
timeout = 600
|
||||||
|
|||||||
@@ -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"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type BaseClientConn struct {
|
type BaseClientConn struct {
|
||||||
@@ -54,12 +55,13 @@ func (this *BaseClientConn) SetServerId(serverId int64) (goNext bool) {
|
|||||||
goNext = true
|
goNext = true
|
||||||
|
|
||||||
// 检查服务相关IP黑名单
|
// 检查服务相关IP黑名单
|
||||||
if serverId > 0 && len(this.rawIP) > 0 {
|
var rawIP = this.RawIP()
|
||||||
|
if serverId > 0 && len(rawIP) > 0 {
|
||||||
// 是否在白名单中
|
// 是否在白名单中
|
||||||
ok, _, expiresAt := iplibrary.AllowIP(this.rawIP, serverId)
|
ok, _, expiresAt := iplibrary.AllowIP(rawIP, serverId)
|
||||||
if !ok {
|
if !ok {
|
||||||
_ = this.rawConn.Close()
|
_ = this.rawConn.Close()
|
||||||
firewalls.DropTemporaryTo(this.rawIP, expiresAt)
|
firewalls.DropTemporaryTo(rawIP, expiresAt)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -123,8 +125,8 @@ func (this *BaseClientConn) TCPConn() (tcpConn *net.TCPConn, ok bool) {
|
|||||||
switch conn := this.rawConn.(type) {
|
switch conn := this.rawConn.(type) {
|
||||||
case *tls.Conn:
|
case *tls.Conn:
|
||||||
var internalConn = conn.NetConn()
|
var internalConn = conn.NetConn()
|
||||||
clientConn, ok := internalConn.(*ClientConn)
|
clientConn, isClientConn := internalConn.(*ClientConn)
|
||||||
if ok {
|
if isClientConn {
|
||||||
return clientConn.TCPConn()
|
return clientConn.TCPConn()
|
||||||
}
|
}
|
||||||
tcpConn, ok = internalConn.(*net.TCPConn)
|
tcpConn, ok = internalConn.(*net.TCPConn)
|
||||||
@@ -145,6 +147,8 @@ func (this *BaseClientConn) SetLinger(seconds int) error {
|
|||||||
|
|
||||||
func (this *BaseClientConn) SetIsPersistent(isPersistent bool) {
|
func (this *BaseClientConn) SetIsPersistent(isPersistent bool) {
|
||||||
this.isPersistent = isPersistent
|
this.isPersistent = isPersistent
|
||||||
|
|
||||||
|
_ = this.rawConn.SetDeadline(time.Time{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetFingerprint 设置指纹信息
|
// SetFingerprint 设置指纹信息
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"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/pires/go-proxyproto"
|
"github.com/pires/go-proxyproto"
|
||||||
|
"golang.org/x/net/http2"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"runtime"
|
"runtime"
|
||||||
@@ -166,6 +167,11 @@ func (this *HTTPClientPool) Client(req *HTTPRequest,
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// support http/2
|
||||||
|
if origin.HTTP2Enabled && origin.Addr != nil && origin.Addr.Protocol == serverconfigs.ProtocolHTTPS {
|
||||||
|
_ = http2.ConfigureTransport(transport.Transport)
|
||||||
|
}
|
||||||
|
|
||||||
rawClient = &http.Client{
|
rawClient = &http.Client{
|
||||||
Timeout: readTimeout,
|
Timeout: readTimeout,
|
||||||
Transport: transport,
|
Transport: transport,
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ type HTTPRequest struct {
|
|||||||
ServerAddr string // 实际启动的服务器监听地址
|
ServerAddr string // 实际启动的服务器监听地址
|
||||||
IsHTTP bool
|
IsHTTP bool
|
||||||
IsHTTPS bool
|
IsHTTPS bool
|
||||||
|
IsHTTP3 bool
|
||||||
|
|
||||||
// 共享参数
|
// 共享参数
|
||||||
nodeConfig *nodeconfigs.NodeConfig
|
nodeConfig *nodeconfigs.NodeConfig
|
||||||
@@ -479,6 +480,17 @@ func (this *HTTPRequest) configureWeb(web *serverconfigs.HTTPWebConfig, isTop bo
|
|||||||
// remote addr
|
// remote addr
|
||||||
if web.RemoteAddr != nil && (web.RemoteAddr.IsPrior || isTop) && web.RemoteAddr.IsOn {
|
if web.RemoteAddr != nil && (web.RemoteAddr.IsPrior || isTop) && web.RemoteAddr.IsOn {
|
||||||
this.web.RemoteAddr = web.RemoteAddr
|
this.web.RemoteAddr = web.RemoteAddr
|
||||||
|
|
||||||
|
// check if from proxy
|
||||||
|
if len(this.web.RemoteAddr.Value) > 0 && this.web.RemoteAddr.Value != "${rawRemoteAddr}" {
|
||||||
|
var requestConn = this.RawReq.Context().Value(HTTPConnContextKey)
|
||||||
|
if requestConn != nil {
|
||||||
|
requestClientConn, ok := requestConn.(ClientConnInterface)
|
||||||
|
if ok {
|
||||||
|
requestClientConn.SetIsPersistent(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// charset
|
// charset
|
||||||
@@ -497,6 +509,11 @@ func (this *HTTPRequest) configureWeb(web *serverconfigs.HTTPWebConfig, isTop bo
|
|||||||
this.web.Compression = web.Compression
|
this.web.Compression = web.Compression
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// optimizer
|
||||||
|
if web.Optimization != nil && (web.Optimization.IsPrior || (isTop && web.Optimization.IsOn())) {
|
||||||
|
this.web.Optimization = web.Optimization
|
||||||
|
}
|
||||||
|
|
||||||
// webp
|
// webp
|
||||||
if web.WebP != nil && (web.WebP.IsPrior || isTop) {
|
if web.WebP != nil && (web.WebP.IsPrior || isTop) {
|
||||||
this.web.WebP = web.WebP
|
this.web.WebP = web.WebP
|
||||||
@@ -866,7 +883,23 @@ func (this *HTTPRequest) Format(source string) string {
|
|||||||
}
|
}
|
||||||
switch suffix[:dotIndex] {
|
switch suffix[:dotIndex] {
|
||||||
case "header":
|
case "header":
|
||||||
return this.writer.Header().Get(suffix[dotIndex+1:])
|
var headers = this.writer.Header()
|
||||||
|
var headerKey = suffix[dotIndex+1:]
|
||||||
|
v, found := headers[headerKey]
|
||||||
|
if found {
|
||||||
|
if len(v) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return v[0]
|
||||||
|
}
|
||||||
|
var canonicalHeaderKey = http.CanonicalHeaderKey(headerKey)
|
||||||
|
if canonicalHeaderKey != headerKey {
|
||||||
|
v = headers[canonicalHeaderKey]
|
||||||
|
if len(v) > 0 {
|
||||||
|
return v[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1288,6 +1321,12 @@ func (this *HTTPRequest) requestQueryParam(name string) string {
|
|||||||
func (this *HTTPRequest) requestHeader(key string) string {
|
func (this *HTTPRequest) requestHeader(key string) string {
|
||||||
v, found := this.RawReq.Header[key]
|
v, found := this.RawReq.Header[key]
|
||||||
if !found {
|
if !found {
|
||||||
|
// 转换为canonical header再尝试
|
||||||
|
var canonicalHeaderKey = http.CanonicalHeaderKey(key)
|
||||||
|
if canonicalHeaderKey != key {
|
||||||
|
return strings.Join(this.RawReq.Header[canonicalHeaderKey], ";")
|
||||||
|
}
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
return strings.Join(v, ";")
|
return strings.Join(v, ";")
|
||||||
@@ -1628,6 +1667,20 @@ func (this *HTTPRequest) processRequestHeaders(reqHeader http.Header) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 非标Header
|
||||||
|
if len(this.web.RequestHeaderPolicy.NonStandardHeaders) > 0 {
|
||||||
|
for _, name := range this.web.RequestHeaderPolicy.NonStandardHeaders {
|
||||||
|
var canonicalKey = http.CanonicalHeaderKey(name)
|
||||||
|
if canonicalKey != name {
|
||||||
|
v, ok := reqHeader[canonicalKey]
|
||||||
|
if ok {
|
||||||
|
delete(reqHeader, canonicalKey)
|
||||||
|
reqHeader[name] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1662,8 +1715,13 @@ func (this *HTTPRequest) fixRequestHeader(header http.Header) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理自定义Response Header
|
// ProcessResponseHeaders 处理自定义Response Header
|
||||||
func (this *HTTPRequest) processResponseHeaders(responseHeader http.Header, statusCode int) {
|
func (this *HTTPRequest) ProcessResponseHeaders(responseHeader http.Header, statusCode int) {
|
||||||
|
// Server Name
|
||||||
|
if this.nodeConfig != nil && this.nodeConfig.GlobalServerConfig != nil && len(this.nodeConfig.GlobalServerConfig.HTTPAll.ServerName) > 0 {
|
||||||
|
responseHeader.Set("Server", this.nodeConfig.GlobalServerConfig.HTTPAll.ServerName)
|
||||||
|
}
|
||||||
|
|
||||||
// 删除/添加/替换Header
|
// 删除/添加/替换Header
|
||||||
// TODO 实现AddTrailers
|
// TODO 实现AddTrailers
|
||||||
if this.web.ResponseHeaderPolicy != nil && this.web.ResponseHeaderPolicy.IsOn {
|
if this.web.ResponseHeaderPolicy != nil && this.web.ResponseHeaderPolicy.IsOn {
|
||||||
@@ -1727,8 +1785,22 @@ func (this *HTTPRequest) processResponseHeaders(responseHeader http.Header, stat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 非标Header
|
||||||
|
if len(this.web.ResponseHeaderPolicy.NonStandardHeaders) > 0 {
|
||||||
|
for _, name := range this.web.ResponseHeaderPolicy.NonStandardHeaders {
|
||||||
|
var canonicalKey = http.CanonicalHeaderKey(name)
|
||||||
|
if canonicalKey != name {
|
||||||
|
v, ok := responseHeader[canonicalKey]
|
||||||
|
if ok {
|
||||||
|
delete(responseHeader, canonicalKey)
|
||||||
|
responseHeader[name] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// CORS
|
// CORS
|
||||||
if this.web.ResponseHeaderPolicy.CORS != nil && this.web.ResponseHeaderPolicy.CORS.IsOn {
|
if this.web.ResponseHeaderPolicy.CORS != nil && this.web.ResponseHeaderPolicy.CORS.IsOn && (!this.web.ResponseHeaderPolicy.CORS.OptionsMethodOnly || this.RawReq.Method == http.MethodOptions) {
|
||||||
var corsConfig = this.web.ResponseHeaderPolicy.CORS
|
var corsConfig = this.web.ResponseHeaderPolicy.CORS
|
||||||
|
|
||||||
// Allow-Origin
|
// Allow-Origin
|
||||||
@@ -1743,7 +1815,7 @@ func (this *HTTPRequest) processResponseHeaders(responseHeader http.Header, stat
|
|||||||
|
|
||||||
// Allow-Methods
|
// Allow-Methods
|
||||||
if len(corsConfig.AllowMethods) == 0 {
|
if len(corsConfig.AllowMethods) == 0 {
|
||||||
responseHeader.Set("Access-Control-Allow-Methods", "PUT, GET, POST, DELETE, HEAD, OPTIONS")
|
responseHeader.Set("Access-Control-Allow-Methods", "PUT, GET, POST, DELETE, HEAD, OPTIONS, PATCH")
|
||||||
} else {
|
} else {
|
||||||
responseHeader.Set("Access-Control-Allow-Methods", strings.Join(corsConfig.AllowMethods, ", "))
|
responseHeader.Set("Access-Control-Allow-Methods", strings.Join(corsConfig.AllowMethods, ", "))
|
||||||
}
|
}
|
||||||
@@ -1753,6 +1825,16 @@ func (this *HTTPRequest) processResponseHeaders(responseHeader http.Header, stat
|
|||||||
responseHeader.Set("Access-Control-Max-Age", types.String(corsConfig.MaxAge))
|
responseHeader.Set("Access-Control-Max-Age", types.String(corsConfig.MaxAge))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Expose-Headers
|
||||||
|
if len(corsConfig.ExposeHeaders) > 0 {
|
||||||
|
responseHeader.Set("Access-Control-Expose-Headers", strings.Join(corsConfig.ExposeHeaders, ", "))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request-Method
|
||||||
|
if len(corsConfig.RequestMethod) > 0 {
|
||||||
|
responseHeader.Set("Access-Control-Request-Method", strings.ToUpper(corsConfig.RequestMethod))
|
||||||
|
}
|
||||||
|
|
||||||
// Allow-Credentials
|
// Allow-Credentials
|
||||||
responseHeader.Set("Access-Control-Allow-Credentials", "true")
|
responseHeader.Set("Access-Control-Allow-Credentials", "true")
|
||||||
}
|
}
|
||||||
@@ -1768,6 +1850,11 @@ func (this *HTTPRequest) processResponseHeaders(responseHeader http.Header, stat
|
|||||||
this.ReqServer.HTTPS.SSLPolicy.HSTS.Match(this.ReqHost) {
|
this.ReqServer.HTTPS.SSLPolicy.HSTS.Match(this.ReqHost) {
|
||||||
responseHeader.Set(this.ReqServer.HTTPS.SSLPolicy.HSTS.HeaderKey(), this.ReqServer.HTTPS.SSLPolicy.HSTS.HeaderValue())
|
responseHeader.Set(this.ReqServer.HTTPS.SSLPolicy.HSTS.HeaderKey(), this.ReqServer.HTTPS.SSLPolicy.HSTS.HeaderValue())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HTTP/3
|
||||||
|
if this.IsHTTPS && !this.IsHTTP3 && this.ReqServer.SupportsHTTP3() {
|
||||||
|
this.processHTTP3Headers(responseHeader)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加错误信息
|
// 添加错误信息
|
||||||
@@ -1837,7 +1924,7 @@ func (this *HTTPRequest) canIgnore(err error) bool {
|
|||||||
|
|
||||||
// 检查连接是否已关闭
|
// 检查连接是否已关闭
|
||||||
func (this *HTTPRequest) isConnClosed() bool {
|
func (this *HTTPRequest) isConnClosed() bool {
|
||||||
requestConn := this.RawReq.Context().Value(HTTPConnContextKey)
|
var requestConn = this.RawReq.Context().Value(HTTPConnContextKey)
|
||||||
if requestConn == nil {
|
if requestConn == nil {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ func (this *HTTPRequest) doCacheRead(useStale bool) (shouldStop bool) {
|
|||||||
var addStatusHeader = this.web.Cache.AddStatusHeader
|
var addStatusHeader = this.web.Cache.AddStatusHeader
|
||||||
if addStatusHeader {
|
if addStatusHeader {
|
||||||
defer func() {
|
defer func() {
|
||||||
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)
|
||||||
}
|
}
|
||||||
@@ -48,7 +48,7 @@ func (this *HTTPRequest) doCacheRead(useStale bool) (shouldStop bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 检查服务独立的缓存条件
|
// 检查服务独立的缓存条件
|
||||||
refType := ""
|
var refType = ""
|
||||||
for _, cacheRef := range this.web.Cache.CacheRefs {
|
for _, cacheRef := range this.web.Cache.CacheRefs {
|
||||||
if !cacheRef.IsOn {
|
if !cacheRef.IsOn {
|
||||||
continue
|
continue
|
||||||
@@ -131,7 +131,7 @@ func (this *HTTPRequest) doCacheRead(useStale bool) (shouldStop bool) {
|
|||||||
this.varMapping["cache.key"] = key
|
this.varMapping["cache.key"] = key
|
||||||
|
|
||||||
// 读取缓存
|
// 读取缓存
|
||||||
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
|
||||||
return
|
return
|
||||||
@@ -241,16 +241,19 @@ func (this *HTTPRequest) doCacheRead(useStale bool) (shouldStop bool) {
|
|||||||
reader, err = storage.OpenReader(key, useStale, false)
|
reader, err = storage.OpenReader(key, useStale, false)
|
||||||
if err != nil && this.cacheRef.AllowPartialContent {
|
if err != nil && this.cacheRef.AllowPartialContent {
|
||||||
// 尝试读取分片的缓存内容
|
// 尝试读取分片的缓存内容
|
||||||
if len(rangeHeader) == 0 {
|
if len(rangeHeader) == 0 && this.cacheRef.ForcePartialContent {
|
||||||
// 默认读取开头
|
// 默认读取开头
|
||||||
rangeHeader = "bytes=0-"
|
rangeHeader = "bytes=0-"
|
||||||
}
|
}
|
||||||
pReader, ranges := this.tryPartialReader(storage, key, useStale, rangeHeader)
|
|
||||||
if pReader != nil {
|
if len(rangeHeader) > 0 {
|
||||||
isPartialCache = true
|
pReader, ranges := this.tryPartialReader(storage, key, useStale, rangeHeader)
|
||||||
reader = pReader
|
if pReader != nil {
|
||||||
partialRanges = ranges
|
isPartialCache = true
|
||||||
err = nil
|
reader = pReader
|
||||||
|
partialRanges = ranges
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -301,13 +304,13 @@ func (this *HTTPRequest) doCacheRead(useStale bool) (shouldStop bool) {
|
|||||||
this.writer.SetSentHeaderBytes(reader.HeaderSize())
|
this.writer.SetSentHeaderBytes(reader.HeaderSize())
|
||||||
var headerPool = this.bytePool(reader.HeaderSize())
|
var headerPool = this.bytePool(reader.HeaderSize())
|
||||||
var headerBuf = headerPool.Get()
|
var headerBuf = headerPool.Get()
|
||||||
err = reader.ReadHeader(headerBuf, func(n int) (goNext bool, err error) {
|
err = reader.ReadHeader(headerBuf, func(n int) (goNext bool, readErr error) {
|
||||||
headerData = append(headerData, headerBuf[:n]...)
|
headerData = append(headerData, headerBuf[:n]...)
|
||||||
for {
|
for {
|
||||||
nIndex := bytes.Index(headerData, []byte{'\n'})
|
var nIndex = bytes.Index(headerData, []byte{'\n'})
|
||||||
if nIndex >= 0 {
|
if nIndex >= 0 {
|
||||||
row := headerData[:nIndex]
|
var row = headerData[:nIndex]
|
||||||
spaceIndex := bytes.Index(row, []byte{':'})
|
var spaceIndex = bytes.Index(row, []byte{':'})
|
||||||
if spaceIndex <= 0 {
|
if spaceIndex <= 0 {
|
||||||
return false, errors.New("invalid header '" + string(row) + "'")
|
return false, errors.New("invalid header '" + string(row) + "'")
|
||||||
}
|
}
|
||||||
@@ -375,7 +378,7 @@ func (this *HTTPRequest) doCacheRead(useStale bool) (shouldStop bool) {
|
|||||||
// 支持 If-None-Match
|
// 支持 If-None-Match
|
||||||
if !this.isLnRequest && !isPartialCache && len(eTag) > 0 && this.requestHeader("If-None-Match") == eTag {
|
if !this.isLnRequest && !isPartialCache && len(eTag) > 0 && this.requestHeader("If-None-Match") == eTag {
|
||||||
// 自定义Header
|
// 自定义Header
|
||||||
this.processResponseHeaders(this.writer.Header(), http.StatusNotModified)
|
this.ProcessResponseHeaders(this.writer.Header(), http.StatusNotModified)
|
||||||
this.addExpiresHeader(reader.ExpiresAt())
|
this.addExpiresHeader(reader.ExpiresAt())
|
||||||
this.writer.WriteHeader(http.StatusNotModified)
|
this.writer.WriteHeader(http.StatusNotModified)
|
||||||
this.isCached = true
|
this.isCached = true
|
||||||
@@ -387,7 +390,7 @@ func (this *HTTPRequest) doCacheRead(useStale bool) (shouldStop bool) {
|
|||||||
// 支持 If-Modified-Since
|
// 支持 If-Modified-Since
|
||||||
if !this.isLnRequest && !isPartialCache && len(modifiedTime) > 0 && this.requestHeader("If-Modified-Since") == modifiedTime {
|
if !this.isLnRequest && !isPartialCache && len(modifiedTime) > 0 && this.requestHeader("If-Modified-Since") == modifiedTime {
|
||||||
// 自定义Header
|
// 自定义Header
|
||||||
this.processResponseHeaders(this.writer.Header(), http.StatusNotModified)
|
this.ProcessResponseHeaders(this.writer.Header(), http.StatusNotModified)
|
||||||
this.addExpiresHeader(reader.ExpiresAt())
|
this.addExpiresHeader(reader.ExpiresAt())
|
||||||
this.writer.WriteHeader(http.StatusNotModified)
|
this.writer.WriteHeader(http.StatusNotModified)
|
||||||
this.isCached = true
|
this.isCached = true
|
||||||
@@ -396,7 +399,7 @@ func (this *HTTPRequest) doCacheRead(useStale bool) (shouldStop bool) {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
this.processResponseHeaders(this.writer.Header(), reader.Status())
|
this.ProcessResponseHeaders(this.writer.Header(), reader.Status())
|
||||||
this.addExpiresHeader(reader.ExpiresAt())
|
this.addExpiresHeader(reader.ExpiresAt())
|
||||||
|
|
||||||
// 返回上级节点过期时间
|
// 返回上级节点过期时间
|
||||||
@@ -425,7 +428,7 @@ func (this *HTTPRequest) doCacheRead(useStale bool) (shouldStop bool) {
|
|||||||
if supportRange {
|
if supportRange {
|
||||||
if len(rangeHeader) > 0 {
|
if len(rangeHeader) > 0 {
|
||||||
if fileSize == 0 {
|
if fileSize == 0 {
|
||||||
this.processResponseHeaders(this.writer.Header(), http.StatusRequestedRangeNotSatisfiable)
|
this.ProcessResponseHeaders(this.writer.Header(), http.StatusRequestedRangeNotSatisfiable)
|
||||||
this.writer.WriteHeader(http.StatusRequestedRangeNotSatisfiable)
|
this.writer.WriteHeader(http.StatusRequestedRangeNotSatisfiable)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -433,7 +436,7 @@ func (this *HTTPRequest) doCacheRead(useStale bool) (shouldStop bool) {
|
|||||||
if len(ranges) == 0 {
|
if len(ranges) == 0 {
|
||||||
ranges, ok = httpRequestParseRangeHeader(rangeHeader)
|
ranges, ok = httpRequestParseRangeHeader(rangeHeader)
|
||||||
if !ok {
|
if !ok {
|
||||||
this.processResponseHeaders(this.writer.Header(), http.StatusRequestedRangeNotSatisfiable)
|
this.ProcessResponseHeaders(this.writer.Header(), http.StatusRequestedRangeNotSatisfiable)
|
||||||
this.writer.WriteHeader(http.StatusRequestedRangeNotSatisfiable)
|
this.writer.WriteHeader(http.StatusRequestedRangeNotSatisfiable)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -442,7 +445,7 @@ func (this *HTTPRequest) doCacheRead(useStale bool) (shouldStop bool) {
|
|||||||
for k, r := range ranges {
|
for k, r := range ranges {
|
||||||
r2, ok := r.Convert(fileSize)
|
r2, ok := r.Convert(fileSize)
|
||||||
if !ok {
|
if !ok {
|
||||||
this.processResponseHeaders(this.writer.Header(), http.StatusRequestedRangeNotSatisfiable)
|
this.ProcessResponseHeaders(this.writer.Header(), http.StatusRequestedRangeNotSatisfiable)
|
||||||
this.writer.WriteHeader(http.StatusRequestedRangeNotSatisfiable)
|
this.writer.WriteHeader(http.StatusRequestedRangeNotSatisfiable)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -460,9 +463,9 @@ func (this *HTTPRequest) doCacheRead(useStale bool) (shouldStop bool) {
|
|||||||
|
|
||||||
var pool = this.bytePool(fileSize)
|
var pool = this.bytePool(fileSize)
|
||||||
var bodyBuf = pool.Get()
|
var bodyBuf = pool.Get()
|
||||||
err = reader.ReadBodyRange(bodyBuf, ranges[0].Start(), ranges[0].End(), func(n int) (goNext bool, err error) {
|
err = reader.ReadBodyRange(bodyBuf, ranges[0].Start(), ranges[0].End(), func(n int) (goNext bool, readErr error) {
|
||||||
_, err = this.writer.Write(bodyBuf[:n])
|
_, readErr = this.writer.Write(bodyBuf[:n])
|
||||||
if err != nil {
|
if readErr != nil {
|
||||||
return false, errWritingToClient
|
return false, errWritingToClient
|
||||||
}
|
}
|
||||||
return true, nil
|
return true, nil
|
||||||
@@ -472,7 +475,7 @@ func (this *HTTPRequest) doCacheRead(useStale bool) (shouldStop bool) {
|
|||||||
this.varMapping["cache.status"] = "MISS"
|
this.varMapping["cache.status"] = "MISS"
|
||||||
|
|
||||||
if err == caches.ErrInvalidRange {
|
if err == caches.ErrInvalidRange {
|
||||||
this.processResponseHeaders(this.writer.Header(), http.StatusRequestedRangeNotSatisfiable)
|
this.ProcessResponseHeaders(this.writer.Header(), http.StatusRequestedRangeNotSatisfiable)
|
||||||
this.writer.WriteHeader(http.StatusRequestedRangeNotSatisfiable)
|
this.writer.WriteHeader(http.StatusRequestedRangeNotSatisfiable)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -485,7 +488,7 @@ func (this *HTTPRequest) doCacheRead(useStale bool) (shouldStop bool) {
|
|||||||
var boundary = httpRequestGenBoundary()
|
var boundary = httpRequestGenBoundary()
|
||||||
respHeader.Set("Content-Type", "multipart/byteranges; boundary="+boundary)
|
respHeader.Set("Content-Type", "multipart/byteranges; boundary="+boundary)
|
||||||
respHeader.Del("Content-Length")
|
respHeader.Del("Content-Length")
|
||||||
contentType := respHeader.Get("Content-Type")
|
var contentType = respHeader.Get("Content-Type")
|
||||||
|
|
||||||
this.writer.WriteHeader(http.StatusPartialContent)
|
this.writer.WriteHeader(http.StatusPartialContent)
|
||||||
|
|
||||||
@@ -516,9 +519,9 @@ func (this *HTTPRequest) doCacheRead(useStale bool) (shouldStop bool) {
|
|||||||
|
|
||||||
var pool = this.bytePool(fileSize)
|
var pool = this.bytePool(fileSize)
|
||||||
var bodyBuf = pool.Get()
|
var bodyBuf = pool.Get()
|
||||||
err := reader.ReadBodyRange(bodyBuf, r.Start(), r.End(), func(n int) (goNext bool, err error) {
|
err = reader.ReadBodyRange(bodyBuf, r.Start(), r.End(), func(n int) (goNext bool, readErr error) {
|
||||||
_, err = this.writer.Write(bodyBuf[:n])
|
_, readErr = this.writer.Write(bodyBuf[:n])
|
||||||
if err != nil {
|
if readErr != nil {
|
||||||
return false, errWritingToClient
|
return false, errWritingToClient
|
||||||
}
|
}
|
||||||
return true, nil
|
return true, nil
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ func (this *HTTPRequest) writeCode(statusCode int, enMessage string, zhMessage s
|
|||||||
return "${" + varName + "}"
|
return "${" + varName + "}"
|
||||||
})
|
})
|
||||||
|
|
||||||
this.processResponseHeaders(this.writer.Header(), statusCode)
|
this.ProcessResponseHeaders(this.writer.Header(), statusCode)
|
||||||
this.writer.WriteHeader(statusCode)
|
this.writer.WriteHeader(statusCode)
|
||||||
|
|
||||||
_, _ = this.writer.Write([]byte(pageContent))
|
_, _ = this.writer.Write([]byte(pageContent))
|
||||||
@@ -110,7 +110,7 @@ func (this *HTTPRequest) write50x(err error, statusCode int, enMessage string, z
|
|||||||
return "${" + varName + "}"
|
return "${" + varName + "}"
|
||||||
})
|
})
|
||||||
|
|
||||||
this.processResponseHeaders(this.writer.Header(), statusCode)
|
this.ProcessResponseHeaders(this.writer.Header(), statusCode)
|
||||||
this.writer.WriteHeader(statusCode)
|
this.writer.WriteHeader(statusCode)
|
||||||
|
|
||||||
_, _ = this.writer.Write([]byte(pageContent))
|
_, _ = this.writer.Write([]byte(pageContent))
|
||||||
|
|||||||
@@ -197,7 +197,7 @@ func (this *HTTPRequest) doFastcgi() (shouldStop bool) {
|
|||||||
|
|
||||||
// 响应Header
|
// 响应Header
|
||||||
this.writer.AddHeaders(resp.Header)
|
this.writer.AddHeaders(resp.Header)
|
||||||
this.processResponseHeaders(this.writer.Header(), resp.StatusCode)
|
this.ProcessResponseHeaders(this.writer.Header(), resp.StatusCode)
|
||||||
|
|
||||||
// 准备
|
// 准备
|
||||||
this.writer.Prepare(resp, resp.ContentLength, resp.StatusCode, true)
|
this.writer.Prepare(resp, resp.ContentLength, resp.StatusCode, true)
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ func (this *HTTPRequest) doHostRedirect() (blocked bool) {
|
|||||||
if this.web.MergeSlashes {
|
if this.web.MergeSlashes {
|
||||||
urlPath = utils.CleanPath(urlPath)
|
urlPath = utils.CleanPath(urlPath)
|
||||||
}
|
}
|
||||||
var fullURL = this.requestScheme() + "://" + this.ReqHost + urlPath
|
|
||||||
for _, u := range this.web.HostRedirects {
|
for _, u := range this.web.HostRedirects {
|
||||||
if !u.IsOn {
|
if !u.IsOn {
|
||||||
continue
|
continue
|
||||||
@@ -35,10 +34,17 @@ func (this *HTTPRequest) doHostRedirect() (blocked bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var fullURL = ""
|
||||||
|
if u.BeforeHasQuery() {
|
||||||
|
fullURL = this.URL()
|
||||||
|
} else {
|
||||||
|
fullURL = this.requestScheme() + "://" + this.ReqHost + urlPath
|
||||||
|
}
|
||||||
|
|
||||||
if len(u.Type) == 0 || u.Type == serverconfigs.HTTPHostRedirectTypeURL {
|
if len(u.Type) == 0 || u.Type == serverconfigs.HTTPHostRedirectTypeURL {
|
||||||
if u.MatchPrefix { // 匹配前缀
|
if u.MatchPrefix { // 匹配前缀
|
||||||
if strings.HasPrefix(fullURL, u.BeforeURL) {
|
if strings.HasPrefix(fullURL, u.BeforeURL) {
|
||||||
afterURL := u.AfterURL
|
var afterURL = u.AfterURL
|
||||||
if u.KeepRequestURI {
|
if u.KeepRequestURI {
|
||||||
afterURL += this.RawReq.URL.RequestURI()
|
afterURL += this.RawReq.URL.RequestURI()
|
||||||
}
|
}
|
||||||
@@ -48,7 +54,7 @@ func (this *HTTPRequest) doHostRedirect() (blocked bool) {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
this.processResponseHeaders(this.writer.Header(), status)
|
this.ProcessResponseHeaders(this.writer.Header(), status)
|
||||||
http.Redirect(this.RawWriter, this.RawReq, afterURL, status)
|
http.Redirect(this.RawWriter, this.RawReq, afterURL, status)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -90,7 +96,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)
|
http.Redirect(this.RawWriter, this.RawReq, afterURL, status)
|
||||||
return true
|
return true
|
||||||
} else { // 精准匹配
|
} else { // 精准匹配
|
||||||
@@ -104,11 +110,16 @@ func (this *HTTPRequest) doHostRedirect() (blocked bool) {
|
|||||||
if u.KeepArgs {
|
if u.KeepArgs {
|
||||||
var qIndex = strings.Index(this.uri, "?")
|
var qIndex = strings.Index(this.uri, "?")
|
||||||
if qIndex >= 0 {
|
if qIndex >= 0 {
|
||||||
afterURL += this.uri[qIndex:]
|
var afterQIndex = strings.Index(u.AfterURL, "?")
|
||||||
|
if afterQIndex >= 0 {
|
||||||
|
afterURL = u.AfterURL[:afterQIndex] + this.uri[qIndex:]
|
||||||
|
} else {
|
||||||
|
afterURL += this.uri[qIndex:]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.processResponseHeaders(this.writer.Header(), status)
|
this.ProcessResponseHeaders(this.writer.Header(), status)
|
||||||
http.Redirect(this.RawWriter, this.RawReq, afterURL, status)
|
http.Redirect(this.RawWriter, this.RawReq, afterURL, status)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -144,7 +155,7 @@ func (this *HTTPRequest) doHostRedirect() (blocked bool) {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
this.processResponseHeaders(this.writer.Header(), status)
|
this.ProcessResponseHeaders(this.writer.Header(), status)
|
||||||
|
|
||||||
// 参数
|
// 参数
|
||||||
var qIndex = strings.Index(this.uri, "?")
|
var qIndex = strings.Index(this.uri, "?")
|
||||||
@@ -200,7 +211,7 @@ func (this *HTTPRequest) doHostRedirect() (blocked bool) {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
this.processResponseHeaders(this.writer.Header(), status)
|
this.ProcessResponseHeaders(this.writer.Header(), status)
|
||||||
http.Redirect(this.RawWriter, this.RawReq, afterURL, status)
|
http.Redirect(this.RawWriter, this.RawReq, afterURL, status)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
10
internal/nodes/http_request_http3.go
Normal file
10
internal/nodes/http_request_http3.go
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||||
|
//go:build !plus
|
||||||
|
|
||||||
|
package nodes
|
||||||
|
|
||||||
|
import "net/http"
|
||||||
|
|
||||||
|
func (this *HTTPRequest) processHTTP3Headers(respHeader http.Header) {
|
||||||
|
// stub
|
||||||
|
}
|
||||||
@@ -12,6 +12,10 @@ const (
|
|||||||
LNExpiresHeader = "X-Edge-Ln-Expires"
|
LNExpiresHeader = "X-Edge-Ln-Expires"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func existsLnNodeIP(nodeIP string) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func (this *HTTPRequest) checkLnRequest() bool {
|
func (this *HTTPRequest) checkLnRequest() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,11 @@ const (
|
|||||||
|
|
||||||
// 日志
|
// 日志
|
||||||
func (this *HTTPRequest) log() {
|
func (this *HTTPRequest) log() {
|
||||||
|
// 检查全局配置
|
||||||
|
if this.nodeConfig != nil && this.nodeConfig.GlobalServerConfig != nil && !this.nodeConfig.GlobalServerConfig.HTTPAccessLog.IsOn {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var ref *serverconfigs.HTTPAccessLogRef
|
var ref *serverconfigs.HTTPAccessLogRef
|
||||||
if !this.forceLog {
|
if !this.forceLog {
|
||||||
if this.disableLog {
|
if this.disableLog {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"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/waf"
|
"github.com/TeaOSLab/EdgeNode/internal/waf"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -32,7 +33,14 @@ func (this *HTTPRequest) doMismatch() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 根据配置进行相应的处理
|
// 根据配置进行相应的处理
|
||||||
if sharedNodeConfig.GlobalServerConfig != nil && sharedNodeConfig.GlobalServerConfig.HTTPAll.MatchDomainStrictly {
|
var globalServerConfig = sharedNodeConfig.GlobalServerConfig
|
||||||
|
if globalServerConfig != nil && globalServerConfig.HTTPAll.MatchDomainStrictly {
|
||||||
|
// 是否正在访问IP
|
||||||
|
if globalServerConfig.HTTPAll.NodeIPShowPage && net.ParseIP(this.ReqHost) != nil {
|
||||||
|
_, _ = this.writer.WriteString(globalServerConfig.HTTPAll.NodeIPPageHTML)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// 检查cc
|
// 检查cc
|
||||||
// TODO 可以在管理端配置是否开启以及最多尝试次数
|
// TODO 可以在管理端配置是否开启以及最多尝试次数
|
||||||
// 要考虑到服务在切换集群时,域名未生效状态时,用户访问的仍然是老集群中的节点,就会产生找不到域名的情况
|
// 要考虑到服务在切换集群时,域名未生效状态时,用户访问的仍然是老集群中的节点,就会产生找不到域名的情况
|
||||||
@@ -47,7 +55,7 @@ func (this *HTTPRequest) doMismatch() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 处理当前连接
|
// 处理当前连接
|
||||||
var httpAllConfig = sharedNodeConfig.GlobalServerConfig.HTTPAll
|
var httpAllConfig = globalServerConfig.HTTPAll
|
||||||
var mismatchAction = httpAllConfig.DomainMismatchAction
|
var mismatchAction = httpAllConfig.DomainMismatchAction
|
||||||
if mismatchAction != nil && mismatchAction.Code == "page" {
|
if mismatchAction != nil && mismatchAction.Code == "page" {
|
||||||
if mismatchAction.Options != nil {
|
if mismatchAction.Options != nil {
|
||||||
|
|||||||
15
internal/nodes/http_request_oss.go
Normal file
15
internal/nodes/http_request_oss.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||||
|
//go:build !plus
|
||||||
|
|
||||||
|
package nodes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (this *HTTPRequest) doOSSOrigin(origin *serverconfigs.OriginConfig) (resp *http.Response, goNext bool, errorCode string, ossBucketName string, err error) {
|
||||||
|
// stub
|
||||||
|
return nil, false, "", "", errors.New("not implemented")
|
||||||
|
}
|
||||||
@@ -1,28 +1,53 @@
|
|||||||
package nodes
|
package nodes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
"github.com/iwind/TeaGo/Tea"
|
"github.com/iwind/TeaGo/Tea"
|
||||||
"github.com/iwind/TeaGo/logs"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
"path"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
var urlPrefixRegexp = regexp.MustCompile("^(?i)(http|https|ftp)://")
|
|
||||||
|
|
||||||
// 请求特殊页面
|
// 请求特殊页面
|
||||||
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 {
|
||||||
|
// 集群自定义页面
|
||||||
|
if this.nodeConfig != nil && this.ReqServer != nil {
|
||||||
|
var httpPagesPolicy = this.nodeConfig.FindHTTPPagesPolicyWithClusterId(this.ReqServer.ClusterId)
|
||||||
|
if httpPagesPolicy != nil && httpPagesPolicy.IsOn && len(httpPagesPolicy.Pages) > 0 {
|
||||||
|
return this.doPageLookup(httpPagesPolicy.Pages, status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, page := range this.web.Pages {
|
// 查找当前网站自定义页面
|
||||||
|
shouldStop = this.doPageLookup(this.web.Pages, status)
|
||||||
|
if shouldStop {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 集群自定义页面
|
||||||
|
if this.nodeConfig != nil && this.ReqServer != nil {
|
||||||
|
var httpPagesPolicy = this.nodeConfig.FindHTTPPagesPolicyWithClusterId(this.ReqServer.ClusterId)
|
||||||
|
if httpPagesPolicy != nil && httpPagesPolicy.IsOn && len(httpPagesPolicy.Pages) > 0 {
|
||||||
|
return this.doPageLookup(httpPagesPolicy.Pages, status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *HTTPRequest) doPageLookup(pages []*serverconfigs.HTTPPageConfig, status int) (shouldStop bool) {
|
||||||
|
for _, page := range pages {
|
||||||
if page.Match(status) {
|
if page.Match(status) {
|
||||||
if len(page.BodyType) == 0 || page.BodyType == shared.BodyTypeURL {
|
if len(page.BodyType) == 0 || page.BodyType == shared.BodyTypeURL {
|
||||||
if urlPrefixRegexp.MatchString(page.URL) {
|
if urlSchemeRegexp.MatchString(page.URL) {
|
||||||
var newStatus = page.NewStatus
|
var newStatus = page.NewStatus
|
||||||
if newStatus <= 0 {
|
if newStatus <= 0 {
|
||||||
newStatus = status
|
newStatus = status
|
||||||
@@ -30,45 +55,45 @@ func (this *HTTPRequest) doPage(status int) (shouldStop bool) {
|
|||||||
this.doURL(http.MethodGet, page.URL, "", newStatus, true)
|
this.doURL(http.MethodGet, page.URL, "", newStatus, true)
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
file := Tea.Root + Tea.DS + page.URL
|
var realpath = path.Clean(page.URL)
|
||||||
fp, err := os.Open(file)
|
if !strings.HasPrefix(realpath, "/pages/") && !strings.HasPrefix(realpath, "pages/") { // only files under "/pages/" can be used
|
||||||
if err != nil {
|
var msg = "404 page not found: '" + page.URL + "'"
|
||||||
logs.Error(err)
|
|
||||||
msg := "404 page not found: '" + page.URL + "'"
|
|
||||||
|
|
||||||
this.writer.WriteHeader(http.StatusNotFound)
|
this.writer.WriteHeader(http.StatusNotFound)
|
||||||
_, err := this.writer.Write([]byte(msg))
|
_, _ = this.writer.Write([]byte(msg))
|
||||||
if err != nil {
|
|
||||||
logs.Error(err)
|
|
||||||
}
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
var file = Tea.Root + Tea.DS + realpath
|
||||||
|
fp, err := os.Open(file)
|
||||||
|
if err != nil {
|
||||||
|
var msg = "404 page not found: '" + page.URL + "'"
|
||||||
|
this.writer.WriteHeader(http.StatusNotFound)
|
||||||
|
_, _ = this.writer.Write([]byte(msg))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = fp.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
stat, err := fp.Stat()
|
stat, err := fp.Stat()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logs.Error(err)
|
var msg = "404 could not read page content: '" + page.URL + "'"
|
||||||
msg := "404 could not read page content: '" + page.URL + "'"
|
|
||||||
|
|
||||||
this.writer.WriteHeader(http.StatusNotFound)
|
this.writer.WriteHeader(http.StatusNotFound)
|
||||||
_, err := this.writer.Write([]byte(msg))
|
_, _ = this.writer.Write([]byte(msg))
|
||||||
if err != nil {
|
|
||||||
logs.Error(err)
|
|
||||||
}
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改状态码
|
// 修改状态码
|
||||||
if page.NewStatus > 0 {
|
if page.NewStatus > 0 {
|
||||||
// 自定义响应Headers
|
// 自定义响应Headers
|
||||||
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.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)
|
||||||
}
|
}
|
||||||
buf := utils.BytePool1k.Get()
|
var buf = utils.BytePool1k.Get()
|
||||||
_, err = utils.CopyWithFilter(this.writer, fp, buf, func(p []byte) []byte {
|
_, err = utils.CopyWithFilter(this.writer, fp, buf, func(p []byte) []byte {
|
||||||
return []byte(this.Format(string(p)))
|
return []byte(this.Format(string(p)))
|
||||||
})
|
})
|
||||||
@@ -80,10 +105,6 @@ func (this *HTTPRequest) doPage(status int) (shouldStop bool) {
|
|||||||
} else {
|
} else {
|
||||||
this.writer.SetOk()
|
this.writer.SetOk()
|
||||||
}
|
}
|
||||||
err = fp.Close()
|
|
||||||
if err != nil {
|
|
||||||
logs.Error(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
@@ -99,11 +120,11 @@ func (this *HTTPRequest) doPage(status int) (shouldStop bool) {
|
|||||||
// 修改状态码
|
// 修改状态码
|
||||||
if page.NewStatus > 0 {
|
if page.NewStatus > 0 {
|
||||||
// 自定义响应Headers
|
// 自定义响应Headers
|
||||||
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.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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ func (this *HTTPRequest) doPlanExpires() {
|
|||||||
this.tags = append(this.tags, "plan")
|
this.tags = append(this.tags, "plan")
|
||||||
|
|
||||||
var statusCode = http.StatusNotFound
|
var statusCode = http.StatusNotFound
|
||||||
this.processResponseHeaders(this.writer.Header(), statusCode)
|
this.ProcessResponseHeaders(this.writer.Header(), statusCode)
|
||||||
|
|
||||||
this.writer.WriteHeader(statusCode)
|
this.writer.WriteHeader(statusCode)
|
||||||
_, _ = this.writer.WriteString(this.Format(serverconfigs.DefaultPlanExpireNoticePageBody))
|
_, _ = this.writer.WriteString(this.Format(serverconfigs.DefaultPlanExpireNoticePageBody))
|
||||||
|
|||||||
@@ -42,7 +42,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)
|
http.Redirect(this.writer, this.RawReq, newURL, statusCode)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
|||||||
@@ -12,9 +12,32 @@ func (this *HTTPRequest) doCheckReferers() (shouldStop bool) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var origin = this.RawReq.Header.Get("Origin")
|
||||||
|
|
||||||
const cacheSeconds = "3600" // 时间不能过长,防止修改设置后长期无法生效
|
const cacheSeconds = "3600" // 时间不能过长,防止修改设置后长期无法生效
|
||||||
|
|
||||||
|
// 处理用到Origin的特殊功能
|
||||||
|
if this.web.Referers.CheckOrigin && len(origin) > 0 {
|
||||||
|
// 处理Websocket
|
||||||
|
if this.web.Websocket != nil && this.web.Websocket.IsOn && this.RawReq.Header.Get("Upgrade") == "websocket" {
|
||||||
|
originHost, _ := httpParseHost(origin)
|
||||||
|
if len(originHost) > 0 && this.web.Websocket.MatchOrigin(originHost) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var refererURL = this.RawReq.Header.Get("Referer")
|
var refererURL = this.RawReq.Header.Get("Referer")
|
||||||
|
if len(refererURL) == 0 && this.web.Referers.CheckOrigin {
|
||||||
|
if len(origin) > 0 && origin != "null" {
|
||||||
|
if urlSchemeRegexp.MatchString(origin) {
|
||||||
|
refererURL = origin
|
||||||
|
} else {
|
||||||
|
refererURL = "https://" + origin
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if len(refererURL) == 0 {
|
if len(refererURL) == 0 {
|
||||||
if this.web.Referers.MatchDomain(this.ReqHost, "") {
|
if this.web.Referers.MatchDomain(this.ReqHost, "") {
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -66,19 +66,21 @@ func (this *HTTPRequest) doOriginRequest(failedOriginIds []int64, failedLnNodeId
|
|||||||
|
|
||||||
// 二级节点
|
// 二级节点
|
||||||
var hasMultipleLnNodes = false
|
var hasMultipleLnNodes = false
|
||||||
if this.cacheRef != nil {
|
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)
|
||||||
if origin != nil {
|
if origin != nil {
|
||||||
// 强制变更原来访问的域名
|
// 强制变更原来访问的域名
|
||||||
requestHost = this.ReqHost
|
requestHost = this.ReqHost
|
||||||
}
|
}
|
||||||
|
|
||||||
// 回源Header中去除If-None-Match和If-Modified-Since
|
if this.cacheRef != nil {
|
||||||
if !this.cacheRef.EnableIfNoneMatch {
|
// 回源Header中去除If-None-Match和If-Modified-Since
|
||||||
this.DeleteHeader("If-None-Match")
|
if !this.cacheRef.EnableIfNoneMatch {
|
||||||
}
|
this.DeleteHeader("If-None-Match")
|
||||||
if !this.cacheRef.EnableIfModifiedSince {
|
}
|
||||||
this.DeleteHeader("If-Modified-Since")
|
if !this.cacheRef.EnableIfModifiedSince {
|
||||||
|
this.DeleteHeader("If-Modified-Since")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,14 +117,20 @@ func (this *HTTPRequest) doOriginRequest(failedOriginIds []int64, failedLnNodeId
|
|||||||
requestHostHasVariables = origin.RequestHostHasVariables()
|
requestHostHasVariables = origin.RequestHostHasVariables()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 处理OSS
|
||||||
|
var isHTTPOrigin = origin.OSS == nil
|
||||||
|
|
||||||
// 处理Scheme
|
// 处理Scheme
|
||||||
if origin.Addr == nil {
|
if isHTTPOrigin && origin.Addr == nil {
|
||||||
err := errors.New(this.URL() + ": Origin '" + strconv.FormatInt(origin.Id, 10) + "' does not has a address")
|
err := errors.New(this.URL() + ": Origin '" + strconv.FormatInt(origin.Id, 10) + "' does not has a address")
|
||||||
remotelogs.ErrorServer("HTTP_REQUEST_REVERSE_PROXY", err.Error())
|
remotelogs.ErrorServer("HTTP_REQUEST_REVERSE_PROXY", err.Error())
|
||||||
this.write50x(err, http.StatusBadGateway, "Origin site did not has a valid address", "源站尚未配置地址", true)
|
this.write50x(err, http.StatusBadGateway, "Origin site did not has a valid address", "源站尚未配置地址", true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.RawReq.URL.Scheme = origin.Addr.Protocol.Primary().Scheme()
|
|
||||||
|
if isHTTPOrigin {
|
||||||
|
this.RawReq.URL.Scheme = origin.Addr.Protocol.Primary().Scheme()
|
||||||
|
}
|
||||||
|
|
||||||
// StripPrefix
|
// StripPrefix
|
||||||
if len(stripPrefix) > 0 {
|
if len(stripPrefix) > 0 {
|
||||||
@@ -159,63 +167,66 @@ func (this *HTTPRequest) doOriginRequest(failedOriginIds []int64, failedLnNodeId
|
|||||||
this.uri = utils.CleanPath(this.uri)
|
this.uri = utils.CleanPath(this.uri)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取源站地址
|
var originAddr = ""
|
||||||
var originAddr = origin.Addr.PickAddress()
|
if isHTTPOrigin {
|
||||||
if origin.Addr.HostHasVariables() {
|
// 获取源站地址
|
||||||
originAddr = this.Format(originAddr)
|
originAddr = origin.Addr.PickAddress()
|
||||||
}
|
if origin.Addr.HostHasVariables() {
|
||||||
|
originAddr = this.Format(originAddr)
|
||||||
// 端口跟随
|
|
||||||
if origin.FollowPort {
|
|
||||||
var originHostIndex = strings.Index(originAddr, ":")
|
|
||||||
if originHostIndex < 0 {
|
|
||||||
var originErr = errors.New(this.URL() + ": Invalid origin address '" + originAddr + "', lacking port")
|
|
||||||
remotelogs.ErrorServer("HTTP_REQUEST_REVERSE_PROXY", originErr.Error())
|
|
||||||
this.write50x(originErr, http.StatusBadGateway, "No port in origin site address", "源站地址中没有配置端口", true)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
originAddr = originAddr[:originHostIndex+1] + types.String(this.requestServerPort())
|
|
||||||
}
|
|
||||||
this.originAddr = originAddr
|
|
||||||
|
|
||||||
// RequestHost
|
// 端口跟随
|
||||||
if len(requestHost) > 0 {
|
if origin.FollowPort {
|
||||||
if requestHostHasVariables {
|
var originHostIndex = strings.Index(originAddr, ":")
|
||||||
this.RawReq.Host = this.Format(requestHost)
|
if originHostIndex < 0 {
|
||||||
|
var originErr = errors.New(this.URL() + ": Invalid origin address '" + originAddr + "', lacking port")
|
||||||
|
remotelogs.ErrorServer("HTTP_REQUEST_REVERSE_PROXY", originErr.Error())
|
||||||
|
this.write50x(originErr, http.StatusBadGateway, "No port in origin site address", "源站地址中没有配置端口", true)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
originAddr = originAddr[:originHostIndex+1] + types.String(this.requestServerPort())
|
||||||
|
}
|
||||||
|
this.originAddr = originAddr
|
||||||
|
|
||||||
|
// RequestHost
|
||||||
|
if len(requestHost) > 0 {
|
||||||
|
if requestHostHasVariables {
|
||||||
|
this.RawReq.Host = this.Format(requestHost)
|
||||||
|
} else {
|
||||||
|
this.RawReq.Host = requestHost
|
||||||
|
}
|
||||||
|
|
||||||
|
// 是否移除端口
|
||||||
|
if this.reverseProxy.RequestHostExcludingPort {
|
||||||
|
this.RawReq.Host = utils.ParseAddrHost(this.RawReq.Host)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.RawReq.URL.Host = this.RawReq.Host
|
||||||
|
} else if this.reverseProxy.RequestHostType == serverconfigs.RequestHostTypeOrigin {
|
||||||
|
// 源站主机名
|
||||||
|
var hostname = originAddr
|
||||||
|
if origin.Addr.Protocol.IsHTTPFamily() {
|
||||||
|
hostname = strings.TrimSuffix(hostname, ":80")
|
||||||
|
} else if origin.Addr.Protocol.IsHTTPSFamily() {
|
||||||
|
hostname = strings.TrimSuffix(hostname, ":443")
|
||||||
|
}
|
||||||
|
|
||||||
|
this.RawReq.Host = hostname
|
||||||
|
|
||||||
|
// 是否移除端口
|
||||||
|
if this.reverseProxy.RequestHostExcludingPort {
|
||||||
|
this.RawReq.Host = utils.ParseAddrHost(this.RawReq.Host)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.RawReq.URL.Host = this.RawReq.Host
|
||||||
} else {
|
} else {
|
||||||
this.RawReq.Host = requestHost
|
this.RawReq.URL.Host = this.ReqHost
|
||||||
}
|
|
||||||
|
|
||||||
// 是否移除端口
|
// 是否移除端口
|
||||||
if this.reverseProxy.RequestHostExcludingPort {
|
if this.reverseProxy.RequestHostExcludingPort {
|
||||||
this.RawReq.Host = utils.ParseAddrHost(this.RawReq.Host)
|
this.RawReq.Host = utils.ParseAddrHost(this.RawReq.Host)
|
||||||
}
|
this.RawReq.URL.Host = utils.ParseAddrHost(this.RawReq.URL.Host)
|
||||||
|
}
|
||||||
this.RawReq.URL.Host = this.RawReq.Host
|
|
||||||
} else if this.reverseProxy.RequestHostType == serverconfigs.RequestHostTypeOrigin {
|
|
||||||
// 源站主机名
|
|
||||||
var hostname = originAddr
|
|
||||||
if origin.Addr.Protocol.IsHTTPFamily() {
|
|
||||||
hostname = strings.TrimSuffix(hostname, ":80")
|
|
||||||
} else if origin.Addr.Protocol.IsHTTPSFamily() {
|
|
||||||
hostname = strings.TrimSuffix(hostname, ":443")
|
|
||||||
}
|
|
||||||
|
|
||||||
this.RawReq.Host = hostname
|
|
||||||
|
|
||||||
// 是否移除端口
|
|
||||||
if this.reverseProxy.RequestHostExcludingPort {
|
|
||||||
this.RawReq.Host = utils.ParseAddrHost(this.RawReq.Host)
|
|
||||||
}
|
|
||||||
|
|
||||||
this.RawReq.URL.Host = this.RawReq.Host
|
|
||||||
} else {
|
|
||||||
this.RawReq.URL.Host = this.ReqHost
|
|
||||||
|
|
||||||
// 是否移除端口
|
|
||||||
if this.reverseProxy.RequestHostExcludingPort {
|
|
||||||
this.RawReq.Host = utils.ParseAddrHost(this.RawReq.Host)
|
|
||||||
this.RawReq.URL.Host = utils.ParseAddrHost(this.RawReq.URL.Host)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -241,34 +252,72 @@ func (this *HTTPRequest) doOriginRequest(failedOriginIds []int64, failedLnNodeId
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 判断是否为Websocket请求
|
// 判断是否为Websocket请求
|
||||||
if this.RawReq.Header.Get("Upgrade") == "websocket" {
|
if isHTTPOrigin && this.RawReq.Header.Get("Upgrade") == "websocket" {
|
||||||
shouldRetry = this.doWebsocket(requestHost, isLastRetry)
|
shouldRetry = this.doWebsocket(requestHost, isLastRetry)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取请求客户端
|
var resp *http.Response
|
||||||
client, err := SharedHTTPClientPool.Client(this, origin, originAddr, this.reverseProxy.ProxyProtocol, this.reverseProxy.FollowRedirects)
|
var requestErr error
|
||||||
if err != nil {
|
var requestErrCode string
|
||||||
remotelogs.ErrorServer("HTTP_REQUEST_REVERSE_PROXY", this.URL()+": Create client failed: "+err.Error())
|
if isHTTPOrigin { // 普通HTTP(S)源站
|
||||||
this.write50x(err, http.StatusBadGateway, "Failed to create origin site client", "构造源站客户端失败", true)
|
// 修复空User-Agent问题
|
||||||
|
_, existsUserAgent := this.RawReq.Header["User-Agent"]
|
||||||
|
if !existsUserAgent {
|
||||||
|
this.RawReq.Header["User-Agent"] = []string{""}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取请求客户端
|
||||||
|
client, err := SharedHTTPClientPool.Client(this, origin, originAddr, this.reverseProxy.ProxyProtocol, this.reverseProxy.FollowRedirects)
|
||||||
|
if err != nil {
|
||||||
|
remotelogs.ErrorServer("HTTP_REQUEST_REVERSE_PROXY", this.URL()+": Create client failed: "+err.Error())
|
||||||
|
this.write50x(err, http.StatusBadGateway, "Failed to create origin site client", "构造源站客户端失败", true)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 尝试自动纠正源站地址中的scheme
|
||||||
|
if this.RawReq.URL.Scheme == "http" && strings.HasSuffix(originAddr, ":443") {
|
||||||
|
this.RawReq.URL.Scheme = "https"
|
||||||
|
} else if this.RawReq.URL.Scheme == "https" && strings.HasSuffix(originAddr, ":80") {
|
||||||
|
this.RawReq.URL.Scheme = "http"
|
||||||
|
}
|
||||||
|
|
||||||
|
// 开始请求
|
||||||
|
resp, requestErr = client.Do(this.RawReq)
|
||||||
|
} else if origin.OSS != nil { // OSS源站
|
||||||
|
var goNext bool
|
||||||
|
resp, goNext, requestErrCode, _, requestErr = this.doOSSOrigin(origin)
|
||||||
|
if requestErr == nil {
|
||||||
|
if resp == nil || !goNext {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.writeCode(http.StatusBadGateway, "The type of origin site has not been supported", "设置的源站类型尚未支持")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if requestErr != nil {
|
||||||
// 开始请求
|
|
||||||
resp, err := client.Do(this.RawReq)
|
|
||||||
if err != nil {
|
|
||||||
// 客户端取消请求,则不提示
|
// 客户端取消请求,则不提示
|
||||||
httpErr, ok := err.(*url.Error)
|
httpErr, ok := requestErr.(*url.Error)
|
||||||
if !ok {
|
if !ok {
|
||||||
SharedOriginStateManager.Fail(origin, requestHost, this.reverseProxy, func() {
|
if isHTTPOrigin {
|
||||||
this.reverseProxy.ResetScheduling()
|
SharedOriginStateManager.Fail(origin, requestHost, this.reverseProxy, func() {
|
||||||
})
|
this.reverseProxy.ResetScheduling()
|
||||||
this.write50x(err, http.StatusBadGateway, "Failed to read origin site", "源站读取失败", true)
|
})
|
||||||
remotelogs.WarnServer("HTTP_REQUEST_REVERSE_PROXY", this.RawReq.URL.String()+": Request origin server failed: "+err.Error())
|
}
|
||||||
|
|
||||||
|
if len(requestErrCode) > 0 {
|
||||||
|
this.write50x(requestErr, http.StatusBadGateway, "Failed to read origin site (error code: "+requestErrCode+")", "源站读取失败(错误代号:"+requestErrCode+")", true)
|
||||||
|
} else {
|
||||||
|
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())
|
||||||
} else if httpErr.Err != context.Canceled {
|
} else if httpErr.Err != context.Canceled {
|
||||||
SharedOriginStateManager.Fail(origin, requestHost, this.reverseProxy, func() {
|
if isHTTPOrigin {
|
||||||
this.reverseProxy.ResetScheduling()
|
SharedOriginStateManager.Fail(origin, requestHost, this.reverseProxy, func() {
|
||||||
})
|
this.reverseProxy.ResetScheduling()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// 是否需要重试
|
// 是否需要重试
|
||||||
if (originId > 0 || (lnNodeId > 0 && hasMultipleLnNodes)) && !isLastRetry {
|
if (originId > 0 || (lnNodeId > 0 && hasMultipleLnNodes)) && !isLastRetry {
|
||||||
@@ -280,21 +329,21 @@ func (this *HTTPRequest) doOriginRequest(failedOriginIds []int64, failedLnNodeId
|
|||||||
}
|
}
|
||||||
|
|
||||||
if httpErr.Err != io.EOF {
|
if httpErr.Err != io.EOF {
|
||||||
remotelogs.WarnServer("HTTP_REQUEST_REVERSE_PROXY", this.URL()+": Request origin server failed: "+err.Error())
|
remotelogs.WarnServer("HTTP_REQUEST_REVERSE_PROXY", this.URL()+": Request origin server failed: "+requestErr.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if httpErr.Timeout() {
|
if httpErr.Timeout() {
|
||||||
this.write50x(err, http.StatusGatewayTimeout, "Read origin site timeout", "源站读取超时", true)
|
this.write50x(requestErr, http.StatusGatewayTimeout, "Read origin site timeout", "源站读取超时", true)
|
||||||
} else if httpErr.Temporary() {
|
} else if httpErr.Temporary() {
|
||||||
this.write50x(err, http.StatusServiceUnavailable, "Origin site unavailable now", "源站当前不可用", true)
|
this.write50x(requestErr, http.StatusServiceUnavailable, "Origin site unavailable now", "源站当前不可用", true)
|
||||||
} else {
|
} else {
|
||||||
this.write50x(err, http.StatusBadGateway, "Failed to read origin site", "源站读取失败", true)
|
this.write50x(requestErr, http.StatusBadGateway, "Failed to read origin site", "源站读取失败", true)
|
||||||
}
|
}
|
||||||
if httpErr.Err != io.EOF {
|
if httpErr.Err != io.EOF {
|
||||||
remotelogs.WarnServer("HTTP_REQUEST_REVERSE_PROXY", this.URL()+": Request origin server failed: "+err.Error())
|
remotelogs.WarnServer("HTTP_REQUEST_REVERSE_PROXY", this.URL()+": Request origin server failed: "+requestErr.Error())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 是否为客户端方面的错误
|
// 是否为客户端方面的错误
|
||||||
@@ -314,7 +363,7 @@ func (this *HTTPRequest) doOriginRequest(failedOriginIds []int64, failedLnNodeId
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !isClientError {
|
if !isClientError {
|
||||||
this.write50x(err, 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 {
|
if resp != nil && resp.Body != nil {
|
||||||
@@ -337,7 +386,7 @@ func (this *HTTPRequest) doOriginRequest(failedOriginIds []int64, failedLnNodeId
|
|||||||
this.originStatus = int32(resp.StatusCode)
|
this.originStatus = int32(resp.StatusCode)
|
||||||
|
|
||||||
// 恢复源站状态
|
// 恢复源站状态
|
||||||
if !origin.IsOk {
|
if !origin.IsOk && isHTTPOrigin {
|
||||||
SharedOriginStateManager.Success(origin, func() {
|
SharedOriginStateManager.Success(origin, func() {
|
||||||
this.reverseProxy.ResetScheduling()
|
this.reverseProxy.ResetScheduling()
|
||||||
})
|
})
|
||||||
@@ -346,7 +395,7 @@ 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()
|
err := resp.Body.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
remotelogs.WarnServer("HTTP_REQUEST_REVERSE_PROXY", this.URL()+": Closing Error (WAF): "+err.Error())
|
remotelogs.WarnServer("HTTP_REQUEST_REVERSE_PROXY", this.URL()+": Closing Error (WAF): "+err.Error())
|
||||||
}
|
}
|
||||||
@@ -356,13 +405,22 @@ func (this *HTTPRequest) doOriginRequest(failedOriginIds []int64, failedLnNodeId
|
|||||||
|
|
||||||
// 特殊页面
|
// 特殊页面
|
||||||
if len(this.web.Pages) > 0 && this.doPage(resp.StatusCode) {
|
if len(this.web.Pages) > 0 && this.doPage(resp.StatusCode) {
|
||||||
err = resp.Body.Close()
|
err := resp.Body.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
remotelogs.WarnServer("HTTP_REQUEST_REVERSE_PROXY", this.URL()+": Closing error (Page): "+err.Error())
|
remotelogs.WarnServer("HTTP_REQUEST_REVERSE_PROXY", this.URL()+": Closing error (Page): "+err.Error())
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Page optimization
|
||||||
|
if this.web.Optimization != nil && resp.Body != nil && this.cacheRef != nil /** must under cache **/ {
|
||||||
|
err := this.web.Optimization.FilterResponse(resp)
|
||||||
|
if err != nil {
|
||||||
|
this.write50x(err, http.StatusBadGateway, "Page Optimization: Fail to read content from origin", "内容优化:从源站读取内容失败", false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 设置Charset
|
// 设置Charset
|
||||||
// TODO 这里应该可以设置文本类型的列表,以及是否强制覆盖所有文本类型的字符集
|
// TODO 这里应该可以设置文本类型的列表,以及是否强制覆盖所有文本类型的字符集
|
||||||
if this.web.Charset != nil && this.web.Charset.IsOn && len(this.web.Charset.Charset) > 0 {
|
if this.web.Charset != nil && this.web.Charset.IsOn && len(this.web.Charset.Charset) > 0 {
|
||||||
@@ -409,10 +467,22 @@ func (this *HTTPRequest) doOriginRequest(failedOriginIds []int64, failedLnNodeId
|
|||||||
|
|
||||||
// 响应Header
|
// 响应Header
|
||||||
this.writer.AddHeaders(resp.Header)
|
this.writer.AddHeaders(resp.Header)
|
||||||
this.processResponseHeaders(this.writer.Header(), resp.StatusCode)
|
this.ProcessResponseHeaders(this.writer.Header(), resp.StatusCode)
|
||||||
|
|
||||||
// 是否需要刷新
|
// 是否需要刷新
|
||||||
var shouldAutoFlush = this.reverseProxy.AutoFlush || this.RawReq.Header.Get("Accept") == "text/event-stream"
|
var shouldAutoFlush = this.reverseProxy.AutoFlush || (resp.Header != nil && strings.Contains(resp.Header.Get("Content-Type"), "stream"))
|
||||||
|
|
||||||
|
// 设置当前连接为Persistence
|
||||||
|
if shouldAutoFlush && this.nodeConfig != nil && this.nodeConfig.HasConnTimeoutSettings() {
|
||||||
|
var requestConn = this.RawReq.Context().Value(HTTPConnContextKey)
|
||||||
|
if requestConn == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
requestClientConn, ok := requestConn.(ClientConnInterface)
|
||||||
|
if ok {
|
||||||
|
requestClientConn.SetIsPersistent(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 准备
|
// 准备
|
||||||
var delayHeaders = this.writer.Prepare(resp, resp.ContentLength, resp.StatusCode, true)
|
var delayHeaders = this.writer.Prepare(resp, resp.ContentLength, resp.StatusCode, true)
|
||||||
@@ -437,6 +507,7 @@ func (this *HTTPRequest) doOriginRequest(failedOriginIds []int64, failedLnNodeId
|
|||||||
// 输出到客户端
|
// 输出到客户端
|
||||||
var pool = this.bytePool(resp.ContentLength)
|
var pool = this.bytePool(resp.ContentLength)
|
||||||
var buf = pool.Get()
|
var buf = pool.Get()
|
||||||
|
var err error
|
||||||
if shouldAutoFlush {
|
if shouldAutoFlush {
|
||||||
for {
|
for {
|
||||||
n, readErr := resp.Body.Read(buf)
|
n, readErr := resp.Body.Read(buf)
|
||||||
|
|||||||
@@ -30,10 +30,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)
|
http.Redirect(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)
|
http.Redirect(this.writer, this.RawReq, this.rewriteReplace, http.StatusTemporaryRedirect)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
|||||||
@@ -217,7 +217,7 @@ func (this *HTTPRequest) doRoot() (isBreak bool) {
|
|||||||
// 支持 If-None-Match
|
// 支持 If-None-Match
|
||||||
if this.requestHeader("If-None-Match") == eTag {
|
if this.requestHeader("If-None-Match") == eTag {
|
||||||
// 自定义Header
|
// 自定义Header
|
||||||
this.processResponseHeaders(this.writer.Header(), http.StatusNotModified)
|
this.ProcessResponseHeaders(this.writer.Header(), http.StatusNotModified)
|
||||||
this.writer.WriteHeader(http.StatusNotModified)
|
this.writer.WriteHeader(http.StatusNotModified)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -225,7 +225,7 @@ func (this *HTTPRequest) doRoot() (isBreak bool) {
|
|||||||
// 支持 If-Modified-Since
|
// 支持 If-Modified-Since
|
||||||
if this.requestHeader("If-Modified-Since") == modifiedTime {
|
if this.requestHeader("If-Modified-Since") == modifiedTime {
|
||||||
// 自定义Header
|
// 自定义Header
|
||||||
this.processResponseHeaders(this.writer.Header(), http.StatusNotModified)
|
this.ProcessResponseHeaders(this.writer.Header(), http.StatusNotModified)
|
||||||
this.writer.WriteHeader(http.StatusNotModified)
|
this.writer.WriteHeader(http.StatusNotModified)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -253,14 +253,14 @@ func (this *HTTPRequest) doRoot() (isBreak bool) {
|
|||||||
var contentRange = this.RawReq.Header.Get("Range")
|
var contentRange = this.RawReq.Header.Get("Range")
|
||||||
if len(contentRange) > 0 {
|
if len(contentRange) > 0 {
|
||||||
if fileSize == 0 {
|
if fileSize == 0 {
|
||||||
this.processResponseHeaders(this.writer.Header(), http.StatusRequestedRangeNotSatisfiable)
|
this.ProcessResponseHeaders(this.writer.Header(), http.StatusRequestedRangeNotSatisfiable)
|
||||||
this.writer.WriteHeader(http.StatusRequestedRangeNotSatisfiable)
|
this.writer.WriteHeader(http.StatusRequestedRangeNotSatisfiable)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
set, ok := httpRequestParseRangeHeader(contentRange)
|
set, ok := httpRequestParseRangeHeader(contentRange)
|
||||||
if !ok {
|
if !ok {
|
||||||
this.processResponseHeaders(this.writer.Header(), http.StatusRequestedRangeNotSatisfiable)
|
this.ProcessResponseHeaders(this.writer.Header(), http.StatusRequestedRangeNotSatisfiable)
|
||||||
this.writer.WriteHeader(http.StatusRequestedRangeNotSatisfiable)
|
this.writer.WriteHeader(http.StatusRequestedRangeNotSatisfiable)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -269,7 +269,7 @@ func (this *HTTPRequest) doRoot() (isBreak bool) {
|
|||||||
for k, r := range ranges {
|
for k, r := range ranges {
|
||||||
r2, ok := r.Convert(fileSize)
|
r2, ok := r.Convert(fileSize)
|
||||||
if !ok {
|
if !ok {
|
||||||
this.processResponseHeaders(this.writer.Header(), http.StatusRequestedRangeNotSatisfiable)
|
this.ProcessResponseHeaders(this.writer.Header(), http.StatusRequestedRangeNotSatisfiable)
|
||||||
this.writer.WriteHeader(http.StatusRequestedRangeNotSatisfiable)
|
this.writer.WriteHeader(http.StatusRequestedRangeNotSatisfiable)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -290,7 +290,7 @@ func (this *HTTPRequest) doRoot() (isBreak bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 自定义Header
|
// 自定义Header
|
||||||
this.processResponseHeaders(this.writer.Header(), http.StatusOK)
|
this.ProcessResponseHeaders(this.writer.Header(), http.StatusOK)
|
||||||
|
|
||||||
// 在Range请求中不能缓存
|
// 在Range请求中不能缓存
|
||||||
if len(ranges) > 0 {
|
if len(ranges) > 0 {
|
||||||
@@ -325,7 +325,7 @@ func (this *HTTPRequest) doRoot() (isBreak bool) {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if !ok {
|
if !ok {
|
||||||
this.processResponseHeaders(this.writer.Header(), http.StatusRequestedRangeNotSatisfiable)
|
this.ProcessResponseHeaders(this.writer.Header(), http.StatusRequestedRangeNotSatisfiable)
|
||||||
this.writer.WriteHeader(http.StatusRequestedRangeNotSatisfiable)
|
this.writer.WriteHeader(http.StatusRequestedRangeNotSatisfiable)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -377,7 +377,7 @@ func (this *HTTPRequest) doRoot() (isBreak bool) {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if !ok {
|
if !ok {
|
||||||
this.processResponseHeaders(this.writer.Header(), http.StatusRequestedRangeNotSatisfiable)
|
this.ProcessResponseHeaders(this.writer.Header(), http.StatusRequestedRangeNotSatisfiable)
|
||||||
this.writer.WriteHeader(http.StatusRequestedRangeNotSatisfiable)
|
this.writer.WriteHeader(http.StatusRequestedRangeNotSatisfiable)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,21 +5,22 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
"github.com/iwind/TeaGo/Tea"
|
"github.com/iwind/TeaGo/Tea"
|
||||||
"github.com/iwind/TeaGo/logs"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 调用临时关闭页面
|
// 调用临时关闭页面
|
||||||
func (this *HTTPRequest) doShutdown() {
|
func (this *HTTPRequest) doShutdown() {
|
||||||
shutdown := this.web.Shutdown
|
var shutdown = this.web.Shutdown
|
||||||
if shutdown == nil {
|
if shutdown == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(shutdown.BodyType) == 0 || shutdown.BodyType == shared.BodyTypeURL {
|
if len(shutdown.BodyType) == 0 || shutdown.BodyType == shared.BodyTypeURL {
|
||||||
// URL
|
// URL
|
||||||
if urlPrefixRegexp.MatchString(shutdown.URL) {
|
if urlSchemeRegexp.MatchString(shutdown.URL) {
|
||||||
this.doURL(http.MethodGet, shutdown.URL, "", shutdown.Status, true)
|
this.doURL(http.MethodGet, shutdown.URL, "", shutdown.Status, true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -28,44 +29,46 @@ func (this *HTTPRequest) doShutdown() {
|
|||||||
if len(shutdown.URL) == 0 {
|
if len(shutdown.URL) == 0 {
|
||||||
// 自定义响应Headers
|
// 自定义响应Headers
|
||||||
if shutdown.Status > 0 {
|
if shutdown.Status > 0 {
|
||||||
this.processResponseHeaders(this.writer.Header(), shutdown.Status)
|
this.ProcessResponseHeaders(this.writer.Header(), shutdown.Status)
|
||||||
this.writer.WriteHeader(shutdown.Status)
|
this.writer.WriteHeader(shutdown.Status)
|
||||||
} else {
|
} else {
|
||||||
this.processResponseHeaders(this.writer.Header(), http.StatusOK)
|
this.ProcessResponseHeaders(this.writer.Header(), http.StatusOK)
|
||||||
this.writer.WriteHeader(http.StatusOK)
|
this.writer.WriteHeader(http.StatusOK)
|
||||||
}
|
}
|
||||||
_, err := this.writer.WriteString("The site have been shutdown.")
|
_, _ = this.writer.WriteString("The site have been shutdown.")
|
||||||
if err != nil {
|
|
||||||
logs.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 从本地文件中读取
|
// 从本地文件中读取
|
||||||
file := Tea.Root + Tea.DS + shutdown.URL
|
var realpath = path.Clean(shutdown.URL)
|
||||||
|
if !strings.HasPrefix(realpath, "/pages/") && !strings.HasPrefix(realpath, "pages/") { // only files under "/pages/" can be used
|
||||||
|
var msg = "404 page not found: '" + shutdown.URL + "'"
|
||||||
|
this.writer.WriteHeader(http.StatusNotFound)
|
||||||
|
_, _ = this.writer.Write([]byte(msg))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var file = Tea.Root + Tea.DS + shutdown.URL
|
||||||
fp, err := os.Open(file)
|
fp, err := os.Open(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logs.Error(err)
|
var msg = "404 page not found: '" + shutdown.URL + "'"
|
||||||
msg := "404 page not found: '" + shutdown.URL + "'"
|
|
||||||
|
|
||||||
this.writer.WriteHeader(http.StatusNotFound)
|
this.writer.WriteHeader(http.StatusNotFound)
|
||||||
_, err = this.writer.Write([]byte(msg))
|
_, _ = this.writer.Write([]byte(msg))
|
||||||
if err != nil {
|
|
||||||
logs.Error(err)
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
_ = fp.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
// 自定义响应Headers
|
// 自定义响应Headers
|
||||||
if shutdown.Status > 0 {
|
if shutdown.Status > 0 {
|
||||||
this.processResponseHeaders(this.writer.Header(), shutdown.Status)
|
this.ProcessResponseHeaders(this.writer.Header(), shutdown.Status)
|
||||||
this.writer.WriteHeader(shutdown.Status)
|
this.writer.WriteHeader(shutdown.Status)
|
||||||
} else {
|
} else {
|
||||||
this.processResponseHeaders(this.writer.Header(), http.StatusOK)
|
this.ProcessResponseHeaders(this.writer.Header(), http.StatusOK)
|
||||||
this.writer.WriteHeader(http.StatusOK)
|
this.writer.WriteHeader(http.StatusOK)
|
||||||
}
|
}
|
||||||
buf := utils.BytePool1k.Get()
|
var buf = utils.BytePool1k.Get()
|
||||||
_, err = utils.CopyWithFilter(this.writer, fp, buf, func(p []byte) []byte {
|
_, err = utils.CopyWithFilter(this.writer, fp, buf, func(p []byte) []byte {
|
||||||
return []byte(this.Format(string(p)))
|
return []byte(this.Format(string(p)))
|
||||||
})
|
})
|
||||||
@@ -77,18 +80,13 @@ func (this *HTTPRequest) doShutdown() {
|
|||||||
} else {
|
} else {
|
||||||
this.writer.SetOk()
|
this.writer.SetOk()
|
||||||
}
|
}
|
||||||
|
|
||||||
err = fp.Close()
|
|
||||||
if err != nil {
|
|
||||||
remotelogs.Warn("HTTP_REQUEST_SHUTDOWN", "close file failed: "+err.Error())
|
|
||||||
}
|
|
||||||
} else if shutdown.BodyType == shared.BodyTypeHTML {
|
} else if shutdown.BodyType == shared.BodyTypeHTML {
|
||||||
// 自定义响应Headers
|
// 自定义响应Headers
|
||||||
if shutdown.Status > 0 {
|
if shutdown.Status > 0 {
|
||||||
this.processResponseHeaders(this.writer.Header(), shutdown.Status)
|
this.ProcessResponseHeaders(this.writer.Header(), shutdown.Status)
|
||||||
this.writer.WriteHeader(shutdown.Status)
|
this.writer.WriteHeader(shutdown.Status)
|
||||||
} else {
|
} else {
|
||||||
this.processResponseHeaders(this.writer.Header(), http.StatusOK)
|
this.ProcessResponseHeaders(this.writer.Header(), http.StatusOK)
|
||||||
this.writer.WriteHeader(http.StatusOK)
|
this.writer.WriteHeader(http.StatusOK)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ func (this *HTTPRequest) doTrafficLimit() {
|
|||||||
this.tags = append(this.tags, "bandwidth")
|
this.tags = append(this.tags, "bandwidth")
|
||||||
|
|
||||||
var statusCode = 509
|
var statusCode = 509
|
||||||
this.processResponseHeaders(this.writer.Header(), statusCode)
|
this.ProcessResponseHeaders(this.writer.Header(), statusCode)
|
||||||
|
|
||||||
this.writer.WriteHeader(statusCode)
|
this.writer.WriteHeader(statusCode)
|
||||||
if len(config.NoticePageBody) != 0 {
|
if len(config.NoticePageBody) != 0 {
|
||||||
|
|||||||
@@ -44,9 +44,9 @@ func (this *HTTPRequest) doURL(method string, url string, host string, statusCod
|
|||||||
|
|
||||||
// Header
|
// Header
|
||||||
if statusCode <= 0 {
|
if statusCode <= 0 {
|
||||||
this.processResponseHeaders(this.writer.Header(), resp.StatusCode)
|
this.ProcessResponseHeaders(this.writer.Header(), resp.StatusCode)
|
||||||
} else {
|
} else {
|
||||||
this.processResponseHeaders(this.writer.Header(), statusCode)
|
this.ProcessResponseHeaders(this.writer.Header(), statusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
if supportVariables {
|
if supportVariables {
|
||||||
@@ -67,8 +67,8 @@ func (this *HTTPRequest) doURL(method string, url string, host string, statusCod
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 输出内容
|
// 输出内容
|
||||||
pool := this.bytePool(resp.ContentLength)
|
var pool = this.bytePool(resp.ContentLength)
|
||||||
buf := pool.Get()
|
var buf = pool.Get()
|
||||||
if supportVariables {
|
if supportVariables {
|
||||||
_, err = utils.CopyWithFilter(this.writer, resp.Body, buf, func(p []byte) []byte {
|
_, err = utils.CopyWithFilter(this.writer, resp.Body, buf, func(p []byte) []byte {
|
||||||
return []byte(this.Format(string(p)))
|
return []byte(this.Format(string(p)))
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/iwind/TeaGo/types"
|
"github.com/iwind/TeaGo/types"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -22,6 +23,9 @@ var spiderRegexp = regexp.MustCompile(`(?i)(python|pycurl|http-client|httpclient
|
|||||||
// 内容范围正则,其中的每个括号里的内容都在被引用,不能轻易修改
|
// 内容范围正则,其中的每个括号里的内容都在被引用,不能轻易修改
|
||||||
var contentRangeRegexp = regexp.MustCompile(`^bytes (\d+)-(\d+)/(\d+|\*)`)
|
var contentRangeRegexp = regexp.MustCompile(`^bytes (\d+)-(\d+)/(\d+|\*)`)
|
||||||
|
|
||||||
|
// URL协议前缀
|
||||||
|
var urlSchemeRegexp = regexp.MustCompile("^(?i)(http|https|ftp)://")
|
||||||
|
|
||||||
// 分解Range
|
// 分解Range
|
||||||
func httpRequestParseRangeHeader(rangeValue string) (result []rangeutils.Range, ok bool) {
|
func httpRequestParseRangeHeader(rangeValue string) (result []rangeutils.Range, ok bool) {
|
||||||
// 参考RFC:https://tools.ietf.org/html/rfc7233
|
// 参考RFC:https://tools.ietf.org/html/rfc7233
|
||||||
@@ -222,3 +226,16 @@ func httpRedirect(writer http.ResponseWriter, req *http.Request, url string, cod
|
|||||||
|
|
||||||
http.Redirect(writer, req, url, code)
|
http.Redirect(writer, req, url, code)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 分析URL中的Host部分
|
||||||
|
func httpParseHost(urlString string) (host string, err error) {
|
||||||
|
if !urlSchemeRegexp.MatchString(urlString) {
|
||||||
|
urlString = "https://" + urlString
|
||||||
|
}
|
||||||
|
|
||||||
|
u, err := url.Parse(urlString)
|
||||||
|
if err != nil && u != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return u.Host, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -145,6 +145,23 @@ func TestHTTPRequest_httpRequestNextId_Concurrent(t *testing.T) {
|
|||||||
a.IsTrue(countDuplicated == 0)
|
a.IsTrue(countDuplicated == 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestHTTPParseURL(t *testing.T) {
|
||||||
|
for _, s := range []string{
|
||||||
|
"",
|
||||||
|
"null",
|
||||||
|
"example.com",
|
||||||
|
"https://example.com",
|
||||||
|
"https://example.com/hello",
|
||||||
|
} {
|
||||||
|
host, err := httpParseHost(s)
|
||||||
|
if err == nil {
|
||||||
|
t.Log(s, "=>", host)
|
||||||
|
} else {
|
||||||
|
t.Log(s, "=>")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func BenchmarkHTTPRequest_httpRequestNextId(b *testing.B) {
|
func BenchmarkHTTPRequest_httpRequestNextId(b *testing.B) {
|
||||||
runtime.GOMAXPROCS(1)
|
runtime.GOMAXPROCS(1)
|
||||||
|
|
||||||
|
|||||||
@@ -9,10 +9,10 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeNode/internal/stats"
|
"github.com/TeaOSLab/EdgeNode/internal/stats"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/waf"
|
"github.com/TeaOSLab/EdgeNode/internal/waf"
|
||||||
"github.com/iwind/TeaGo/Tea"
|
"github.com/iwind/TeaGo/Tea"
|
||||||
"github.com/iwind/TeaGo/lists"
|
|
||||||
"github.com/iwind/TeaGo/types"
|
"github.com/iwind/TeaGo/types"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 调用WAF
|
// 调用WAF
|
||||||
@@ -163,47 +163,66 @@ func (this *HTTPRequest) checkWAFRequest(firewallPolicy *firewallconfigs.HTTPFir
|
|||||||
// 检查地区封禁
|
// 检查地区封禁
|
||||||
if firewallPolicy.Mode == firewallconfigs.FirewallModeDefend {
|
if firewallPolicy.Mode == firewallconfigs.FirewallModeDefend {
|
||||||
if firewallPolicy.Inbound.Region != nil && firewallPolicy.Inbound.Region.IsOn {
|
if firewallPolicy.Inbound.Region != nil && firewallPolicy.Inbound.Region.IsOn {
|
||||||
regionConfig := firewallPolicy.Inbound.Region
|
var regionConfig = firewallPolicy.Inbound.Region
|
||||||
if regionConfig.IsNotEmpty() {
|
if regionConfig.IsNotEmpty() {
|
||||||
for _, remoteAddr := range remoteAddrs {
|
for _, remoteAddr := range remoteAddrs {
|
||||||
var result = iplib.LookupIP(remoteAddr)
|
var result = iplib.LookupIP(remoteAddr)
|
||||||
if result != nil && result.IsOk() {
|
if result != nil && result.IsOk() {
|
||||||
// 检查国家/地区级别封禁
|
var currentURL = this.URL()
|
||||||
var countryId = result.CountryId()
|
if regionConfig.MatchCountryURL(currentURL) {
|
||||||
if countryId > 0 && lists.ContainsInt64(regionConfig.DenyCountryIds, countryId) {
|
// 检查国家/地区级别封禁
|
||||||
this.firewallPolicyId = firewallPolicy.Id
|
if !regionConfig.IsAllowedCountry(result.CountryId(), result.ProvinceId()) {
|
||||||
|
this.firewallPolicyId = firewallPolicy.Id
|
||||||
|
|
||||||
this.writeCode(http.StatusForbidden, "", "")
|
if len(regionConfig.CountryHTML) > 0 {
|
||||||
this.writer.Flush()
|
this.writer.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||||
this.writer.Close()
|
this.writer.Header().Set("Content-Length", types.String(len(regionConfig.CountryHTML)))
|
||||||
|
this.writer.WriteHeader(http.StatusForbidden)
|
||||||
|
_, _ = this.writer.Write([]byte(regionConfig.CountryHTML))
|
||||||
|
} else {
|
||||||
|
this.writeCode(http.StatusForbidden, "The region has been denied.", "当前区域禁止访问")
|
||||||
|
}
|
||||||
|
|
||||||
// 停止日志
|
// 延时返回,避免攻击
|
||||||
if !logDenying {
|
time.Sleep(1 * time.Second)
|
||||||
this.disableLog = true
|
|
||||||
} else {
|
// 停止日志
|
||||||
this.tags = append(this.tags, "denyCountry")
|
if !logDenying {
|
||||||
|
this.disableLog = true
|
||||||
|
} else {
|
||||||
|
this.tags = append(this.tags, "denyCountry")
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, false
|
||||||
}
|
}
|
||||||
|
|
||||||
return true, false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查省份封禁
|
if regionConfig.MatchProvinceURL(currentURL) {
|
||||||
var provinceId = result.ProvinceId()
|
// 检查省份封禁
|
||||||
if provinceId > 0 && lists.ContainsInt64(regionConfig.DenyProvinceIds, provinceId) {
|
if !regionConfig.IsAllowedProvince(result.CountryId(), result.ProvinceId()) {
|
||||||
this.firewallPolicyId = firewallPolicy.Id
|
this.firewallPolicyId = firewallPolicy.Id
|
||||||
|
|
||||||
this.writeCode(http.StatusForbidden, "", "")
|
if len(regionConfig.ProvinceHTML) > 0 {
|
||||||
this.writer.Flush()
|
this.writer.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||||
this.writer.Close()
|
this.writer.Header().Set("Content-Length", types.String(len(regionConfig.ProvinceHTML)))
|
||||||
|
this.writer.WriteHeader(http.StatusForbidden)
|
||||||
|
_, _ = this.writer.Write([]byte(regionConfig.ProvinceHTML))
|
||||||
|
} else {
|
||||||
|
this.writeCode(http.StatusForbidden, "The region has been denied.", "当前区域禁止访问")
|
||||||
|
}
|
||||||
|
|
||||||
// 停止日志
|
// 延时返回,避免攻击
|
||||||
if !logDenying {
|
time.Sleep(1 * time.Second)
|
||||||
this.disableLog = true
|
|
||||||
} else {
|
// 停止日志
|
||||||
this.tags = append(this.tags, "denyProvince")
|
if !logDenying {
|
||||||
|
this.disableLog = true
|
||||||
|
} else {
|
||||||
|
this.tags = append(this.tags, "denyProvince")
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, false
|
||||||
}
|
}
|
||||||
|
|
||||||
return true, false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ func (this *HTTPRequest) doWebsocket(requestHost string, isLastRetry bool) (shou
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.processResponseHeaders(resp.Header, resp.StatusCode)
|
this.ProcessResponseHeaders(resp.Header, resp.StatusCode)
|
||||||
this.writer.statusCode = resp.StatusCode
|
this.writer.statusCode = resp.StatusCode
|
||||||
|
|
||||||
// 将响应写回客户端
|
// 将响应写回客户端
|
||||||
|
|||||||
@@ -362,7 +362,10 @@ 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" || (this.isPartial && k == "Content-Range") {
|
if k == "Set-Cookie" ||
|
||||||
|
k == "Strict-Transport-Security" ||
|
||||||
|
k == "Alt-Svc" ||
|
||||||
|
(this.isPartial && k == "Content-Range") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, v1 := range v {
|
for _, v1 := range v {
|
||||||
@@ -690,7 +693,10 @@ 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" || (this.isPartial && k == "Content-Range") {
|
if k == "Set-Cookie" ||
|
||||||
|
k == "Strict-Transport-Security" ||
|
||||||
|
k == "Alt-Svc" ||
|
||||||
|
(this.isPartial && k == "Content-Range") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, v1 := range v {
|
for _, v1 := range v {
|
||||||
@@ -780,12 +786,18 @@ func (this *HTTPWriter) AddHeaders(header http.Header) {
|
|||||||
if this.rawWriter == nil {
|
if this.rawWriter == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
var newHeaders = this.rawWriter.Header()
|
||||||
for key, value := range header {
|
for key, value := range header {
|
||||||
if key == "Connection" {
|
if key == "Connection" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, v := range value {
|
switch key {
|
||||||
this.rawWriter.Header().Add(key, v)
|
case "ETag":
|
||||||
|
newHeaders[key] = value
|
||||||
|
default:
|
||||||
|
for _, v := range value {
|
||||||
|
newHeaders.Add(key, v)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -837,6 +849,14 @@ func (this *HTTPWriter) WriteHeader(statusCode int) {
|
|||||||
|
|
||||||
// Send 直接发送内容,并终止请求
|
// Send 直接发送内容,并终止请求
|
||||||
func (this *HTTPWriter) Send(status int, body string) {
|
func (this *HTTPWriter) Send(status int, body string) {
|
||||||
|
this.req.ProcessResponseHeaders(this.Header(), status)
|
||||||
|
|
||||||
|
// content-length
|
||||||
|
_, hasContentLength := this.Header()["Content-Length"]
|
||||||
|
if !hasContentLength {
|
||||||
|
this.Header()["Content-Length"] = []string{types.String(len(body))}
|
||||||
|
}
|
||||||
|
|
||||||
this.WriteHeader(status)
|
this.WriteHeader(status)
|
||||||
_, _ = this.WriteString(body)
|
_, _ = this.WriteString(body)
|
||||||
this.isFinished = true
|
this.isFinished = true
|
||||||
@@ -882,6 +902,7 @@ func (this *HTTPWriter) SendResp(resp *http.Response) (int64, error) {
|
|||||||
for k, v := range resp.Header {
|
for k, v := range resp.Header {
|
||||||
this.SetHeader(k, v)
|
this.SetHeader(k, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.WriteHeader(resp.StatusCode)
|
this.WriteHeader(resp.StatusCode)
|
||||||
var bufPool = this.req.bytePool(resp.ContentLength)
|
var bufPool = this.req.bytePool(resp.ContentLength)
|
||||||
var buf = bufPool.Get()
|
var buf = bufPool.Get()
|
||||||
@@ -1018,7 +1039,9 @@ 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 k == "Set-Cookie" ||
|
||||||
|
k == "Strict-Transport-Security" ||
|
||||||
|
k == "Alt-Svc" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1237,7 +1260,10 @@ 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" || (this.isPartial && k == "Content-Range") {
|
if k == "Set-Cookie" ||
|
||||||
|
k == "Strict-Transport-Security" ||
|
||||||
|
k == "Alt-Svc" ||
|
||||||
|
(this.isPartial && k == "Content-Range") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, v1 := range v {
|
for _, v1 := range v {
|
||||||
|
|||||||
@@ -91,10 +91,15 @@ func (this *BaseListener) matchSSL(domain string) (*sslconfigs.SSLPolicy, *tls.C
|
|||||||
return nil, nil, errors.New("no configure found")
|
return nil, nil, errors.New("no configure found")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var globalServerConfig *serverconfigs.GlobalServerConfig
|
||||||
|
if sharedNodeConfig != nil {
|
||||||
|
globalServerConfig = sharedNodeConfig.GlobalServerConfig
|
||||||
|
}
|
||||||
|
|
||||||
// 如果域名为空,则取第一个
|
// 如果域名为空,则取第一个
|
||||||
// 通常域名为空是因为是直接通过IP访问的
|
// 通常域名为空是因为是直接通过IP访问的
|
||||||
if len(domain) == 0 {
|
if len(domain) == 0 {
|
||||||
if group.IsHTTPS() && sharedNodeConfig.GlobalServerConfig != nil && sharedNodeConfig.GlobalServerConfig.HTTPAll.MatchDomainStrictly {
|
if group.IsHTTPS() && globalServerConfig != nil && globalServerConfig.HTTPAll.MatchDomainStrictly {
|
||||||
return nil, nil, errors.New("no tls server name matched")
|
return nil, nil, errors.New("no tls server name matched")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,23 +116,42 @@ func (this *BaseListener) matchSSL(domain string) (*sslconfigs.SSLPolicy, *tls.C
|
|||||||
return nil, nil, errors.New("no tls server name found")
|
return nil, nil, errors.New("no tls server name found")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 通过代理服务域名配置匹配
|
// 通过网站域名配置匹配
|
||||||
server, _ := this.findNamedServer(domain)
|
server, _ := this.findNamedServer(domain)
|
||||||
if server == nil || server.SSLPolicy() == nil || !server.SSLPolicy().IsOn {
|
if server == nil {
|
||||||
// 找不到或者此时的服务没有配置证书,需要搜索所有的Server,通过SSL证书内容中的DNSName匹配
|
// 找不到或者此时的服务没有配置证书,需要搜索所有的Server,通过SSL证书内容中的DNSName匹配
|
||||||
// TODO 需要思考这种情况下是否允许访问
|
// 此功能仅为了兼容以往版本(v1.0.4),不应该作为常态启用
|
||||||
for _, server := range group.Servers() {
|
if globalServerConfig != nil && globalServerConfig.HTTPAll.MatchCertFromAllServers {
|
||||||
if server.SSLPolicy() == nil || !server.SSLPolicy().IsOn {
|
for _, searchingServer := range group.Servers() {
|
||||||
continue
|
if searchingServer.SSLPolicy() == nil || !searchingServer.SSLPolicy().IsOn {
|
||||||
}
|
continue
|
||||||
cert, ok := server.SSLPolicy().MatchDomain(domain)
|
}
|
||||||
if ok {
|
cert, ok := searchingServer.SSLPolicy().MatchDomain(domain)
|
||||||
return server.SSLPolicy(), cert, nil
|
if ok {
|
||||||
|
return searchingServer.SSLPolicy(), cert, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, nil, errors.New("no server found for '" + domain + "'")
|
return nil, nil, errors.New("no server found for '" + domain + "'")
|
||||||
}
|
}
|
||||||
|
if server.SSLPolicy() == nil || !server.SSLPolicy().IsOn {
|
||||||
|
// 找不到或者此时的服务没有配置证书,需要搜索所有的Server,通过SSL证书内容中的DNSName匹配
|
||||||
|
// 此功能仅为了兼容以往版本(v1.0.4),不应该作为常态启用
|
||||||
|
if globalServerConfig != nil && globalServerConfig.HTTPAll.MatchCertFromAllServers {
|
||||||
|
for _, searchingServer := range group.Servers() {
|
||||||
|
if searchingServer.SSLPolicy() == nil || !searchingServer.SSLPolicy().IsOn {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
cert, ok := searchingServer.SSLPolicy().MatchDomain(domain)
|
||||||
|
if ok {
|
||||||
|
return searchingServer.SSLPolicy(), cert, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil, errors.New("no cert found for '" + domain + "'")
|
||||||
|
}
|
||||||
|
|
||||||
// 证书是否匹配
|
// 证书是否匹配
|
||||||
var sslConfig = server.SSLPolicy()
|
var sslConfig = server.SSLPolicy()
|
||||||
@@ -150,19 +174,26 @@ func (this *BaseListener) findNamedServer(name string) (serverConfig *serverconf
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var matchDomainStrictly = sharedNodeConfig.GlobalServerConfig != nil && sharedNodeConfig.GlobalServerConfig.HTTPAll.MatchDomainStrictly
|
var globalServerConfig = sharedNodeConfig.GlobalServerConfig
|
||||||
|
var matchDomainStrictly = globalServerConfig != nil && globalServerConfig.HTTPAll.MatchDomainStrictly
|
||||||
|
|
||||||
if sharedNodeConfig.GlobalServerConfig != nil &&
|
if globalServerConfig != nil &&
|
||||||
len(sharedNodeConfig.GlobalServerConfig.HTTPAll.DefaultDomain) > 0 &&
|
len(globalServerConfig.HTTPAll.DefaultDomain) > 0 &&
|
||||||
(!matchDomainStrictly || configutils.MatchDomains(sharedNodeConfig.GlobalServerConfig.HTTPAll.AllowMismatchDomains, name) || (sharedNodeConfig.GlobalServerConfig.HTTPAll.AllowNodeIP && net.ParseIP(name) != nil)) {
|
(!matchDomainStrictly || configutils.MatchDomains(globalServerConfig.HTTPAll.AllowMismatchDomains, name) || (globalServerConfig.HTTPAll.AllowNodeIP && net.ParseIP(name) != nil)) {
|
||||||
var defaultDomain = sharedNodeConfig.GlobalServerConfig.HTTPAll.DefaultDomain
|
if globalServerConfig.HTTPAll.AllowNodeIP &&
|
||||||
serverConfig, serverName = this.findNamedServerMatched(defaultDomain)
|
globalServerConfig.HTTPAll.NodeIPShowPage &&
|
||||||
if serverConfig != nil {
|
net.ParseIP(name) != nil {
|
||||||
return
|
return
|
||||||
|
} else {
|
||||||
|
var defaultDomain = globalServerConfig.HTTPAll.DefaultDomain
|
||||||
|
serverConfig, serverName = this.findNamedServerMatched(defaultDomain)
|
||||||
|
if serverConfig != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if matchDomainStrictly && !configutils.MatchDomains(sharedNodeConfig.GlobalServerConfig.HTTPAll.AllowMismatchDomains, name) && (!sharedNodeConfig.GlobalServerConfig.HTTPAll.AllowNodeIP || net.ParseIP(name) == nil) {
|
if matchDomainStrictly && !configutils.MatchDomains(globalServerConfig.HTTPAll.AllowMismatchDomains, name) && (!globalServerConfig.HTTPAll.AllowNodeIP || net.ParseIP(name) == nil) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ type HTTPListener struct {
|
|||||||
addr string
|
addr string
|
||||||
isHTTP bool
|
isHTTP bool
|
||||||
isHTTPS bool
|
isHTTPS bool
|
||||||
|
isHTTP3 bool
|
||||||
httpServer *http.Server
|
httpServer *http.Server
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,6 +200,7 @@ func (this *HTTPListener) ServeHTTP(rawWriter http.ResponseWriter, rawReq *http.
|
|||||||
ServerAddr: this.addr,
|
ServerAddr: this.addr,
|
||||||
IsHTTP: this.isHTTP,
|
IsHTTP: this.isHTTP,
|
||||||
IsHTTPS: this.isHTTPS,
|
IsHTTPS: this.isHTTPS,
|
||||||
|
IsHTTP3: this.isHTTP3,
|
||||||
|
|
||||||
nodeConfig: sharedNodeConfig,
|
nodeConfig: sharedNodeConfig,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import (
|
|||||||
"github.com/iwind/TeaGo/maps"
|
"github.com/iwind/TeaGo/maps"
|
||||||
"github.com/iwind/TeaGo/types"
|
"github.com/iwind/TeaGo/types"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os/exec"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sort"
|
"sort"
|
||||||
@@ -36,9 +35,11 @@ func init() {
|
|||||||
|
|
||||||
// ListenerManager 端口监听管理器
|
// ListenerManager 端口监听管理器
|
||||||
type ListenerManager struct {
|
type ListenerManager struct {
|
||||||
listenersMap map[string]*Listener // addr => *Listener
|
listenersMap map[string]*Listener // addr => *Listener
|
||||||
locker sync.Mutex
|
http3Listener *HTTPListener
|
||||||
lastConfig *nodeconfigs.NodeConfig
|
|
||||||
|
locker sync.Mutex
|
||||||
|
lastConfig *nodeconfigs.NodeConfig
|
||||||
|
|
||||||
retryListenerMap map[string]*Listener // 需要重试的监听器 addr => Listener
|
retryListenerMap map[string]*Listener // 需要重试的监听器 addr => Listener
|
||||||
ticker *time.Ticker
|
ticker *time.Ticker
|
||||||
@@ -73,7 +74,7 @@ func NewListenerManager() *ListenerManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Start 启动监听
|
// Start 启动监听
|
||||||
func (this *ListenerManager) Start(node *nodeconfigs.NodeConfig) error {
|
func (this *ListenerManager) Start(nodeConfig *nodeconfigs.NodeConfig) error {
|
||||||
this.locker.Lock()
|
this.locker.Lock()
|
||||||
defer this.locker.Unlock()
|
defer this.locker.Unlock()
|
||||||
|
|
||||||
@@ -84,12 +85,12 @@ func (this *ListenerManager) Start(node *nodeconfigs.NodeConfig) error {
|
|||||||
/**if this.lastConfig != nil && this.lastConfig.Version == node.Version {
|
/**if this.lastConfig != nil && this.lastConfig.Version == node.Version {
|
||||||
return nil
|
return nil
|
||||||
}**/
|
}**/
|
||||||
this.lastConfig = node
|
this.lastConfig = nodeConfig
|
||||||
|
|
||||||
// 所有的新地址
|
// 所有的新地址
|
||||||
groupAddrs := []string{}
|
var groupAddrs = []string{}
|
||||||
availableServerGroups := node.AvailableGroups()
|
var availableServerGroups = nodeConfig.AvailableGroups()
|
||||||
if !node.IsOn {
|
if !nodeConfig.IsOn {
|
||||||
availableServerGroups = []*serverconfigs.ServerAddressGroup{}
|
availableServerGroups = []*serverconfigs.ServerAddressGroup{}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,13 +99,13 @@ func (this *ListenerManager) Start(node *nodeconfigs.NodeConfig) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, group := range availableServerGroups {
|
for _, group := range availableServerGroups {
|
||||||
addr := group.FullAddr()
|
var addr = group.FullAddr()
|
||||||
groupAddrs = append(groupAddrs, addr)
|
groupAddrs = append(groupAddrs, addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 停掉老的
|
// 停掉老的
|
||||||
for listenerKey, listener := range this.listenersMap {
|
for listenerKey, listener := range this.listenersMap {
|
||||||
addr := listener.FullAddr()
|
var addr = listener.FullAddr()
|
||||||
if !lists.ContainsString(groupAddrs, addr) {
|
if !lists.ContainsString(groupAddrs, addr) {
|
||||||
remotelogs.Println("LISTENER_MANAGER", "close '"+addr+"'")
|
remotelogs.Println("LISTENER_MANAGER", "close '"+addr+"'")
|
||||||
_ = listener.Close()
|
_ = listener.Close()
|
||||||
@@ -115,7 +116,7 @@ func (this *ListenerManager) Start(node *nodeconfigs.NodeConfig) error {
|
|||||||
|
|
||||||
// 启动新的或修改老的
|
// 启动新的或修改老的
|
||||||
for _, group := range availableServerGroups {
|
for _, group := range availableServerGroups {
|
||||||
addr := group.FullAddr()
|
var addr = group.FullAddr()
|
||||||
listener, ok := this.listenersMap[addr]
|
listener, ok := this.listenersMap[addr]
|
||||||
if ok {
|
if ok {
|
||||||
// 不需要打印reload信息,防止日志数量过多
|
// 不需要打印reload信息,防止日志数量过多
|
||||||
@@ -129,7 +130,7 @@ func (this *ListenerManager) Start(node *nodeconfigs.NodeConfig) error {
|
|||||||
// 放入到重试队列中
|
// 放入到重试队列中
|
||||||
this.retryListenerMap[addr] = listener
|
this.retryListenerMap[addr] = listener
|
||||||
|
|
||||||
firstServer := group.FirstServer()
|
var firstServer = group.FirstServer()
|
||||||
if firstServer == nil {
|
if firstServer == nil {
|
||||||
remotelogs.Error("LISTENER_MANAGER", err.Error())
|
remotelogs.Error("LISTENER_MANAGER", err.Error())
|
||||||
} else {
|
} else {
|
||||||
@@ -167,10 +168,15 @@ func (this *ListenerManager) TotalActiveConnections() int {
|
|||||||
this.locker.Lock()
|
this.locker.Lock()
|
||||||
defer this.locker.Unlock()
|
defer this.locker.Unlock()
|
||||||
|
|
||||||
total := 0
|
var total = 0
|
||||||
for _, listener := range this.listenersMap {
|
for _, listener := range this.listenersMap {
|
||||||
total += listener.listener.CountActiveConnections()
|
total += listener.listener.CountActiveConnections()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if this.http3Listener != nil {
|
||||||
|
total += this.http3Listener.CountActiveConnections()
|
||||||
|
}
|
||||||
|
|
||||||
return total
|
return total
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,7 +212,7 @@ func (this *ListenerManager) findProcessNameWithPort(isUdp bool, port string) st
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
path, err := exec.LookPath("ss")
|
path, err := executils.LookPath("ss")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
@@ -239,6 +245,17 @@ func (this *ListenerManager) addToFirewalld(groupAddrs []string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HTTP/3相关端口
|
||||||
|
var http3Ports = sharedNodeConfig.FindHTTP3Ports()
|
||||||
|
if len(http3Ports) > 0 {
|
||||||
|
for _, port := range http3Ports {
|
||||||
|
var groupAddr = "udp://:" + types.String(port)
|
||||||
|
if !lists.ContainsString(groupAddrs, groupAddr) {
|
||||||
|
groupAddrs = append(groupAddrs, groupAddr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 组合端口号
|
// 组合端口号
|
||||||
var portStrings = []string{}
|
var portStrings = []string{}
|
||||||
var udpPorts = []int{}
|
var udpPorts = []int{}
|
||||||
@@ -272,7 +289,9 @@ func (this *ListenerManager) addToFirewalld(groupAddrs []string) {
|
|||||||
if newPortStrings == this.lastPortStrings {
|
if newPortStrings == this.lastPortStrings {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
this.locker.Lock()
|
||||||
this.lastPortStrings = newPortStrings
|
this.lastPortStrings = newPortStrings
|
||||||
|
this.locker.Unlock()
|
||||||
|
|
||||||
remotelogs.Println("FIREWALLD", "opening ports automatically ...")
|
remotelogs.Println("FIREWALLD", "opening ports automatically ...")
|
||||||
defer func() {
|
defer func() {
|
||||||
@@ -284,8 +303,10 @@ func (this *ListenerManager) addToFirewalld(groupAddrs []string) {
|
|||||||
var udpPortRanges = utils.MergePorts(udpPorts)
|
var udpPortRanges = utils.MergePorts(udpPorts)
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
|
this.locker.Lock()
|
||||||
this.lastTCPPortRanges = tcpPortRanges
|
this.lastTCPPortRanges = tcpPortRanges
|
||||||
this.lastUDPPortRanges = udpPortRanges
|
this.lastUDPPortRanges = udpPortRanges
|
||||||
|
this.locker.Unlock()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// 删除老的不存在的端口
|
// 删除老的不存在的端口
|
||||||
@@ -321,3 +342,28 @@ func (this *ListenerManager) addToFirewalld(groupAddrs []string) {
|
|||||||
_ = this.firewalld.AllowPortRangesPermanently(tcpPortRanges, "tcp")
|
_ = this.firewalld.AllowPortRangesPermanently(tcpPortRanges, "tcp")
|
||||||
_ = this.firewalld.AllowPortRangesPermanently(udpPortRanges, "udp")
|
_ = this.firewalld.AllowPortRangesPermanently(udpPortRanges, "udp")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *ListenerManager) reloadFirewalld() {
|
||||||
|
this.locker.Lock()
|
||||||
|
defer this.locker.Unlock()
|
||||||
|
|
||||||
|
var nodeConfig = sharedNodeConfig
|
||||||
|
|
||||||
|
// 所有的新地址
|
||||||
|
var groupAddrs = []string{}
|
||||||
|
var availableServerGroups = nodeConfig.AvailableGroups()
|
||||||
|
if !nodeConfig.IsOn {
|
||||||
|
availableServerGroups = []*serverconfigs.ServerAddressGroup{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(availableServerGroups) == 0 {
|
||||||
|
remotelogs.Println("LISTENER_MANAGER", "no available servers to startup")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, group := range availableServerGroups {
|
||||||
|
var addr = group.FullAddr()
|
||||||
|
groupAddrs = append(groupAddrs, addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
go this.addToFirewalld(groupAddrs)
|
||||||
|
}
|
||||||
|
|||||||
@@ -693,6 +693,7 @@ func (this *Node) listenSock() error {
|
|||||||
var lastReadAt int64
|
var lastReadAt int64
|
||||||
var lastWriteAt int64
|
var lastWriteAt int64
|
||||||
var lastErrString = ""
|
var lastErrString = ""
|
||||||
|
var protocol = "tcp"
|
||||||
clientConn, ok := conn.(*ClientConn)
|
clientConn, ok := conn.(*ClientConn)
|
||||||
if ok {
|
if ok {
|
||||||
createdAt = clientConn.CreatedAt()
|
createdAt = clientConn.CreatedAt()
|
||||||
@@ -703,6 +704,8 @@ func (this *Node) listenSock() error {
|
|||||||
if lastErr != nil {
|
if lastErr != nil {
|
||||||
lastErrString = lastErr.Error()
|
lastErrString = lastErr.Error()
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
protocol = "udp"
|
||||||
}
|
}
|
||||||
var age int64 = -1
|
var age int64 = -1
|
||||||
var lastReadAge int64 = -1
|
var lastReadAge int64 = -1
|
||||||
@@ -719,6 +722,7 @@ func (this *Node) listenSock() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
connMaps = append(connMaps, maps.Map{
|
connMaps = append(connMaps, maps.Map{
|
||||||
|
"protocol": protocol,
|
||||||
"addr": conn.RemoteAddr().String(),
|
"addr": conn.RemoteAddr().String(),
|
||||||
"age": age,
|
"age": age,
|
||||||
"readAge": lastReadAge,
|
"readAge": lastReadAge,
|
||||||
|
|||||||
@@ -13,11 +13,13 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/trackers"
|
"github.com/TeaOSLab/EdgeNode/internal/trackers"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
|
fsutils "github.com/TeaOSLab/EdgeNode/internal/utils/fs"
|
||||||
"github.com/iwind/TeaGo/lists"
|
"github.com/iwind/TeaGo/lists"
|
||||||
"github.com/iwind/TeaGo/maps"
|
"github.com/iwind/TeaGo/maps"
|
||||||
"github.com/shirou/gopsutil/v3/cpu"
|
"github.com/shirou/gopsutil/v3/cpu"
|
||||||
"github.com/shirou/gopsutil/v3/disk"
|
"github.com/shirou/gopsutil/v3/disk"
|
||||||
"golang.org/x/sys/unix"
|
"github.com/shirou/gopsutil/v3/net"
|
||||||
|
"math"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -25,12 +27,17 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type NodeStatusExecutor struct {
|
type NodeStatusExecutor struct {
|
||||||
isFirstTime bool
|
isFirstTime bool
|
||||||
|
lastUpdatedTime time.Time
|
||||||
|
|
||||||
cpuUpdatedTime time.Time
|
|
||||||
cpuLogicalCount int
|
cpuLogicalCount int
|
||||||
cpuPhysicalCount int
|
cpuPhysicalCount int
|
||||||
|
|
||||||
|
// 流量统计
|
||||||
|
lastIOCounterStat net.IOCountersStat
|
||||||
|
lastUDPInDatagrams int64
|
||||||
|
lastUDPOutDatagrams int64
|
||||||
|
|
||||||
apiCallStat *rpc.CallStat
|
apiCallStat *rpc.CallStat
|
||||||
|
|
||||||
ticker *time.Ticker
|
ticker *time.Ticker
|
||||||
@@ -40,12 +47,15 @@ func NewNodeStatusExecutor() *NodeStatusExecutor {
|
|||||||
return &NodeStatusExecutor{
|
return &NodeStatusExecutor{
|
||||||
ticker: time.NewTicker(30 * time.Second),
|
ticker: time.NewTicker(30 * time.Second),
|
||||||
apiCallStat: rpc.NewCallStat(10),
|
apiCallStat: rpc.NewCallStat(10),
|
||||||
|
|
||||||
|
lastUDPInDatagrams: -1,
|
||||||
|
lastUDPOutDatagrams: -1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *NodeStatusExecutor) Listen() {
|
func (this *NodeStatusExecutor) Listen() {
|
||||||
this.isFirstTime = true
|
this.isFirstTime = true
|
||||||
this.cpuUpdatedTime = time.Now()
|
this.lastUpdatedTime = time.Now()
|
||||||
this.update()
|
this.update()
|
||||||
|
|
||||||
events.OnKey(events.EventQuit, this, func() {
|
events.OnKey(events.EventQuit, this, func() {
|
||||||
@@ -119,6 +129,11 @@ func (this *NodeStatusExecutor) update() {
|
|||||||
this.updateCacheSpace(status)
|
this.updateCacheSpace(status)
|
||||||
cacheSpaceTR.End()
|
cacheSpaceTR.End()
|
||||||
|
|
||||||
|
this.updateAllTraffic(status)
|
||||||
|
|
||||||
|
// 修改更新时间
|
||||||
|
this.lastUpdatedTime = time.Now()
|
||||||
|
|
||||||
status.UpdatedAt = time.Now().Unix()
|
status.UpdatedAt = time.Now().Unix()
|
||||||
status.Timestamp = status.UpdatedAt
|
status.Timestamp = status.UpdatedAt
|
||||||
|
|
||||||
@@ -173,8 +188,6 @@ func (this *NodeStatusExecutor) updateCPU(status *nodeconfigs.NodeStatus) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if this.cpuLogicalCount == 0 && this.cpuPhysicalCount == 0 {
|
if this.cpuLogicalCount == 0 && this.cpuPhysicalCount == 0 {
|
||||||
this.cpuUpdatedTime = time.Now()
|
|
||||||
|
|
||||||
status.CPULogicalCount, err = cpu.Counts(true)
|
status.CPULogicalCount, err = cpu.Counts(true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
status.Error = "cpu.Counts(): " + err.Error()
|
status.Error = "cpu.Counts(): " + err.Error()
|
||||||
@@ -266,19 +279,97 @@ 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 {
|
||||||
var stat unix.Statfs_t
|
stat, err := fsutils.Stat(path)
|
||||||
err := unix.Statfs(path, &stat)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
result = append(result, maps.Map{
|
result = append(result, maps.Map{
|
||||||
"path": path,
|
"path": path,
|
||||||
"total": stat.Blocks * uint64(stat.Bsize),
|
"total": stat.TotalSize(),
|
||||||
"avail": stat.Bavail * uint64(stat.Bsize),
|
"avail": stat.FreeSize(),
|
||||||
"used": (stat.Blocks - stat.Bavail) * uint64(stat.Bsize),
|
"used": stat.UsedSize(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
monitor.SharedValueQueue.Add(nodeconfigs.NodeValueItemCacheDir, maps.Map{
|
monitor.SharedValueQueue.Add(nodeconfigs.NodeValueItemCacheDir, maps.Map{
|
||||||
"dirs": result,
|
"dirs": result,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 流量
|
||||||
|
func (this *NodeStatusExecutor) updateAllTraffic(status *nodeconfigs.NodeStatus) {
|
||||||
|
trafficCounters, err := net.IOCounters(true)
|
||||||
|
if err != nil {
|
||||||
|
remotelogs.Warn("NODE_STATUS_EXECUTOR", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var allCounter = net.IOCountersStat{}
|
||||||
|
for _, counter := range trafficCounters {
|
||||||
|
// 跳过lo
|
||||||
|
if counter.Name == "lo" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
allCounter.BytesRecv += counter.BytesRecv
|
||||||
|
allCounter.BytesSent += counter.BytesSent
|
||||||
|
}
|
||||||
|
if allCounter.BytesSent == 0 && allCounter.BytesRecv == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if this.lastIOCounterStat.BytesSent > 0 {
|
||||||
|
// 记录监控数据
|
||||||
|
if allCounter.BytesSent >= this.lastIOCounterStat.BytesSent && allCounter.BytesRecv >= this.lastIOCounterStat.BytesRecv {
|
||||||
|
var costSeconds = int(math.Ceil(time.Since(this.lastUpdatedTime).Seconds()))
|
||||||
|
if costSeconds > 0 {
|
||||||
|
var bytesSent = allCounter.BytesSent - this.lastIOCounterStat.BytesSent
|
||||||
|
var bytesRecv = allCounter.BytesRecv - this.lastIOCounterStat.BytesRecv
|
||||||
|
|
||||||
|
// UDP
|
||||||
|
var udpInDatagrams int64 = 0
|
||||||
|
var udpOutDatagrams int64 = 0
|
||||||
|
protoStats, protoErr := net.ProtoCounters([]string{"udp"})
|
||||||
|
if protoErr == nil {
|
||||||
|
for _, protoStat := range protoStats {
|
||||||
|
if protoStat.Protocol == "udp" {
|
||||||
|
udpInDatagrams = protoStat.Stats["InDatagrams"]
|
||||||
|
udpOutDatagrams = protoStat.Stats["OutDatagrams"]
|
||||||
|
if udpInDatagrams < 0 {
|
||||||
|
udpInDatagrams = 0
|
||||||
|
}
|
||||||
|
if udpOutDatagrams < 0 {
|
||||||
|
udpOutDatagrams = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var avgUDPInDatagrams int64 = 0
|
||||||
|
var avgUDPOutDatagrams int64 = 0
|
||||||
|
if this.lastUDPInDatagrams >= 0 && this.lastUDPOutDatagrams >= 0 {
|
||||||
|
avgUDPInDatagrams = (udpInDatagrams - this.lastUDPInDatagrams) / int64(costSeconds)
|
||||||
|
avgUDPOutDatagrams = (udpOutDatagrams - this.lastUDPOutDatagrams) / int64(costSeconds)
|
||||||
|
if avgUDPInDatagrams < 0 {
|
||||||
|
avgUDPInDatagrams = 0
|
||||||
|
}
|
||||||
|
if avgUDPOutDatagrams < 0 {
|
||||||
|
avgUDPOutDatagrams = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.lastUDPInDatagrams = udpInDatagrams
|
||||||
|
this.lastUDPOutDatagrams = udpOutDatagrams
|
||||||
|
|
||||||
|
monitor.SharedValueQueue.Add(nodeconfigs.NodeValueItemAllTraffic, maps.Map{
|
||||||
|
"inBytes": bytesRecv,
|
||||||
|
"outBytes": bytesSent,
|
||||||
|
"avgInBytes": bytesRecv / uint64(costSeconds),
|
||||||
|
"avgOutBytes": bytesSent / uint64(costSeconds),
|
||||||
|
|
||||||
|
"avgUDPInDatagrams": avgUDPInDatagrams,
|
||||||
|
"avgUDPOutDatagrams": avgUDPOutDatagrams,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.lastIOCounterStat = allCounter
|
||||||
|
}
|
||||||
|
|||||||
@@ -80,6 +80,12 @@ func (this *Node) execTask(rpcClient *rpc.RPCClient, task *pb.NodeTask) error {
|
|||||||
err = this.execUserServersStateChangedTask(rpcClient, task)
|
err = this.execUserServersStateChangedTask(rpcClient, task)
|
||||||
case "uamPolicyChanged":
|
case "uamPolicyChanged":
|
||||||
err = this.execUAMPolicyChangedTask(rpcClient)
|
err = this.execUAMPolicyChangedTask(rpcClient)
|
||||||
|
case "httpCCPolicyChanged":
|
||||||
|
err = this.execHTTPCCPolicyChangedTask(rpcClient)
|
||||||
|
case "http3PolicyChanged":
|
||||||
|
err = this.execHTTP3PolicyChangedTask(rpcClient)
|
||||||
|
case "httpPagesPolicyChanged":
|
||||||
|
err = this.execHTTPPagesPolicyChangedTask(rpcClient)
|
||||||
case "updatingServers":
|
case "updatingServers":
|
||||||
err = this.execUpdatingServersTask(rpcClient)
|
err = this.execUpdatingServersTask(rpcClient)
|
||||||
case "plusChanged":
|
case "plusChanged":
|
||||||
@@ -124,15 +130,6 @@ func (this *Node) execNodeVersionChangedTask() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 脚本库变更
|
|
||||||
func (this *Node) execScriptsChangedTask() error {
|
|
||||||
err := this.reloadCommonScripts()
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("reload common scripts failed: " + err.Error())
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 节点级别变更
|
// 节点级别变更
|
||||||
func (this *Node) execNodeLevelChangedTask(rpcClient *rpc.RPCClient) error {
|
func (this *Node) execNodeLevelChangedTask(rpcClient *rpc.RPCClient) error {
|
||||||
levelInfoResp, err := rpcClient.NodeRPC.FindNodeLevelInfo(rpcClient.Context(), &pb.FindNodeLevelInfoRequest{})
|
levelInfoResp, err := rpcClient.NodeRPC.FindNodeLevelInfo(rpcClient.Context(), &pb.FindNodeLevelInfoRequest{})
|
||||||
@@ -159,34 +156,6 @@ func (this *Node) execNodeLevelChangedTask(rpcClient *rpc.RPCClient) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UAM策略变更
|
|
||||||
func (this *Node) execUAMPolicyChangedTask(rpcClient *rpc.RPCClient) error {
|
|
||||||
remotelogs.Println("NODE", "updating uam policies ...")
|
|
||||||
resp, err := rpcClient.NodeRPC.FindNodeUAMPolicies(rpcClient.Context(), &pb.FindNodeUAMPoliciesRequest{})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
var uamPolicyMap = map[int64]*nodeconfigs.UAMPolicy{}
|
|
||||||
for _, policy := range resp.UamPolicies {
|
|
||||||
if len(policy.UamPolicyJSON) > 0 {
|
|
||||||
var uamPolicy = &nodeconfigs.UAMPolicy{}
|
|
||||||
err = json.Unmarshal(policy.UamPolicyJSON, uamPolicy)
|
|
||||||
if err != nil {
|
|
||||||
remotelogs.Error("NODE", "decode uam policy failed: "+err.Error())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
err = uamPolicy.Init()
|
|
||||||
if err != nil {
|
|
||||||
remotelogs.Error("NODE", "initialize uam policy failed: "+err.Error())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
uamPolicyMap[policy.NodeClusterId] = uamPolicy
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sharedNodeConfig.UpdateUAMPolicies(uamPolicyMap)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DDoS配置变更
|
// DDoS配置变更
|
||||||
func (this *Node) execDDoSProtectionChangedTask(rpcClient *rpc.RPCClient) error {
|
func (this *Node) execDDoSProtectionChangedTask(rpcClient *rpc.RPCClient) error {
|
||||||
resp, err := rpcClient.NodeRPC.FindNodeDDoSProtection(rpcClient.Context(), &pb.FindNodeDDoSProtectionRequest{})
|
resp, err := rpcClient.NodeRPC.FindNodeDDoSProtection(rpcClient.Context(), &pb.FindNodeDDoSProtectionRequest{})
|
||||||
@@ -228,7 +197,7 @@ func (this *Node) execGlobalServerConfigChangedTask(rpcClient *rpc.RPCClient) er
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if len(resp.GlobalServerConfigJSON) > 0 {
|
if len(resp.GlobalServerConfigJSON) > 0 {
|
||||||
var globalServerConfig = serverconfigs.DefaultGlobalServerConfig()
|
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 errors.New("decode global server config failed: " + err.Error())
|
||||||
|
|||||||
31
internal/nodes/node_tasks_ext.go
Normal file
31
internal/nodes/node_tasks_ext.go
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||||
|
//go:build !plus
|
||||||
|
|
||||||
|
package nodes
|
||||||
|
|
||||||
|
import "github.com/TeaOSLab/EdgeNode/internal/rpc"
|
||||||
|
|
||||||
|
func (this *Node) execScriptsChangedTask() error {
|
||||||
|
// stub
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Node) execUAMPolicyChangedTask(rpcClient *rpc.RPCClient) error {
|
||||||
|
// stub
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Node) execHTTPCCPolicyChangedTask(rpcClient *rpc.RPCClient) error {
|
||||||
|
// stub
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Node) execHTTP3PolicyChangedTask(rpcClient *rpc.RPCClient) error {
|
||||||
|
// stub
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Node) execHTTPPagesPolicyChangedTask(rpcClient *rpc.RPCClient) error {
|
||||||
|
// stub
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -13,7 +13,6 @@ import (
|
|||||||
executils "github.com/TeaOSLab/EdgeNode/internal/utils/exec"
|
executils "github.com/TeaOSLab/EdgeNode/internal/utils/exec"
|
||||||
"github.com/iwind/TeaGo/maps"
|
"github.com/iwind/TeaGo/maps"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -84,7 +83,7 @@ func (this *SystemServiceManager) setupSystemd(params maps.Map) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 检查当前的service
|
// 检查当前的service
|
||||||
systemctl, err := exec.LookPath("systemctl")
|
systemctl, err := executils.LookPath("systemctl")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,10 +27,9 @@ func init() {
|
|||||||
sharedOCSPTask.Start()
|
sharedOCSPTask.Start()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
events.On(events.EventQuit, func() {
|
events.OnClose(func() {
|
||||||
sharedOCSPTask.Stop()
|
sharedOCSPTask.Stop()
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// OCSPUpdateTask 更新OCSP任务
|
// OCSPUpdateTask 更新OCSP任务
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ func init() {
|
|||||||
sharedSyncAPINodesTask.Start()
|
sharedSyncAPINodesTask.Start()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
events.On(events.EventQuit, func() {
|
events.OnClose(func() {
|
||||||
sharedSyncAPINodesTask.Stop()
|
sharedSyncAPINodesTask.Stop()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -164,6 +164,8 @@ func (this *Regexp) ParseKeywords(exp string) (keywords []string) {
|
|||||||
return this.ParseKeywords(reg.Sub[0].String())
|
return this.ParseKeywords(reg.Sub[0].String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const maxComposedKeywords = 32
|
||||||
|
|
||||||
switch reg.Op {
|
switch reg.Op {
|
||||||
case syntax.OpConcat:
|
case syntax.OpConcat:
|
||||||
var prevKeywords = []string{}
|
var prevKeywords = []string{}
|
||||||
@@ -190,6 +192,11 @@ func (this *Regexp) ParseKeywords(exp string) (keywords []string) {
|
|||||||
for _, prevKeyword := range prevKeywords {
|
for _, prevKeyword := range prevKeywords {
|
||||||
for _, subKeyword := range subKeywords {
|
for _, subKeyword := range subKeywords {
|
||||||
keywords = append(keywords, prevKeyword+subKeyword)
|
keywords = append(keywords, prevKeyword+subKeyword)
|
||||||
|
|
||||||
|
// 限制不能超出最大关键词
|
||||||
|
if len(keywords) > maxComposedKeywords {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
prevKeywords = keywords
|
prevKeywords = keywords
|
||||||
|
|||||||
@@ -58,59 +58,6 @@ func TestRegexp_ParseKeywords(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestRegexp_Special(t *testing.T) {
|
func TestRegexp_Special(t *testing.T) {
|
||||||
var unescape = func(v string) string {
|
|
||||||
//replace urlencoded characters
|
|
||||||
|
|
||||||
var chars = [][2]string{
|
|
||||||
{`\s`, `(\s|%09|%0A|\+)`},
|
|
||||||
{`\(`, `(\(|%28)`},
|
|
||||||
{`=`, `(=|%3D)`},
|
|
||||||
{`<`, `(<|%3C)`},
|
|
||||||
{`\*`, `(\*|%2A)`},
|
|
||||||
{`\\`, `(\\|%2F)`},
|
|
||||||
{`!`, `(!|%21)`},
|
|
||||||
{`/`, `(/|%2F)`},
|
|
||||||
{`;`, `(;|%3B)`},
|
|
||||||
{`\+`, `(\+|%20)`},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, c := range chars {
|
|
||||||
if !strings.Contains(v, c[0]) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
var pieces = strings.Split(v, c[0])
|
|
||||||
|
|
||||||
// 修复piece中错误的\
|
|
||||||
for pieceIndex, piece := range pieces {
|
|
||||||
var l = len(piece)
|
|
||||||
if l == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if piece[l-1] != '\\' {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// 计算\的数量
|
|
||||||
var countBackSlashes = 0
|
|
||||||
for i := l - 1; i >= 0; i-- {
|
|
||||||
if piece[i] == '\\' {
|
|
||||||
countBackSlashes++
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if countBackSlashes%2 == 1 {
|
|
||||||
// 去掉最后一个
|
|
||||||
pieces[pieceIndex] = piece[:len(piece)-1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
v = strings.Join(pieces, c[1])
|
|
||||||
}
|
|
||||||
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, s := range []string{
|
for _, s := range []string{
|
||||||
`\\s`,
|
`\\s`,
|
||||||
`\s\W`,
|
`\s\W`,
|
||||||
@@ -121,7 +68,7 @@ func TestRegexp_Special(t *testing.T) {
|
|||||||
`aaaa\\\=\W`,
|
`aaaa\\\=\W`,
|
||||||
`aaaa\\\\=\W`,
|
`aaaa\\\\=\W`,
|
||||||
} {
|
} {
|
||||||
var es = unescape(s)
|
var es = testUnescape(t, s)
|
||||||
t.Log(s, "=>", es)
|
t.Log(s, "=>", es)
|
||||||
_, err := re.Compile(es)
|
_, err := re.Compile(es)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -130,6 +77,17 @@ func TestRegexp_Special(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRegexp_Special2(t *testing.T) {
|
||||||
|
r, err := re.Compile(testUnescape(t, `/api/ios/a
|
||||||
|
/api/ios/b
|
||||||
|
/api/ios/c
|
||||||
|
/report`))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log(r.Keywords())
|
||||||
|
}
|
||||||
|
|
||||||
func TestRegexp_ParseKeywords2(t *testing.T) {
|
func TestRegexp_ParseKeywords2(t *testing.T) {
|
||||||
var a = assert.NewAssertion(t)
|
var a = assert.NewAssertion(t)
|
||||||
|
|
||||||
@@ -232,6 +190,14 @@ func BenchmarkRegexp_MatchString_VS_FindSubString2(b *testing.B) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSplitAndJoin(t *testing.T) {
|
||||||
|
var pieces = strings.Split(`/api/ios/a
|
||||||
|
/api/ios/b
|
||||||
|
/api/ios/c
|
||||||
|
/report`, "/")
|
||||||
|
t.Log(strings.Join(pieces, `(/|%2F)`))
|
||||||
|
}
|
||||||
|
|
||||||
func testCompareStrings(s1 []string, s2 []string) bool {
|
func testCompareStrings(s1 []string, s2 []string) bool {
|
||||||
if len(s1) != len(s2) {
|
if len(s1) != len(s2) {
|
||||||
return false
|
return false
|
||||||
@@ -243,3 +209,55 @@ func testCompareStrings(s1 []string, s2 []string) bool {
|
|||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testUnescape(t *testing.T, v string) string {
|
||||||
|
// replace urlencoded characters
|
||||||
|
var unescapeChars = [][2]string{
|
||||||
|
{`\s`, `(\s|%09|%0A|\+)`},
|
||||||
|
{`\(`, `(\(|%28)`},
|
||||||
|
{`=`, `(=|%3D)`},
|
||||||
|
{`<`, `(<|%3C)`},
|
||||||
|
{`\*`, `(\*|%2A)`},
|
||||||
|
{`\\`, `(\\|%2F)`},
|
||||||
|
{`!`, `(!|%21)`},
|
||||||
|
{`/`, `(/|%2F)`},
|
||||||
|
{`;`, `(;|%3B)`},
|
||||||
|
{`\+`, `(\+|%20)`},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range unescapeChars {
|
||||||
|
if !strings.Contains(v, c[0]) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var pieces = strings.Split(v, c[0])
|
||||||
|
|
||||||
|
// 修复piece中错误的\
|
||||||
|
for pieceIndex, piece := range pieces {
|
||||||
|
var l = len(piece)
|
||||||
|
if l == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if piece[l-1] != '\\' {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算\的数量
|
||||||
|
var countBackSlashes = 0
|
||||||
|
for i := l - 1; i >= 0; i-- {
|
||||||
|
if piece[i] == '\\' {
|
||||||
|
countBackSlashes++
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if countBackSlashes%2 == 1 {
|
||||||
|
// 去掉最后一个
|
||||||
|
pieces[pieceIndex] = piece[:len(piece)-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
v = strings.Join(pieces, c[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|||||||
@@ -289,6 +289,10 @@ Loop:
|
|||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case log := <-logChan:
|
case log := <-logChan:
|
||||||
|
if log.NodeId <= 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// 是否已存在
|
// 是否已存在
|
||||||
var hash = xxhash.Sum64String(types.String(log.ServerId) + "_" + log.Description)
|
var hash = xxhash.Sum64String(types.String(log.ServerId) + "_" + log.Description)
|
||||||
var found = false
|
var found = false
|
||||||
@@ -312,6 +316,7 @@ Loop:
|
|||||||
break Loop
|
break Loop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(logList) == 0 {
|
if len(logList) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ func init() {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
events.On(events.EventQuit, func() {
|
events.OnClose(func() {
|
||||||
SharedBandwidthStatManager.Cancel()
|
SharedBandwidthStatManager.Cancel()
|
||||||
|
|
||||||
err := SharedBandwidthStatManager.Save()
|
err := SharedBandwidthStatManager.Save()
|
||||||
@@ -74,15 +74,12 @@ type BandwidthStatManager struct {
|
|||||||
|
|
||||||
ticker *time.Ticker
|
ticker *time.Ticker
|
||||||
locker sync.Mutex
|
locker sync.Mutex
|
||||||
|
|
||||||
cacheFile string // 上一次的缓存文件
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBandwidthStatManager() *BandwidthStatManager {
|
func NewBandwidthStatManager() *BandwidthStatManager {
|
||||||
return &BandwidthStatManager{
|
return &BandwidthStatManager{
|
||||||
m: map[string]*BandwidthStat{},
|
m: map[string]*BandwidthStat{},
|
||||||
ticker: time.NewTicker(1 * time.Minute), // 时间小于1分钟是为了更快速地上传结果
|
ticker: time.NewTicker(1 * time.Minute), // 时间小于1分钟是为了更快速地上传结果
|
||||||
cacheFile: Tea.Root + "/data/bandwidth.dat",
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -271,13 +268,17 @@ func (this *BandwidthStatManager) Save() error {
|
|||||||
this.locker.Lock()
|
this.locker.Lock()
|
||||||
defer this.locker.Unlock()
|
defer this.locker.Unlock()
|
||||||
|
|
||||||
|
if len(this.m) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
data, err := json.Marshal(this.m)
|
data, err := json.Marshal(this.m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = os.Remove(this.cacheFile)
|
_ = os.Remove(this.cacheFile())
|
||||||
return os.WriteFile(this.cacheFile, data, 0666)
|
return os.WriteFile(this.cacheFile(), data, 0666)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cancel 取消上传
|
// Cancel 取消上传
|
||||||
@@ -287,7 +288,7 @@ func (this *BandwidthStatManager) Cancel() {
|
|||||||
|
|
||||||
// 从本地缓存文件中恢复数据
|
// 从本地缓存文件中恢复数据
|
||||||
func (this *BandwidthStatManager) recover() {
|
func (this *BandwidthStatManager) recover() {
|
||||||
cacheData, err := os.ReadFile(this.cacheFile)
|
cacheData, err := os.ReadFile(this.cacheFile())
|
||||||
if err == nil {
|
if err == nil {
|
||||||
var m = map[string]*BandwidthStat{}
|
var m = map[string]*BandwidthStat{}
|
||||||
err = json.Unmarshal(cacheData, &m)
|
err = json.Unmarshal(cacheData, &m)
|
||||||
@@ -309,6 +310,12 @@ func (this *BandwidthStatManager) recover() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ = os.Remove(this.cacheFile)
|
_ = os.Remove(this.cacheFile())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取缓存文件
|
||||||
|
// 不能在init()中初始化,避免无法获得正确的路径
|
||||||
|
func (this *BandwidthStatManager) cacheFile() string {
|
||||||
|
return Tea.Root + "/data/bandwidth.dat"
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ package stats
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
"github.com/mssola/user_agent"
|
"github.com/TeaOSLab/EdgeNode/internal/utils/fnv"
|
||||||
|
"github.com/mssola/useragent"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -12,10 +13,10 @@ var SharedUserAgentParser = NewUserAgentParser()
|
|||||||
|
|
||||||
// UserAgentParser UserAgent解析器
|
// UserAgentParser UserAgent解析器
|
||||||
type UserAgentParser struct {
|
type UserAgentParser struct {
|
||||||
parser *user_agent.UserAgent
|
parser *useragent.UserAgent
|
||||||
|
|
||||||
cacheMap1 map[string]UserAgentParserResult
|
cacheMap1 map[uint64]UserAgentParserResult
|
||||||
cacheMap2 map[string]UserAgentParserResult
|
cacheMap2 map[uint64]UserAgentParserResult
|
||||||
maxCacheItems int
|
maxCacheItems int
|
||||||
|
|
||||||
cacheCursor int
|
cacheCursor int
|
||||||
@@ -24,9 +25,9 @@ type UserAgentParser struct {
|
|||||||
|
|
||||||
func NewUserAgentParser() *UserAgentParser {
|
func NewUserAgentParser() *UserAgentParser {
|
||||||
var parser = &UserAgentParser{
|
var parser = &UserAgentParser{
|
||||||
parser: &user_agent.UserAgent{},
|
parser: &useragent.UserAgent{},
|
||||||
cacheMap1: map[string]UserAgentParserResult{},
|
cacheMap1: map[uint64]UserAgentParserResult{},
|
||||||
cacheMap2: map[string]UserAgentParserResult{},
|
cacheMap2: map[uint64]UserAgentParserResult{},
|
||||||
cacheCursor: 0,
|
cacheCursor: 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,14 +54,16 @@ func (this *UserAgentParser) Parse(userAgent string) (result UserAgentParserResu
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var userAgentKey = fnv.HashString(userAgent)
|
||||||
|
|
||||||
this.locker.RLock()
|
this.locker.RLock()
|
||||||
cacheResult, ok := this.cacheMap1[userAgent]
|
cacheResult, ok := this.cacheMap1[userAgentKey]
|
||||||
if ok {
|
if ok {
|
||||||
this.locker.RUnlock()
|
this.locker.RUnlock()
|
||||||
return cacheResult
|
return cacheResult
|
||||||
}
|
}
|
||||||
|
|
||||||
cacheResult, ok = this.cacheMap2[userAgent]
|
cacheResult, ok = this.cacheMap2[userAgentKey]
|
||||||
if ok {
|
if ok {
|
||||||
this.locker.RUnlock()
|
this.locker.RUnlock()
|
||||||
return cacheResult
|
return cacheResult
|
||||||
@@ -68,6 +71,8 @@ func (this *UserAgentParser) Parse(userAgent string) (result UserAgentParserResu
|
|||||||
this.locker.RUnlock()
|
this.locker.RUnlock()
|
||||||
|
|
||||||
this.locker.Lock()
|
this.locker.Lock()
|
||||||
|
defer this.locker.Unlock()
|
||||||
|
|
||||||
this.parser.Parse(userAgent)
|
this.parser.Parse(userAgent)
|
||||||
result.OS = this.parser.OSInfo()
|
result.OS = this.parser.OSInfo()
|
||||||
result.BrowserName, result.BrowserVersion = this.parser.Browser()
|
result.BrowserName, result.BrowserVersion = this.parser.Browser()
|
||||||
@@ -83,19 +88,18 @@ func (this *UserAgentParser) Parse(userAgent string) (result UserAgentParserResu
|
|||||||
}
|
}
|
||||||
|
|
||||||
if this.cacheCursor == 0 {
|
if this.cacheCursor == 0 {
|
||||||
this.cacheMap1[userAgent] = result
|
this.cacheMap1[userAgentKey] = result
|
||||||
if len(this.cacheMap1) >= this.maxCacheItems {
|
if len(this.cacheMap1) >= this.maxCacheItems {
|
||||||
this.cacheCursor = 1
|
this.cacheCursor = 1
|
||||||
this.cacheMap2 = map[string]UserAgentParserResult{}
|
this.cacheMap2 = map[uint64]UserAgentParserResult{}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.cacheMap2[userAgent] = result
|
this.cacheMap2[userAgentKey] = result
|
||||||
if len(this.cacheMap2) >= this.maxCacheItems {
|
if len(this.cacheMap2) >= this.maxCacheItems {
|
||||||
this.cacheCursor = 0
|
this.cacheCursor = 0
|
||||||
this.cacheMap1 = map[string]UserAgentParserResult{}
|
this.cacheMap1 = map[uint64]UserAgentParserResult{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.locker.Unlock()
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,12 @@
|
|||||||
|
|
||||||
package stats
|
package stats
|
||||||
|
|
||||||
import "github.com/mssola/user_agent"
|
import (
|
||||||
|
"github.com/mssola/useragent"
|
||||||
|
)
|
||||||
|
|
||||||
type UserAgentParserResult struct {
|
type UserAgentParserResult struct {
|
||||||
OS user_agent.OSInfo
|
OS useragent.OSInfo
|
||||||
BrowserName string
|
BrowserName string
|
||||||
BrowserVersion string
|
BrowserVersion string
|
||||||
IsMobile bool
|
IsMobile bool
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import (
|
|||||||
|
|
||||||
func TestUserAgentParser_Parse(t *testing.T) {
|
func TestUserAgentParser_Parse(t *testing.T) {
|
||||||
var parser = NewUserAgentParser()
|
var parser = NewUserAgentParser()
|
||||||
for i := 0; i < 4; i ++ {
|
for i := 0; i < 4; i++ {
|
||||||
t.Log(parser.Parse("Mozilla/5.0 (Windows NT 10.0; WOW64; rv:49.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36 Test/1"))
|
t.Log(parser.Parse("Mozilla/5.0 (Windows NT 10.0; WOW64; rv:49.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36 Test/1"))
|
||||||
t.Log(parser.Parse("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36"))
|
t.Log(parser.Parse("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36"))
|
||||||
}
|
}
|
||||||
@@ -47,7 +47,25 @@ func TestUserAgentParser_Memory(t *testing.T) {
|
|||||||
func BenchmarkUserAgentParser_Parse(b *testing.B) {
|
func BenchmarkUserAgentParser_Parse(b *testing.B) {
|
||||||
var parser = NewUserAgentParser()
|
var parser = NewUserAgentParser()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
parser.Parse("Mozilla/5.0 (Windows NT 10.0; WOW64; rv:49.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36 Test/" + types.String(rands.Int(0, 40000)))
|
parser.Parse("Mozilla/5.0 (Windows NT 10.0; WOW64; rv:49.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36 Test/" + types.String(rands.Int(0, 1_000_000)))
|
||||||
}
|
}
|
||||||
b.Log(len(parser.cacheMap1), len(parser.cacheMap2))
|
b.Log(len(parser.cacheMap1), len(parser.cacheMap2))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkUserAgentParser_Parse2(b *testing.B) {
|
||||||
|
var parser = NewUserAgentParser()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
parser.Parse("Mozilla/5.0 (Windows NT 10.0; WOW64; rv:49.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36 Test/" + types.String(rands.Int(0, 100_000)))
|
||||||
|
}
|
||||||
|
b.Log(len(parser.cacheMap1), len(parser.cacheMap2))
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkUserAgentParser_Parse3(b *testing.B) {
|
||||||
|
var parser = NewUserAgentParser()
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
for pb.Next() {
|
||||||
|
parser.Parse("Mozilla/5.0 (Windows NT 10.0; WOW64; rv:49.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36 Test/" + types.String(rands.Int(0, 100_000)))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
b.Log(len(parser.cacheMap1), len(parser.cacheMap2))
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeNode/internal/utils/fasttime"
|
"github.com/TeaOSLab/EdgeNode/internal/utils/fasttime"
|
||||||
)
|
)
|
||||||
|
|
||||||
var SharedCache = NewCache()
|
var SharedCache = NewBigCache()
|
||||||
|
|
||||||
// Cache TTL缓存
|
// Cache TTL缓存
|
||||||
// 最大的缓存时间为30 * 86400
|
// 最大的缓存时间为30 * 86400
|
||||||
@@ -14,8 +14,6 @@ var SharedCache = NewCache()
|
|||||||
// Piece1 | Piece2 | Piece3 | ...
|
// Piece1 | Piece2 | Piece3 | ...
|
||||||
// [ Item1, Item2, ... ] | ...
|
// [ Item1, Item2, ... ] | ...
|
||||||
//
|
//
|
||||||
// KeyMap列表数据结构
|
|
||||||
// { timestamp1 => [key1, key2, ...] }, ...
|
|
||||||
type Cache struct {
|
type Cache struct {
|
||||||
isDestroyed bool
|
isDestroyed bool
|
||||||
pieces []*Piece
|
pieces []*Piece
|
||||||
@@ -25,6 +23,14 @@ type Cache struct {
|
|||||||
gcPieceIndex int
|
gcPieceIndex int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewBigCache() *Cache {
|
||||||
|
var delta = utils.SystemMemoryGB() / 2
|
||||||
|
if delta <= 0 {
|
||||||
|
delta = 1
|
||||||
|
}
|
||||||
|
return NewCache(NewMaxItemsOption(delta * 1_000_000))
|
||||||
|
}
|
||||||
|
|
||||||
func NewCache(opt ...OptionInterface) *Cache {
|
func NewCache(opt ...OptionInterface) *Cache {
|
||||||
var countPieces = 256
|
var countPieces = 256
|
||||||
var maxItems = 1_000_000
|
var maxItems = 1_000_000
|
||||||
@@ -34,7 +40,7 @@ func NewCache(opt ...OptionInterface) *Cache {
|
|||||||
// 我们限制内存过小的服务能够使用的数量
|
// 我们限制内存过小的服务能够使用的数量
|
||||||
maxItems = 500_000
|
maxItems = 500_000
|
||||||
} else {
|
} else {
|
||||||
var delta = totalMemory / 8
|
var delta = totalMemory / 4
|
||||||
if delta > 0 {
|
if delta > 0 {
|
||||||
maxItems *= delta
|
maxItems *= delta
|
||||||
}
|
}
|
||||||
@@ -138,12 +144,20 @@ func (this *Cache) Count() (count int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *Cache) GC() {
|
func (this *Cache) GC() {
|
||||||
this.pieces[this.gcPieceIndex].GC()
|
var index = this.gcPieceIndex
|
||||||
var newIndex = this.gcPieceIndex + 1
|
const maxPiecesPerGC = 4
|
||||||
if newIndex >= int(this.countPieces) {
|
for i := index; i < index+maxPiecesPerGC; i++ {
|
||||||
newIndex = 0
|
if i >= int(this.countPieces) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
this.pieces[i].GC()
|
||||||
}
|
}
|
||||||
this.gcPieceIndex = newIndex
|
|
||||||
|
index += maxPiecesPerGC
|
||||||
|
if index >= int(this.countPieces) {
|
||||||
|
index = 0
|
||||||
|
}
|
||||||
|
this.gcPieceIndex = index
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *Cache) Clean() {
|
func (this *Cache) Clean() {
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ import (
|
|||||||
func TestNewCache(t *testing.T) {
|
func TestNewCache(t *testing.T) {
|
||||||
var cache = NewCache()
|
var cache = NewCache()
|
||||||
cache.Write("a", 1, time.Now().Unix()+3600)
|
cache.Write("a", 1, time.Now().Unix()+3600)
|
||||||
cache.Write("b", 2, time.Now().Unix()+3601)
|
cache.Write("b", 2, time.Now().Unix()+1)
|
||||||
cache.Write("a", 1, time.Now().Unix()+3602)
|
cache.Write("c", 1, time.Now().Unix()+3602)
|
||||||
cache.Write("d", 1, time.Now().Unix()+1)
|
cache.Write("d", 1, time.Now().Unix()+1)
|
||||||
|
|
||||||
for _, piece := range cache.pieces {
|
for _, piece := range cache.pieces {
|
||||||
@@ -28,8 +28,14 @@ func TestNewCache(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
t.Log("a:", cache.Read("a"))
|
t.Log("a:", cache.Read("a"))
|
||||||
time.Sleep(2 * time.Second)
|
time.Sleep(5 * time.Second)
|
||||||
t.Log("d:", cache.Read("d")) // should be nil
|
|
||||||
|
for i := 0; i < len(cache.pieces); i++ {
|
||||||
|
cache.GC()
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Log("b:", cache.Read("b"))
|
||||||
|
t.Log("d:", cache.Read("d"))
|
||||||
t.Log("left:", cache.Count(), "items")
|
t.Log("left:", cache.Count(), "items")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,7 +213,7 @@ func BenchmarkCache_Add_Parallel(b *testing.B) {
|
|||||||
b.RunParallel(func(pb *testing.PB) {
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
for pb.Next() {
|
for pb.Next() {
|
||||||
var j = atomic.AddInt64(&i, 1)
|
var j = atomic.AddInt64(&i, 1)
|
||||||
cache.Write(types.String(j), j, fasttime.Now().Unix()+i%1024)
|
cache.Write(types.String(j%1e6), j, fasttime.Now().Unix()+i%1024)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ type DB struct {
|
|||||||
func NewDB(path string) *DB {
|
func NewDB(path string) *DB {
|
||||||
var db = &DB{path: path}
|
var db = &DB{path: path}
|
||||||
|
|
||||||
events.On(events.EventQuit, func() {
|
events.OnClose(func() {
|
||||||
_ = db.Close()
|
_ = db.Close()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import (
|
|||||||
executils "github.com/TeaOSLab/EdgeNode/internal/utils/exec"
|
executils "github.com/TeaOSLab/EdgeNode/internal/utils/exec"
|
||||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||||
"net"
|
"net"
|
||||||
"os/exec"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -86,7 +85,7 @@ func (this *ClockManager) Sync() error {
|
|||||||
|
|
||||||
// check chrony
|
// check chrony
|
||||||
if config.CheckChrony {
|
if config.CheckChrony {
|
||||||
chronycExe, err := exec.LookPath("chronyc")
|
chronycExe, err := executils.LookPath("chronyc")
|
||||||
if err == nil && len(chronycExe) > 0 {
|
if err == nil && len(chronycExe) > 0 {
|
||||||
var chronyCmd = executils.NewTimeoutCmd(3*time.Second, chronycExe, "tracking")
|
var chronyCmd = executils.NewTimeoutCmd(3*time.Second, chronycExe, "tracking")
|
||||||
err = chronyCmd.Run()
|
err = chronyCmd.Run()
|
||||||
@@ -101,11 +100,11 @@ func (this *ClockManager) Sync() error {
|
|||||||
server = "pool.ntp.org"
|
server = "pool.ntp.org"
|
||||||
}
|
}
|
||||||
|
|
||||||
ntpdate, err := exec.LookPath("ntpdate")
|
ntpdate, err := executils.LookPath("ntpdate")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// 使用 date 命令设置
|
// 使用 date 命令设置
|
||||||
// date --set TIME
|
// date --set TIME
|
||||||
dateExe, err := exec.LookPath("date")
|
dateExe, err := executils.LookPath("date")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
currentTime, err := this.ReadServer(server)
|
currentTime, err := this.ReadServer(server)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
179
internal/utils/counters/counter.go
Normal file
179
internal/utils/counters/counter.go
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||||
|
|
||||||
|
package counters
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/utils/fasttime"
|
||||||
|
syncutils "github.com/TeaOSLab/EdgeNode/internal/utils/sync"
|
||||||
|
"github.com/cespare/xxhash"
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Counter struct {
|
||||||
|
countMaps uint64
|
||||||
|
locker *syncutils.RWMutex
|
||||||
|
itemMaps []map[uint64]*Item
|
||||||
|
|
||||||
|
gcTicker *time.Ticker
|
||||||
|
gcIndex int
|
||||||
|
gcLocker sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCounter create new counter
|
||||||
|
func NewCounter() *Counter {
|
||||||
|
var count = runtime.NumCPU() * 4
|
||||||
|
if count < 8 {
|
||||||
|
count = 8
|
||||||
|
} else if count > 128 {
|
||||||
|
count = 128
|
||||||
|
}
|
||||||
|
|
||||||
|
var itemMaps = []map[uint64]*Item{}
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
itemMaps = append(itemMaps, map[uint64]*Item{})
|
||||||
|
}
|
||||||
|
|
||||||
|
var counter = &Counter{
|
||||||
|
countMaps: uint64(count),
|
||||||
|
locker: syncutils.NewRWMutex(count),
|
||||||
|
itemMaps: itemMaps,
|
||||||
|
}
|
||||||
|
|
||||||
|
return counter
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithGC start the counter with gc automatically
|
||||||
|
func (this *Counter) WithGC() *Counter {
|
||||||
|
if this.gcTicker != nil {
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
this.gcTicker = time.NewTicker(1 * time.Second)
|
||||||
|
go func() {
|
||||||
|
for range this.gcTicker.C {
|
||||||
|
this.GC()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increase key
|
||||||
|
func (this *Counter) Increase(key uint64, lifeSeconds int) uint64 {
|
||||||
|
var index = int(key % this.countMaps)
|
||||||
|
this.locker.RLock(index)
|
||||||
|
var item = this.itemMaps[index][key]
|
||||||
|
this.locker.RUnlock(index)
|
||||||
|
if item == nil { // no need to care about duplication
|
||||||
|
item = NewItem(lifeSeconds)
|
||||||
|
this.locker.Lock(index)
|
||||||
|
|
||||||
|
// check again
|
||||||
|
oldItem, ok := this.itemMaps[index][key]
|
||||||
|
if !ok {
|
||||||
|
this.itemMaps[index][key] = item
|
||||||
|
} else {
|
||||||
|
item = oldItem
|
||||||
|
}
|
||||||
|
|
||||||
|
this.locker.Unlock(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.locker.Lock(index)
|
||||||
|
var result = item.Increase()
|
||||||
|
this.locker.Unlock(index)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// IncreaseKey increase string key
|
||||||
|
func (this *Counter) IncreaseKey(key string, lifeSeconds int) uint64 {
|
||||||
|
return this.Increase(this.hash(key), lifeSeconds)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get value of key
|
||||||
|
func (this *Counter) Get(key uint64) uint64 {
|
||||||
|
var index = int(key % this.countMaps)
|
||||||
|
this.locker.RLock(index)
|
||||||
|
defer this.locker.RUnlock(index)
|
||||||
|
var item = this.itemMaps[index][key]
|
||||||
|
if item != nil {
|
||||||
|
return item.Sum()
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetKey get value of string key
|
||||||
|
func (this *Counter) GetKey(key string) uint64 {
|
||||||
|
return this.Get(this.hash(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset key
|
||||||
|
func (this *Counter) Reset(key uint64) {
|
||||||
|
var index = int(key % this.countMaps)
|
||||||
|
this.locker.RLock(index)
|
||||||
|
var item = this.itemMaps[index][key]
|
||||||
|
this.locker.RUnlock(index)
|
||||||
|
|
||||||
|
if item != nil {
|
||||||
|
this.locker.Lock(index)
|
||||||
|
delete(this.itemMaps[index], key)
|
||||||
|
this.locker.Unlock(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResetKey string key
|
||||||
|
func (this *Counter) ResetKey(key string) {
|
||||||
|
this.Reset(this.hash(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TotalItems get items count
|
||||||
|
func (this *Counter) TotalItems() int {
|
||||||
|
var total = 0
|
||||||
|
|
||||||
|
for i := 0; i < int(this.countMaps); i++ {
|
||||||
|
this.locker.RLock(i)
|
||||||
|
total += len(this.itemMaps[i])
|
||||||
|
this.locker.RUnlock(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
return total
|
||||||
|
}
|
||||||
|
|
||||||
|
// GC garbage expired items
|
||||||
|
func (this *Counter) GC() {
|
||||||
|
this.gcLocker.Lock()
|
||||||
|
var gcIndex = this.gcIndex
|
||||||
|
|
||||||
|
this.gcIndex++
|
||||||
|
if this.gcIndex >= int(this.countMaps) {
|
||||||
|
this.gcIndex = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
this.gcLocker.Unlock()
|
||||||
|
|
||||||
|
var currentTime = fasttime.Now().Unix()
|
||||||
|
|
||||||
|
this.locker.RLock(gcIndex)
|
||||||
|
var itemMap = this.itemMaps[gcIndex]
|
||||||
|
var expiredKeys = []uint64{}
|
||||||
|
for key, item := range itemMap {
|
||||||
|
if item.IsExpired(currentTime) {
|
||||||
|
expiredKeys = append(expiredKeys, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.locker.RUnlock(gcIndex)
|
||||||
|
|
||||||
|
if len(expiredKeys) > 0 {
|
||||||
|
for _, key := range expiredKeys {
|
||||||
|
this.locker.Lock(gcIndex)
|
||||||
|
delete(itemMap, key)
|
||||||
|
this.locker.Unlock(gcIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate hash of the key
|
||||||
|
func (this *Counter) hash(key string) uint64 {
|
||||||
|
return xxhash.Sum64String(key)
|
||||||
|
}
|
||||||
173
internal/utils/counters/counter_test.go
Normal file
173
internal/utils/counters/counter_test.go
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||||
|
|
||||||
|
package counters_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/utils/counters"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/utils/testutils"
|
||||||
|
"github.com/iwind/TeaGo/assert"
|
||||||
|
"github.com/iwind/TeaGo/rands"
|
||||||
|
"github.com/iwind/TeaGo/types"
|
||||||
|
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||||
|
"runtime"
|
||||||
|
"sync/atomic"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCounter_Increase(t *testing.T) {
|
||||||
|
var a = assert.NewAssertion(t)
|
||||||
|
|
||||||
|
var counter = counters.NewCounter()
|
||||||
|
a.IsTrue(counter.Increase(1, 10) == 1)
|
||||||
|
a.IsTrue(counter.Increase(1, 10) == 2)
|
||||||
|
a.IsTrue(counter.Increase(2, 10) == 1)
|
||||||
|
|
||||||
|
counter.Reset(1)
|
||||||
|
a.IsTrue(counter.Get(1) == 0) // changed
|
||||||
|
a.IsTrue(counter.Get(2) == 1) // not changed
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCounter_IncreaseKey(t *testing.T) {
|
||||||
|
var a = assert.NewAssertion(t)
|
||||||
|
|
||||||
|
var counter = counters.NewCounter()
|
||||||
|
a.IsTrue(counter.IncreaseKey("1", 10) == 1)
|
||||||
|
a.IsTrue(counter.IncreaseKey("1", 10) == 2)
|
||||||
|
a.IsTrue(counter.IncreaseKey("2", 10) == 1)
|
||||||
|
|
||||||
|
counter.ResetKey("1")
|
||||||
|
a.IsTrue(counter.GetKey("1") == 0) // changed
|
||||||
|
a.IsTrue(counter.GetKey("2") == 1) // not changed
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCounter_GC(t *testing.T) {
|
||||||
|
if !testutils.IsSingleTesting() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var counter = counters.NewCounter()
|
||||||
|
counter.Increase(1, 20)
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
counter.Increase(1, 20)
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
counter.Increase(1, 20)
|
||||||
|
counter.GC()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCounter_GC2(t *testing.T) {
|
||||||
|
if !testutils.IsSingleTesting() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var counter = counters.NewCounter().WithGC()
|
||||||
|
for i := 0; i < 1e5; i++ {
|
||||||
|
counter.Increase(uint64(i), rands.Int(10, 300))
|
||||||
|
}
|
||||||
|
|
||||||
|
var ticker = time.NewTicker(1 * time.Second)
|
||||||
|
for range ticker.C {
|
||||||
|
t.Log(timeutil.Format("H:i:s"), counter.TotalItems())
|
||||||
|
if counter.TotalItems() == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCounterMemory(t *testing.T) {
|
||||||
|
var stat = &runtime.MemStats{}
|
||||||
|
runtime.ReadMemStats(stat)
|
||||||
|
|
||||||
|
var counter = counters.NewCounter()
|
||||||
|
for i := 0; i < 1e5; i++ {
|
||||||
|
counter.Increase(uint64(i), rands.Int(10, 300))
|
||||||
|
}
|
||||||
|
|
||||||
|
var stat1 = &runtime.MemStats{}
|
||||||
|
runtime.ReadMemStats(stat1)
|
||||||
|
t.Log((stat1.TotalAlloc-stat.TotalAlloc)/(1<<20), "MB")
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkCounter_Increase(b *testing.B) {
|
||||||
|
runtime.GOMAXPROCS(4)
|
||||||
|
|
||||||
|
var counter = counters.NewCounter()
|
||||||
|
b.ResetTimer()
|
||||||
|
|
||||||
|
var i uint64
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
for pb.Next() {
|
||||||
|
counter.Increase(atomic.AddUint64(&i, 1)%1e6, 20)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
//b.Log(counter.TotalItems())
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkCounter_IncreaseKey(b *testing.B) {
|
||||||
|
runtime.GOMAXPROCS(4)
|
||||||
|
|
||||||
|
var counter = counters.NewCounter()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
var ticker = time.NewTicker(100 * time.Millisecond)
|
||||||
|
for range ticker.C {
|
||||||
|
counter.GC()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
|
||||||
|
var i uint64
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
for pb.Next() {
|
||||||
|
counter.IncreaseKey(types.String(atomic.AddUint64(&i, 1)%1e6), 20)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
//b.Log(counter.TotalItems())
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkCounter_IncreaseKey2(b *testing.B) {
|
||||||
|
runtime.GOMAXPROCS(4)
|
||||||
|
|
||||||
|
var counter = counters.NewCounter()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
var ticker = time.NewTicker(1 * time.Millisecond)
|
||||||
|
for range ticker.C {
|
||||||
|
counter.GC()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
|
||||||
|
var i uint64
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
for pb.Next() {
|
||||||
|
counter.IncreaseKey(types.String(atomic.AddUint64(&i, 1)%1e5), 20)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
//b.Log(counter.TotalItems())
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkCounter_GC(b *testing.B) {
|
||||||
|
runtime.GOMAXPROCS(4)
|
||||||
|
|
||||||
|
var counter = counters.NewCounter()
|
||||||
|
|
||||||
|
for i := uint64(0); i < 1e5; i++ {
|
||||||
|
counter.IncreaseKey(types.String(i), 20)
|
||||||
|
}
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
for pb.Next() {
|
||||||
|
counter.GC()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
//b.Log(counter.TotalItems())
|
||||||
|
}
|
||||||
77
internal/utils/counters/item.go
Normal file
77
internal/utils/counters/item.go
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||||
|
|
||||||
|
package counters
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/utils/fasttime"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Item struct {
|
||||||
|
lifeSeconds int64
|
||||||
|
|
||||||
|
spanSeconds int64
|
||||||
|
spans []*Span
|
||||||
|
|
||||||
|
lastUpdateTime int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewItem(lifeSeconds int) *Item {
|
||||||
|
if lifeSeconds <= 0 {
|
||||||
|
lifeSeconds = 60
|
||||||
|
}
|
||||||
|
var spanSeconds = lifeSeconds / 10
|
||||||
|
if spanSeconds < 1 {
|
||||||
|
spanSeconds = 1
|
||||||
|
}
|
||||||
|
var countSpans = lifeSeconds/spanSeconds + 1 /** prevent index out of bounds **/
|
||||||
|
var spans = []*Span{}
|
||||||
|
for i := 0; i < countSpans; i++ {
|
||||||
|
spans = append(spans, NewSpan())
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Item{
|
||||||
|
lifeSeconds: int64(lifeSeconds),
|
||||||
|
spanSeconds: int64(spanSeconds),
|
||||||
|
spans: spans,
|
||||||
|
lastUpdateTime: fasttime.Now().Unix(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Item) Increase() uint64 {
|
||||||
|
var currentTime = fasttime.Now().Unix()
|
||||||
|
var spanIndex = int(currentTime % this.lifeSeconds / this.spanSeconds)
|
||||||
|
var span = this.spans[spanIndex]
|
||||||
|
var roundTime = currentTime / this.spanSeconds * this.spanSeconds
|
||||||
|
|
||||||
|
this.lastUpdateTime = currentTime
|
||||||
|
|
||||||
|
if span.Timestamp < roundTime {
|
||||||
|
span.Timestamp = roundTime // update time
|
||||||
|
span.Count = 0 // reset count
|
||||||
|
}
|
||||||
|
span.Count++
|
||||||
|
|
||||||
|
return this.Sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Item) Sum() uint64 {
|
||||||
|
var result uint64 = 0
|
||||||
|
var currentTimestamp = fasttime.Now().Unix()
|
||||||
|
for _, span := range this.spans {
|
||||||
|
if span.Timestamp >= currentTimestamp-this.lifeSeconds {
|
||||||
|
result += span.Count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Item) Reset() {
|
||||||
|
for _, span := range this.spans {
|
||||||
|
span.Count = 0
|
||||||
|
span.Timestamp = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Item) IsExpired(currentTime int64) bool {
|
||||||
|
return this.lastUpdateTime < currentTime-this.lifeSeconds-this.spanSeconds
|
||||||
|
}
|
||||||
56
internal/utils/counters/item_test.go
Normal file
56
internal/utils/counters/item_test.go
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||||
|
|
||||||
|
package counters_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/utils/counters"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/utils/testutils"
|
||||||
|
"github.com/iwind/TeaGo/assert"
|
||||||
|
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||||
|
"runtime"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestItem_Increase(t *testing.T) {
|
||||||
|
// run only under single testing
|
||||||
|
if !testutils.IsSingleTesting() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var a = assert.NewAssertion(t)
|
||||||
|
|
||||||
|
var item = counters.NewItem(20)
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
t.Log(item.Increase(), timeutil.Format("i:s"))
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
item.Reset()
|
||||||
|
a.IsTrue(item.Sum() == 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestItem_IsExpired(t *testing.T) {
|
||||||
|
if !testutils.IsSingleTesting() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentTime = time.Now().Unix()
|
||||||
|
|
||||||
|
var item = counters.NewItem(10)
|
||||||
|
t.Log(item.IsExpired(currentTime))
|
||||||
|
time.Sleep(10 * time.Second)
|
||||||
|
t.Log(item.IsExpired(currentTime))
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
t.Log(item.IsExpired(currentTime))
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkItem_Increase(b *testing.B) {
|
||||||
|
runtime.GOMAXPROCS(1)
|
||||||
|
|
||||||
|
var item = counters.NewItem(60)
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
item.Increase()
|
||||||
|
}
|
||||||
|
}
|
||||||
12
internal/utils/counters/span.go
Normal file
12
internal/utils/counters/span.go
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||||
|
|
||||||
|
package counters
|
||||||
|
|
||||||
|
type Span struct {
|
||||||
|
Timestamp int64
|
||||||
|
Count uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSpan() *Span {
|
||||||
|
return &Span{}
|
||||||
|
}
|
||||||
@@ -14,26 +14,28 @@ type batchItem struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Batch struct {
|
type Batch struct {
|
||||||
db *sql.DB
|
db *DB
|
||||||
n int
|
n int
|
||||||
|
|
||||||
enableStat bool
|
enableStat bool
|
||||||
|
|
||||||
onFail func(err error)
|
onFail func(err error)
|
||||||
|
|
||||||
queue chan *batchItem
|
queue chan *batchItem
|
||||||
close chan bool
|
closeEvent chan bool
|
||||||
|
|
||||||
isClosed bool
|
isClosed bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBatch(db *sql.DB, n int) *Batch {
|
func NewBatch(db *DB, n int) *Batch {
|
||||||
return &Batch{
|
var batch = &Batch{
|
||||||
db: db,
|
db: db,
|
||||||
n: n,
|
n: n,
|
||||||
queue: make(chan *batchItem),
|
queue: make(chan *batchItem, 16),
|
||||||
close: make(chan bool, 1),
|
closeEvent: make(chan bool, 1),
|
||||||
}
|
}
|
||||||
|
db.batches = append(db.batches, batch)
|
||||||
|
return batch
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *Batch) EnableStat(b bool) {
|
func (this *Batch) EnableStat(b bool) {
|
||||||
@@ -45,6 +47,9 @@ func (this *Batch) OnFail(callback func(err error)) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *Batch) Add(query string, args ...any) {
|
func (this *Batch) Add(query string, args ...any) {
|
||||||
|
if this.isClosed {
|
||||||
|
return
|
||||||
|
}
|
||||||
this.queue <- &batchItem{
|
this.queue <- &batchItem{
|
||||||
query: query,
|
query: query,
|
||||||
args: args,
|
args: args,
|
||||||
@@ -65,7 +70,7 @@ For:
|
|||||||
// closed
|
// closed
|
||||||
if this.isClosed {
|
if this.isClosed {
|
||||||
if lastTx != nil {
|
if lastTx != nil {
|
||||||
_ = lastTx.Commit()
|
_ = this.commitTx(lastTx)
|
||||||
lastTx = nil
|
lastTx = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,6 +88,9 @@ For:
|
|||||||
|
|
||||||
err := this.execItem(lastTx, item)
|
err := this.execItem(lastTx, item)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if IsClosedErr(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
this.processErr(item.query, err)
|
this.processErr(item.query, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,9 +98,12 @@ For:
|
|||||||
|
|
||||||
if count == n {
|
if count == n {
|
||||||
count = 0
|
count = 0
|
||||||
err = lastTx.Commit()
|
err = this.commitTx(lastTx)
|
||||||
lastTx = nil
|
lastTx = nil
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if IsClosedErr(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
this.processErr("commit", err)
|
this.processErr("commit", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -101,16 +112,19 @@ For:
|
|||||||
continue For
|
continue For
|
||||||
}
|
}
|
||||||
count = 0
|
count = 0
|
||||||
err := lastTx.Commit()
|
err := this.commitTx(lastTx)
|
||||||
lastTx = nil
|
lastTx = nil
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if IsClosedErr(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
this.processErr("commit", err)
|
this.processErr("commit", err)
|
||||||
}
|
}
|
||||||
case <-this.close:
|
case <-this.closeEvent:
|
||||||
// closed
|
// closed
|
||||||
|
|
||||||
if lastTx != nil {
|
if lastTx != nil {
|
||||||
_ = lastTx.Commit()
|
_ = this.commitTx(lastTx)
|
||||||
lastTx = nil
|
lastTx = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,26 +133,44 @@ For:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *Batch) Close() {
|
func (this *Batch) close() {
|
||||||
this.isClosed = true
|
this.isClosed = true
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case this.close <- true:
|
case this.closeEvent <- true:
|
||||||
default:
|
default:
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *Batch) beginTx() *sql.Tx {
|
func (this *Batch) beginTx() *sql.Tx {
|
||||||
|
if !this.db.BeginUpdating() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
tx, err := this.db.Begin()
|
tx, err := this.db.Begin()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.processErr("begin transaction", err)
|
this.processErr("begin transaction", err)
|
||||||
|
this.db.EndUpdating()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return tx
|
return tx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *Batch) commitTx(tx *sql.Tx) error {
|
||||||
|
// always commit without checking database closing status
|
||||||
|
this.db.EndUpdating()
|
||||||
|
return tx.Commit()
|
||||||
|
}
|
||||||
|
|
||||||
func (this *Batch) execItem(tx *sql.Tx, item *batchItem) error {
|
func (this *Batch) execItem(tx *sql.Tx, item *batchItem) error {
|
||||||
|
// check database status
|
||||||
|
if this.db.BeginUpdating() {
|
||||||
|
defer this.db.EndUpdating()
|
||||||
|
} else {
|
||||||
|
return errDBIsClosed
|
||||||
|
}
|
||||||
|
|
||||||
if this.enableStat {
|
if this.enableStat {
|
||||||
defer SharedQueryStatManager.AddQuery(item.query).End()
|
defer SharedQueryStatManager.AddQuery(item.query).End()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,19 +5,31 @@ package dbs
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/events"
|
"github.com/TeaOSLab/EdgeNode/internal/events"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils/fileutils"
|
"github.com/TeaOSLab/EdgeNode/internal/utils/fileutils"
|
||||||
_ "github.com/mattn/go-sqlite3"
|
_ "github.com/mattn/go-sqlite3"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var errDBIsClosed = errors.New("the database is closed")
|
||||||
|
|
||||||
type DB struct {
|
type DB struct {
|
||||||
locker *fileutils.Locker
|
locker *fileutils.Locker
|
||||||
rawDB *sql.DB
|
rawDB *sql.DB
|
||||||
|
|
||||||
|
statusLocker sync.Mutex
|
||||||
|
countUpdating int32
|
||||||
|
|
||||||
|
isClosing bool
|
||||||
|
|
||||||
enableStat bool
|
enableStat bool
|
||||||
|
|
||||||
|
batches []*Batch
|
||||||
}
|
}
|
||||||
|
|
||||||
func OpenWriter(dsn string) (*DB, error) {
|
func OpenWriter(dsn string) (*DB, error) {
|
||||||
@@ -63,10 +75,10 @@ func NewDB(rawDB *sql.DB) *DB {
|
|||||||
}
|
}
|
||||||
|
|
||||||
events.OnKey(events.EventQuit, fmt.Sprintf("db_%p", db), func() {
|
events.OnKey(events.EventQuit, fmt.Sprintf("db_%p", db), func() {
|
||||||
_ = rawDB.Close()
|
_ = db.Close()
|
||||||
})
|
})
|
||||||
events.OnKey(events.EventTerminated, fmt.Sprintf("db_%p", db), func() {
|
events.OnKey(events.EventTerminated, fmt.Sprintf("db_%p", db), func() {
|
||||||
_ = rawDB.Close()
|
_ = db.Close()
|
||||||
})
|
})
|
||||||
|
|
||||||
return db
|
return db
|
||||||
@@ -81,6 +93,13 @@ func (this *DB) EnableStat(b bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *DB) Begin() (*sql.Tx, error) {
|
func (this *DB) Begin() (*sql.Tx, error) {
|
||||||
|
// check database status
|
||||||
|
if this.BeginUpdating() {
|
||||||
|
defer this.EndUpdating()
|
||||||
|
} else {
|
||||||
|
return nil, errDBIsClosed
|
||||||
|
}
|
||||||
|
|
||||||
return this.rawDB.Begin()
|
return this.rawDB.Begin()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,7 +109,7 @@ func (this *DB) Prepare(query string) (*Stmt, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var s = NewStmt(stmt, query)
|
var s = NewStmt(this, stmt, query)
|
||||||
if this.enableStat {
|
if this.enableStat {
|
||||||
s.EnableStat()
|
s.EnableStat()
|
||||||
}
|
}
|
||||||
@@ -98,13 +117,28 @@ func (this *DB) Prepare(query string) (*Stmt, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *DB) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) {
|
func (this *DB) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) {
|
||||||
|
// check database status
|
||||||
|
if this.BeginUpdating() {
|
||||||
|
defer this.EndUpdating()
|
||||||
|
} else {
|
||||||
|
return nil, errDBIsClosed
|
||||||
|
}
|
||||||
|
|
||||||
if this.enableStat {
|
if this.enableStat {
|
||||||
defer SharedQueryStatManager.AddQuery(query).End()
|
defer SharedQueryStatManager.AddQuery(query).End()
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.rawDB.ExecContext(ctx, query, args...)
|
return this.rawDB.ExecContext(ctx, query, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *DB) Exec(query string, args ...interface{}) (sql.Result, error) {
|
func (this *DB) Exec(query string, args ...interface{}) (sql.Result, error) {
|
||||||
|
// check database status
|
||||||
|
if this.BeginUpdating() {
|
||||||
|
defer this.EndUpdating()
|
||||||
|
} else {
|
||||||
|
return nil, errDBIsClosed
|
||||||
|
}
|
||||||
|
|
||||||
if this.enableStat {
|
if this.enableStat {
|
||||||
defer SharedQueryStatManager.AddQuery(query).End()
|
defer SharedQueryStatManager.AddQuery(query).End()
|
||||||
}
|
}
|
||||||
@@ -125,7 +159,32 @@ func (this *DB) QueryRow(query string, args ...interface{}) *sql.Row {
|
|||||||
return this.rawDB.QueryRow(query, args...)
|
return this.rawDB.QueryRow(query, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Close the database
|
||||||
func (this *DB) Close() error {
|
func (this *DB) Close() error {
|
||||||
|
// check database status
|
||||||
|
this.statusLocker.Lock()
|
||||||
|
if this.isClosing {
|
||||||
|
this.statusLocker.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
this.isClosing = true
|
||||||
|
this.statusLocker.Unlock()
|
||||||
|
|
||||||
|
// waiting for updating operations to finish
|
||||||
|
for {
|
||||||
|
this.statusLocker.Lock()
|
||||||
|
var countUpdating = this.countUpdating
|
||||||
|
this.statusLocker.Unlock()
|
||||||
|
if countUpdating <= 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
time.Sleep(1 * time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, batch := range this.batches {
|
||||||
|
batch.close()
|
||||||
|
}
|
||||||
|
|
||||||
events.Remove(fmt.Sprintf("db_%p", this))
|
events.Remove(fmt.Sprintf("db_%p", this))
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
@@ -137,6 +196,24 @@ func (this *DB) Close() error {
|
|||||||
return this.rawDB.Close()
|
return this.rawDB.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *DB) BeginUpdating() bool {
|
||||||
|
this.statusLocker.Lock()
|
||||||
|
defer this.statusLocker.Unlock()
|
||||||
|
|
||||||
|
if this.isClosing {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
this.countUpdating++
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *DB) EndUpdating() {
|
||||||
|
this.statusLocker.Lock()
|
||||||
|
this.countUpdating--
|
||||||
|
this.statusLocker.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
func (this *DB) RawDB() *sql.DB {
|
func (this *DB) RawDB() *sql.DB {
|
||||||
return this.rawDB
|
return this.rawDB
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,14 +8,16 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Stmt struct {
|
type Stmt struct {
|
||||||
|
db *DB
|
||||||
rawStmt *sql.Stmt
|
rawStmt *sql.Stmt
|
||||||
query string
|
query string
|
||||||
|
|
||||||
enableStat bool
|
enableStat bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewStmt(rawStmt *sql.Stmt, query string) *Stmt {
|
func NewStmt(db *DB, rawStmt *sql.Stmt, query string) *Stmt {
|
||||||
return &Stmt{
|
return &Stmt{
|
||||||
|
db: db,
|
||||||
rawStmt: rawStmt,
|
rawStmt: rawStmt,
|
||||||
query: query,
|
query: query,
|
||||||
}
|
}
|
||||||
@@ -26,6 +28,13 @@ func (this *Stmt) EnableStat() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *Stmt) ExecContext(ctx context.Context, args ...interface{}) (sql.Result, error) {
|
func (this *Stmt) ExecContext(ctx context.Context, args ...interface{}) (sql.Result, error) {
|
||||||
|
// check database status
|
||||||
|
if this.db.BeginUpdating() {
|
||||||
|
defer this.db.EndUpdating()
|
||||||
|
} else {
|
||||||
|
return nil, errDBIsClosed
|
||||||
|
}
|
||||||
|
|
||||||
if this.enableStat {
|
if this.enableStat {
|
||||||
defer SharedQueryStatManager.AddQuery(this.query).End()
|
defer SharedQueryStatManager.AddQuery(this.query).End()
|
||||||
}
|
}
|
||||||
@@ -33,6 +42,13 @@ func (this *Stmt) ExecContext(ctx context.Context, args ...interface{}) (sql.Res
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *Stmt) Exec(args ...interface{}) (sql.Result, error) {
|
func (this *Stmt) Exec(args ...interface{}) (sql.Result, error) {
|
||||||
|
// check database status
|
||||||
|
if this.db.BeginUpdating() {
|
||||||
|
defer this.db.EndUpdating()
|
||||||
|
} else {
|
||||||
|
return nil, errDBIsClosed
|
||||||
|
}
|
||||||
|
|
||||||
if this.enableStat {
|
if this.enableStat {
|
||||||
defer SharedQueryStatManager.AddQuery(this.query).End()
|
defer SharedQueryStatManager.AddQuery(this.query).End()
|
||||||
}
|
}
|
||||||
|
|||||||
7
internal/utils/dbs/utils.go
Normal file
7
internal/utils/dbs/utils.go
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||||
|
|
||||||
|
package dbs
|
||||||
|
|
||||||
|
func IsClosedErr(err error) bool {
|
||||||
|
return err == errDBIsClosed
|
||||||
|
}
|
||||||
59
internal/utils/exec/look_linux.go
Normal file
59
internal/utils/exec/look_linux.go
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||||
|
//go:build linux
|
||||||
|
|
||||||
|
package executils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
"io/fs"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LookPath customize our LookPath() function, to work in broken $PATH environment variable
|
||||||
|
func LookPath(file string) (string, error) {
|
||||||
|
result, err := exec.LookPath(file)
|
||||||
|
if err == nil && len(result) > 0 {
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// add common dirs contains executable files these may be excluded in $PATH environment variable
|
||||||
|
var binPaths = []string{
|
||||||
|
"/usr/sbin",
|
||||||
|
"/usr/bin",
|
||||||
|
"/usr/local/sbin",
|
||||||
|
"/usr/local/bin",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, binPath := range binPaths {
|
||||||
|
var fullPath = binPath + string(os.PathSeparator) + file
|
||||||
|
|
||||||
|
stat, err := os.Stat(fullPath)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if stat.IsDir() {
|
||||||
|
return "", syscall.EISDIR
|
||||||
|
}
|
||||||
|
|
||||||
|
var mode = stat.Mode()
|
||||||
|
if mode.IsDir() {
|
||||||
|
return "", syscall.EISDIR
|
||||||
|
}
|
||||||
|
err = syscall.Faccessat(unix.AT_FDCWD, fullPath, unix.X_OK, unix.AT_EACCESS)
|
||||||
|
if err == nil || (err != syscall.ENOSYS && err != syscall.EPERM) {
|
||||||
|
return fullPath, err
|
||||||
|
}
|
||||||
|
if mode&0111 != 0 {
|
||||||
|
return fullPath, nil
|
||||||
|
}
|
||||||
|
return "", fs.ErrPermission
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", &exec.Error{
|
||||||
|
Name: file,
|
||||||
|
Err: exec.ErrNotFound,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
10
internal/utils/exec/look_others.go
Normal file
10
internal/utils/exec/look_others.go
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||||
|
//go:build !linux
|
||||||
|
|
||||||
|
package executils
|
||||||
|
|
||||||
|
import "os/exec"
|
||||||
|
|
||||||
|
func LookPath(file string) (string, error) {
|
||||||
|
return exec.LookPath(file)
|
||||||
|
}
|
||||||
@@ -24,7 +24,7 @@ func init() {
|
|||||||
SharedFreeHoursManager.Start()
|
SharedFreeHoursManager.Start()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
events.On(events.EventQuit, func() {
|
events.OnClose(func() {
|
||||||
SharedFreeHoursManager.Stop()
|
SharedFreeHoursManager.Stop()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
81
internal/utils/fs/stat.go
Normal file
81
internal/utils/fs/stat.go
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||||
|
|
||||||
|
package fsutils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/utils/fasttime"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Stat device contains the path
|
||||||
|
func Stat(path string) (*StatResult, error) {
|
||||||
|
var stat = &unix.Statfs_t{}
|
||||||
|
err := unix.Statfs(path, stat)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return NewStatResult(stat), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var locker = &sync.RWMutex{}
|
||||||
|
var cacheMap = map[string]*StatResult{} // path => StatResult
|
||||||
|
|
||||||
|
const cacheLife = 3 // seconds
|
||||||
|
|
||||||
|
// StatCache stat device with cache
|
||||||
|
func StatCache(path string) (*StatResult, error) {
|
||||||
|
locker.RLock()
|
||||||
|
stat, ok := cacheMap[path]
|
||||||
|
if ok && stat.updatedAt >= fasttime.Now().Unix()-cacheLife {
|
||||||
|
locker.RUnlock()
|
||||||
|
return stat, nil
|
||||||
|
}
|
||||||
|
locker.RUnlock()
|
||||||
|
|
||||||
|
locker.Lock()
|
||||||
|
defer locker.Unlock()
|
||||||
|
|
||||||
|
stat, err := Stat(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cacheMap[path] = stat
|
||||||
|
return stat, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type StatResult struct {
|
||||||
|
rawStat *unix.Statfs_t
|
||||||
|
blockSize uint64
|
||||||
|
|
||||||
|
updatedAt int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStatResult(rawStat *unix.Statfs_t) *StatResult {
|
||||||
|
var blockSize = rawStat.Bsize
|
||||||
|
if blockSize < 0 {
|
||||||
|
blockSize = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return &StatResult{
|
||||||
|
rawStat: rawStat,
|
||||||
|
blockSize: uint64(blockSize),
|
||||||
|
updatedAt: fasttime.Now().Unix(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *StatResult) FreeSize() uint64 {
|
||||||
|
return this.rawStat.Bfree * this.blockSize
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *StatResult) TotalSize() uint64 {
|
||||||
|
return this.rawStat.Blocks * this.blockSize
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *StatResult) UsedSize() uint64 {
|
||||||
|
if this.rawStat.Bfree <= this.rawStat.Blocks {
|
||||||
|
return (this.rawStat.Blocks - this.rawStat.Bfree) * this.blockSize
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
69
internal/utils/fs/stat_test.go
Normal file
69
internal/utils/fs/stat_test.go
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||||
|
|
||||||
|
package fsutils_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
fsutils "github.com/TeaOSLab/EdgeNode/internal/utils/fs"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestStat(t *testing.T) {
|
||||||
|
stat, err := fsutils.Stat("/usr/local")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log("free:", stat.FreeSize()/(1<<30), "total:", stat.TotalSize()/(1<<30), "used:", stat.UsedSize()/(1<<30))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStatCache(t *testing.T) {
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
stat, err := fsutils.StatCache("/usr/local")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log("free:", stat.FreeSize()/(1<<30), "total:", stat.TotalSize()/(1<<30), "used:", stat.UsedSize()/(1<<30))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConcurrent(t *testing.T) {
|
||||||
|
var before = time.Now()
|
||||||
|
defer func() {
|
||||||
|
t.Log(time.Since(before).Seconds()*1000, "ms")
|
||||||
|
}()
|
||||||
|
|
||||||
|
var count = 10000
|
||||||
|
var wg = sync.WaitGroup{}
|
||||||
|
wg.Add(count)
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
_, _ = fsutils.Stat("/usr/local")
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkStat(b *testing.B) {
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
for pb.Next() {
|
||||||
|
_, err := fsutils.Stat("/usr/local")
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkStatCache(b *testing.B) {
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
for pb.Next() {
|
||||||
|
_, err := fsutils.StatCache("/usr/local")
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user