Compare commits
74 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8bec1cf68e | ||
|
|
2cd1bb7f95 | ||
|
|
19e6329a2b | ||
|
|
fce2879567 | ||
|
|
0973765919 | ||
|
|
827679721e | ||
|
|
735279bc7a | ||
|
|
3eb2ed9897 | ||
|
|
3a913d98c7 | ||
|
|
9bfcd79e36 | ||
|
|
a81d610302 | ||
|
|
64b1753c4d | ||
|
|
afcb5c2957 | ||
|
|
7d0b9208a3 | ||
|
|
c0f0ec43bb | ||
|
|
30bd66958c | ||
|
|
fafac1a038 | ||
|
|
f979f9503e | ||
|
|
b233c3cc7a | ||
|
|
597ac936f7 | ||
|
|
a590254eb3 | ||
|
|
0498bcf30c | ||
|
|
59f9b5c724 | ||
|
|
80729935b6 | ||
|
|
4ca57fb99c | ||
|
|
9b35902ad4 | ||
|
|
3b8bd09190 | ||
|
|
71a5bc0652 | ||
|
|
ac6a8c4e85 | ||
|
|
f58a808c3a | ||
|
|
51037be772 | ||
|
|
443ff9aff7 | ||
|
|
57cb00edf0 | ||
|
|
3fb39b479a | ||
|
|
4a1daff143 | ||
|
|
dd1dbd424e | ||
|
|
305cb4b46e | ||
|
|
ef90dce29b | ||
|
|
3cb69f4c71 | ||
|
|
af4cd05df2 | ||
|
|
64e0ae80b7 | ||
|
|
8bba228745 | ||
|
|
8cc06e6707 | ||
|
|
52fdee2eeb | ||
|
|
b5f52dd136 | ||
|
|
abda886de5 | ||
|
|
18f08525b9 | ||
|
|
b2a9a31fe5 | ||
|
|
f578114aeb | ||
|
|
bf4f47fc35 | ||
|
|
0be951742a | ||
|
|
59d3d6ae4b | ||
|
|
d061876f7e | ||
|
|
ddaec82415 | ||
|
|
8afd00f00d | ||
|
|
0b306f0a22 | ||
|
|
b36a36172b | ||
|
|
770278bbbc | ||
|
|
ca24818571 | ||
|
|
cd0af22655 | ||
|
|
96cb8d8af7 | ||
|
|
91face15bf | ||
|
|
6f52df63a5 | ||
|
|
509d81dc66 | ||
|
|
817f2a6f91 | ||
|
|
d74e10c7a8 | ||
|
|
1a1280b76e | ||
|
|
85d3b1169f | ||
|
|
1b32292e0c | ||
|
|
e6ba44fb2f | ||
|
|
9b6e022f46 | ||
|
|
4e9ab19fc0 | ||
|
|
4d634d8fa5 | ||
|
|
a538282e4f |
@@ -52,7 +52,6 @@ function build() {
|
|||||||
cp "$ROOT"/configs/api.template.yaml "$DIST"/configs
|
cp "$ROOT"/configs/api.template.yaml "$DIST"/configs
|
||||||
cp -R "$ROOT"/www "$DIST"/
|
cp -R "$ROOT"/www "$DIST"/
|
||||||
cp -R "$ROOT"/pages "$DIST"/
|
cp -R "$ROOT"/pages "$DIST"/
|
||||||
cp -R "$ROOT"/resources "$DIST"/
|
|
||||||
|
|
||||||
# we support TOA on linux/amd64 only
|
# we support TOA on linux/amd64 only
|
||||||
if [ "$OS" == "linux" -a "$ARCH" == "amd64" ]
|
if [ "$OS" == "linux" -a "$ARCH" == "amd64" ]
|
||||||
|
|||||||
Binary file not shown.
@@ -318,6 +318,21 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
app.On("bandwidth", func() {
|
||||||
|
var sock = gosock.NewTmpSock(teaconst.ProcessName)
|
||||||
|
reply, err := sock.Send(&gosock.Command{Code: "bandwidth"})
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("[ERROR]" + err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var statsMap = maps.NewMap(reply.Params).Get("stats")
|
||||||
|
statsJSON, err := json.MarshalIndent(statsMap, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("[ERROR]" + err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Println(string(statsJSON))
|
||||||
|
})
|
||||||
app.Run(func() {
|
app.Run(func() {
|
||||||
var node = nodes.NewNode()
|
var node = nodes.NewNode()
|
||||||
node.Start()
|
node.Start()
|
||||||
|
|||||||
11
go.mod
11
go.mod
@@ -4,8 +4,7 @@ go 1.18
|
|||||||
|
|
||||||
replace (
|
replace (
|
||||||
github.com/TeaOSLab/EdgeCommon => ../EdgeCommon
|
github.com/TeaOSLab/EdgeCommon => ../EdgeCommon
|
||||||
github.com/fsnotify/fsnotify => /Users/WorkSpace/Projects/fsnotify
|
github.com/fsnotify/fsnotify => github.com/iwind/fsnotify v1.5.2-0.20220817040843-193be2051ff4
|
||||||
rogchap.com/v8go => /Users/Workspace/Projects/v8go
|
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@@ -21,7 +20,7 @@ require (
|
|||||||
github.com/iwind/TeaGo v0.0.0-20220807030847-31de8e1cbe55
|
github.com/iwind/TeaGo v0.0.0-20220807030847-31de8e1cbe55
|
||||||
github.com/iwind/gofcgi v0.0.0-20210528023741-a92711d45f11
|
github.com/iwind/gofcgi v0.0.0-20210528023741-a92711d45f11
|
||||||
github.com/iwind/gosock v0.0.0-20211103081026-ee4652210ca4
|
github.com/iwind/gosock v0.0.0-20211103081026-ee4652210ca4
|
||||||
github.com/iwind/gowebp v0.0.0-20211029040624-7331ecc78ed8
|
github.com/iwind/gowebp v0.0.0-20220819053541-c235395277b5
|
||||||
github.com/klauspost/compress v1.15.8
|
github.com/klauspost/compress v1.15.8
|
||||||
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.4.2
|
||||||
@@ -29,9 +28,9 @@ require (
|
|||||||
github.com/mssola/user_agent v0.5.3
|
github.com/mssola/user_agent v0.5.3
|
||||||
github.com/pires/go-proxyproto v0.6.1
|
github.com/pires/go-proxyproto v0.6.1
|
||||||
github.com/shirou/gopsutil/v3 v3.22.2
|
github.com/shirou/gopsutil/v3 v3.22.2
|
||||||
golang.org/x/image v0.0.0-20211028202545-6944b10bf410
|
golang.org/x/image v0.0.0-20220722155232-062f8c9fd539
|
||||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f
|
golang.org/x/net v0.0.0-20220225172249-27dd8689420f
|
||||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab
|
||||||
golang.org/x/text v0.3.7
|
golang.org/x/text v0.3.7
|
||||||
google.golang.org/grpc v1.45.0
|
google.golang.org/grpc v1.45.0
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
@@ -41,7 +40,7 @@ require (
|
|||||||
require (
|
require (
|
||||||
github.com/BurntSushi/toml v0.4.1 // indirect
|
github.com/BurntSushi/toml v0.4.1 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||||
github.com/chai2010/webp v1.1.0 // indirect
|
github.com/chai2010/webp v1.1.1 // 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-sql-driver/mysql v1.5.0 // indirect
|
||||||
|
|||||||
77
go.sum
77
go.sum
@@ -1,17 +1,13 @@
|
|||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
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=
|
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
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 h1:GaI7EiDXDRfa8VshkTj7Fym7ha+y8/XxIgD2okUIjLw=
|
||||||
github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
|
||||||
github.com/DataDog/sketches-go v0.0.0-20190923095040-43f19ad77ff7/go.mod h1:Q5DbzQ+3AkgGwymQO7aZFNP7ns2lZKGtvRBzRXfdi60=
|
|
||||||
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/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
|
||||||
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||||
github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=
|
|
||||||
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/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
@@ -20,8 +16,8 @@ github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghf
|
|||||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
||||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/chai2010/webp v1.1.0 h1:4Ei0/BRroMF9FaXDG2e4OxwFcuW2vcXd+A6tyqTJUQQ=
|
github.com/chai2010/webp v1.1.1 h1:jTRmEccAJ4MGrhFOrPMpNGIJ/eybIgwKpcACsrTEapk=
|
||||||
github.com/chai2010/webp v1.1.0/go.mod h1:LP12PG5IFmLGHUU26tBiCBKnghxx3toZFwDjOYvd3Ow=
|
github.com/chai2010/webp v1.1.1/go.mod h1:0XVwvZWdjjdxpUEIf7b9g9VkHFnInUSYujwqTLEuldU=
|
||||||
github.com/cilium/ebpf v0.5.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=
|
github.com/cilium/ebpf v0.5.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=
|
||||||
github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA=
|
github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
@@ -37,7 +33,6 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
|||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f h1:q/DpyjJjZs94bziQ7YkBmIlpqbVP7yw179rnzoNVX1M=
|
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f h1:q/DpyjJjZs94bziQ7YkBmIlpqbVP7yw179rnzoNVX1M=
|
||||||
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-20200624174652-8d2f3be8b2d9/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
|
||||||
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/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
@@ -47,15 +42,9 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m
|
|||||||
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
|
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
|
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
|
||||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
|
||||||
github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
|
|
||||||
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
|
|
||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
|
||||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
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-redis/redis/v8 v8.0.0-beta.7/go.mod h1:FGJAWDWFht1sQ4qxyJHZZbVyvnVcKQN0E3u5/5lRz+g=
|
|
||||||
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-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
||||||
@@ -87,22 +76,20 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
|||||||
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 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/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
|
||||||
github.com/google/nftables v0.0.0-20220407195405-950e408d48c6 h1:btadZscaRmsi/+fOhkyUguRpSnrf6dykNEWxDeUCj9I=
|
github.com/google/nftables v0.0.0-20220407195405-950e408d48c6 h1:btadZscaRmsi/+fOhkyUguRpSnrf6dykNEWxDeUCj9I=
|
||||||
github.com/google/nftables v0.0.0-20220407195405-950e408d48c6/go.mod h1:0F8on3JWMkm+xahTHItkiu/E1SPqMd0TOxNweQv8ptE=
|
github.com/google/nftables v0.0.0-20220407195405-950e408d48c6/go.mod h1:0F8on3JWMkm+xahTHItkiu/E1SPqMd0TOxNweQv8ptE=
|
||||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
|
||||||
github.com/iwind/TeaGo v0.0.0-20220807023459-448081424640 h1:nBVQzDI4mrQS+Egg+Li6BGiTToBsv+XTck+BItgI52k=
|
|
||||||
github.com/iwind/TeaGo v0.0.0-20220807023459-448081424640/go.mod h1:+K2l6Num4Evl0jH7TYlZJ1oFJX8sA8YUC31Pb+I1mJk=
|
|
||||||
github.com/iwind/TeaGo v0.0.0-20220807030847-31de8e1cbe55 h1:shQNx0flJFBwKsGE7Hs3bI2bDz+YF0zl/4qE8B2KRiY=
|
github.com/iwind/TeaGo v0.0.0-20220807030847-31de8e1cbe55 h1:shQNx0flJFBwKsGE7Hs3bI2bDz+YF0zl/4qE8B2KRiY=
|
||||||
github.com/iwind/TeaGo v0.0.0-20220807030847-31de8e1cbe55/go.mod h1:fi/Pq+/5m2HZoseM+39dMF57ANXRt6w4PkGu3NXPc5s=
|
github.com/iwind/TeaGo v0.0.0-20220807030847-31de8e1cbe55/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/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-20211029040624-7331ecc78ed8 h1:AojsHz9Es9B3He2MQQxeRq3TyD//o9huxUo7r1wh44g=
|
github.com/iwind/gowebp v0.0.0-20220819053541-c235395277b5 h1:SgKhrqRhWVpu0ZKxQM8MGjdhy7hlH3Xax8snwsZWSic=
|
||||||
github.com/iwind/gowebp v0.0.0-20211029040624-7331ecc78ed8/go.mod h1:QJBY2txYhLMzwLO29iB5ujDJ3s3V7DsZ582nw4Ss+tM=
|
github.com/iwind/gowebp v0.0.0-20220819053541-c235395277b5/go.mod h1:Re7TEhwL+ygnxFg52fC0PWy01ULAIZp2QR0q5WwEOQA=
|
||||||
github.com/josharian/native v0.0.0-20200817173448-b6b71def0850 h1:uhL5Gw7BINiiPAo24A2sxkcDI0Jt/sqp1v5xQCniEFA=
|
github.com/josharian/native v0.0.0-20200817173448-b6b71def0850 h1:uhL5Gw7BINiiPAo24A2sxkcDI0Jt/sqp1v5xQCniEFA=
|
||||||
github.com/josharian/native v0.0.0-20200817173448-b6b71def0850/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
github.com/josharian/native v0.0.0-20200817173448-b6b71def0850/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||||
github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw=
|
github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw=
|
||||||
@@ -119,7 +106,6 @@ github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e h1:LvL4XsI70QxOGHed
|
|||||||
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.15.8 h1:JahtItbkWjf2jzm/T+qgMxkP9EMHsqEUA6vCMGmXvhA=
|
||||||
github.com/klauspost/compress v1.15.8/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
|
github.com/klauspost/compress v1.15.8/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
|
||||||
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
||||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
@@ -155,19 +141,9 @@ 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/mssola/user_agent v0.5.3 h1:lBRPML9mdFuIZgI2cmlQ+atbpJdLdeVl2IDodjBR578=
|
||||||
github.com/mssola/user_agent v0.5.3/go.mod h1:TTPno8LPY3wAIEKRpAtkdMT0f8SE24pLRGPahjCH4uw=
|
github.com/mssola/user_agent v0.5.3/go.mod h1:TTPno8LPY3wAIEKRpAtkdMT0f8SE24pLRGPahjCH4uw=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
|
||||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
|
||||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
|
||||||
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
|
||||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
|
||||||
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
|
||||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
|
||||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
|
||||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
|
||||||
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
|
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
|
||||||
github.com/opentracing/opentracing-go v1.1.1-0.20190913142402-a7454ce5950e/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
|
||||||
github.com/pires/go-proxyproto v0.6.1 h1:EBupykFmo22SDjv4fQVQd2J9NOoLPmyZA/15ldOGkPw=
|
github.com/pires/go-proxyproto v0.6.1 h1:EBupykFmo22SDjv4fQVQd2J9NOoLPmyZA/15ldOGkPw=
|
||||||
github.com/pires/go-proxyproto v0.6.1/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY=
|
github.com/pires/go-proxyproto v0.6.1/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
@@ -182,7 +158,6 @@ github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1
|
|||||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
|
||||||
github.com/stretchr/testify v1.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/tklauser/go-sysconf v0.3.9 h1:JeUVdAOWhhxVcU6Eqr/ATFHgXk/mmiItdKeJPev3vTo=
|
github.com/tklauser/go-sysconf v0.3.9 h1:JeUVdAOWhhxVcU6Eqr/ATFHgXk/mmiItdKeJPev3vTo=
|
||||||
@@ -196,41 +171,30 @@ github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1
|
|||||||
github.com/yuin/goldmark v1.4.1/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/otel v0.7.0/go.mod h1:aZMyHG5TqDOXEgH2tyLiXSUKly1jT3yqE9PmrzIeCdo=
|
|
||||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
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-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/image v0.0.0-20220722155232-062f8c9fd539 h1:/eM0PCrQI2xd471rI+snWuu251/+/jpBpZqir2mPdnU=
|
||||||
golang.org/x/exp v0.0.0-20200513190911-00229845015e/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
|
golang.org/x/image v0.0.0-20220722155232-062f8c9fd539/go.mod h1:doUCurBvlfPMKfmIpRIywoHmhN3VyhnoFDbvIEWF4hY=
|
||||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
|
||||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
|
||||||
golang.org/x/image v0.0.0-20211028202545-6944b10bf410 h1:hTftEOvwiOq2+O8k2D5/Q7COC7k5Qcrgc2TFURJYnvQ=
|
|
||||||
golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
|
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
|
||||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
|
||||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
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.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38=
|
||||||
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
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-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-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-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/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-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-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
|
||||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
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-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-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
@@ -259,22 +223,15 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
|
|||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
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/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/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-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-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-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-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/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-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-20201118182958-a01c418693c7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@@ -292,7 +249,6 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
@@ -302,11 +258,10 @@ golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-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-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU=
|
||||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/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/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.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=
|
||||||
@@ -318,7 +273,6 @@ golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3
|
|||||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
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-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
|
||||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||||
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
||||||
golang.org/x/tools v0.1.8 h1:P1HhGGuLW4aAclzjtmJdf0mJOjVUZUzOTqkAkWL+l6w=
|
golang.org/x/tools v0.1.8 h1:P1HhGGuLW4aAclzjtmJdf0mJOjVUZUzOTqkAkWL+l6w=
|
||||||
@@ -332,7 +286,6 @@ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9Ywl
|
|||||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
google.golang.org/genproto v0.0.0-20191009194640-548a555dbc03/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
|
||||||
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
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-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 h1:fNKDNuUyC4WH+inqDMpfXDdfvwfYILbsX+oskGZ8hxg=
|
||||||
@@ -341,7 +294,6 @@ google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZi
|
|||||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
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.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
|
||||||
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
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.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||||
google.golang.org/grpc v1.45.0 h1:NEpgUqV3Z+ZjkqMsxMg11IaDrXY4RY6CQukSGK0uI1M=
|
google.golang.org/grpc v1.45.0 h1:NEpgUqV3Z+ZjkqMsxMg11IaDrXY4RY6CQukSGK0uI1M=
|
||||||
@@ -360,22 +312,13 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ
|
|||||||
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
|
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
|
||||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
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-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/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/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
|
||||||
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/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
|
||||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
gopkg.in/yaml.v2 v2.4.0 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-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ func (this *LogWriter) Init() {
|
|||||||
this.c = make(chan string, 1024)
|
this.c = make(chan string, 1024)
|
||||||
|
|
||||||
// 异步写入文件
|
// 异步写入文件
|
||||||
var maxFileSize = 2 * sizes.G // 文件最大尺寸,超出此尺寸则清空
|
var maxFileSize = 128 * sizes.M // 文件最大尺寸,超出此尺寸则清空
|
||||||
if fp != nil {
|
if fp != nil {
|
||||||
goman.New(func() {
|
goman.New(func() {
|
||||||
var totalSize int64 = 0
|
var totalSize int64 = 0
|
||||||
|
|||||||
@@ -96,13 +96,13 @@ func (this *FileList) Reset() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *FileList) Add(hash string, item *Item) error {
|
func (this *FileList) Add(hash string, item *Item) error {
|
||||||
var db = this.getDB(hash)
|
var db = this.GetDB(hash)
|
||||||
|
|
||||||
if !db.IsReady() {
|
if !db.IsReady() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
err := db.Add(hash, item)
|
err := db.AddAsync(hash, item)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -120,12 +120,17 @@ func (this *FileList) Add(hash string, item *Item) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *FileList) Exist(hash string) (bool, error) {
|
func (this *FileList) Exist(hash string) (bool, error) {
|
||||||
var db = this.getDB(hash)
|
var db = this.GetDB(hash)
|
||||||
|
|
||||||
if !db.IsReady() {
|
if !db.IsReady() {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 如果Hash列表里不存在,那么必然不存在
|
||||||
|
if !db.hashMap.Exist(hash) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
var item = this.memoryCache.Read(hash)
|
var item = this.memoryCache.Read(hash)
|
||||||
if item != nil {
|
if item != nil {
|
||||||
return true, nil
|
return true, nil
|
||||||
@@ -225,7 +230,7 @@ func (this *FileList) PurgeLFU(count int, callback func(hash string) error) erro
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if notFound {
|
if notFound {
|
||||||
_, err = db.deleteHitByHashStmt.Exec(hash)
|
err = db.DeleteHitAsync(hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return db.WrapError(err)
|
return db.WrapError(err)
|
||||||
}
|
}
|
||||||
@@ -266,7 +271,7 @@ func (this *FileList) Stat(check func(hash string) bool) (*Stat, error) {
|
|||||||
// 这里不设置过期时间、不使用 check 函数,目的是让查询更快速一些
|
// 这里不设置过期时间、不使用 check 函数,目的是让查询更快速一些
|
||||||
_ = check
|
_ = check
|
||||||
|
|
||||||
row := db.statStmt.QueryRow()
|
var row = db.statStmt.QueryRow()
|
||||||
if row.Err() != nil {
|
if row.Err() != nil {
|
||||||
return nil, row.Err()
|
return nil, row.Err()
|
||||||
}
|
}
|
||||||
@@ -291,13 +296,13 @@ func (this *FileList) Count() (int64, error) {
|
|||||||
|
|
||||||
// IncreaseHit 增加点击量
|
// IncreaseHit 增加点击量
|
||||||
func (this *FileList) IncreaseHit(hash string) error {
|
func (this *FileList) IncreaseHit(hash string) error {
|
||||||
var db = this.getDB(hash)
|
var db = this.GetDB(hash)
|
||||||
|
|
||||||
if !db.IsReady() {
|
if !db.IsReady() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return db.IncreaseHit(hash)
|
return db.IncreaseHitAsync(hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnAdd 添加事件
|
// OnAdd 添加事件
|
||||||
@@ -326,17 +331,23 @@ func (this *FileList) GetDBIndex(hash string) uint64 {
|
|||||||
return fnv.HashString(hash) % CountFileDB
|
return fnv.HashString(hash) % CountFileDB
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *FileList) getDB(hash string) *FileListDB {
|
func (this *FileList) GetDB(hash string) *FileListDB {
|
||||||
return this.dbList[fnv.HashString(hash)%CountFileDB]
|
return this.dbList[fnv.HashString(hash)%CountFileDB]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *FileList) remove(hash string) (notFound bool, err error) {
|
func (this *FileList) remove(hash string) (notFound bool, err error) {
|
||||||
var db = this.getDB(hash)
|
var db = this.GetDB(hash)
|
||||||
|
|
||||||
if !db.IsReady() {
|
if !db.IsReady() {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HashMap中不存在,则确定不存在
|
||||||
|
if !db.hashMap.Exist(hash) {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
defer db.hashMap.Delete(hash)
|
||||||
|
|
||||||
// 从缓存中删除
|
// 从缓存中删除
|
||||||
this.memoryCache.Delete(hash)
|
this.memoryCache.Delete(hash)
|
||||||
|
|
||||||
@@ -357,14 +368,14 @@ func (this *FileList) remove(hash string) (notFound bool, err error) {
|
|||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = db.deleteByHashStmt.Exec(hash)
|
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)
|
atomic.AddInt64(&this.total, -1)
|
||||||
|
|
||||||
_, err = db.deleteHitByHashStmt.Exec(hash)
|
err = db.DeleteHitAsync(hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, db.WrapError(err)
|
return false, db.WrapError(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,10 +6,14 @@ import (
|
|||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
"errors"
|
||||||
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils/dbs"
|
"github.com/TeaOSLab/EdgeNode/internal/utils/dbs"
|
||||||
|
"github.com/iwind/TeaGo/logs"
|
||||||
"github.com/iwind/TeaGo/types"
|
"github.com/iwind/TeaGo/types"
|
||||||
timeutil "github.com/iwind/TeaGo/utils/time"
|
timeutil "github.com/iwind/TeaGo/utils/time"
|
||||||
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -21,6 +25,10 @@ type FileListDB struct {
|
|||||||
readDB *dbs.DB
|
readDB *dbs.DB
|
||||||
writeDB *dbs.DB
|
writeDB *dbs.DB
|
||||||
|
|
||||||
|
writeBatch *dbs.Batch
|
||||||
|
|
||||||
|
hashMap *FileListHashMap
|
||||||
|
|
||||||
itemsTableName string
|
itemsTableName string
|
||||||
hitsTableName string
|
hitsTableName string
|
||||||
|
|
||||||
@@ -30,52 +38,90 @@ type FileListDB struct {
|
|||||||
isReady bool
|
isReady bool
|
||||||
|
|
||||||
// cacheItems
|
// cacheItems
|
||||||
existsByHashStmt *dbs.Stmt // 根据hash检查是否存在
|
existsByHashStmt *dbs.Stmt // 根据hash检查是否存在
|
||||||
insertStmt *dbs.Stmt // 写入数据
|
|
||||||
selectByHashStmt *dbs.Stmt // 使用hash查询数据
|
insertStmt *dbs.Stmt // 写入数据
|
||||||
deleteByHashStmt *dbs.Stmt // 根据hash删除数据
|
insertSQL string
|
||||||
statStmt *dbs.Stmt // 统计
|
|
||||||
purgeStmt *dbs.Stmt // 清理
|
selectByHashStmt *dbs.Stmt // 使用hash查询数据
|
||||||
deleteAllStmt *dbs.Stmt // 删除所有数据
|
|
||||||
listOlderItemsStmt *dbs.Stmt // 读取较早存储的缓存
|
selectHashListStmt *dbs.Stmt
|
||||||
|
|
||||||
|
deleteByHashStmt *dbs.Stmt // 根据hash删除数据
|
||||||
|
deleteByHashSQL string
|
||||||
|
|
||||||
|
statStmt *dbs.Stmt // 统计
|
||||||
|
purgeStmt *dbs.Stmt // 清理
|
||||||
|
deleteAllStmt *dbs.Stmt // 删除所有数据
|
||||||
|
listOlderItemsStmt *dbs.Stmt // 读取较早存储的缓存
|
||||||
|
updateAccessWeekSQL string // 修改访问日期
|
||||||
|
|
||||||
// hits
|
// hits
|
||||||
insertHitStmt *dbs.Stmt // 写入数据
|
insertHitSQL string // 写入数据
|
||||||
increaseHitStmt *dbs.Stmt // 增加点击量
|
increaseHitSQL string // 增加点击量
|
||||||
deleteHitByHashStmt *dbs.Stmt // 根据hash删除数据
|
deleteHitByHashSQL string // 根据hash删除数据
|
||||||
lfuHitsStmt *dbs.Stmt // 读取老的数据
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFileListDB() *FileListDB {
|
func NewFileListDB() *FileListDB {
|
||||||
return &FileListDB{}
|
return &FileListDB{
|
||||||
|
hashMap: NewFileListHashMap(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *FileListDB) Open(dbPath string) error {
|
func (this *FileListDB) Open(dbPath string) error {
|
||||||
this.dbPath = dbPath
|
this.dbPath = dbPath
|
||||||
|
|
||||||
|
// 动态调整Cache值
|
||||||
|
var cacheSize = 32000
|
||||||
|
var memoryGB = utils.SystemMemoryGB()
|
||||||
|
if memoryGB >= 8 {
|
||||||
|
cacheSize += 32000 * memoryGB / 8
|
||||||
|
}
|
||||||
|
|
||||||
// write db
|
// write db
|
||||||
writeDB, err := sql.Open("sqlite3", "file:"+dbPath+"?cache=private&mode=rwc&_journal_mode=WAL&_sync=OFF&_cache_size=32000&_secure_delete=FAST")
|
writeDB, err := sql.Open("sqlite3", "file:"+dbPath+"?cache=private&mode=rwc&_journal_mode=WAL&_sync=OFF&_cache_size="+types.String(cacheSize)+"&_secure_delete=FAST")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("open write database failed: " + err.Error())
|
return errors.New("open write database failed: " + err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
writeDB.SetMaxOpenConns(1)
|
writeDB.SetMaxOpenConns(1)
|
||||||
|
|
||||||
|
this.writeDB = dbs.NewDB(writeDB)
|
||||||
|
|
||||||
// TODO 耗时过长,暂时不整理数据库
|
// TODO 耗时过长,暂时不整理数据库
|
||||||
// TODO 需要根据行数来判断是否VACUUM
|
// TODO 需要根据行数来判断是否VACUUM
|
||||||
|
// TODO 注意VACUUM反而可能让数据库文件变大
|
||||||
/**_, err = db.Exec("VACUUM")
|
/**_, err = db.Exec("VACUUM")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}**/
|
}**/
|
||||||
|
|
||||||
this.writeDB = dbs.NewDB(writeDB)
|
// 检查是否损坏
|
||||||
|
// TODO 暂时屏蔽,因为用时过长
|
||||||
|
|
||||||
|
var recoverEnv, _ = os.LookupEnv("EdgeRecover")
|
||||||
|
if len(recoverEnv) > 0 && this.shouldRecover() {
|
||||||
|
for _, indexName := range []string{"staleAt", "hash"} {
|
||||||
|
_, _ = this.writeDB.Exec(`REINDEX "` + indexName + `"`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.writeBatch = dbs.NewBatch(writeDB, 4)
|
||||||
|
this.writeBatch.OnFail(func(err error) {
|
||||||
|
remotelogs.Warn("LIST_FILE_DB", "run batch failed: "+err.Error())
|
||||||
|
})
|
||||||
|
|
||||||
|
goman.New(func() {
|
||||||
|
this.writeBatch.Exec()
|
||||||
|
})
|
||||||
|
|
||||||
if teaconst.EnableDBStat {
|
if teaconst.EnableDBStat {
|
||||||
|
this.writeBatch.EnableStat(true)
|
||||||
this.writeDB.EnableStat(true)
|
this.writeDB.EnableStat(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// read db
|
// read db
|
||||||
readDB, err := sql.Open("sqlite3", "file:"+dbPath+"?cache=private&mode=ro&_journal_mode=WAL&_sync=OFF&_cache_size=32000")
|
readDB, err := sql.Open("sqlite3", "file:"+dbPath+"?cache=private&mode=ro&_journal_mode=WAL&_sync=OFF&_cache_size="+types.String(cacheSize))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("open read database failed: " + err.Error())
|
return errors.New("open read database failed: " + err.Error())
|
||||||
}
|
}
|
||||||
@@ -119,7 +165,8 @@ func (this *FileListDB) Init() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
this.insertStmt, err = this.writeDB.Prepare(`INSERT INTO "` + this.itemsTableName + `" ("hash", "key", "headerSize", "bodySize", "metaSize", "expiredAt", "staleAt", "host", "serverId", "createdAt") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
this.insertSQL = `INSERT INTO "` + this.itemsTableName + `" ("hash", "key", "headerSize", "bodySize", "metaSize", "expiredAt", "staleAt", "host", "serverId", "createdAt", "accessWeek") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
||||||
|
this.insertStmt, err = this.writeDB.Prepare(this.insertSQL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -129,7 +176,10 @@ func (this *FileListDB) Init() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
this.deleteByHashStmt, err = this.writeDB.Prepare(`DELETE FROM "` + this.itemsTableName + `" WHERE "hash"=?`)
|
this.selectHashListStmt, err = this.readDB.Prepare(`SELECT "id", "hash" FROM "` + this.itemsTableName + `" WHERE id>:id ORDER BY id ASC LIMIT 2000`)
|
||||||
|
|
||||||
|
this.deleteByHashSQL = `DELETE FROM "` + this.itemsTableName + `" WHERE "hash"=?`
|
||||||
|
this.deleteByHashStmt, err = this.writeDB.Prepare(this.deleteByHashSQL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -149,27 +199,29 @@ func (this *FileListDB) Init() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
this.listOlderItemsStmt, err = this.readDB.Prepare(`SELECT "hash" FROM "` + this.itemsTableName + `" ORDER BY "id" ASC LIMIT ?`)
|
this.listOlderItemsStmt, err = this.readDB.Prepare(`SELECT "hash" FROM "` + this.itemsTableName + `" ORDER BY "accessWeek" ASC, "id" ASC LIMIT ?`)
|
||||||
|
|
||||||
this.insertHitStmt, err = this.writeDB.Prepare(`INSERT INTO "` + this.hitsTableName + `" ("hash", "week2Hits", "week") VALUES (?, 1, ?)`)
|
|
||||||
|
|
||||||
this.increaseHitStmt, err = this.writeDB.Prepare(`INSERT INTO "` + this.hitsTableName + `" ("hash", "week2Hits", "week") VALUES (?, 1, ?) ON CONFLICT("hash") DO UPDATE SET "week1Hits"=IIF("week"=?, "week1Hits", "week2Hits"), "week2Hits"=IIF("week"=?, "week2Hits"+1, 1), "week"=?`)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
this.deleteHitByHashStmt, err = this.writeDB.Prepare(`DELETE FROM "` + this.hitsTableName + `" WHERE "hash"=?`)
|
this.updateAccessWeekSQL = `UPDATE "` + this.itemsTableName + `" SET "accessWeek"=? WHERE "hash"=?`
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
this.lfuHitsStmt, err = this.readDB.Prepare(`SELECT "hash" FROM "` + this.hitsTableName + `" ORDER BY "week" ASC, "week1Hits"+"week2Hits" ASC LIMIT ?`)
|
this.insertHitSQL = `INSERT INTO "` + this.hitsTableName + `" ("hash", "week2Hits", "week") VALUES (?, 1, ?)`
|
||||||
if err != nil {
|
|
||||||
return err
|
this.increaseHitSQL = `INSERT INTO "` + this.hitsTableName + `" ("hash", "week2Hits", "week") VALUES (?, 1, ?) ON CONFLICT("hash") DO UPDATE SET "week1Hits"=IIF("week"=?, "week1Hits", "week2Hits"), "week2Hits"=IIF("week"=?, "week2Hits"+1, 1), "week"=?`
|
||||||
}
|
|
||||||
|
this.deleteHitByHashSQL = `DELETE FROM "` + this.hitsTableName + `" WHERE "hash"=?`
|
||||||
|
|
||||||
this.isReady = true
|
this.isReady = true
|
||||||
|
|
||||||
|
// 加载HashMap
|
||||||
|
go func() {
|
||||||
|
err := this.hashMap.Load(this)
|
||||||
|
if err != nil {
|
||||||
|
remotelogs.Error("LIST_FILE_DB", "load hash map failed: "+err.Error()+"(file: "+this.dbPath+")")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,13 +233,26 @@ func (this *FileListDB) Total() int64 {
|
|||||||
return this.total
|
return this.total
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *FileListDB) Add(hash string, item *Item) error {
|
func (this *FileListDB) AddAsync(hash string, item *Item) error {
|
||||||
|
this.hashMap.Add(hash)
|
||||||
|
|
||||||
if item.StaleAt == 0 {
|
if item.StaleAt == 0 {
|
||||||
item.StaleAt = item.ExpiredAt
|
item.StaleAt = item.ExpiredAt
|
||||||
}
|
}
|
||||||
|
|
||||||
// 放入队列
|
this.writeBatch.Add(this.insertSQL, hash, item.Key, item.HeaderSize, item.BodySize, item.MetaSize, item.ExpiredAt, item.StaleAt, item.Host, item.ServerId, utils.UnixTime(), timeutil.Format("YW"))
|
||||||
_, err := this.insertStmt.Exec(hash, item.Key, item.HeaderSize, item.BodySize, item.MetaSize, item.ExpiredAt, item.StaleAt, item.Host, item.ServerId, utils.UnixTime())
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *FileListDB) AddSync(hash string, item *Item) error {
|
||||||
|
this.hashMap.Add(hash)
|
||||||
|
|
||||||
|
if item.StaleAt == 0 {
|
||||||
|
item.StaleAt = item.ExpiredAt
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := this.insertStmt.Exec(hash, item.Key, item.HeaderSize, item.BodySize, item.MetaSize, item.ExpiredAt, item.StaleAt, item.Host, item.ServerId, utils.UnixTime(), timeutil.Format("YW"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return this.WrapError(err)
|
return this.WrapError(err)
|
||||||
}
|
}
|
||||||
@@ -195,6 +260,23 @@ func (this *FileListDB) Add(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 {
|
||||||
|
this.hashMap.Delete(hash)
|
||||||
|
|
||||||
|
_, err := this.deleteByHashStmt.Exec(hash)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (this *FileListDB) ListExpiredItems(count int) (hashList []string, err error) {
|
func (this *FileListDB) ListExpiredItems(count int) (hashList []string, err error) {
|
||||||
if !this.isReady {
|
if !this.isReady {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
@@ -232,28 +314,56 @@ func (this *FileListDB) ListLFUItems(count int) (hashList []string, err error) {
|
|||||||
count = 100
|
count = 100
|
||||||
}
|
}
|
||||||
|
|
||||||
hashList, err = this.listLFUItems(count)
|
// 先找过期的
|
||||||
|
hashList, err = this.ListExpiredItems(count)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
var l = len(hashList)
|
||||||
|
|
||||||
if len(hashList) > count/2 {
|
// 从旧缓存中补充
|
||||||
return
|
if l < count {
|
||||||
|
oldHashList, err := this.listOlderItems(count - l)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
hashList = append(hashList, oldHashList...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 不足补齐
|
return hashList, nil
|
||||||
olderHashList, err := this.listOlderItems(count - len(hashList))
|
}
|
||||||
|
|
||||||
|
func (this *FileListDB) ListHashes(lastId int64) (hashList []string, maxId int64, err error) {
|
||||||
|
rows, err := this.selectHashListStmt.Query(lastId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
hashList = append(hashList, olderHashList...)
|
var id int64
|
||||||
|
var hash string
|
||||||
|
for rows.Next() {
|
||||||
|
err = rows.Scan(&id, &hash)
|
||||||
|
if err != nil {
|
||||||
|
_ = rows.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
maxId = id
|
||||||
|
hashList = append(hashList, hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = rows.Close()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *FileListDB) IncreaseHit(hash string) error {
|
func (this *FileListDB) IncreaseHitAsync(hash string) error {
|
||||||
var week = timeutil.Format("YW")
|
var week = timeutil.Format("YW")
|
||||||
_, err := this.increaseHitStmt.Exec(hash, week, week, week, week)
|
this.writeBatch.Add(this.increaseHitSQL, hash, week, week, week, week)
|
||||||
return this.WrapError(err)
|
this.writeBatch.Add(this.updateAccessWeekSQL, week, hash)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *FileListDB) DeleteHitAsync(hash string) error {
|
||||||
|
this.writeBatch.Add(this.deleteHitByHashSQL, hash)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *FileListDB) CleanPrefix(prefix string) error {
|
func (this *FileListDB) CleanPrefix(prefix string) error {
|
||||||
@@ -288,6 +398,8 @@ func (this *FileListDB) CleanAll() error {
|
|||||||
return this.WrapError(err)
|
return this.WrapError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.hashMap.Clean()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -304,6 +416,9 @@ func (this *FileListDB) Close() error {
|
|||||||
if this.selectByHashStmt != nil {
|
if this.selectByHashStmt != nil {
|
||||||
_ = this.selectByHashStmt.Close()
|
_ = this.selectByHashStmt.Close()
|
||||||
}
|
}
|
||||||
|
if this.selectHashListStmt != nil {
|
||||||
|
_ = this.selectHashListStmt.Close()
|
||||||
|
}
|
||||||
if this.deleteByHashStmt != nil {
|
if this.deleteByHashStmt != nil {
|
||||||
_ = this.deleteByHashStmt.Close()
|
_ = this.deleteByHashStmt.Close()
|
||||||
}
|
}
|
||||||
@@ -320,17 +435,8 @@ func (this *FileListDB) Close() error {
|
|||||||
_ = this.listOlderItemsStmt.Close()
|
_ = this.listOlderItemsStmt.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
if this.insertHitStmt != nil {
|
if this.writeBatch != nil {
|
||||||
_ = this.insertHitStmt.Close()
|
this.writeBatch.Close()
|
||||||
}
|
|
||||||
if this.increaseHitStmt != nil {
|
|
||||||
_ = this.increaseHitStmt.Close()
|
|
||||||
}
|
|
||||||
if this.deleteHitByHashStmt != nil {
|
|
||||||
_ = this.deleteHitByHashStmt.Close()
|
|
||||||
}
|
|
||||||
if this.lfuHitsStmt != nil {
|
|
||||||
_ = this.lfuHitsStmt.Close()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var errStrings []string
|
var errStrings []string
|
||||||
@@ -379,7 +485,8 @@ func (this *FileListDB) initTables(times int) error {
|
|||||||
"staleAt" integer DEFAULT 0,
|
"staleAt" integer DEFAULT 0,
|
||||||
"createdAt" integer DEFAULT 0,
|
"createdAt" integer DEFAULT 0,
|
||||||
"host" varchar(128),
|
"host" varchar(128),
|
||||||
"serverId" integer
|
"serverId" integer,
|
||||||
|
"accessWeek" varchar(6)
|
||||||
);
|
);
|
||||||
|
|
||||||
DROP INDEX IF EXISTS "createdAt";
|
DROP INDEX IF EXISTS "createdAt";
|
||||||
@@ -395,19 +502,28 @@ CREATE UNIQUE INDEX IF NOT EXISTS "hash"
|
|||||||
ON "` + this.itemsTableName + `" (
|
ON "` + this.itemsTableName + `" (
|
||||||
"hash" ASC
|
"hash" ASC
|
||||||
);
|
);
|
||||||
|
|
||||||
|
ALTER TABLE "cacheItems" ADD "accessWeek" varchar(6);
|
||||||
`)
|
`)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// 尝试删除重建
|
// 忽略可以预期的错误
|
||||||
if times < 3 {
|
if strings.Contains(err.Error(), "duplicate column name") {
|
||||||
_, dropErr := this.writeDB.Exec(`DROP TABLE "` + this.itemsTableName + `"`)
|
err = nil
|
||||||
if dropErr == nil {
|
|
||||||
return this.initTables(times + 1)
|
|
||||||
}
|
|
||||||
return this.WrapError(err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.WrapError(err)
|
// 尝试删除重建
|
||||||
|
if err != nil {
|
||||||
|
if times < 3 {
|
||||||
|
_, dropErr := this.writeDB.Exec(`DROP TABLE "` + this.itemsTableName + `"`)
|
||||||
|
if dropErr == nil {
|
||||||
|
return this.initTables(times + 1)
|
||||||
|
}
|
||||||
|
return this.WrapError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.WrapError(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -442,27 +558,6 @@ ON "` + this.hitsTableName + `" (
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *FileListDB) listLFUItems(count int) (hashList []string, err error) {
|
|
||||||
rows, err := this.lfuHitsStmt.Query(count)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
_ = rows.Close()
|
|
||||||
}()
|
|
||||||
|
|
||||||
for rows.Next() {
|
|
||||||
var hash string
|
|
||||||
err = rows.Scan(&hash)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
hashList = append(hashList, hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
return hashList, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *FileListDB) listOlderItems(count int) (hashList []string, err error) {
|
func (this *FileListDB) listOlderItems(count int) (hashList []string, err error) {
|
||||||
rows, err := this.listOlderItemsStmt.Query(count)
|
rows, err := this.listOlderItemsStmt.Query(count)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -483,3 +578,21 @@ func (this *FileListDB) listOlderItems(count int) (hashList []string, err error)
|
|||||||
|
|
||||||
return hashList, nil
|
return hashList, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *FileListDB) shouldRecover() bool {
|
||||||
|
result, err := this.writeDB.Query("pragma integrity_check;")
|
||||||
|
if err != nil {
|
||||||
|
logs.Println(result)
|
||||||
|
}
|
||||||
|
var errString = ""
|
||||||
|
var shouldRecover = false
|
||||||
|
for result.Next() {
|
||||||
|
err = result.Scan(&errString)
|
||||||
|
if strings.TrimSpace(errString) != "ok" {
|
||||||
|
shouldRecover = true
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
_ = result.Close()
|
||||||
|
return shouldRecover
|
||||||
|
}
|
||||||
|
|||||||
49
internal/caches/list_file_db_test.go
Normal file
49
internal/caches/list_file_db_test.go
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||||
|
|
||||||
|
package caches_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/caches"
|
||||||
|
"github.com/iwind/TeaGo/Tea"
|
||||||
|
_ "github.com/iwind/TeaGo/bootstrap"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFileListDB_ListLFUItems(t *testing.T) {
|
||||||
|
var db = caches.NewFileListDB()
|
||||||
|
err := db.Open(Tea.Root + "/data/cache-db-large.db")
|
||||||
|
//err := db.Open(Tea.Root + "/data/cache-index/p1/db-0.db")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
err = db.Init()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
_ = db.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
hashList, err := db.ListLFUItems(100)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log("[", len(hashList), "]", hashList)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFileListDB_IncreaseHitAsync(t *testing.T) {
|
||||||
|
var db = caches.NewFileListDB()
|
||||||
|
err := db.Open(Tea.Root + "/data/cache-db-large.db")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
err = db.Init()
|
||||||
|
err = db.IncreaseHitAsync("4598e5231ba47d6ec7aa9ea640ff2eaf")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
// wait transaction
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
120
internal/caches/list_file_hash_map.go
Normal file
120
internal/caches/list_file_hash_map.go
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||||
|
|
||||||
|
package caches
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/zero"
|
||||||
|
"math/big"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FileListHashMap 文件Hash列表
|
||||||
|
type FileListHashMap struct {
|
||||||
|
m map[uint64]zero.Zero
|
||||||
|
|
||||||
|
locker sync.RWMutex
|
||||||
|
isAvailable bool
|
||||||
|
isReady bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFileListHashMap() *FileListHashMap {
|
||||||
|
return &FileListHashMap{
|
||||||
|
m: map[uint64]zero.Zero{},
|
||||||
|
isAvailable: false,
|
||||||
|
isReady: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *FileListHashMap) Load(db *FileListDB) error {
|
||||||
|
// 如果系统内存过小,我们不缓存
|
||||||
|
if utils.SystemMemoryGB() < 3 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isAvailable = true
|
||||||
|
|
||||||
|
var lastId int64
|
||||||
|
for {
|
||||||
|
hashList, maxId, err := db.ListHashes(lastId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(hashList) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
this.AddHashes(hashList)
|
||||||
|
lastId = maxId
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isReady = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *FileListHashMap) Add(hash string) {
|
||||||
|
if !this.isAvailable {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.locker.Lock()
|
||||||
|
this.m[this.bigInt(hash)] = zero.New()
|
||||||
|
this.locker.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *FileListHashMap) AddHashes(hashes []string) {
|
||||||
|
if !this.isAvailable {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.locker.Lock()
|
||||||
|
for _, hash := range hashes {
|
||||||
|
this.m[this.bigInt(hash)] = zero.New()
|
||||||
|
}
|
||||||
|
this.locker.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *FileListHashMap) Delete(hash string) {
|
||||||
|
if !this.isAvailable {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.locker.Lock()
|
||||||
|
delete(this.m, this.bigInt(hash))
|
||||||
|
this.locker.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *FileListHashMap) Exist(hash string) bool {
|
||||||
|
if !this.isAvailable {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if !this.isReady {
|
||||||
|
// 只有完全Ready时才能判断是否为false
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
this.locker.RLock()
|
||||||
|
_, ok := this.m[this.bigInt(hash)]
|
||||||
|
this.locker.RUnlock()
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *FileListHashMap) Clean() {
|
||||||
|
this.locker.Lock()
|
||||||
|
this.m = map[uint64]zero.Zero{}
|
||||||
|
this.locker.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *FileListHashMap) IsReady() bool {
|
||||||
|
return this.isReady
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *FileListHashMap) Len() int {
|
||||||
|
this.locker.Lock()
|
||||||
|
defer this.locker.Unlock()
|
||||||
|
return len(this.m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *FileListHashMap) bigInt(hash string) uint64 {
|
||||||
|
var bigInt = big.NewInt(0)
|
||||||
|
bigInt.SetString(hash, 16)
|
||||||
|
return bigInt.Uint64()
|
||||||
|
}
|
||||||
96
internal/caches/list_file_hash_map_test.go
Normal file
96
internal/caches/list_file_hash_map_test.go
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||||
|
|
||||||
|
package caches_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/caches"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/zero"
|
||||||
|
"github.com/iwind/TeaGo/Tea"
|
||||||
|
"github.com/iwind/TeaGo/types"
|
||||||
|
stringutil "github.com/iwind/TeaGo/utils/string"
|
||||||
|
"math/big"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFileListHashMap_Memory(t *testing.T) {
|
||||||
|
var stat1 = &runtime.MemStats{}
|
||||||
|
runtime.ReadMemStats(stat1)
|
||||||
|
|
||||||
|
var m = caches.NewFileListHashMap()
|
||||||
|
|
||||||
|
for i := 0; i < 1_000_000; i++ {
|
||||||
|
m.Add(stringutil.Md5(types.String(i)))
|
||||||
|
}
|
||||||
|
|
||||||
|
var stat2 = &runtime.MemStats{}
|
||||||
|
runtime.ReadMemStats(stat2)
|
||||||
|
|
||||||
|
t.Log("ready", (stat2.Alloc-stat1.Alloc)/1024/1024, "M")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFileListHashMap_Memory2(t *testing.T) {
|
||||||
|
var stat1 = &runtime.MemStats{}
|
||||||
|
runtime.ReadMemStats(stat1)
|
||||||
|
|
||||||
|
var m = map[uint64]zero.Zero{}
|
||||||
|
|
||||||
|
for i := 0; i < 1_000_000; i++ {
|
||||||
|
m[uint64(i)] = zero.New()
|
||||||
|
}
|
||||||
|
|
||||||
|
var stat2 = &runtime.MemStats{}
|
||||||
|
runtime.ReadMemStats(stat2)
|
||||||
|
|
||||||
|
t.Log("ready", (stat2.Alloc-stat1.Alloc)/1024/1024, "M")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFileListHashMap_BigInt(t *testing.T) {
|
||||||
|
for _, s := range []string{"1", "2", "3", "123", "123456"} {
|
||||||
|
var hash = stringutil.Md5(s)
|
||||||
|
|
||||||
|
var bigInt = big.NewInt(0)
|
||||||
|
bigInt.SetString(hash, 16)
|
||||||
|
t.Log(s, "=>", bigInt.Uint64(), "hash:", hash, "format:", strconv.FormatUint(bigInt.Uint64(), 16))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFileListHashMap_Load(t *testing.T) {
|
||||||
|
var list = caches.NewFileList(Tea.Root + "/data/cache-index/p1").(*caches.FileList)
|
||||||
|
err := list.Init()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
_ = list.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
var m = caches.NewFileListHashMap()
|
||||||
|
var before = time.Now()
|
||||||
|
var db = list.GetDB("abc")
|
||||||
|
err = m.Load(db)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log(time.Since(before).Seconds()*1000, "ms")
|
||||||
|
t.Log("count:", m.Len())
|
||||||
|
m.Add("abc")
|
||||||
|
|
||||||
|
for _, hash := range []string{"33347bb4441265405347816cad36a0f8", "a", "abc", "123"} {
|
||||||
|
t.Log(hash, "=>", m.Exist(hash))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Benchmark_BigInt(b *testing.B) {
|
||||||
|
var hash = stringutil.Md5("123456")
|
||||||
|
b.ResetTimer()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
var bigInt = big.NewInt(0)
|
||||||
|
bigInt.SetString(hash, 16)
|
||||||
|
_ = bigInt.Uint64()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -69,8 +69,8 @@ func TestFileList_Add_Many(t *testing.T) {
|
|||||||
_ = list.Close()
|
_ = list.Close()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
before := time.Now()
|
var before = time.Now()
|
||||||
for i := 0; i < 100_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)
|
||||||
_ = list.Add(stringutil.Md5(u), &caches.Item{
|
_ = list.Add(stringutil.Md5(u), &caches.Item{
|
||||||
Key: u,
|
Key: u,
|
||||||
@@ -290,12 +290,12 @@ func TestFileList_Stat(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFileList_Count(t *testing.T) {
|
func TestFileList_Count(t *testing.T) {
|
||||||
list := caches.NewFileList(Tea.Root + "/data")
|
var list = caches.NewFileList(Tea.Root + "/data")
|
||||||
err := list.Init()
|
err := list.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
before := time.Now()
|
var before = time.Now()
|
||||||
count, err := list.Count()
|
count, err := list.Count()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -367,6 +367,7 @@ func BenchmarkFileList_Exist(b *testing.B) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
_, _ = list.Exist("f0eb5b87e0b0041f3917002c0707475f" + types.String(i))
|
_, _ = list.Exist("f0eb5b87e0b0041f3917002c0707475f" + types.String(i))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import (
|
|||||||
"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/iwind/TeaGo/lists"
|
"github.com/iwind/TeaGo/lists"
|
||||||
"github.com/iwind/TeaGo/logs"
|
|
||||||
"github.com/iwind/TeaGo/types"
|
"github.com/iwind/TeaGo/types"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -16,7 +15,7 @@ var SharedManager = NewManager()
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
events.On(events.EventQuit, func() {
|
events.On(events.EventQuit, func() {
|
||||||
logs.Println("CACHE", "quiting cache manager")
|
remotelogs.Println("CACHE", "quiting cache manager")
|
||||||
SharedManager.UpdatePolicies([]*serverconfigs.HTTPCachePolicy{})
|
SharedManager.UpdatePolicies([]*serverconfigs.HTTPCachePolicy{})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -179,7 +179,7 @@ func (this *FileStorage) UpdatePolicy(newPolicy *serverconfigs.HTTPCachePolicy)
|
|||||||
// open cache
|
// open cache
|
||||||
oldOpenFileCacheJSON, _ := json.Marshal(oldOpenFileCache)
|
oldOpenFileCacheJSON, _ := json.Marshal(oldOpenFileCache)
|
||||||
newOpenFileCacheJSON, _ := json.Marshal(this.options.OpenFileCache)
|
newOpenFileCacheJSON, _ := json.Marshal(this.options.OpenFileCache)
|
||||||
if bytes.Compare(oldOpenFileCacheJSON, newOpenFileCacheJSON) != 0 {
|
if !bytes.Equal(oldOpenFileCacheJSON, newOpenFileCacheJSON) {
|
||||||
this.initOpenFileCache()
|
this.initOpenFileCache()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -520,8 +520,6 @@ func (this *MemoryStorage) flushItem(key string) {
|
|||||||
|
|
||||||
// 从内存中移除
|
// 从内存中移除
|
||||||
_ = this.Delete(key)
|
_ = this.Delete(key)
|
||||||
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *MemoryStorage) memoryCapacityBytes() int64 {
|
func (this *MemoryStorage) memoryCapacityBytes() int64 {
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
package configs
|
|
||||||
|
|
||||||
import "sync"
|
|
||||||
|
|
||||||
var sharedLocker = &sync.RWMutex{}
|
|
||||||
117
internal/conns/map.go
Normal file
117
internal/conns/map.go
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||||
|
|
||||||
|
package conns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
var SharedMap = NewMap()
|
||||||
|
|
||||||
|
type Map struct {
|
||||||
|
m map[string]map[int]net.Conn // ip => { port => Conn }
|
||||||
|
|
||||||
|
locker sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMap() *Map {
|
||||||
|
return &Map{
|
||||||
|
m: map[string]map[int]net.Conn{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Map) Add(conn net.Conn) {
|
||||||
|
if conn == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tcpAddr, ok := conn.RemoteAddr().(*net.TCPAddr)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var ip = tcpAddr.IP.String()
|
||||||
|
var port = tcpAddr.Port
|
||||||
|
|
||||||
|
this.locker.Lock()
|
||||||
|
defer this.locker.Unlock()
|
||||||
|
connMap, ok := this.m[ip]
|
||||||
|
if !ok {
|
||||||
|
this.m[ip] = map[int]net.Conn{
|
||||||
|
port: conn,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
connMap[port] = conn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Map) Remove(conn net.Conn) {
|
||||||
|
if conn == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tcpAddr, ok := conn.RemoteAddr().(*net.TCPAddr)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var ip = tcpAddr.IP.String()
|
||||||
|
var port = tcpAddr.Port
|
||||||
|
|
||||||
|
this.locker.Lock()
|
||||||
|
defer this.locker.Unlock()
|
||||||
|
|
||||||
|
connMap, ok := this.m[ip]
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
delete(connMap, port)
|
||||||
|
|
||||||
|
if len(connMap) == 0 {
|
||||||
|
delete(this.m, ip)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Map) CountIPConns(ip string) int {
|
||||||
|
this.locker.RLock()
|
||||||
|
var l = len(this.m[ip])
|
||||||
|
this.locker.RUnlock()
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Map) CloseIPConns(ip string) {
|
||||||
|
var conns = []net.Conn{}
|
||||||
|
|
||||||
|
this.locker.RLock()
|
||||||
|
connMap, ok := this.m[ip]
|
||||||
|
|
||||||
|
// 复制,防止在Close时产生并发冲突
|
||||||
|
if ok {
|
||||||
|
for _, conn := range connMap {
|
||||||
|
conns = append(conns, conn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 需要在Close之前结束,防止死循环
|
||||||
|
this.locker.RUnlock()
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
for _, conn := range conns {
|
||||||
|
_ = conn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 这里不需要从 m 中删除,因为关闭时会自然触发回调
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Map) AllConns() []net.Conn {
|
||||||
|
this.locker.RLock()
|
||||||
|
defer this.locker.RUnlock()
|
||||||
|
|
||||||
|
var result = []net.Conn{}
|
||||||
|
for _, m := range this.m {
|
||||||
|
for _, conn := range m {
|
||||||
|
result = append(result, conn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package teaconst
|
package teaconst
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Version = "0.5.0"
|
Version = "0.5.3"
|
||||||
|
|
||||||
ProductName = "Edge Node"
|
ProductName = "Edge Node"
|
||||||
ProcessName = "edge-node"
|
ProcessName = "edge-node"
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||||
//go:build linux
|
//go:build linux
|
||||||
// +build linux
|
|
||||||
|
|
||||||
package firewalls
|
package firewalls
|
||||||
|
|
||||||
@@ -14,6 +13,7 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeNode/internal/firewalls/nftables"
|
"github.com/TeaOSLab/EdgeNode/internal/firewalls/nftables"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
|
executils "github.com/TeaOSLab/EdgeNode/internal/utils/exec"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/zero"
|
"github.com/TeaOSLab/EdgeNode/internal/zero"
|
||||||
"github.com/iwind/TeaGo/lists"
|
"github.com/iwind/TeaGo/lists"
|
||||||
"github.com/iwind/TeaGo/types"
|
"github.com/iwind/TeaGo/types"
|
||||||
@@ -21,6 +21,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var SharedDDoSProtectionManager = NewDDoSProtectionManager()
|
var SharedDDoSProtectionManager = NewDDoSProtectionManager()
|
||||||
@@ -53,19 +54,13 @@ func init() {
|
|||||||
|
|
||||||
// DDoSProtectionManager DDoS防护
|
// DDoSProtectionManager DDoS防护
|
||||||
type DDoSProtectionManager struct {
|
type DDoSProtectionManager struct {
|
||||||
nftPath string
|
|
||||||
|
|
||||||
lastAllowIPList []string
|
lastAllowIPList []string
|
||||||
lastConfig []byte
|
lastConfig []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDDoSProtectionManager 获取新对象
|
// NewDDoSProtectionManager 获取新对象
|
||||||
func NewDDoSProtectionManager() *DDoSProtectionManager {
|
func NewDDoSProtectionManager() *DDoSProtectionManager {
|
||||||
nftPath, _ := exec.LookPath("nft")
|
return &DDoSProtectionManager{}
|
||||||
|
|
||||||
return &DDoSProtectionManager{
|
|
||||||
nftPath: nftPath,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply 应用配置
|
// Apply 应用配置
|
||||||
@@ -91,7 +86,7 @@ func (this *DDoSProtectionManager) Apply(config *ddosconfigs.ProtectionConfig) e
|
|||||||
}
|
}
|
||||||
remotelogs.Println("FIREWALL", "change DDoS protection config")
|
remotelogs.Println("FIREWALL", "change DDoS protection config")
|
||||||
|
|
||||||
if len(this.nftPath) == 0 {
|
if len(this.nftExe()) == 0 {
|
||||||
return errors.New("can not find nft command")
|
return errors.New("can not find nft command")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,6 +149,11 @@ func (this *DDoSProtectionManager) Apply(config *ddosconfigs.ProtectionConfig) e
|
|||||||
|
|
||||||
// 添加TCP规则
|
// 添加TCP规则
|
||||||
func (this *DDoSProtectionManager) addTCPRules(tcpConfig *ddosconfigs.TCPConfig) error {
|
func (this *DDoSProtectionManager) addTCPRules(tcpConfig *ddosconfigs.TCPConfig) error {
|
||||||
|
var nftExe = this.nftExe()
|
||||||
|
if len(nftExe) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// 检查nft版本不能小于0.9
|
// 检查nft版本不能小于0.9
|
||||||
if len(nftablesInstance.version) > 0 && stringutil.VersionCompare("0.9", nftablesInstance.version) > 0 {
|
if len(nftablesInstance.version) > 0 && stringutil.VersionCompare("0.9", nftablesInstance.version) > 0 {
|
||||||
return nil
|
return nil
|
||||||
@@ -195,14 +195,31 @@ func (this *DDoSProtectionManager) addTCPRules(tcpConfig *ddosconfigs.TCPConfig)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// new connections rate
|
// new connections rate (minutely)
|
||||||
var newConnectionsRate = tcpConfig.NewConnectionsRate
|
var newConnectionsMinutelyRate = tcpConfig.NewConnectionsMinutelyRate
|
||||||
if newConnectionsRate <= 0 {
|
if newConnectionsMinutelyRate <= 0 {
|
||||||
newConnectionsRate = nodeconfigs.DefaultTCPNewConnectionsRate
|
newConnectionsMinutelyRate = nodeconfigs.DefaultTCPNewConnectionsMinutelyRate
|
||||||
if newConnectionsRate <= 0 {
|
if newConnectionsMinutelyRate <= 0 {
|
||||||
newConnectionsRate = 100000
|
newConnectionsMinutelyRate = 100000
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var newConnectionsMinutelyRateBlockTimeout = tcpConfig.NewConnectionsMinutelyRateBlockTimeout
|
||||||
|
if newConnectionsMinutelyRateBlockTimeout < 0 {
|
||||||
|
newConnectionsMinutelyRateBlockTimeout = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// new connections rate (secondly)
|
||||||
|
var newConnectionsSecondlyRate = tcpConfig.NewConnectionsSecondlyRate
|
||||||
|
if newConnectionsSecondlyRate <= 0 {
|
||||||
|
newConnectionsSecondlyRate = nodeconfigs.DefaultTCPNewConnectionsSecondlyRate
|
||||||
|
if newConnectionsSecondlyRate <= 0 {
|
||||||
|
newConnectionsSecondlyRate = 10000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var newConnectionsSecondlyRateBlockTimeout = tcpConfig.NewConnectionsSecondlyRateBlockTimeout
|
||||||
|
if newConnectionsSecondlyRateBlockTimeout < 0 {
|
||||||
|
newConnectionsSecondlyRateBlockTimeout = 0
|
||||||
|
}
|
||||||
|
|
||||||
// 检查是否有变化
|
// 检查是否有变化
|
||||||
var hasChanges = false
|
var hasChanges = false
|
||||||
@@ -215,7 +232,11 @@ func (this *DDoSProtectionManager) addTCPRules(tcpConfig *ddosconfigs.TCPConfig)
|
|||||||
hasChanges = true
|
hasChanges = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if !this.existsRule(oldRules, []string{"tcp", types.String(port), "newConnectionsRate", types.String(newConnectionsRate)}) {
|
if !this.existsRule(oldRules, []string{"tcp", types.String(port), "newConnectionsRate", types.String(newConnectionsMinutelyRate), types.String(newConnectionsMinutelyRateBlockTimeout)}) {
|
||||||
|
hasChanges = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if !this.existsRule(oldRules, []string{"tcp", types.String(port), "newConnectionsSecondlyRate", types.String(newConnectionsSecondlyRate), types.String(newConnectionsSecondlyRateBlockTimeout)}) {
|
||||||
hasChanges = true
|
hasChanges = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -242,33 +263,61 @@ func (this *DDoSProtectionManager) addTCPRules(tcpConfig *ddosconfigs.TCPConfig)
|
|||||||
// 添加新规则
|
// 添加新规则
|
||||||
for _, port := range ports {
|
for _, port := range ports {
|
||||||
if maxConnections > 0 {
|
if maxConnections > 0 {
|
||||||
var cmd = exec.Command(this.nftPath, "add", "rule", protocol, filter.Name, nftablesChainName, "tcp", "dport", types.String(port), "ct", "count", "over", types.String(maxConnections), "counter", "drop", "comment", this.encodeUserData([]string{"tcp", types.String(port), "maxConnections", types.String(maxConnections)}))
|
var cmd = executils.NewTimeoutCmd(10*time.Second, nftExe, "add", "rule", protocol, filter.Name, nftablesChainName, "tcp", "dport", types.String(port), "ct", "count", "over", types.String(maxConnections), "counter", "drop", "comment", this.encodeUserData([]string{"tcp", types.String(port), "maxConnections", types.String(maxConnections)}))
|
||||||
var stderr = &bytes.Buffer{}
|
cmd.WithStderr()
|
||||||
cmd.Stderr = stderr
|
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("add nftables rule '" + cmd.String() + "' failed: " + err.Error() + " (" + stderr.String() + ")")
|
return errors.New("add nftables rule '" + cmd.String() + "' failed: " + err.Error() + " (" + cmd.Stderr() + ")")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO 让用户选择是drop还是reject
|
||||||
if maxConnectionsPerIP > 0 {
|
if maxConnectionsPerIP > 0 {
|
||||||
var cmd = exec.Command(this.nftPath, "add", "rule", protocol, filter.Name, nftablesChainName, "tcp", "dport", types.String(port), "meter", "meter-"+protocol+"-"+types.String(port)+"-max-connections", "{ "+protocol+" saddr ct count over "+types.String(maxConnectionsPerIP)+" }", "counter", "drop", "comment", this.encodeUserData([]string{"tcp", types.String(port), "maxConnectionsPerIP", types.String(maxConnectionsPerIP)}))
|
var cmd = executils.NewTimeoutCmd(10*time.Second, nftExe, "add", "rule", protocol, filter.Name, nftablesChainName, "tcp", "dport", types.String(port), "meter", "meter-"+protocol+"-"+types.String(port)+"-max-connections", "{ "+protocol+" saddr ct count over "+types.String(maxConnectionsPerIP)+" }", "counter", "drop", "comment", this.encodeUserData([]string{"tcp", types.String(port), "maxConnectionsPerIP", types.String(maxConnectionsPerIP)}))
|
||||||
var stderr = &bytes.Buffer{}
|
cmd.WithStderr()
|
||||||
cmd.Stderr = stderr
|
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("add nftables rule '" + cmd.String() + "' failed: " + err.Error() + " (" + stderr.String() + ")")
|
return errors.New("add nftables rule '" + cmd.String() + "' failed: " + err.Error() + " (" + cmd.Stderr() + ")")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if newConnectionsRate > 0 {
|
// 超过一定速率就drop或者加入黑名单(分钟)
|
||||||
// TODO 思考是否有惩罚机制
|
// TODO 让用户选择是drop还是reject
|
||||||
var cmd = exec.Command(this.nftPath, "add", "rule", protocol, filter.Name, nftablesChainName, "tcp", "dport", types.String(port), "ct", "state", "new", "meter", "meter-"+protocol+"-"+types.String(port)+"-new-connections-rate", "{ "+protocol+" saddr limit rate over "+types.String(newConnectionsRate)+"/minute burst "+types.String(newConnectionsRate+3)+" packets }" /**"add", "@deny_set", "{"+protocol+" saddr}",**/, "counter", "drop", "comment", this.encodeUserData([]string{"tcp", types.String(port), "newConnectionsRate", types.String(newConnectionsRate)}))
|
if newConnectionsMinutelyRate > 0 {
|
||||||
var stderr = &bytes.Buffer{}
|
if newConnectionsMinutelyRateBlockTimeout > 0 {
|
||||||
cmd.Stderr = stderr
|
var cmd = executils.NewTimeoutCmd(10*time.Second, nftExe, "add", "rule", protocol, filter.Name, nftablesChainName, "tcp", "dport", types.String(port), "ct", "state", "new", "meter", "meter-"+protocol+"-"+types.String(port)+"-new-connections-rate", "{ "+protocol+" saddr limit rate over "+types.String(newConnectionsMinutelyRate)+"/minute burst "+types.String(newConnectionsMinutelyRate+3)+" packets }", "add", "@deny_set", "{"+protocol+" saddr timeout "+types.String(newConnectionsMinutelyRateBlockTimeout)+"s}", "comment", this.encodeUserData([]string{"tcp", types.String(port), "newConnectionsRate", types.String(newConnectionsMinutelyRate), types.String(newConnectionsMinutelyRateBlockTimeout)}))
|
||||||
err := cmd.Run()
|
cmd.WithStderr()
|
||||||
if err != nil {
|
err := cmd.Run()
|
||||||
return errors.New("add nftables rule '" + cmd.String() + "' failed: " + err.Error() + " (" + stderr.String() + ")")
|
if err != nil {
|
||||||
|
return errors.New("add nftables rule '" + cmd.String() + "' failed: " + err.Error() + " (" + cmd.Stderr() + ")")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var cmd = executils.NewTimeoutCmd(10*time.Second, nftExe, "add", "rule", protocol, filter.Name, nftablesChainName, "tcp", "dport", types.String(port), "ct", "state", "new", "meter", "meter-"+protocol+"-"+types.String(port)+"-new-connections-rate", "{ "+protocol+" saddr limit rate over "+types.String(newConnectionsMinutelyRate)+"/minute burst "+types.String(newConnectionsMinutelyRate+3)+" packets }" /**"add", "@deny_set", "{"+protocol+" saddr}",**/, "counter", "drop", "comment", this.encodeUserData([]string{"tcp", types.String(port), "newConnectionsRate", "0"}))
|
||||||
|
cmd.WithStderr()
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("add nftables rule '" + cmd.String() + "' failed: " + err.Error() + " (" + cmd.Stderr() + ")")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 超过一定速率就drop或者加入黑名单(秒)
|
||||||
|
// TODO 让用户选择是drop还是reject
|
||||||
|
if newConnectionsSecondlyRate > 0 {
|
||||||
|
if newConnectionsSecondlyRateBlockTimeout > 0 {
|
||||||
|
var cmd = executils.NewTimeoutCmd(10*time.Second, nftExe, "add", "rule", protocol, filter.Name, nftablesChainName, "tcp", "dport", types.String(port), "ct", "state", "new", "meter", "meter-"+protocol+"-"+types.String(port)+"-new-connections-secondly-rate", "{ "+protocol+" saddr limit rate over "+types.String(newConnectionsSecondlyRate)+"/second burst "+types.String(newConnectionsSecondlyRate+3)+" packets }", "add", "@deny_set", "{"+protocol+" saddr timeout "+types.String(newConnectionsSecondlyRateBlockTimeout)+"s}", "comment", this.encodeUserData([]string{"tcp", types.String(port), "newConnectionsSecondlyRate", types.String(newConnectionsSecondlyRate), types.String(newConnectionsSecondlyRateBlockTimeout)}))
|
||||||
|
cmd.WithStderr()
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("add nftables rule '" + cmd.String() + "' failed: " + err.Error() + " (" + cmd.Stderr() + ")")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var cmd = executils.NewTimeoutCmd(10*time.Second, nftExe, "add", "rule", protocol, filter.Name, nftablesChainName, "tcp", "dport", types.String(port), "ct", "state", "new", "meter", "meter-"+protocol+"-"+types.String(port)+"-new-connections-secondly-rate", "{ "+protocol+" saddr limit rate over "+types.String(newConnectionsSecondlyRate)+"/second burst "+types.String(newConnectionsSecondlyRate+3)+" packets }" /**"add", "@deny_set", "{"+protocol+" saddr}",**/, "counter", "drop", "comment", this.encodeUserData([]string{"tcp", types.String(port), "newConnectionsSecondlyRate", "0"}))
|
||||||
|
cmd.WithStderr()
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("add nftables rule '" + cmd.String() + "' failed: " + err.Error() + " (" + cmd.Stderr() + ")")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -335,14 +384,14 @@ func (this *DDoSProtectionManager) decodeUserData(data []byte) []string {
|
|||||||
func (this *DDoSProtectionManager) removeOldTCPRules(chain *nftables.Chain, rules []*nftables.Rule) error {
|
func (this *DDoSProtectionManager) removeOldTCPRules(chain *nftables.Chain, rules []*nftables.Rule) error {
|
||||||
for _, rule := range rules {
|
for _, rule := range rules {
|
||||||
var pieces = this.decodeUserData(rule.UserData())
|
var pieces = this.decodeUserData(rule.UserData())
|
||||||
if len(pieces) != 4 {
|
if len(pieces) < 4 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if pieces[0] != "tcp" {
|
if pieces[0] != "tcp" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
switch pieces[2] {
|
switch pieces[2] {
|
||||||
case "maxConnections", "maxConnectionsPerIP", "newConnectionsRate":
|
case "maxConnections", "maxConnectionsPerIP", "newConnectionsRate", "newConnectionsSecondlyRate":
|
||||||
err := chain.DeleteRule(rule)
|
err := chain.DeleteRule(rule)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -500,3 +549,8 @@ func (this *DDoSProtectionManager) updateAllowIPList(allIPList []string) error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *DDoSProtectionManager) nftExe() string {
|
||||||
|
path, _ := exec.LookPath("nft")
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import (
|
|||||||
var SharedDDoSProtectionManager = NewDDoSProtectionManager()
|
var SharedDDoSProtectionManager = NewDDoSProtectionManager()
|
||||||
|
|
||||||
type DDoSProtectionManager struct {
|
type DDoSProtectionManager struct {
|
||||||
nftPath string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDDoSProtectionManager() *DDoSProtectionManager {
|
func NewDDoSProtectionManager() *DDoSProtectionManager {
|
||||||
|
|||||||
47
internal/firewalls/firewall_base.go
Normal file
47
internal/firewalls/firewall_base.go
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||||
|
|
||||||
|
package firewalls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/iwind/TeaGo/types"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BaseFirewall struct {
|
||||||
|
locker sync.Mutex
|
||||||
|
latestIPTimes []string // [ip@time, ....]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否在最近添加过
|
||||||
|
func (this *BaseFirewall) checkLatestIP(ip string) bool {
|
||||||
|
this.locker.Lock()
|
||||||
|
defer this.locker.Unlock()
|
||||||
|
|
||||||
|
var expiredIndex = -1
|
||||||
|
for index, ipTime := range this.latestIPTimes {
|
||||||
|
var pieces = strings.Split(ipTime, "@")
|
||||||
|
var oldIP = pieces[0]
|
||||||
|
var oldTimestamp = pieces[1]
|
||||||
|
if types.Int64(oldTimestamp) < time.Now().Unix()-3 /** 3秒外表示过期 **/ {
|
||||||
|
expiredIndex = index
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if oldIP == ip {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if expiredIndex > -1 {
|
||||||
|
this.latestIPTimes = this.latestIPTimes[expiredIndex+1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
this.latestIPTimes = append(this.latestIPTimes, ip+"@"+types.String(time.Now().Unix()))
|
||||||
|
const maxLen = 128
|
||||||
|
if len(this.latestIPTimes) > maxLen {
|
||||||
|
this.latestIPTimes = this.latestIPTimes[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
@@ -4,27 +4,37 @@ package firewalls
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/conns"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
|
executils "github.com/TeaOSLab/EdgeNode/internal/utils/exec"
|
||||||
"github.com/iwind/TeaGo/types"
|
"github.com/iwind/TeaGo/types"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type firewalldCmd struct {
|
||||||
|
cmd *executils.Cmd
|
||||||
|
denyIP string
|
||||||
|
}
|
||||||
|
|
||||||
type Firewalld struct {
|
type Firewalld struct {
|
||||||
|
BaseFirewall
|
||||||
|
|
||||||
isReady bool
|
isReady bool
|
||||||
exe string
|
exe string
|
||||||
cmdQueue chan *exec.Cmd
|
cmdQueue chan *firewalldCmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFirewalld() *Firewalld {
|
func NewFirewalld() *Firewalld {
|
||||||
var firewalld = &Firewalld{
|
var firewalld = &Firewalld{
|
||||||
cmdQueue: make(chan *exec.Cmd, 4096),
|
cmdQueue: make(chan *firewalldCmd, 4096),
|
||||||
}
|
}
|
||||||
|
|
||||||
path, err := exec.LookPath("firewall-cmd")
|
path, err := exec.LookPath("firewall-cmd")
|
||||||
if err == nil && len(path) > 0 {
|
if err == nil && len(path) > 0 {
|
||||||
var cmd = exec.Command(path, "--state")
|
var cmd = executils.NewTimeoutCmd(3*time.Second, path, "--state")
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
firewalld.exe = path
|
firewalld.exe = path
|
||||||
@@ -41,13 +51,19 @@ func NewFirewalld() *Firewalld {
|
|||||||
|
|
||||||
func (this *Firewalld) init() {
|
func (this *Firewalld) init() {
|
||||||
goman.New(func() {
|
goman.New(func() {
|
||||||
for cmd := range this.cmdQueue {
|
for c := range this.cmdQueue {
|
||||||
|
var cmd = c.cmd
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if strings.HasPrefix(err.Error(), "Warning:") {
|
if strings.HasPrefix(err.Error(), "Warning:") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
remotelogs.Warn("FIREWALL", "run command failed '"+cmd.String()+"': "+err.Error())
|
remotelogs.Warn("FIREWALL", "run command failed '"+cmd.String()+"': "+err.Error())
|
||||||
|
} else {
|
||||||
|
// 关闭连接
|
||||||
|
if len(c.denyIP) > 0 {
|
||||||
|
conns.SharedMap.CloseIPConns(c.denyIP)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -71,8 +87,8 @@ func (this *Firewalld) AllowPort(port int, protocol string) error {
|
|||||||
if !this.isReady {
|
if !this.isReady {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
var cmd = exec.Command(this.exe, "--add-port="+types.String(port)+"/"+protocol)
|
var cmd = executils.NewTimeoutCmd(10*time.Second, this.exe, "--add-port="+types.String(port)+"/"+protocol)
|
||||||
this.pushCmd(cmd)
|
this.pushCmd(cmd, "")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,13 +97,13 @@ func (this *Firewalld) AllowPortRangesPermanently(portRanges [][2]int, protocol
|
|||||||
var port = this.PortRangeString(portRange, protocol)
|
var port = this.PortRangeString(portRange, protocol)
|
||||||
|
|
||||||
{
|
{
|
||||||
var cmd = exec.Command(this.exe, "--add-port="+port, "--permanent")
|
var cmd = executils.NewTimeoutCmd(10*time.Second, this.exe, "--add-port="+port, "--permanent")
|
||||||
this.pushCmd(cmd)
|
this.pushCmd(cmd, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var cmd = exec.Command(this.exe, "--add-port="+port)
|
var cmd = executils.NewTimeoutCmd(10*time.Second, this.exe, "--add-port="+port)
|
||||||
this.pushCmd(cmd)
|
this.pushCmd(cmd, "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,8 +114,8 @@ func (this *Firewalld) RemovePort(port int, protocol string) error {
|
|||||||
if !this.isReady {
|
if !this.isReady {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
var cmd = exec.Command(this.exe, "--remove-port="+types.String(port)+"/"+protocol)
|
var cmd = executils.NewTimeoutCmd(10*time.Second, this.exe, "--remove-port="+types.String(port)+"/"+protocol)
|
||||||
this.pushCmd(cmd)
|
this.pushCmd(cmd, "")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,13 +123,13 @@ func (this *Firewalld) RemovePortRangePermanently(portRange [2]int, protocol str
|
|||||||
var port = this.PortRangeString(portRange, protocol)
|
var port = this.PortRangeString(portRange, protocol)
|
||||||
|
|
||||||
{
|
{
|
||||||
var cmd = exec.Command(this.exe, "--remove-port="+port, "--permanent")
|
var cmd = executils.NewTimeoutCmd(10*time.Second, this.exe, "--remove-port="+port, "--permanent")
|
||||||
this.pushCmd(cmd)
|
this.pushCmd(cmd, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var cmd = exec.Command(this.exe, "--remove-port="+port)
|
var cmd = executils.NewTimeoutCmd(10*time.Second, this.exe, "--remove-port="+port)
|
||||||
this.pushCmd(cmd)
|
this.pushCmd(cmd, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -131,6 +147,12 @@ func (this *Firewalld) RejectSourceIP(ip string, timeoutSeconds int) error {
|
|||||||
if !this.isReady {
|
if !this.isReady {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 避免短时间内重复添加
|
||||||
|
if this.checkLatestIP(ip) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
var family = "ipv4"
|
var family = "ipv4"
|
||||||
if strings.Contains(ip, ":") {
|
if strings.Contains(ip, ":") {
|
||||||
family = "ipv6"
|
family = "ipv6"
|
||||||
@@ -139,8 +161,8 @@ func (this *Firewalld) RejectSourceIP(ip string, timeoutSeconds int) error {
|
|||||||
if timeoutSeconds > 0 {
|
if timeoutSeconds > 0 {
|
||||||
args = append(args, "--timeout="+types.String(timeoutSeconds)+"s")
|
args = append(args, "--timeout="+types.String(timeoutSeconds)+"s")
|
||||||
}
|
}
|
||||||
var cmd = exec.Command(this.exe, args...)
|
var cmd = executils.NewTimeoutCmd(10*time.Second, this.exe, args...)
|
||||||
this.pushCmd(cmd)
|
this.pushCmd(cmd, ip)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,6 +170,12 @@ func (this *Firewalld) DropSourceIP(ip string, timeoutSeconds int, async bool) e
|
|||||||
if !this.isReady {
|
if !this.isReady {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 避免短时间内重复添加
|
||||||
|
if async && this.checkLatestIP(ip) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
var family = "ipv4"
|
var family = "ipv4"
|
||||||
if strings.Contains(ip, ":") {
|
if strings.Contains(ip, ":") {
|
||||||
family = "ipv6"
|
family = "ipv6"
|
||||||
@@ -156,12 +184,15 @@ func (this *Firewalld) DropSourceIP(ip string, timeoutSeconds int, async bool) e
|
|||||||
if timeoutSeconds > 0 {
|
if timeoutSeconds > 0 {
|
||||||
args = append(args, "--timeout="+types.String(timeoutSeconds)+"s")
|
args = append(args, "--timeout="+types.String(timeoutSeconds)+"s")
|
||||||
}
|
}
|
||||||
var cmd = exec.Command(this.exe, args...)
|
var cmd = executils.NewTimeoutCmd(10*time.Second, this.exe, args...)
|
||||||
if async {
|
if async {
|
||||||
this.pushCmd(cmd)
|
this.pushCmd(cmd, ip)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 关闭连接
|
||||||
|
defer conns.SharedMap.CloseIPConns(ip)
|
||||||
|
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("run command failed '" + cmd.String() + "': " + err.Error())
|
return errors.New("run command failed '" + cmd.String() + "': " + err.Error())
|
||||||
@@ -173,21 +204,22 @@ func (this *Firewalld) RemoveSourceIP(ip string) error {
|
|||||||
if !this.isReady {
|
if !this.isReady {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var family = "ipv4"
|
var family = "ipv4"
|
||||||
if strings.Contains(ip, ":") {
|
if strings.Contains(ip, ":") {
|
||||||
family = "ipv6"
|
family = "ipv6"
|
||||||
}
|
}
|
||||||
for _, action := range []string{"reject", "drop"} {
|
for _, action := range []string{"reject", "drop"} {
|
||||||
var args = []string{"--remove-rich-rule=rule family='" + family + "' source address='" + ip + "' " + action}
|
var args = []string{"--remove-rich-rule=rule family='" + family + "' source address='" + ip + "' " + action}
|
||||||
var cmd = exec.Command(this.exe, args...)
|
var cmd = executils.NewTimeoutCmd(10*time.Second, this.exe, args...)
|
||||||
this.pushCmd(cmd)
|
this.pushCmd(cmd, "")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *Firewalld) pushCmd(cmd *exec.Cmd) {
|
func (this *Firewalld) pushCmd(cmd *executils.Cmd, denyIP string) {
|
||||||
select {
|
select {
|
||||||
case this.cmdQueue <- cmd:
|
case this.cmdQueue <- &firewalldCmd{cmd: cmd, denyIP: denyIP}:
|
||||||
default:
|
default:
|
||||||
// we discard the command
|
// we discard the command
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,13 +5,14 @@
|
|||||||
package firewalls
|
package firewalls
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"errors"
|
"errors"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/conns"
|
||||||
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/events"
|
"github.com/TeaOSLab/EdgeNode/internal/events"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/firewalls/nftables"
|
"github.com/TeaOSLab/EdgeNode/internal/firewalls/nftables"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
|
executils "github.com/TeaOSLab/EdgeNode/internal/utils/exec"
|
||||||
"github.com/iwind/TeaGo/types"
|
"github.com/iwind/TeaGo/types"
|
||||||
"net"
|
"net"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
@@ -100,6 +101,8 @@ func NewNFTablesFirewall() (*NFTablesFirewall, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type NFTablesFirewall struct {
|
type NFTablesFirewall struct {
|
||||||
|
BaseFirewall
|
||||||
|
|
||||||
conn *nftables.Conn
|
conn *nftables.Conn
|
||||||
isReady bool
|
isReady bool
|
||||||
version string
|
version string
|
||||||
@@ -344,6 +347,14 @@ func (this *NFTablesFirewall) DropSourceIP(ip string, timeoutSeconds int, async
|
|||||||
return errors.New("invalid ip '" + ip + "'")
|
return errors.New("invalid ip '" + ip + "'")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 尝试关闭连接
|
||||||
|
conns.SharedMap.CloseIPConns(ip)
|
||||||
|
|
||||||
|
// 避免短时间内重复添加
|
||||||
|
if async && this.checkLatestIP(ip) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
if async {
|
if async {
|
||||||
select {
|
select {
|
||||||
case this.dropIPQueue <- &blockIPItem{
|
case this.dropIPQueue <- &blockIPItem{
|
||||||
@@ -357,6 +368,9 @@ func (this *NFTablesFirewall) DropSourceIP(ip string, timeoutSeconds int, async
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 再次尝试关闭连接
|
||||||
|
defer conns.SharedMap.CloseIPConns(ip)
|
||||||
|
|
||||||
if strings.Contains(ip, ":") { // ipv6
|
if strings.Contains(ip, ":") { // ipv6
|
||||||
if this.denyIPv6Set == nil {
|
if this.denyIPv6Set == nil {
|
||||||
return errors.New("ipv6 ip set is nil")
|
return errors.New("ipv6 ip set is nil")
|
||||||
@@ -418,18 +432,49 @@ func (this *NFTablesFirewall) RemoveSourceIP(ip string) error {
|
|||||||
|
|
||||||
// 读取版本号
|
// 读取版本号
|
||||||
func (this *NFTablesFirewall) readVersion(nftPath string) string {
|
func (this *NFTablesFirewall) readVersion(nftPath string) string {
|
||||||
var cmd = exec.Command(nftPath, "--version")
|
var cmd = executils.NewTimeoutCmd(10*time.Second, nftPath, "--version")
|
||||||
var output = &bytes.Buffer{}
|
cmd.WithStdout()
|
||||||
cmd.Stdout = output
|
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
var outputString = output.String()
|
var outputString = cmd.Stdout()
|
||||||
var versionMatches = regexp.MustCompile(`nftables v([\d.]+)`).FindStringSubmatch(outputString)
|
var versionMatches = regexp.MustCompile(`nftables v([\d.]+)`).FindStringSubmatch(outputString)
|
||||||
if len(versionMatches) <= 1 {
|
if len(versionMatches) <= 1 {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
return versionMatches[1]
|
return versionMatches[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 检查是否在最近添加过
|
||||||
|
func (this *NFTablesFirewall) existLatestIP(ip string) bool {
|
||||||
|
this.locker.Lock()
|
||||||
|
defer this.locker.Unlock()
|
||||||
|
|
||||||
|
var expiredIndex = -1
|
||||||
|
for index, ipTime := range this.latestIPTimes {
|
||||||
|
var pieces = strings.Split(ipTime, "@")
|
||||||
|
var oldIP = pieces[0]
|
||||||
|
var oldTimestamp = pieces[1]
|
||||||
|
if types.Int64(oldTimestamp) < time.Now().Unix()-3 /** 3秒外表示过期 **/ {
|
||||||
|
expiredIndex = index
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if oldIP == ip {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if expiredIndex > -1 {
|
||||||
|
this.latestIPTimes = this.latestIPTimes[expiredIndex+1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
this.latestIPTimes = append(this.latestIPTimes, ip+"@"+types.String(time.Now().Unix()))
|
||||||
|
const maxLen = 128
|
||||||
|
if len(this.latestIPTimes) > maxLen {
|
||||||
|
this.latestIPTimes = this.latestIPTimes[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||||
//go:build linux
|
//go:build linux
|
||||||
// +build linux
|
|
||||||
|
|
||||||
package nftables
|
package nftables
|
||||||
|
|
||||||
|
|||||||
108
internal/firewalls/nftables/installer.go
Normal file
108
internal/firewalls/nftables/installer.go
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||||
|
|
||||||
|
package nftables
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/events"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
|
executils "github.com/TeaOSLab/EdgeNode/internal/utils/exec"
|
||||||
|
"github.com/iwind/TeaGo/logs"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"runtime"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
events.On(events.EventReload, func() {
|
||||||
|
// linux only
|
||||||
|
if runtime.GOOS != "linux" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeConfig, err := nodeconfigs.SharedNodeConfig()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if nodeConfig == nil || !nodeConfig.AutoInstallNftables {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if os.Getgid() == 0 { // root user only
|
||||||
|
_, err := exec.LookPath("nft")
|
||||||
|
if err == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
goman.New(func() {
|
||||||
|
err := NewInstaller().Install()
|
||||||
|
if err != nil {
|
||||||
|
// 不需要传到API节点
|
||||||
|
logs.Println("[NFTABLES]install nftables failed: " + err.Error())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type Installer struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewInstaller() *Installer {
|
||||||
|
return &Installer{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Installer) Install() error {
|
||||||
|
// linux only
|
||||||
|
if runtime.GOOS != "linux" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否已经存在
|
||||||
|
_, err := exec.LookPath("nft")
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var cmd *executils.Cmd
|
||||||
|
|
||||||
|
// check dnf
|
||||||
|
dnfExe, err := exec.LookPath("dnf")
|
||||||
|
if err == nil {
|
||||||
|
cmd = executils.NewCmd(dnfExe, "-y", "install", "nftables")
|
||||||
|
}
|
||||||
|
|
||||||
|
// check apt
|
||||||
|
if cmd == nil {
|
||||||
|
aptExe, err := exec.LookPath("apt")
|
||||||
|
if err == nil {
|
||||||
|
cmd = executils.NewCmd(aptExe, "install", "nftables")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check yum
|
||||||
|
if cmd == nil {
|
||||||
|
yumExe, err := exec.LookPath("yum")
|
||||||
|
if err == nil {
|
||||||
|
cmd = executils.NewCmd(yumExe, "-y", "install", "nftables")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.WithTimeout(10 * time.Minute)
|
||||||
|
cmd.WithStderr()
|
||||||
|
err = cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
return errors.New(err.Error() + ": " + cmd.Stderr())
|
||||||
|
}
|
||||||
|
|
||||||
|
remotelogs.Println("NFTABLES", "installed nftables with command '"+cmd.String()+"' successfully")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||||
|
//go:build linux
|
||||||
|
|
||||||
package nftables
|
package nftables
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||||
|
//go:build linux
|
||||||
|
|
||||||
package nftables_test
|
package nftables_test
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
package iplibrary
|
package iplibrary
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
||||||
|
executils "github.com/TeaOSLab/EdgeNode/internal/utils/exec"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"runtime"
|
"runtime"
|
||||||
"time"
|
"time"
|
||||||
@@ -13,9 +13,9 @@ import (
|
|||||||
|
|
||||||
// FirewalldAction Firewalld动作管理
|
// FirewalldAction Firewalld动作管理
|
||||||
// 常用命令:
|
// 常用命令:
|
||||||
// - 查询列表: firewall-cmd --list-all
|
// - 查询列表: firewall-cmd --list-all
|
||||||
// - 添加IP:firewall-cmd --add-rich-rule="rule family='ipv4' source address='192.168.2.32' reject" --timeout=30s
|
// - 添加IP:firewall-cmd --add-rich-rule="rule family='ipv4' source address='192.168.2.32' reject" --timeout=30s
|
||||||
// - 删除IP:firewall-cmd --remove-rich-rule="rule family='ipv4' source address='192.168.2.32' reject" --timeout=30s
|
// - 删除IP:firewall-cmd --remove-rich-rule="rule family='ipv4' source address='192.168.2.32' reject" --timeout=30s
|
||||||
type FirewalldAction struct {
|
type FirewalldAction struct {
|
||||||
BaseAction
|
BaseAction
|
||||||
|
|
||||||
@@ -144,12 +144,11 @@ func (this *FirewalldAction) runActionSingleIP(action string, listType IPListTyp
|
|||||||
// MAC OS直接返回
|
// MAC OS直接返回
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
cmd := exec.Command(path, args...)
|
cmd := executils.NewTimeoutCmd(30*time.Second, path, args...)
|
||||||
stderr := bytes.NewBuffer([]byte{})
|
cmd.WithStderr()
|
||||||
cmd.Stderr = stderr
|
|
||||||
err = cmd.Run()
|
err = cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New(err.Error() + ", output: " + string(stderr.Bytes()))
|
return errors.New(err.Error() + ", output: " + cmd.Stderr())
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
package iplibrary
|
package iplibrary
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"errors"
|
"errors"
|
||||||
"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"
|
||||||
"github.com/iwind/TeaGo/types"
|
"github.com/iwind/TeaGo/types"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"runtime"
|
"runtime"
|
||||||
@@ -16,12 +16,12 @@ import (
|
|||||||
// IPSetAction IPSet动作
|
// IPSetAction IPSet动作
|
||||||
// 相关命令:
|
// 相关命令:
|
||||||
// - 利用Firewalld管理set:
|
// - 利用Firewalld管理set:
|
||||||
// - 添加:firewall-cmd --permanent --new-ipset=edge_ip_list --type=hash:ip --option="timeout=0"
|
// - 添加:firewall-cmd --permanent --new-ipset=edge_ip_list --type=hash:ip --option="timeout=0"
|
||||||
// - 删除:firewall-cmd --permanent --delete-ipset=edge_ip_list
|
// - 删除:firewall-cmd --permanent --delete-ipset=edge_ip_list
|
||||||
// - 重载:firewall-cmd --reload
|
// - 重载:firewall-cmd --reload
|
||||||
// - firewalld+ipset: firewall-cmd --permanent --add-rich-rule="rule source ipset='edge_ip_list' reject"
|
// - firewalld+ipset: firewall-cmd --permanent --add-rich-rule="rule source ipset='edge_ip_list' reject"
|
||||||
// - 利用IPTables管理set:
|
// - 利用IPTables管理set:
|
||||||
// - 添加:iptables -A INPUT -m set --match-set edge_ip_list src -j REJECT
|
// - 添加:iptables -A INPUT -m set --match-set edge_ip_list src -j REJECT
|
||||||
// - 添加Item:ipset add edge_ip_list 192.168.2.32 timeout 30
|
// - 添加Item:ipset add edge_ip_list 192.168.2.32 timeout 30
|
||||||
// - 删除Item: ipset del edge_ip_list 192.168.2.32
|
// - 删除Item: ipset del edge_ip_list 192.168.2.32
|
||||||
// - 创建set:ipset create edge_ip_list hash:ip timeout 0
|
// - 创建set:ipset create edge_ip_list hash:ip timeout 0
|
||||||
@@ -30,16 +30,13 @@ import (
|
|||||||
type IPSetAction struct {
|
type IPSetAction struct {
|
||||||
BaseAction
|
BaseAction
|
||||||
|
|
||||||
config *firewallconfigs.FirewallActionIPSetConfig
|
config *firewallconfigs.FirewallActionIPSetConfig
|
||||||
errorBuf *bytes.Buffer
|
|
||||||
|
|
||||||
ipsetNotfound bool
|
ipsetNotfound bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewIPSetAction() *IPSetAction {
|
func NewIPSetAction() *IPSetAction {
|
||||||
return &IPSetAction{
|
return &IPSetAction{}
|
||||||
errorBuf: &bytes.Buffer{},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *IPSetAction) Init(config *firewallconfigs.FirewallActionConfig) error {
|
func (this *IPSetAction) Init(config *firewallconfigs.FirewallActionConfig) error {
|
||||||
@@ -68,14 +65,13 @@ func (this *IPSetAction) Init(config *firewallconfigs.FirewallActionConfig) erro
|
|||||||
if len(listName) == 0 {
|
if len(listName) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
var cmd = exec.Command(path, "create", listName, "hash:ip", "timeout", "0", "maxelem", "1000000")
|
var cmd = executils.NewTimeoutCmd(30*time.Second, path, "create", listName, "hash:ip", "timeout", "0", "maxelem", "1000000")
|
||||||
var stderr = bytes.NewBuffer([]byte{})
|
cmd.WithStderr()
|
||||||
cmd.Stderr = stderr
|
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var output = stderr.Bytes()
|
var output = cmd.Stderr()
|
||||||
if !bytes.Contains(output, []byte("already exists")) {
|
if !strings.Contains(output, "already exists") {
|
||||||
return errors.New("create ipset '" + listName + "': " + err.Error() + ", output: " + string(output))
|
return errors.New("create ipset '" + listName + "': " + err.Error() + ", output: " + output)
|
||||||
} else {
|
} else {
|
||||||
err = nil
|
err = nil
|
||||||
}
|
}
|
||||||
@@ -87,14 +83,13 @@ func (this *IPSetAction) Init(config *firewallconfigs.FirewallActionConfig) erro
|
|||||||
if len(listName) == 0 {
|
if len(listName) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
var cmd = exec.Command(path, "create", listName, "hash:ip", "family", "inet6", "timeout", "0", "maxelem", "1000000")
|
var cmd = executils.NewTimeoutCmd(30*time.Second, path, "create", listName, "hash:ip", "family", "inet6", "timeout", "0", "maxelem", "1000000")
|
||||||
var stderr = bytes.NewBuffer([]byte{})
|
cmd.WithStderr()
|
||||||
cmd.Stderr = stderr
|
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var output = stderr.Bytes()
|
var output = cmd.Stderr()
|
||||||
if !bytes.Contains(output, []byte("already exists")) {
|
if !strings.Contains(output, "already exists") {
|
||||||
return errors.New("create ipset '" + listName + "': " + err.Error() + ", output: " + string(output))
|
return errors.New("create ipset '" + listName + "': " + err.Error() + ", output: " + output)
|
||||||
} else {
|
} else {
|
||||||
err = nil
|
err = nil
|
||||||
}
|
}
|
||||||
@@ -114,16 +109,15 @@ func (this *IPSetAction) Init(config *firewallconfigs.FirewallActionConfig) erro
|
|||||||
if len(listName) == 0 {
|
if len(listName) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
cmd := exec.Command(path, "--permanent", "--new-ipset="+listName, "--type=hash:ip", "--option=timeout=0", "--option=maxelem=1000000")
|
var cmd = executils.NewTimeoutCmd(30*time.Second, path, "--permanent", "--new-ipset="+listName, "--type=hash:ip", "--option=timeout=0", "--option=maxelem=1000000")
|
||||||
stderr := bytes.NewBuffer([]byte{})
|
cmd.WithStderr()
|
||||||
cmd.Stderr = stderr
|
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
output := stderr.Bytes()
|
var output = cmd.Stderr()
|
||||||
if bytes.Contains(output, []byte("NAME_CONFLICT")) {
|
if strings.Contains(output, "NAME_CONFLICT") {
|
||||||
err = nil
|
err = nil
|
||||||
} else {
|
} else {
|
||||||
return errors.New("firewall-cmd add ipset '" + listName + "': " + err.Error() + ", output: " + string(output))
|
return errors.New("firewall-cmd add ipset '" + listName + "': " + err.Error() + ", output: " + output)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -133,16 +127,15 @@ func (this *IPSetAction) Init(config *firewallconfigs.FirewallActionConfig) erro
|
|||||||
if len(listName) == 0 {
|
if len(listName) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
cmd := exec.Command(path, "--permanent", "--new-ipset="+listName, "--type=hash:ip", "--option=family=inet6", "--option=timeout=0", "--option=maxelem=1000000")
|
var cmd = executils.NewTimeoutCmd(30*time.Second, path, "--permanent", "--new-ipset="+listName, "--type=hash:ip", "--option=family=inet6", "--option=timeout=0", "--option=maxelem=1000000")
|
||||||
stderr := bytes.NewBuffer([]byte{})
|
cmd.WithStderr()
|
||||||
cmd.Stderr = stderr
|
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var output = stderr.Bytes()
|
var output = cmd.Stderr()
|
||||||
if bytes.Contains(output, []byte("NAME_CONFLICT")) {
|
if strings.Contains(output, "NAME_CONFLICT") {
|
||||||
err = nil
|
err = nil
|
||||||
} else {
|
} else {
|
||||||
return errors.New("firewall-cmd add ipset '" + listName + "': " + err.Error() + ", output: " + string(output))
|
return errors.New("firewall-cmd add ipset '" + listName + "': " + err.Error() + ", output: " + output)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -152,13 +145,11 @@ func (this *IPSetAction) Init(config *firewallconfigs.FirewallActionConfig) erro
|
|||||||
if len(listName) == 0 {
|
if len(listName) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
var cmd = exec.Command(path, "--permanent", "--add-rich-rule=rule source ipset='"+listName+"' accept")
|
var cmd = executils.NewTimeoutCmd(30*time.Second, path, "--permanent", "--add-rich-rule=rule source ipset='"+listName+"' accept")
|
||||||
var stderr = bytes.NewBuffer([]byte{})
|
cmd.WithStderr()
|
||||||
cmd.Stderr = stderr
|
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var output = stderr.Bytes()
|
return errors.New("firewall-cmd add rich rule '" + listName + "': " + err.Error() + ", output: " + cmd.Stderr())
|
||||||
return errors.New("firewall-cmd add rich rule '" + listName + "': " + err.Error() + ", output: " + string(output))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,25 +158,21 @@ func (this *IPSetAction) Init(config *firewallconfigs.FirewallActionConfig) erro
|
|||||||
if len(listName) == 0 {
|
if len(listName) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
var cmd = exec.Command(path, "--permanent", "--add-rich-rule=rule source ipset='"+listName+"' reject")
|
var cmd = executils.NewTimeoutCmd(30*time.Second, path, "--permanent", "--add-rich-rule=rule source ipset='"+listName+"' reject")
|
||||||
var stderr = bytes.NewBuffer([]byte{})
|
cmd.WithStderr()
|
||||||
cmd.Stderr = stderr
|
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var output = stderr.Bytes()
|
return errors.New("firewall-cmd add rich rule '" + listName + "': " + err.Error() + ", output: " + cmd.Stderr())
|
||||||
return errors.New("firewall-cmd add rich rule '" + listName + "': " + err.Error() + ", output: " + string(output))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// reload
|
// reload
|
||||||
{
|
{
|
||||||
cmd := exec.Command(path, "--reload")
|
var cmd = executils.NewTimeoutCmd(30*time.Second, path, "--reload")
|
||||||
stderr := bytes.NewBuffer([]byte{})
|
cmd.WithStderr()
|
||||||
cmd.Stderr = stderr
|
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var output = stderr.Bytes()
|
return errors.New("firewall-cmd reload: " + err.Error() + ", output: " + cmd.Stderr())
|
||||||
return errors.New("firewall-cmd reload: " + err.Error() + ", output: " + string(output))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -204,19 +191,17 @@ func (this *IPSetAction) Init(config *firewallconfigs.FirewallActionConfig) erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 检查规则是否存在
|
// 检查规则是否存在
|
||||||
var cmd = exec.Command(path, "-C", "INPUT", "-m", "set", "--match-set", listName, "src", "-j", "ACCEPT")
|
var cmd = executils.NewTimeoutCmd(30*time.Second, path, "-C", "INPUT", "-m", "set", "--match-set", listName, "src", "-j", "ACCEPT")
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
var exists = err == nil
|
var exists = err == nil
|
||||||
|
|
||||||
// 添加规则
|
// 添加规则
|
||||||
if !exists {
|
if !exists {
|
||||||
var cmd = exec.Command(path, "-A", "INPUT", "-m", "set", "--match-set", listName, "src", "-j", "ACCEPT")
|
var cmd = executils.NewTimeoutCmd(30*time.Second, path, "-A", "INPUT", "-m", "set", "--match-set", listName, "src", "-j", "ACCEPT")
|
||||||
var stderr = bytes.NewBuffer([]byte{})
|
cmd.WithStderr()
|
||||||
cmd.Stderr = stderr
|
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var output = stderr.Bytes()
|
return errors.New("iptables add rule: " + err.Error() + ", output: " + cmd.Stderr())
|
||||||
return errors.New("iptables add rule: " + err.Error() + ", output: " + string(output))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -228,18 +213,16 @@ func (this *IPSetAction) Init(config *firewallconfigs.FirewallActionConfig) erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 检查规则是否存在
|
// 检查规则是否存在
|
||||||
var cmd = exec.Command(path, "-C", "INPUT", "-m", "set", "--match-set", listName, "src", "-j", "REJECT")
|
var cmd = executils.NewTimeoutCmd(30*time.Second, path, "-C", "INPUT", "-m", "set", "--match-set", listName, "src", "-j", "REJECT")
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
var exists = err == nil
|
var exists = err == nil
|
||||||
|
|
||||||
if !exists {
|
if !exists {
|
||||||
var cmd = exec.Command(path, "-A", "INPUT", "-m", "set", "--match-set", listName, "src", "-j", "REJECT")
|
var cmd = executils.NewTimeoutCmd(30*time.Second, path, "-A", "INPUT", "-m", "set", "--match-set", listName, "src", "-j", "REJECT")
|
||||||
var stderr = bytes.NewBuffer([]byte{})
|
cmd.WithStderr()
|
||||||
cmd.Stderr = stderr
|
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var output = stderr.Bytes()
|
return errors.New("iptables add rule: " + err.Error() + ", output: " + cmd.Stderr())
|
||||||
return errors.New("iptables add rule: " + err.Error() + ", output: " + string(output))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -361,12 +344,11 @@ func (this *IPSetAction) runActionSingleIP(action string, listType IPListType, i
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
this.errorBuf.Reset()
|
var cmd = executils.NewTimeoutCmd(30*time.Second, path, args...)
|
||||||
var cmd = exec.Command(path, args...)
|
cmd.WithStderr()
|
||||||
cmd.Stderr = this.errorBuf
|
|
||||||
err = cmd.Run()
|
err = cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var errString = this.errorBuf.String()
|
var errString = cmd.Stderr()
|
||||||
if action == "deleteItem" && strings.Contains(errString, "not added") {
|
if action == "deleteItem" && strings.Contains(errString, "not added") {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,23 @@
|
|||||||
package iplibrary
|
package iplibrary
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"errors"
|
"errors"
|
||||||
"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"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// IPTablesAction IPTables动作
|
// IPTablesAction IPTables动作
|
||||||
// 相关命令:
|
// 相关命令:
|
||||||
// iptables -A INPUT -s "192.168.2.32" -j ACCEPT
|
//
|
||||||
// iptables -A INPUT -s "192.168.2.32" -j REJECT
|
// iptables -A INPUT -s "192.168.2.32" -j ACCEPT
|
||||||
// iptables -D INPUT ...
|
// iptables -A INPUT -s "192.168.2.32" -j REJECT
|
||||||
// iptables -F INPUT
|
// iptables -D INPUT ...
|
||||||
|
// iptables -F INPUT
|
||||||
type IPTablesAction struct {
|
type IPTablesAction struct {
|
||||||
BaseAction
|
BaseAction
|
||||||
|
|
||||||
@@ -110,16 +113,15 @@ func (this *IPTablesAction) runActionSingleIP(action string, listType IPListType
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := exec.Command(path, args...)
|
var cmd = executils.NewTimeoutCmd(30*time.Second, path, args...)
|
||||||
stderr := bytes.NewBuffer([]byte{})
|
cmd.WithStderr()
|
||||||
cmd.Stderr = stderr
|
|
||||||
err = cmd.Run()
|
err = cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
output := stderr.Bytes()
|
var output = cmd.Stderr()
|
||||||
if bytes.Contains(output, []byte("No chain/target/match")) {
|
if strings.Contains(output, "No chain/target/match") {
|
||||||
err = nil
|
err = nil
|
||||||
} else {
|
} else {
|
||||||
return errors.New(err.Error() + ", output: " + string(output))
|
return errors.New(err.Error() + ", output: " + output)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ func (this *ActionManager) UpdateActions(actions []*firewallconfigs.FirewallActi
|
|||||||
remotelogs.Error("IPLIBRARY/ACTION_MANAGER", "action "+strconv.FormatInt(newAction.Id, 10)+", type:"+newAction.Type+": "+err.Error())
|
remotelogs.Error("IPLIBRARY/ACTION_MANAGER", "action "+strconv.FormatInt(newAction.Id, 10)+", type:"+newAction.Type+": "+err.Error())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if bytes.Compare(newConfigJSON, oldConfigJSON) != 0 {
|
if !bytes.Equal(newConfigJSON, oldConfigJSON) {
|
||||||
_ = oldInstance.Close()
|
_ = oldInstance.Close()
|
||||||
|
|
||||||
// 重新创建
|
// 重新创建
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
package iplibrary
|
package iplibrary
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
||||||
"os/exec"
|
executils "github.com/TeaOSLab/EdgeNode/internal/utils/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ScriptAction 脚本命令动作
|
// ScriptAction 脚本命令动作
|
||||||
@@ -45,25 +45,24 @@ func (this *ScriptAction) DeleteItem(listType IPListType, item *pb.IPItem) error
|
|||||||
|
|
||||||
func (this *ScriptAction) runAction(action string, listType IPListType, item *pb.IPItem) error {
|
func (this *ScriptAction) runAction(action string, listType IPListType, item *pb.IPItem) error {
|
||||||
// TODO 智能支持 .sh 脚本文件
|
// TODO 智能支持 .sh 脚本文件
|
||||||
cmd := exec.Command(this.config.Path)
|
var cmd = executils.NewTimeoutCmd(30*time.Second, this.config.Path)
|
||||||
cmd.Env = []string{
|
cmd.WithEnv([]string{
|
||||||
"ACTION=" + action,
|
"ACTION=" + action,
|
||||||
"TYPE=" + item.Type,
|
"TYPE=" + item.Type,
|
||||||
"IP_FROM=" + item.IpFrom,
|
"IP_FROM=" + item.IpFrom,
|
||||||
"IP_TO=" + item.IpTo,
|
"IP_TO=" + item.IpTo,
|
||||||
"EXPIRED_AT=" + fmt.Sprintf("%d", item.ExpiredAt),
|
"EXPIRED_AT=" + fmt.Sprintf("%d", item.ExpiredAt),
|
||||||
"LIST_TYPE=" + listType,
|
"LIST_TYPE=" + listType,
|
||||||
}
|
})
|
||||||
if len(this.config.Cwd) > 0 {
|
if len(this.config.Cwd) > 0 {
|
||||||
cmd.Dir = this.config.Cwd
|
cmd.WithDir(this.config.Cwd)
|
||||||
} else {
|
} else {
|
||||||
cmd.Dir = filepath.Dir(this.config.Path)
|
cmd.WithDir(filepath.Dir(this.config.Path))
|
||||||
}
|
}
|
||||||
stderr := bytes.NewBuffer([]byte{})
|
cmd.WithStderr()
|
||||||
cmd.Stderr = stderr
|
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New(err.Error() + ", output: " + string(stderr.Bytes()))
|
return errors.New(err.Error() + ", output: " + cmd.Stderr())
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,130 +0,0 @@
|
|||||||
// 源码改自:https://github.com/lionsoul2014/ip2region/blob/master/binding/golang/ip2region/ip2Region.go
|
|
||||||
|
|
||||||
package iplibrary
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
IndexBlockLength = 12
|
|
||||||
)
|
|
||||||
|
|
||||||
var err error
|
|
||||||
|
|
||||||
type IP2Region struct {
|
|
||||||
headerSip []int64
|
|
||||||
headerPtr []int64
|
|
||||||
headerLen int64
|
|
||||||
|
|
||||||
// super block index info
|
|
||||||
firstIndexPtr int64
|
|
||||||
lastIndexPtr int64
|
|
||||||
totalBlocks int64
|
|
||||||
|
|
||||||
dbData []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
type IpInfo struct {
|
|
||||||
CityId int64
|
|
||||||
Country string
|
|
||||||
Region string
|
|
||||||
Province string
|
|
||||||
City string
|
|
||||||
ISP string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ip IpInfo) String() string {
|
|
||||||
return strconv.FormatInt(ip.CityId, 10) + "|" + ip.Country + "|" + ip.Region + "|" + ip.Province + "|" + ip.City + "|" + ip.ISP
|
|
||||||
}
|
|
||||||
|
|
||||||
func getIpInfo(cityId int64, line []byte) *IpInfo {
|
|
||||||
lineSlice := strings.Split(string(line), "|")
|
|
||||||
ipInfo := &IpInfo{}
|
|
||||||
length := len(lineSlice)
|
|
||||||
ipInfo.CityId = cityId
|
|
||||||
if length < 5 {
|
|
||||||
for i := 0; i <= 5-length; i++ {
|
|
||||||
lineSlice = append(lineSlice, "")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ipInfo.Country = lineSlice[0]
|
|
||||||
ipInfo.Region = lineSlice[1]
|
|
||||||
ipInfo.Province = lineSlice[2]
|
|
||||||
ipInfo.City = lineSlice[3]
|
|
||||||
ipInfo.ISP = lineSlice[4]
|
|
||||||
return ipInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewIP2Region(path string) (*IP2Region, error) {
|
|
||||||
var region = &IP2Region{}
|
|
||||||
region.dbData, err = os.ReadFile(path)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
region.firstIndexPtr = region.ipLongAtOffset(0)
|
|
||||||
region.lastIndexPtr = region.ipLongAtOffset(4)
|
|
||||||
region.totalBlocks = (region.lastIndexPtr-region.firstIndexPtr)/IndexBlockLength + 1
|
|
||||||
return region, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *IP2Region) MemorySearch(ipStr string) (ipInfo *IpInfo, err error) {
|
|
||||||
ip, err := ip2long(ipStr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
h := this.totalBlocks
|
|
||||||
var dataPtr, l int64
|
|
||||||
for l <= h {
|
|
||||||
m := (l + h) >> 1
|
|
||||||
p := this.firstIndexPtr + m*IndexBlockLength
|
|
||||||
sip := this.ipLongAtOffset(p)
|
|
||||||
if ip < sip {
|
|
||||||
h = m - 1
|
|
||||||
} else {
|
|
||||||
eip := this.ipLongAtOffset(p + 4)
|
|
||||||
if ip > eip {
|
|
||||||
l = m + 1
|
|
||||||
} else {
|
|
||||||
dataPtr = this.ipLongAtOffset(p + 8)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if dataPtr == 0 {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
dataLen := (dataPtr >> 24) & 0xFF
|
|
||||||
dataPtr = dataPtr & 0x00FFFFFF
|
|
||||||
return getIpInfo(this.ipLongAtOffset(dataPtr), this.dbData[(dataPtr)+4:dataPtr+dataLen]), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *IP2Region) ipLongAtOffset(offset int64) int64 {
|
|
||||||
return int64(this.dbData[offset]) |
|
|
||||||
int64(this.dbData[offset+1])<<8 |
|
|
||||||
int64(this.dbData[offset+2])<<16 |
|
|
||||||
int64(this.dbData[offset+3])<<24
|
|
||||||
}
|
|
||||||
|
|
||||||
func ip2long(IpStr string) (int64, error) {
|
|
||||||
bits := strings.Split(IpStr, ".")
|
|
||||||
if len(bits) != 4 {
|
|
||||||
return 0, errors.New("ip format error")
|
|
||||||
}
|
|
||||||
|
|
||||||
var sum int64
|
|
||||||
for i, n := range bits {
|
|
||||||
bit, _ := strconv.ParseInt(n, 10, 64)
|
|
||||||
sum += bit << uint(24-8*i)
|
|
||||||
}
|
|
||||||
|
|
||||||
return sum, nil
|
|
||||||
}
|
|
||||||
@@ -18,7 +18,8 @@ import (
|
|||||||
type IPListDB struct {
|
type IPListDB struct {
|
||||||
db *sql.DB
|
db *sql.DB
|
||||||
|
|
||||||
itemTableName string
|
itemTableName string
|
||||||
|
|
||||||
deleteExpiredItemsStmt *sql.Stmt
|
deleteExpiredItemsStmt *sql.Stmt
|
||||||
deleteItemStmt *sql.Stmt
|
deleteItemStmt *sql.Stmt
|
||||||
insertItemStmt *sql.Stmt
|
insertItemStmt *sql.Stmt
|
||||||
@@ -53,7 +54,9 @@ func (this *IPListDB) init() error {
|
|||||||
remotelogs.Println("IP_LIST_DB", "create data dir '"+this.dir+"'")
|
remotelogs.Println("IP_LIST_DB", "create data dir '"+this.dir+"'")
|
||||||
}
|
}
|
||||||
|
|
||||||
db, err := sql.Open("sqlite3", "file:"+this.dir+"/ip_list.db?cache=shared&mode=rwc&_journal_mode=WAL&_sync=OFF")
|
var path = this.dir + "/ip_list.db"
|
||||||
|
|
||||||
|
db, err := sql.Open("sqlite3", "file:"+path+"?cache=shared&mode=rwc&_journal_mode=WAL&_sync=OFF")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -66,6 +69,14 @@ func (this *IPListDB) init() error {
|
|||||||
|
|
||||||
this.db = db
|
this.db = db
|
||||||
|
|
||||||
|
// 恢复数据库
|
||||||
|
var recoverEnv, _ = os.LookupEnv("EdgeRecover")
|
||||||
|
if len(recoverEnv) > 0 {
|
||||||
|
for _, indexName := range []string{"ip_list_itemId", "ip_list_expiredAt"} {
|
||||||
|
_, _ = db.Exec(`REINDEX "` + indexName + `"`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 初始化数据库
|
// 初始化数据库
|
||||||
_, err = db.Exec(`CREATE TABLE IF NOT EXISTS "` + this.itemTableName + `" (
|
_, err = db.Exec(`CREATE TABLE IF NOT EXISTS "` + this.itemTableName + `" (
|
||||||
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
|
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
@@ -159,6 +170,12 @@ func (this *IPListDB) AddItem(item *pb.IPItem) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 如果是删除,则不再创建新记录
|
||||||
|
if item.IsDeleted {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
_, err = this.insertItemStmt.Exec(item.ListId, item.ListType, item.IsGlobal, item.Type, item.Id, item.IpFrom, item.IpTo, item.ExpiredAt, item.EventLevel, item.IsDeleted, item.Version, item.NodeId, item.ServerId)
|
_, err = this.insertItemStmt.Exec(item.ListId, item.ListType, item.IsGlobal, item.Type, item.Id, item.IpFrom, item.IpTo, item.ExpiredAt, item.EventLevel, item.IsDeleted, item.Version, item.NodeId, item.ServerId)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -194,12 +211,12 @@ func (this *IPListDB) ReadMaxVersion() int64 {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
row := this.selectMaxVersionStmt.QueryRow()
|
var row = this.selectMaxVersionStmt.QueryRow()
|
||||||
if row == nil {
|
if row == nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
var version int64
|
var version int64
|
||||||
err = row.Scan(&version)
|
err := row.Scan(&version)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ func TestIPListDB_AddItem(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = db.AddItem(&pb.IPItem{
|
err = db.AddItem(&pb.IPItem{
|
||||||
Id: 1,
|
Id: 1,
|
||||||
IpFrom: "192.168.1.101",
|
IpFrom: "192.168.1.101",
|
||||||
@@ -45,6 +46,12 @@ func TestIPListDB_AddItem(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = db.Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
t.Log("ok")
|
t.Log("ok")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
package iplibrary
|
|
||||||
|
|
||||||
type LibraryInterface interface {
|
|
||||||
// Load 加载数据库文件
|
|
||||||
Load(dbPath string) error
|
|
||||||
|
|
||||||
// Lookup 查询IP
|
|
||||||
// 返回结果有可能为空
|
|
||||||
Lookup(ip string) (*Result, error)
|
|
||||||
|
|
||||||
// Close 关闭数据库文件
|
|
||||||
Close()
|
|
||||||
}
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
package iplibrary
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/errors"
|
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
|
||||||
"net"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type IP2RegionLibrary struct {
|
|
||||||
db *IP2Region
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *IP2RegionLibrary) Load(dbPath string) error {
|
|
||||||
db, err := NewIP2Region(dbPath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
this.db = db
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *IP2RegionLibrary) Lookup(ip string) (*Result, error) {
|
|
||||||
// 暂不支持IPv6
|
|
||||||
if strings.Contains(ip, ":") {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
if net.ParseIP(ip) == nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if this.db == nil {
|
|
||||||
return nil, errors.New("library has not been loaded")
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
// 防止panic发生
|
|
||||||
err := recover()
|
|
||||||
if err != nil {
|
|
||||||
remotelogs.Error("IP2RegionLibrary", "panic: "+fmt.Sprintf("%#v", err))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
info, err := this.db.MemorySearch(ip)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if info == nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if info.Country == "0" {
|
|
||||||
info.Country = ""
|
|
||||||
}
|
|
||||||
if info.Region == "0" {
|
|
||||||
info.Region = ""
|
|
||||||
}
|
|
||||||
if info.Province == "0" {
|
|
||||||
info.Province = ""
|
|
||||||
}
|
|
||||||
if info.City == "0" {
|
|
||||||
info.City = ""
|
|
||||||
}
|
|
||||||
if info.ISP == "0" {
|
|
||||||
info.ISP = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
return &Result{
|
|
||||||
CityId: info.CityId,
|
|
||||||
Country: info.Country,
|
|
||||||
Region: info.Region,
|
|
||||||
Province: info.Province,
|
|
||||||
City: info.City,
|
|
||||||
ISP: info.ISP,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *IP2RegionLibrary) Close() {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,114 +0,0 @@
|
|||||||
package iplibrary
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/iwind/TeaGo/Tea"
|
|
||||||
_ "github.com/iwind/TeaGo/bootstrap"
|
|
||||||
"github.com/iwind/TeaGo/rands"
|
|
||||||
"runtime"
|
|
||||||
"strconv"
|
|
||||||
"sync"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestIP2RegionLibrary_Lookup_MemoryUsage(t *testing.T) {
|
|
||||||
var mem = &runtime.MemStats{}
|
|
||||||
runtime.ReadMemStats(mem)
|
|
||||||
|
|
||||||
library := &IP2RegionLibrary{}
|
|
||||||
err := library.Load(Tea.Root + "/resources/ipdata/ip2region/ip2region.db")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var mem2 = &runtime.MemStats{}
|
|
||||||
runtime.ReadMemStats(mem2)
|
|
||||||
t.Log((mem2.HeapInuse-mem.HeapInuse)/1024/1024, "MB")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIP2RegionLibrary_Lookup_Single(t *testing.T) {
|
|
||||||
library := &IP2RegionLibrary{}
|
|
||||||
err := library.Load(Tea.Root + "/resources/ipdata/ip2region/ip2region.db")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, ip := range []string{"8.8.9.9"} {
|
|
||||||
result, err := library.Lookup(ip)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
t.Log("IP:", ip, "result:", result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIP2RegionLibrary_Lookup(t *testing.T) {
|
|
||||||
library := &IP2RegionLibrary{}
|
|
||||||
err := library.Load(Tea.Root + "/resources/ipdata/ip2region/ip2region.db")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, ip := range []string{"", "a", "1.1.1", "192.168.1.100", "114.240.223.47", "8.8.9.9", "::1"} {
|
|
||||||
result, err := library.Lookup(ip)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
t.Log("IP:", ip, "result:", result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIP2RegionLibrary_Lookup_Concurrent(t *testing.T) {
|
|
||||||
library := &IP2RegionLibrary{}
|
|
||||||
err := library.Load(Tea.Root + "/resources/ipdata/ip2region/ip2region.db")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var count = 4000
|
|
||||||
var wg = sync.WaitGroup{}
|
|
||||||
wg.Add(count)
|
|
||||||
for i := 0; i < count; i++ {
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
|
|
||||||
for i := 0; i < 100; i++ {
|
|
||||||
_, _ = library.Lookup(strconv.Itoa(rands.Int(0, 254)) + "." + strconv.Itoa(rands.Int(0, 254)) + "." + strconv.Itoa(rands.Int(0, 254)) + "." + strconv.Itoa(rands.Int(0, 254)))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
wg.Done()
|
|
||||||
t.Log("ok")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIP2RegionLibrary_Memory(t *testing.T) {
|
|
||||||
library := &IP2RegionLibrary{}
|
|
||||||
err := library.Load(Tea.Root + "/resources/ipdata/ip2region/ip2region.db")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
before := time.Now()
|
|
||||||
|
|
||||||
for i := 0; i < 1_000_000; i++ {
|
|
||||||
_, _ = library.Lookup(strconv.Itoa(rands.Int(0, 254)) + "." + strconv.Itoa(rands.Int(0, 254)) + "." + strconv.Itoa(rands.Int(0, 254)) + "." + strconv.Itoa(rands.Int(0, 254)))
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Log("cost:", time.Since(before).Seconds()*1000, "ms")
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkIP2RegionLibrary_Lookup(b *testing.B) {
|
|
||||||
runtime.GOMAXPROCS(1)
|
|
||||||
|
|
||||||
var library = &IP2RegionLibrary{}
|
|
||||||
err := library.Load(Tea.Root + "/resources/ipdata/ip2region/ip2region.db")
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
b.ResetTimer()
|
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
_, _ = library.Lookup("8.8.8.8")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
package iplibrary
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/errors"
|
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/events"
|
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
|
||||||
"github.com/iwind/TeaGo/Tea"
|
|
||||||
"github.com/iwind/TeaGo/files"
|
|
||||||
"github.com/iwind/TeaGo/types"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
var SharedManager = NewManager()
|
|
||||||
var SharedLibrary LibraryInterface
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
events.On(events.EventLoaded, func() {
|
|
||||||
// 初始化
|
|
||||||
library, err := SharedManager.Load()
|
|
||||||
if err != nil {
|
|
||||||
remotelogs.ErrorObject("IP_LIBRARY", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
SharedLibrary = library
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
type Manager struct {
|
|
||||||
code string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewManager() *Manager {
|
|
||||||
return &Manager{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *Manager) Load() (LibraryInterface, error) {
|
|
||||||
nodeConfig, err := nodeconfigs.SharedNodeConfig()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
config := nodeConfig.GlobalConfig
|
|
||||||
if config == nil {
|
|
||||||
config = &serverconfigs.GlobalConfig{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 当前正在使用的IP库代号
|
|
||||||
code := config.IPLibrary.Code
|
|
||||||
if len(code) == 0 {
|
|
||||||
code = serverconfigs.DefaultIPLibraryType
|
|
||||||
}
|
|
||||||
|
|
||||||
dir := Tea.Root + "/resources/ipdata/" + code
|
|
||||||
var lastVersion int64 = -1
|
|
||||||
lastFilename := ""
|
|
||||||
for _, file := range files.NewFile(dir).List() {
|
|
||||||
filename := file.Name()
|
|
||||||
|
|
||||||
reg := regexp.MustCompile(`^` + regexp.QuoteMeta(code) + `.(\d+)\.`)
|
|
||||||
if reg.MatchString(filename) { // 先查找有版本号的
|
|
||||||
result := reg.FindStringSubmatch(filename)
|
|
||||||
version := types.Int64(result[1])
|
|
||||||
if version > lastVersion {
|
|
||||||
lastVersion = version
|
|
||||||
lastFilename = filename
|
|
||||||
}
|
|
||||||
} else if strings.HasPrefix(filename, code+".") { // 后查找默认的
|
|
||||||
if lastVersion == -1 {
|
|
||||||
lastFilename = filename
|
|
||||||
lastVersion = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(lastFilename) == 0 {
|
|
||||||
return nil, errors.New("ip library file not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
var libraryPtr LibraryInterface
|
|
||||||
switch code {
|
|
||||||
case serverconfigs.IPLibraryTypeIP2Region:
|
|
||||||
libraryPtr = &IP2RegionLibrary{}
|
|
||||||
default:
|
|
||||||
return nil, errors.New("invalid ip library code '" + code + "'")
|
|
||||||
}
|
|
||||||
|
|
||||||
err = libraryPtr.Load(dir + "/" + lastFilename)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return libraryPtr, nil
|
|
||||||
}
|
|
||||||
@@ -1,155 +0,0 @@
|
|||||||
package iplibrary
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/md5"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/events"
|
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
|
||||||
"github.com/iwind/TeaGo/Tea"
|
|
||||||
_ "github.com/iwind/TeaGo/bootstrap"
|
|
||||||
"github.com/iwind/TeaGo/types"
|
|
||||||
"os"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var SharedCityManager = NewCityManager()
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
events.On(events.EventLoaded, func() {
|
|
||||||
goman.New(func() {
|
|
||||||
SharedCityManager.Start()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
events.On(events.EventQuit, func() {
|
|
||||||
SharedCityManager.Stop()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// CityManager 中国省份信息管理
|
|
||||||
type CityManager struct {
|
|
||||||
ticker *time.Ticker
|
|
||||||
|
|
||||||
cacheFile string
|
|
||||||
|
|
||||||
cityMap map[string]int64 // provinceName_cityName => cityName
|
|
||||||
dataHash string // 国家JSON的md5
|
|
||||||
|
|
||||||
locker sync.RWMutex
|
|
||||||
|
|
||||||
isUpdated bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewCityManager() *CityManager {
|
|
||||||
return &CityManager{
|
|
||||||
cacheFile: Tea.Root + "/configs/region_city.json.cache",
|
|
||||||
cityMap: map[string]int64{},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *CityManager) Start() {
|
|
||||||
// 从缓存中读取
|
|
||||||
err := this.load()
|
|
||||||
if err != nil {
|
|
||||||
remotelogs.ErrorObject("CITY_MANAGER", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 第一次更新
|
|
||||||
err = this.loop()
|
|
||||||
if err != nil {
|
|
||||||
remotelogs.ErrorObject("City_MANAGER", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 定时更新
|
|
||||||
this.ticker = time.NewTicker(4 * time.Hour)
|
|
||||||
for range this.ticker.C {
|
|
||||||
err := this.loop()
|
|
||||||
if err != nil {
|
|
||||||
remotelogs.ErrorObject("CITY_MANAGER", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *CityManager) Stop() {
|
|
||||||
if this.ticker != nil {
|
|
||||||
this.ticker.Stop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *CityManager) Lookup(provinceId int64, cityName string) (cityId int64) {
|
|
||||||
this.locker.RLock()
|
|
||||||
cityId, _ = this.cityMap[types.String(provinceId)+"_"+cityName]
|
|
||||||
this.locker.RUnlock()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 从缓存中读取
|
|
||||||
func (this *CityManager) load() error {
|
|
||||||
data, err := os.ReadFile(this.cacheFile)
|
|
||||||
if err != nil {
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
m := map[string]int64{}
|
|
||||||
err = json.Unmarshal(data, &m)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if m != nil && len(m) > 0 {
|
|
||||||
this.cityMap = m
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新城市信息
|
|
||||||
func (this *CityManager) loop() error {
|
|
||||||
if this.isUpdated {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
rpcClient, err := rpc.SharedRPC()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
resp, err := rpcClient.RegionCityRPC().FindAllRegionCities(rpcClient.Context(), &pb.FindAllRegionCitiesRequest{})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
m := map[string]int64{}
|
|
||||||
for _, city := range resp.RegionCities {
|
|
||||||
for _, code := range city.Codes {
|
|
||||||
m[types.String(city.RegionProvinceId)+"_"+code] = city.Id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查是否有更新
|
|
||||||
data, err := json.Marshal(m)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
hash := md5.New()
|
|
||||||
hash.Write(data)
|
|
||||||
dataHash := fmt.Sprintf("%x", hash.Sum(nil))
|
|
||||||
if this.dataHash == dataHash {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
this.dataHash = dataHash
|
|
||||||
|
|
||||||
this.locker.Lock()
|
|
||||||
this.cityMap = m
|
|
||||||
this.isUpdated = true
|
|
||||||
this.locker.Unlock()
|
|
||||||
|
|
||||||
// 保存到本地缓存
|
|
||||||
|
|
||||||
err = os.WriteFile(this.cacheFile, data, 0666)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
|
||||||
|
|
||||||
package iplibrary
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
func TestNewCityManager(t *testing.T) {
|
|
||||||
var manager = NewCityManager()
|
|
||||||
err := manager.loop()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
t.Log(manager.Lookup(16, "许昌市"))
|
|
||||||
}
|
|
||||||
@@ -1,153 +0,0 @@
|
|||||||
package iplibrary
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/md5"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/events"
|
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
|
||||||
"github.com/iwind/TeaGo/Tea"
|
|
||||||
_ "github.com/iwind/TeaGo/bootstrap"
|
|
||||||
"os"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var SharedCountryManager = NewCountryManager()
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
events.On(events.EventLoaded, func() {
|
|
||||||
goman.New(func() {
|
|
||||||
SharedCountryManager.Start()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
events.On(events.EventQuit, func() {
|
|
||||||
SharedCountryManager.Stop()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// CountryManager 国家/地区信息管理
|
|
||||||
type CountryManager struct {
|
|
||||||
ticker *time.Ticker
|
|
||||||
|
|
||||||
cacheFile string
|
|
||||||
|
|
||||||
countryMap map[string]int64 // countryName => countryId
|
|
||||||
dataHash string // 国家JSON的md5
|
|
||||||
|
|
||||||
locker sync.RWMutex
|
|
||||||
|
|
||||||
isUpdated bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewCountryManager() *CountryManager {
|
|
||||||
return &CountryManager{
|
|
||||||
cacheFile: Tea.Root + "/configs/region_country.json.cache",
|
|
||||||
countryMap: map[string]int64{},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *CountryManager) Start() {
|
|
||||||
// 从缓存中读取
|
|
||||||
err := this.load()
|
|
||||||
if err != nil {
|
|
||||||
remotelogs.ErrorObject("COUNTRY_MANAGER", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 第一次更新
|
|
||||||
err = this.loop()
|
|
||||||
if err != nil {
|
|
||||||
remotelogs.ErrorObject("COUNTRY_MANAGER", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 定时更新
|
|
||||||
this.ticker = time.NewTicker(4 * time.Hour)
|
|
||||||
for range this.ticker.C {
|
|
||||||
err := this.loop()
|
|
||||||
if err != nil {
|
|
||||||
remotelogs.ErrorObject("COUNTRY_MANAGER", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *CountryManager) Stop() {
|
|
||||||
if this.ticker != nil {
|
|
||||||
this.ticker.Stop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *CountryManager) Lookup(countryName string) (countryId int64) {
|
|
||||||
this.locker.RLock()
|
|
||||||
countryId, _ = this.countryMap[countryName]
|
|
||||||
this.locker.RUnlock()
|
|
||||||
return countryId
|
|
||||||
}
|
|
||||||
|
|
||||||
// 从缓存中读取
|
|
||||||
func (this *CountryManager) load() error {
|
|
||||||
data, err := os.ReadFile(this.cacheFile)
|
|
||||||
if err != nil {
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
m := map[string]int64{}
|
|
||||||
err = json.Unmarshal(data, &m)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if m != nil && len(m) > 0 {
|
|
||||||
this.countryMap = m
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新国家信息
|
|
||||||
func (this *CountryManager) loop() error {
|
|
||||||
if this.isUpdated {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
rpcClient, err := rpc.SharedRPC()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
resp, err := rpcClient.RegionCountryRPC().FindAllRegionCountries(rpcClient.Context(), &pb.FindAllRegionCountriesRequest{})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
m := map[string]int64{}
|
|
||||||
for _, country := range resp.RegionCountries {
|
|
||||||
for _, code := range country.Codes {
|
|
||||||
m[code] = country.Id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查是否有更新
|
|
||||||
data, err := json.Marshal(m)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
hash := md5.New()
|
|
||||||
hash.Write(data)
|
|
||||||
dataHash := fmt.Sprintf("%x", hash.Sum(nil))
|
|
||||||
if this.dataHash == dataHash {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
this.dataHash = dataHash
|
|
||||||
|
|
||||||
this.locker.Lock()
|
|
||||||
this.countryMap = m
|
|
||||||
this.isUpdated = true
|
|
||||||
this.locker.Unlock()
|
|
||||||
|
|
||||||
// 保存到本地缓存
|
|
||||||
err = os.WriteFile(this.cacheFile, data, 0666)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
package iplibrary
|
|
||||||
|
|
||||||
import (
|
|
||||||
"runtime"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestCountryManager_load(t *testing.T) {
|
|
||||||
manager := NewCountryManager()
|
|
||||||
err := manager.load()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
t.Log("ok", manager.countryMap)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCountryManager_loop(t *testing.T) {
|
|
||||||
manager := NewCountryManager()
|
|
||||||
err := manager.loop()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
t.Log("ok", manager.countryMap)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCountryManager_loop_skip(t *testing.T) {
|
|
||||||
manager := NewCountryManager()
|
|
||||||
for i := 0; i < 10; i++ {
|
|
||||||
err := manager.loop()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCountryManager_Lookup(t *testing.T) {
|
|
||||||
manager := NewCountryManager()
|
|
||||||
err := manager.load()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
t.Log(manager.Lookup("中国"), manager.Lookup("美国 "))
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkCountryManager_Lookup(b *testing.B) {
|
|
||||||
runtime.GOMAXPROCS(1)
|
|
||||||
|
|
||||||
manager := NewCountryManager()
|
|
||||||
err := manager.load()
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
_ = manager.Lookup("中国")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,6 +2,7 @@ package iplibrary
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
|
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/events"
|
"github.com/TeaOSLab/EdgeNode/internal/events"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
@@ -18,6 +19,10 @@ var SharedIPListManager = NewIPListManager()
|
|||||||
var IPListUpdateNotify = make(chan bool, 1)
|
var IPListUpdateNotify = make(chan bool, 1)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
if teaconst.IsDaemon {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
events.On(events.EventLoaded, func() {
|
events.On(events.EventLoaded, func() {
|
||||||
goman.New(func() {
|
goman.New(func() {
|
||||||
SharedIPListManager.Start()
|
SharedIPListManager.Start()
|
||||||
@@ -26,6 +31,13 @@ func init() {
|
|||||||
events.On(events.EventQuit, func() {
|
events.On(events.EventQuit, func() {
|
||||||
SharedIPListManager.Stop()
|
SharedIPListManager.Stop()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
var ticker = time.NewTicker(24 * time.Hour)
|
||||||
|
goman.New(func() {
|
||||||
|
for range ticker.C {
|
||||||
|
SharedIPListManager.DeleteExpiredItems()
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// IPListManager IP名单管理
|
// IPListManager IP名单管理
|
||||||
@@ -43,7 +55,7 @@ type IPListManager struct {
|
|||||||
|
|
||||||
func NewIPListManager() *IPListManager {
|
func NewIPListManager() *IPListManager {
|
||||||
return &IPListManager{
|
return &IPListManager{
|
||||||
pageSize: 500,
|
pageSize: 1000,
|
||||||
listMap: map[int64]*IPList{},
|
listMap: map[int64]*IPList{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -111,15 +123,19 @@ func (this *IPListManager) init() {
|
|||||||
var size int64 = 1000
|
var size int64 = 1000
|
||||||
for {
|
for {
|
||||||
items, err := db.ReadItems(offset, size)
|
items, err := db.ReadItems(offset, size)
|
||||||
|
var l = len(items)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
remotelogs.Error("IP_LIST_MANAGER", "read ip list from local database failed: "+err.Error())
|
remotelogs.Error("IP_LIST_MANAGER", "read ip list from local database failed: "+err.Error())
|
||||||
} else {
|
} else {
|
||||||
if len(items) == 0 {
|
if l == 0 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
this.processItems(items, false)
|
this.processItems(items, false)
|
||||||
|
if int64(l) < size {
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
offset += int64(len(items))
|
offset += int64(l)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -144,7 +160,7 @@ func (this *IPListManager) fetch() (hasNext bool, err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
itemsResp, err := rpcClient.IPItemRPC().ListIPItemsAfterVersion(rpcClient.Context(), &pb.ListIPItemsAfterVersionRequest{
|
itemsResp, err := rpcClient.IPItemRPC.ListIPItemsAfterVersion(rpcClient.Context(), &pb.ListIPItemsAfterVersionRequest{
|
||||||
Version: this.version,
|
Version: this.version,
|
||||||
Size: this.pageSize,
|
Size: this.pageSize,
|
||||||
})
|
})
|
||||||
@@ -155,7 +171,7 @@ func (this *IPListManager) fetch() (hasNext bool, err error) {
|
|||||||
}
|
}
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
items := itemsResp.IpItems
|
var items = itemsResp.IpItems
|
||||||
if len(items) == 0 {
|
if len(items) == 0 {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
@@ -182,8 +198,13 @@ func (this *IPListManager) FindList(listId int64) *IPList {
|
|||||||
return list
|
return list
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *IPListManager) DeleteExpiredItems() {
|
||||||
|
if this.db != nil {
|
||||||
|
_ = this.db.DeleteExpiredItems()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (this *IPListManager) processItems(items []*pb.IPItem, fromRemote bool) {
|
func (this *IPListManager) processItems(items []*pb.IPItem, fromRemote bool) {
|
||||||
this.locker.Lock()
|
|
||||||
var changedLists = map[*IPList]zero.Zero{}
|
var changedLists = map[*IPList]zero.Zero{}
|
||||||
for _, item := range items {
|
for _, item := range items {
|
||||||
var list *IPList
|
var list *IPList
|
||||||
@@ -203,11 +224,15 @@ func (this *IPListManager) processItems(items []*pb.IPItem, fromRemote bool) {
|
|||||||
list = GlobalWhiteIPList
|
list = GlobalWhiteIPList
|
||||||
}
|
}
|
||||||
} else { // 其他List
|
} else { // 其他List
|
||||||
|
this.locker.Lock()
|
||||||
list = this.listMap[item.ListId]
|
list = this.listMap[item.ListId]
|
||||||
|
this.locker.Unlock()
|
||||||
}
|
}
|
||||||
if list == nil {
|
if list == nil {
|
||||||
list = NewIPList()
|
list = NewIPList()
|
||||||
|
this.locker.Lock()
|
||||||
this.listMap[item.ListId] = list
|
this.listMap[item.ListId] = list
|
||||||
|
this.locker.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
changedLists[list] = zero.New()
|
changedLists[list] = zero.New()
|
||||||
@@ -246,8 +271,6 @@ func (this *IPListManager) processItems(items []*pb.IPItem, fromRemote bool) {
|
|||||||
changedList.Sort()
|
changedList.Sort()
|
||||||
}
|
}
|
||||||
|
|
||||||
this.locker.Unlock()
|
|
||||||
|
|
||||||
if fromRemote {
|
if fromRemote {
|
||||||
var latestVersion = items[len(items)-1].Version
|
var latestVersion = items[len(items)-1].Version
|
||||||
if latestVersion > this.version {
|
if latestVersion > this.version {
|
||||||
|
|||||||
@@ -1,154 +0,0 @@
|
|||||||
package iplibrary
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/md5"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/events"
|
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
|
||||||
"github.com/iwind/TeaGo/Tea"
|
|
||||||
_ "github.com/iwind/TeaGo/bootstrap"
|
|
||||||
"os"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var SharedProviderManager = NewProviderManager()
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
events.On(events.EventLoaded, func() {
|
|
||||||
goman.New(func() {
|
|
||||||
SharedProviderManager.Start()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
events.On(events.EventQuit, func() {
|
|
||||||
SharedProviderManager.Stop()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// ProviderManager 中国省份信息管理
|
|
||||||
type ProviderManager struct {
|
|
||||||
ticker *time.Ticker
|
|
||||||
|
|
||||||
cacheFile string
|
|
||||||
|
|
||||||
providerMap map[string]int64 // name => id
|
|
||||||
dataHash string // 国家JSON的md5
|
|
||||||
|
|
||||||
locker sync.RWMutex
|
|
||||||
|
|
||||||
isUpdated bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewProviderManager() *ProviderManager {
|
|
||||||
return &ProviderManager{
|
|
||||||
cacheFile: Tea.Root + "/configs/region_provider.json.cache",
|
|
||||||
providerMap: map[string]int64{},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *ProviderManager) Start() {
|
|
||||||
// 从缓存中读取
|
|
||||||
err := this.load()
|
|
||||||
if err != nil {
|
|
||||||
remotelogs.ErrorObject("PROVIDER_MANAGER", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 第一次更新
|
|
||||||
err = this.loop()
|
|
||||||
if err != nil {
|
|
||||||
remotelogs.ErrorObject("PROVIDER_MANAGER", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 定时更新
|
|
||||||
this.ticker = time.NewTicker(4 * time.Hour)
|
|
||||||
for range this.ticker.C {
|
|
||||||
err := this.loop()
|
|
||||||
if err != nil {
|
|
||||||
remotelogs.ErrorObject("PROVIDER_MANAGER", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *ProviderManager) Stop() {
|
|
||||||
if this.ticker != nil {
|
|
||||||
this.ticker.Stop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *ProviderManager) Lookup(providerName string) (providerId int64) {
|
|
||||||
this.locker.RLock()
|
|
||||||
providerId, _ = this.providerMap[providerName]
|
|
||||||
this.locker.RUnlock()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 从缓存中读取
|
|
||||||
func (this *ProviderManager) load() error {
|
|
||||||
data, err := os.ReadFile(this.cacheFile)
|
|
||||||
if err != nil {
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
m := map[string]int64{}
|
|
||||||
err = json.Unmarshal(data, &m)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if m != nil && len(m) > 0 {
|
|
||||||
this.providerMap = m
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新服务商信息
|
|
||||||
func (this *ProviderManager) loop() error {
|
|
||||||
if this.isUpdated {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
rpcClient, err := rpc.SharedRPC()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
resp, err := rpcClient.RegionProviderRPC().FindAllRegionProviders(rpcClient.Context(), &pb.FindAllRegionProvidersRequest{})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
m := map[string]int64{}
|
|
||||||
for _, provider := range resp.RegionProviders {
|
|
||||||
for _, code := range provider.Codes {
|
|
||||||
m[code] = provider.Id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查是否有更新
|
|
||||||
data, err := json.Marshal(m)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
hash := md5.New()
|
|
||||||
hash.Write(data)
|
|
||||||
dataHash := fmt.Sprintf("%x", hash.Sum(nil))
|
|
||||||
if this.dataHash == dataHash {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
this.dataHash = dataHash
|
|
||||||
|
|
||||||
this.locker.Lock()
|
|
||||||
this.providerMap = m
|
|
||||||
this.isUpdated = true
|
|
||||||
this.locker.Unlock()
|
|
||||||
|
|
||||||
// 保存到本地缓存
|
|
||||||
|
|
||||||
err = os.WriteFile(this.cacheFile, data, 0666)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
|
||||||
|
|
||||||
package iplibrary
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
func TestNewProviderManager(t *testing.T) {
|
|
||||||
var manager = NewProviderManager()
|
|
||||||
err := manager.loop()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
t.Log(manager.Lookup("阿里云"))
|
|
||||||
t.Log(manager.Lookup("阿里云2"))
|
|
||||||
}
|
|
||||||
@@ -1,160 +0,0 @@
|
|||||||
package iplibrary
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/md5"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/events"
|
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
|
||||||
"github.com/iwind/TeaGo/Tea"
|
|
||||||
_ "github.com/iwind/TeaGo/bootstrap"
|
|
||||||
"os"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
ChinaCountryId int64 = 1
|
|
||||||
)
|
|
||||||
|
|
||||||
var SharedProvinceManager = NewProvinceManager()
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
events.On(events.EventLoaded, func() {
|
|
||||||
goman.New(func() {
|
|
||||||
SharedProvinceManager.Start()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
events.On(events.EventQuit, func() {
|
|
||||||
SharedProvinceManager.Stop()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// ProvinceManager 中国省份信息管理
|
|
||||||
type ProvinceManager struct {
|
|
||||||
ticker *time.Ticker
|
|
||||||
|
|
||||||
cacheFile string
|
|
||||||
|
|
||||||
provinceMap map[string]int64 // provinceName => provinceId
|
|
||||||
dataHash string // 国家JSON的md5
|
|
||||||
|
|
||||||
locker sync.RWMutex
|
|
||||||
|
|
||||||
isUpdated bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewProvinceManager() *ProvinceManager {
|
|
||||||
return &ProvinceManager{
|
|
||||||
cacheFile: Tea.Root + "/configs/region_province.json.cache",
|
|
||||||
provinceMap: map[string]int64{},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *ProvinceManager) Start() {
|
|
||||||
// 从缓存中读取
|
|
||||||
err := this.load()
|
|
||||||
if err != nil {
|
|
||||||
remotelogs.ErrorObject("PROVINCE_MANAGER", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 第一次更新
|
|
||||||
err = this.loop()
|
|
||||||
if err != nil {
|
|
||||||
remotelogs.ErrorObject("PROVINCE_MANAGER", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 定时更新
|
|
||||||
this.ticker = time.NewTicker(4 * time.Hour)
|
|
||||||
for range this.ticker.C {
|
|
||||||
err := this.loop()
|
|
||||||
if err != nil {
|
|
||||||
remotelogs.ErrorObject("PROVINCE_MANAGER", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *ProvinceManager) Stop() {
|
|
||||||
if this.ticker != nil {
|
|
||||||
this.ticker.Stop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *ProvinceManager) Lookup(provinceName string) (provinceId int64) {
|
|
||||||
this.locker.RLock()
|
|
||||||
provinceId, _ = this.provinceMap[provinceName]
|
|
||||||
this.locker.RUnlock()
|
|
||||||
return provinceId
|
|
||||||
}
|
|
||||||
|
|
||||||
// 从缓存中读取
|
|
||||||
func (this *ProvinceManager) load() error {
|
|
||||||
data, err := os.ReadFile(this.cacheFile)
|
|
||||||
if err != nil {
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
m := map[string]int64{}
|
|
||||||
err = json.Unmarshal(data, &m)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if m != nil && len(m) > 0 {
|
|
||||||
this.provinceMap = m
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新省份信息
|
|
||||||
func (this *ProvinceManager) loop() error {
|
|
||||||
if this.isUpdated {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
rpcClient, err := rpc.SharedRPC()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
resp, err := rpcClient.RegionProvinceRPC().FindAllRegionProvincesWithRegionCountryId(rpcClient.Context(), &pb.FindAllRegionProvincesWithRegionCountryIdRequest{
|
|
||||||
RegionCountryId: ChinaCountryId,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
m := map[string]int64{}
|
|
||||||
for _, province := range resp.RegionProvinces {
|
|
||||||
for _, code := range province.Codes {
|
|
||||||
m[code] = province.Id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查是否有更新
|
|
||||||
data, err := json.Marshal(m)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
hash := md5.New()
|
|
||||||
hash.Write(data)
|
|
||||||
dataHash := fmt.Sprintf("%x", hash.Sum(nil))
|
|
||||||
if this.dataHash == dataHash {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
this.dataHash = dataHash
|
|
||||||
|
|
||||||
this.locker.Lock()
|
|
||||||
this.provinceMap = m
|
|
||||||
this.isUpdated = true
|
|
||||||
this.locker.Unlock()
|
|
||||||
|
|
||||||
// 保存到本地缓存
|
|
||||||
|
|
||||||
err = os.WriteFile(this.cacheFile, data, 0666)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
package iplibrary
|
|
||||||
|
|
||||||
import (
|
|
||||||
"runtime"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestProvinceManager_load(t *testing.T) {
|
|
||||||
manager := NewProvinceManager()
|
|
||||||
err := manager.load()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
t.Log("ok", manager.provinceMap)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestProvinceManager_loop(t *testing.T) {
|
|
||||||
manager := NewProvinceManager()
|
|
||||||
err := manager.loop()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
t.Log("ok", manager.provinceMap)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestProvinceManager_loop_skip(t *testing.T) {
|
|
||||||
manager := NewProvinceManager()
|
|
||||||
for i := 0; i < 10; i++ {
|
|
||||||
err := manager.loop()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestProvinceManager_Lookup(t *testing.T) {
|
|
||||||
manager := NewProvinceManager()
|
|
||||||
err := manager.load()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
t.Log(manager.Lookup("安徽省"), manager.Lookup("北京市"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkProvinceManager_Lookup(b *testing.B) {
|
|
||||||
runtime.GOMAXPROCS(1)
|
|
||||||
|
|
||||||
manager := NewProvinceManager()
|
|
||||||
err := manager.load()
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
_ = manager.Lookup("安徽省")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
package iplibrary
|
|
||||||
|
|
||||||
import (
|
|
||||||
_ "github.com/iwind/TeaGo/bootstrap"
|
|
||||||
"github.com/iwind/TeaGo/dbs"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestManager_Load(t *testing.T) {
|
|
||||||
dbs.NotifyReady()
|
|
||||||
|
|
||||||
manager := NewManager()
|
|
||||||
lib, err := manager.Load()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
t.Log(lib.Lookup("1.2.3.4"))
|
|
||||||
t.Log(lib.Lookup("2.3.4.5"))
|
|
||||||
t.Log(lib.Lookup("200.200.200.200"))
|
|
||||||
t.Log(lib.Lookup("202.106.0.20"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewManager(t *testing.T) {
|
|
||||||
dbs.NotifyReady()
|
|
||||||
t.Log(SharedLibrary)
|
|
||||||
}
|
|
||||||
@@ -1,153 +0,0 @@
|
|||||||
package iplibrary
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/errors"
|
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/events"
|
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
|
||||||
"github.com/iwind/TeaGo/Tea"
|
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var SharedUpdater = NewUpdater()
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
events.On(events.EventStart, func() {
|
|
||||||
goman.New(func() {
|
|
||||||
SharedUpdater.Start()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
events.On(events.EventQuit, func() {
|
|
||||||
SharedUpdater.Stop()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Updater IP库更新程序
|
|
||||||
type Updater struct {
|
|
||||||
ticker *time.Ticker
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewUpdater 获取新对象
|
|
||||||
func NewUpdater() *Updater {
|
|
||||||
return &Updater{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start 开始更新
|
|
||||||
func (this *Updater) Start() {
|
|
||||||
// 这里不需要太频繁检查更新,因为通常不需要更新IP库
|
|
||||||
this.ticker = time.NewTicker(1 * time.Hour)
|
|
||||||
for range this.ticker.C {
|
|
||||||
err := this.loop()
|
|
||||||
if err != nil {
|
|
||||||
remotelogs.ErrorObject("IP_LIBRARY", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *Updater) Stop() {
|
|
||||||
if this.ticker != nil {
|
|
||||||
this.ticker.Stop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 单次任务
|
|
||||||
func (this *Updater) loop() error {
|
|
||||||
nodeConfig, err := nodeconfigs.SharedNodeConfig()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if nodeConfig.GlobalConfig == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
code := nodeConfig.GlobalConfig.IPLibrary.Code
|
|
||||||
if len(code) == 0 {
|
|
||||||
code = serverconfigs.DefaultIPLibraryType
|
|
||||||
}
|
|
||||||
|
|
||||||
rpcClient, err := rpc.SharedRPC()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
libraryResp, err := rpcClient.IPLibraryRPC().FindLatestIPLibraryWithType(rpcClient.Context(), &pb.FindLatestIPLibraryWithTypeRequest{Type: code})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
lib := libraryResp.IpLibrary
|
|
||||||
if lib == nil || lib.File == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
typeInfo := serverconfigs.FindIPLibraryWithType(code)
|
|
||||||
if typeInfo == nil {
|
|
||||||
return errors.New("invalid ip library code '" + code + "'")
|
|
||||||
}
|
|
||||||
|
|
||||||
path := Tea.Root + "/resources/ipdata/" + code + "/" + code + "." + fmt.Sprintf("%d", lib.CreatedAt) + typeInfo.GetString("ext")
|
|
||||||
|
|
||||||
// 是否已经存在
|
|
||||||
_, err = os.Stat(path)
|
|
||||||
if err == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 开始下载
|
|
||||||
fileChunkIdsResp, err := rpcClient.FileChunkRPC().FindAllFileChunkIds(rpcClient.Context(), &pb.FindAllFileChunkIdsRequest{FileId: lib.File.Id})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
chunkIds := fileChunkIdsResp.FileChunkIds
|
|
||||||
if len(chunkIds) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
isOk := false
|
|
||||||
|
|
||||||
fp, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0666)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
// 如果保存不成功就直接删除
|
|
||||||
if !isOk {
|
|
||||||
_ = fp.Close()
|
|
||||||
_ = os.Remove(path)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
for _, chunkId := range chunkIds {
|
|
||||||
chunkResp, err := rpcClient.FileChunkRPC().DownloadFileChunk(rpcClient.Context(), &pb.DownloadFileChunkRequest{FileChunkId: chunkId})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
chunk := chunkResp.FileChunk
|
|
||||||
|
|
||||||
if chunk == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
_, err = fp.Write(chunk.Data)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err = fp.Close()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// 重新加载
|
|
||||||
library, err := SharedManager.Load()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
SharedLibrary = library
|
|
||||||
|
|
||||||
isOk = true
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
package iplibrary
|
|
||||||
|
|
||||||
import (
|
|
||||||
_ "github.com/iwind/TeaGo/bootstrap"
|
|
||||||
"github.com/iwind/TeaGo/dbs"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestUpdater_loop(t *testing.T) {
|
|
||||||
dbs.NotifyReady()
|
|
||||||
|
|
||||||
updater := NewUpdater()
|
|
||||||
err := updater.loop()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
t.Log("ok")
|
|
||||||
}
|
|
||||||
@@ -4,6 +4,7 @@ package metrics
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/events"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -11,7 +12,15 @@ import (
|
|||||||
|
|
||||||
var SharedManager = NewManager()
|
var SharedManager = NewManager()
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
events.On(events.EventQuit, func() {
|
||||||
|
SharedManager.Quit()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
type Manager struct {
|
type Manager struct {
|
||||||
|
isQuiting bool
|
||||||
|
|
||||||
tasks map[int64]*Task // itemId => *Task
|
tasks map[int64]*Task // itemId => *Task
|
||||||
categoryTasks map[string][]*Task // category => []*Task
|
categoryTasks map[string][]*Task // category => []*Task
|
||||||
locker sync.RWMutex
|
locker sync.RWMutex
|
||||||
@@ -29,6 +38,10 @@ func NewManager() *Manager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *Manager) Update(items []*serverconfigs.MetricItemConfig) {
|
func (this *Manager) Update(items []*serverconfigs.MetricItemConfig) {
|
||||||
|
if this.isQuiting {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
this.locker.Lock()
|
this.locker.Lock()
|
||||||
defer this.locker.Unlock()
|
defer this.locker.Unlock()
|
||||||
|
|
||||||
@@ -101,6 +114,10 @@ func (this *Manager) Update(items []*serverconfigs.MetricItemConfig) {
|
|||||||
|
|
||||||
// Add 添加数据
|
// Add 添加数据
|
||||||
func (this *Manager) Add(obj MetricInterface) {
|
func (this *Manager) Add(obj MetricInterface) {
|
||||||
|
if this.isQuiting {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
this.locker.RLock()
|
this.locker.RLock()
|
||||||
for _, task := range this.categoryTasks[obj.MetricCategory()] {
|
for _, task := range this.categoryTasks[obj.MetricCategory()] {
|
||||||
task.Add(obj)
|
task.Add(obj)
|
||||||
@@ -119,3 +136,17 @@ func (this *Manager) HasTCPMetrics() bool {
|
|||||||
func (this *Manager) HasUDPMetrics() bool {
|
func (this *Manager) HasUDPMetrics() bool {
|
||||||
return this.hasUDPMetrics
|
return this.hasUDPMetrics
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Quit 退出管理器
|
||||||
|
func (this *Manager) Quit() {
|
||||||
|
this.isQuiting = true
|
||||||
|
|
||||||
|
remotelogs.Println("METRIC_MANAGER", "quit")
|
||||||
|
|
||||||
|
this.locker.Lock()
|
||||||
|
for _, task := range this.tasks {
|
||||||
|
_ = task.Stop()
|
||||||
|
}
|
||||||
|
this.tasks = map[int64]*Task{}
|
||||||
|
this.locker.Unlock()
|
||||||
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeNode/internal/utils/dbs"
|
"github.com/TeaOSLab/EdgeNode/internal/utils/dbs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/zero"
|
"github.com/TeaOSLab/EdgeNode/internal/zero"
|
||||||
"github.com/iwind/TeaGo/Tea"
|
"github.com/iwind/TeaGo/Tea"
|
||||||
|
"github.com/iwind/TeaGo/types"
|
||||||
_ "github.com/mattn/go-sqlite3"
|
_ "github.com/mattn/go-sqlite3"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -89,13 +90,23 @@ func (this *Task) Init() error {
|
|||||||
remotelogs.Println("METRIC", "create data dir '"+dir+"'")
|
remotelogs.Println("METRIC", "create data dir '"+dir+"'")
|
||||||
}
|
}
|
||||||
|
|
||||||
db, err := sql.Open("sqlite3", "file:"+dir+"/metric."+strconv.FormatInt(this.item.Id, 10)+".db?cache=shared&mode=rwc&_journal_mode=WAL&_sync=OFF")
|
var path = dir + "/metric." + types.String(this.item.Id) + ".db"
|
||||||
|
|
||||||
|
db, err := sql.Open("sqlite3", "file:"+path+"?cache=shared&mode=rwc&_journal_mode=WAL&_sync=OFF")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
db.SetMaxOpenConns(1)
|
db.SetMaxOpenConns(1)
|
||||||
this.db = dbs.NewDB(db)
|
this.db = dbs.NewDB(db)
|
||||||
|
|
||||||
|
// 恢复数据库
|
||||||
|
var recoverEnv, _ = os.LookupEnv("EdgeRecover")
|
||||||
|
if len(recoverEnv) > 0 {
|
||||||
|
for _, indexName := range []string{"serverId", "hash"} {
|
||||||
|
_, _ = db.Exec(`REINDEX "` + indexName + `"`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if teaconst.EnableDBStat {
|
if teaconst.EnableDBStat {
|
||||||
this.db.EnableStat(true)
|
this.db.EnableStat(true)
|
||||||
}
|
}
|
||||||
@@ -424,7 +435,7 @@ func (this *Task) Upload(pauseDuration time.Duration) error {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = rpcClient.MetricStatRPC().UploadMetricStats(rpcClient.Context(), &pb.UploadMetricStatsRequest{
|
_, err = rpcClient.MetricStatRPC.UploadMetricStats(rpcClient.Context(), &pb.UploadMetricStatsRequest{
|
||||||
MetricStats: pbStats,
|
MetricStats: pbStats,
|
||||||
Time: currentTime,
|
Time: currentTime,
|
||||||
ServerId: serverId,
|
ServerId: serverId,
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ func (this *ValueQueue) Loop() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for value := range this.valuesChan {
|
for value := range this.valuesChan {
|
||||||
_, err = rpcClient.NodeValueRPC().CreateNodeValue(rpcClient.Context(), &pb.CreateNodeValueRequest{
|
_, err = rpcClient.NodeValueRPC.CreateNodeValue(rpcClient.Context(), &pb.CreateNodeValueRequest{
|
||||||
Item: value.Item,
|
Item: value.Item,
|
||||||
ValueJSON: value.ValueJSON,
|
ValueJSON: value.ValueJSON,
|
||||||
CreatedAt: value.CreatedAt,
|
CreatedAt: value.CreatedAt,
|
||||||
|
|||||||
@@ -16,9 +16,9 @@ func TestValueQueue_RPC(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
_, err = rpcClient.NodeValueRPC().CreateNodeValue(rpcClient.Context(), &pb.CreateNodeValueRequest{})
|
_, err = rpcClient.NodeValueRPC.CreateNodeValue(rpcClient.Context(), &pb.CreateNodeValueRequest{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
statusErr, ok:= status.FromError(err)
|
statusErr, ok := status.FromError(err)
|
||||||
if ok {
|
if ok {
|
||||||
logs.Println(statusErr.Code())
|
logs.Println(statusErr.Code())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package nodes
|
package nodes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -17,7 +16,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/TeaOSLab/EdgeNode/internal/rpc"
|
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
executils "github.com/TeaOSLab/EdgeNode/internal/utils/exec"
|
||||||
"github.com/iwind/TeaGo/Tea"
|
"github.com/iwind/TeaGo/Tea"
|
||||||
"github.com/iwind/TeaGo/maps"
|
"github.com/iwind/TeaGo/maps"
|
||||||
"net/url"
|
"net/url"
|
||||||
@@ -73,7 +72,7 @@ func (this *APIStream) loop() error {
|
|||||||
cancelFunc()
|
cancelFunc()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
nodeStream, err := rpcClient.NodeRPC().NodeStream(ctx)
|
nodeStream, err := rpcClient.NodeRPC.NodeStream(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if this.isQuiting {
|
if this.isQuiting {
|
||||||
return nil
|
return nil
|
||||||
@@ -347,15 +346,15 @@ func (this *APIStream) handleCheckSystemdService(message *pb.NodeStreamMessage)
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var cmd = utils.NewCommandExecutor()
|
var shortName = teaconst.SystemdServiceName
|
||||||
shortName := teaconst.SystemdServiceName
|
var cmd = executils.NewTimeoutCmd(10*time.Second, systemctl, "is-enabled", shortName)
|
||||||
cmd.Add(systemctl, "is-enabled", shortName)
|
cmd.WithStdout()
|
||||||
output, err := cmd.Run()
|
err = cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.replyFail(message.RequestId, "'systemctl' command error: "+err.Error())
|
this.replyFail(message.RequestId, "'systemctl' command error: "+err.Error())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if output == "enabled" {
|
if cmd.Stdout() == "enabled" {
|
||||||
this.replyOk(message.RequestId, "ok")
|
this.replyOk(message.RequestId, "ok")
|
||||||
} else {
|
} else {
|
||||||
this.replyFail(message.RequestId, "not installed")
|
this.replyFail(message.RequestId, "not installed")
|
||||||
@@ -385,16 +384,15 @@ func (this *APIStream) handleCheckLocalFirewall(message *pb.NodeStreamMessage) e
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var cmd = exec.Command(nft, "--version")
|
var cmd = executils.NewTimeoutCmd(10*time.Second, nft, "--version")
|
||||||
var output = &bytes.Buffer{}
|
cmd.WithStdout()
|
||||||
cmd.Stdout = output
|
|
||||||
err = cmd.Run()
|
err = cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.replyFail(message.RequestId, "get version failed: "+err.Error())
|
this.replyFail(message.RequestId, "get version failed: "+err.Error())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var outputString = output.String()
|
var outputString = cmd.Stdout()
|
||||||
var versionMatches = regexp.MustCompile(`nftables v([\d.]+)`).FindStringSubmatch(outputString)
|
var versionMatches = regexp.MustCompile(`nftables v([\d.]+)`).FindStringSubmatch(outputString)
|
||||||
if len(versionMatches) <= 1 {
|
if len(versionMatches) <= 1 {
|
||||||
this.replyFail(message.RequestId, "can not get nft version")
|
this.replyFail(message.RequestId, "can not get nft version")
|
||||||
|
|||||||
@@ -5,55 +5,57 @@ package nodes
|
|||||||
import (
|
import (
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/conns"
|
||||||
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/ttlcache"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/waf"
|
"github.com/TeaOSLab/EdgeNode/internal/waf"
|
||||||
|
"github.com/iwind/TeaGo/Tea"
|
||||||
"github.com/iwind/TeaGo/types"
|
"github.com/iwind/TeaGo/types"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ClientConn 客户端连接
|
// ClientConn 客户端连接
|
||||||
type ClientConn struct {
|
type ClientConn struct {
|
||||||
once sync.Once
|
BaseClientConn
|
||||||
|
|
||||||
isTLS bool
|
isTLS bool
|
||||||
hasDeadline bool
|
hasDeadline bool
|
||||||
hasRead bool
|
hasRead bool
|
||||||
|
|
||||||
isLO bool // 是否为环路
|
isLO bool // 是否为环路
|
||||||
|
isInAllowList bool
|
||||||
|
|
||||||
hasResetSYNFlood bool
|
hasResetSYNFlood bool
|
||||||
|
|
||||||
BaseClientConn
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClientConn(conn net.Conn, isTLS bool, quickClose bool) net.Conn {
|
func NewClientConn(rawConn net.Conn, isTLS bool, quickClose bool, isInAllowList bool) net.Conn {
|
||||||
if quickClose {
|
|
||||||
// TCP
|
|
||||||
tcpConn, ok := conn.(*net.TCPConn)
|
|
||||||
if ok {
|
|
||||||
// TODO 可以在配置中设置此值
|
|
||||||
_ = tcpConn.SetLinger(nodeconfigs.DefaultTCPLinger)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 是否为环路
|
// 是否为环路
|
||||||
var remoteAddr = conn.RemoteAddr().String()
|
var remoteAddr = rawConn.RemoteAddr().String()
|
||||||
var isLO = strings.HasPrefix(remoteAddr, "127.0.0.1:") || strings.HasPrefix(remoteAddr, "[::1]:")
|
var isLO = strings.HasPrefix(remoteAddr, "127.0.0.1:") || strings.HasPrefix(remoteAddr, "[::1]:")
|
||||||
|
|
||||||
return &ClientConn{
|
var conn = &ClientConn{
|
||||||
BaseClientConn: BaseClientConn{rawConn: conn},
|
BaseClientConn: BaseClientConn{rawConn: rawConn},
|
||||||
isTLS: isTLS,
|
isTLS: isTLS,
|
||||||
isLO: isLO,
|
isLO: isLO,
|
||||||
|
isInAllowList: isInAllowList,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if quickClose {
|
||||||
|
// TODO 可以在配置中设置此值
|
||||||
|
_ = conn.SetLinger(nodeconfigs.DefaultTCPLinger)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加入到Map
|
||||||
|
conns.SharedMap.Add(conn)
|
||||||
|
|
||||||
|
return conn
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *ClientConn) Read(b []byte) (n int, err error) {
|
func (this *ClientConn) Read(b []byte) (n int, err error) {
|
||||||
@@ -86,20 +88,24 @@ func (this *ClientConn) Read(b []byte) (n int, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 检测是否为握手错误
|
||||||
var isHandshakeError = err != nil && os.IsTimeout(err) && !this.hasRead
|
var isHandshakeError = err != nil && os.IsTimeout(err) && !this.hasRead
|
||||||
if isHandshakeError {
|
if isHandshakeError {
|
||||||
_ = this.SetLinger(0)
|
_ = this.SetLinger(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SYN Flood检测
|
// 忽略白名单和局域网
|
||||||
if this.serverId == 0 || !this.hasResetSYNFlood {
|
if !this.isInAllowList && !utils.IsLocalIP(this.RawIP()) {
|
||||||
var synFloodConfig = sharedNodeConfig.SYNFloodConfig()
|
// SYN Flood检测
|
||||||
if synFloodConfig != nil && synFloodConfig.IsOn {
|
if this.serverId == 0 || !this.hasResetSYNFlood {
|
||||||
if isHandshakeError {
|
var synFloodConfig = sharedNodeConfig.SYNFloodConfig()
|
||||||
this.increaseSYNFlood(synFloodConfig)
|
if synFloodConfig != nil && synFloodConfig.IsOn {
|
||||||
} else if err == nil && !this.hasResetSYNFlood {
|
if isHandshakeError {
|
||||||
this.hasResetSYNFlood = true
|
this.increaseSYNFlood(synFloodConfig)
|
||||||
this.resetSYNFlood()
|
} else if err == nil && !this.hasResetSYNFlood {
|
||||||
|
this.hasResetSYNFlood = true
|
||||||
|
this.resetSYNFlood()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -110,11 +116,10 @@ func (this *ClientConn) Read(b []byte) (n int, err error) {
|
|||||||
func (this *ClientConn) Write(b []byte) (n int, err error) {
|
func (this *ClientConn) Write(b []byte) (n int, err error) {
|
||||||
n, err = this.rawConn.Write(b)
|
n, err = this.rawConn.Write(b)
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
atomic.AddUint64(&teaconst.OutTrafficBytes, uint64(n))
|
|
||||||
|
|
||||||
// 统计当前服务带宽
|
// 统计当前服务带宽
|
||||||
if this.serverId > 0 {
|
if this.serverId > 0 {
|
||||||
if !this.isLO { // 环路不统计带宽,避免缓存预热等行为产生带宽
|
if !this.isLO || Tea.IsTesting() { // 环路不统计带宽,避免缓存预热等行为产生带宽
|
||||||
|
atomic.AddUint64(&teaconst.OutTrafficBytes, uint64(n))
|
||||||
stats.SharedBandwidthStatManager.Add(this.userId, this.serverId, int64(n))
|
stats.SharedBandwidthStatManager.Add(this.userId, this.serverId, int64(n))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -132,6 +137,9 @@ func (this *ClientConn) Close() error {
|
|||||||
// 不能加条件限制,因为服务配置随时有变化
|
// 不能加条件限制,因为服务配置随时有变化
|
||||||
sharedClientConnLimiter.Remove(this.rawConn.RemoteAddr().String())
|
sharedClientConnLimiter.Remove(this.rawConn.RemoteAddr().String())
|
||||||
|
|
||||||
|
// 从conn map中移除
|
||||||
|
conns.SharedMap.Remove(this)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,6 +185,11 @@ func (this *ClientConn) increaseSYNFlood(synFloodConfig *firewallconfigs.SYNFloo
|
|||||||
if timeout <= 0 {
|
if timeout <= 0 {
|
||||||
timeout = 600
|
timeout = 600
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 关闭当前连接
|
||||||
|
_ = this.SetLinger(0)
|
||||||
|
_ = this.Close()
|
||||||
|
|
||||||
waf.SharedIPBlackList.RecordIP(waf.IPTypeAll, firewallconfigs.FirewallScopeGlobal, 0, ip, time.Now().Unix()+int64(timeout), 0, true, 0, 0, "疑似SYN Flood攻击,当前1分钟"+types.String(result)+"次空连接")
|
waf.SharedIPBlackList.RecordIP(waf.IPTypeAll, firewallconfigs.FirewallScopeGlobal, 0, ip, time.Now().Unix()+int64(timeout), 0, true, 0, 0, "疑似SYN Flood攻击,当前1分钟"+types.String(result)+"次空连接")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,10 @@
|
|||||||
|
|
||||||
package nodes
|
package nodes
|
||||||
|
|
||||||
import "net"
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
type BaseClientConn struct {
|
type BaseClientConn struct {
|
||||||
rawConn net.Conn
|
rawConn net.Conn
|
||||||
@@ -14,6 +17,8 @@ type BaseClientConn struct {
|
|||||||
hasLimit bool
|
hasLimit bool
|
||||||
|
|
||||||
isClosed bool
|
isClosed bool
|
||||||
|
|
||||||
|
rawIP string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *BaseClientConn) IsClosed() bool {
|
func (this *BaseClientConn) IsClosed() bool {
|
||||||
@@ -42,6 +47,17 @@ func (this *BaseClientConn) Bind(serverId int64, remoteAddr string, maxConnsPerS
|
|||||||
// SetServerId 设置服务ID
|
// SetServerId 设置服务ID
|
||||||
func (this *BaseClientConn) SetServerId(serverId int64) {
|
func (this *BaseClientConn) SetServerId(serverId int64) {
|
||||||
this.serverId = serverId
|
this.serverId = serverId
|
||||||
|
|
||||||
|
// 设置包装前连接
|
||||||
|
switch conn := this.rawConn.(type) {
|
||||||
|
case *tls.Conn:
|
||||||
|
nativeConn, ok := conn.NetConn().(ClientConnInterface)
|
||||||
|
if ok {
|
||||||
|
nativeConn.SetServerId(serverId)
|
||||||
|
}
|
||||||
|
case *ClientConn:
|
||||||
|
conn.SetServerId(serverId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServerId 读取当前连接绑定的服务ID
|
// ServerId 读取当前连接绑定的服务ID
|
||||||
@@ -52,6 +68,17 @@ func (this *BaseClientConn) ServerId() int64 {
|
|||||||
// SetUserId 设置所属服务的用户ID
|
// SetUserId 设置所属服务的用户ID
|
||||||
func (this *BaseClientConn) SetUserId(userId int64) {
|
func (this *BaseClientConn) SetUserId(userId int64) {
|
||||||
this.userId = userId
|
this.userId = userId
|
||||||
|
|
||||||
|
// 设置包装前连接
|
||||||
|
switch conn := this.rawConn.(type) {
|
||||||
|
case *tls.Conn:
|
||||||
|
nativeConn, ok := conn.NetConn().(ClientConnInterface)
|
||||||
|
if ok {
|
||||||
|
nativeConn.SetUserId(userId)
|
||||||
|
}
|
||||||
|
case *ClientConn:
|
||||||
|
conn.SetUserId(userId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// UserId 获取当前连接所属服务的用户ID
|
// UserId 获取当前连接所属服务的用户ID
|
||||||
@@ -61,14 +88,30 @@ func (this *BaseClientConn) UserId() int64 {
|
|||||||
|
|
||||||
// RawIP 原本IP
|
// RawIP 原本IP
|
||||||
func (this *BaseClientConn) RawIP() string {
|
func (this *BaseClientConn) RawIP() string {
|
||||||
|
if len(this.rawIP) > 0 {
|
||||||
|
return this.rawIP
|
||||||
|
}
|
||||||
|
|
||||||
ip, _, _ := net.SplitHostPort(this.rawConn.RemoteAddr().String())
|
ip, _, _ := net.SplitHostPort(this.rawConn.RemoteAddr().String())
|
||||||
|
this.rawIP = ip
|
||||||
return ip
|
return ip
|
||||||
}
|
}
|
||||||
|
|
||||||
// TCPConn 转换为TCPConn
|
// TCPConn 转换为TCPConn
|
||||||
func (this *BaseClientConn) TCPConn() (*net.TCPConn, bool) {
|
func (this *BaseClientConn) TCPConn() (tcpConn *net.TCPConn, ok bool) {
|
||||||
conn, ok := this.rawConn.(*net.TCPConn)
|
// 设置包装前连接
|
||||||
return conn, ok
|
switch conn := this.rawConn.(type) {
|
||||||
|
case *tls.Conn:
|
||||||
|
var internalConn = conn.NetConn()
|
||||||
|
clientConn, ok := internalConn.(*ClientConn)
|
||||||
|
if ok {
|
||||||
|
return clientConn.TCPConn()
|
||||||
|
}
|
||||||
|
tcpConn, ok = internalConn.(*net.TCPConn)
|
||||||
|
default:
|
||||||
|
tcpConn, ok = this.rawConn.(*net.TCPConn)
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetLinger 设置Linger
|
// SetLinger 设置Linger
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeNode/internal/iplibrary"
|
"github.com/TeaOSLab/EdgeNode/internal/iplibrary"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/waf"
|
"github.com/TeaOSLab/EdgeNode/internal/waf"
|
||||||
"net"
|
"net"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ClientListener 客户端网络监听
|
// ClientListener 客户端网络监听
|
||||||
@@ -40,12 +41,32 @@ func (this *ClientListener) Accept() (net.Conn, error) {
|
|||||||
|
|
||||||
// 是否在WAF名单中
|
// 是否在WAF名单中
|
||||||
ip, _, err := net.SplitHostPort(conn.RemoteAddr().String())
|
ip, _, err := net.SplitHostPort(conn.RemoteAddr().String())
|
||||||
|
var isInAllowList = false
|
||||||
if err == nil {
|
if err == nil {
|
||||||
canGoNext, _ := iplibrary.AllowIP(ip, 0)
|
canGoNext, inAllowList := iplibrary.AllowIP(ip, 0)
|
||||||
var beingDenied = !waf.SharedIPWhiteList.Contains(waf.IPTypeAll, firewallconfigs.FirewallScopeGlobal, 0, ip) &&
|
isInAllowList = inAllowList
|
||||||
waf.SharedIPBlackList.Contains(waf.IPTypeAll, firewallconfigs.FirewallScopeGlobal, 0, ip)
|
if !waf.SharedIPWhiteList.Contains(waf.IPTypeAll, firewallconfigs.FirewallScopeGlobal, 0, ip) {
|
||||||
|
expiresAt, ok := waf.SharedIPBlackList.ContainsExpires(waf.IPTypeAll, firewallconfigs.FirewallScopeGlobal, 0, ip)
|
||||||
|
if ok {
|
||||||
|
var timeout = expiresAt - time.Now().Unix()
|
||||||
|
if timeout > 0 {
|
||||||
|
canGoNext = false
|
||||||
|
|
||||||
if !canGoNext || beingDenied {
|
if timeout > 3600 {
|
||||||
|
timeout = 3600
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用本地防火墙延长封禁
|
||||||
|
var fw = firewalls.Firewall()
|
||||||
|
if fw != nil && !fw.IsMock() {
|
||||||
|
// 这里 int(int64) 转换的前提是限制了 timeout <= 3600,否则将有整型溢出的风险
|
||||||
|
_ = fw.DropSourceIP(ip, int(timeout), true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !canGoNext {
|
||||||
tcpConn, ok := conn.(*net.TCPConn)
|
tcpConn, ok := conn.(*net.TCPConn)
|
||||||
if ok {
|
if ok {
|
||||||
_ = tcpConn.SetLinger(0)
|
_ = tcpConn.SetLinger(0)
|
||||||
@@ -53,19 +74,11 @@ func (this *ClientListener) Accept() (net.Conn, error) {
|
|||||||
|
|
||||||
_ = conn.Close()
|
_ = conn.Close()
|
||||||
|
|
||||||
// 使用本地防火墙延长封禁
|
|
||||||
if beingDenied {
|
|
||||||
var fw = firewalls.Firewall()
|
|
||||||
if fw != nil && !fw.IsMock() {
|
|
||||||
_ = fw.DropSourceIP(ip, 120, true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.Accept()
|
return this.Accept()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return NewClientConn(conn, this.isTLS, this.quickClose), nil
|
return NewClientConn(conn, this.isTLS, this.quickClose, isInAllowList), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *ClientListener) Close() error {
|
func (this *ClientListener) Close() error {
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -96,7 +98,7 @@ Loop:
|
|||||||
this.rpcClient = client
|
this.rpcClient = client
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := this.rpcClient.HTTPAccessLogRPC().CreateHTTPAccessLogs(this.rpcClient.Context(), &pb.CreateHTTPAccessLogsRequest{HttpAccessLogs: accessLogs})
|
_, err := this.rpcClient.HTTPAccessLogRPC.CreateHTTPAccessLogs(this.rpcClient.Context(), &pb.CreateHTTPAccessLogsRequest{HttpAccessLogs: accessLogs})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// 是否包含了invalid UTF-8
|
// 是否包含了invalid UTF-8
|
||||||
if strings.Contains(err.Error(), "string field contains invalid UTF-8") {
|
if strings.Contains(err.Error(), "string field contains invalid UTF-8") {
|
||||||
@@ -105,7 +107,20 @@ Loop:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 重新提交
|
// 重新提交
|
||||||
_, err = this.rpcClient.HTTPAccessLogRPC().CreateHTTPAccessLogs(this.rpcClient.Context(), &pb.CreateHTTPAccessLogsRequest{HttpAccessLogs: accessLogs})
|
_, err = this.rpcClient.HTTPAccessLogRPC.CreateHTTPAccessLogs(this.rpcClient.Context(), &pb.CreateHTTPAccessLogsRequest{HttpAccessLogs: accessLogs})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 是否请求内容过大
|
||||||
|
statusCode, ok := status.FromError(err)
|
||||||
|
if ok && statusCode.Code() == codes.ResourceExhausted {
|
||||||
|
// 去除Body
|
||||||
|
for _, accessLog := range accessLogs {
|
||||||
|
accessLog.RequestBody = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重新提交
|
||||||
|
_, err = this.rpcClient.HTTPAccessLogRPC.CreateHTTPAccessLogs(this.rpcClient.Context(), &pb.CreateHTTPAccessLogsRequest{HttpAccessLogs: accessLogs})
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ func TestHTTPAccessLogQueue_Push(t *testing.T) {
|
|||||||
// logs.PrintAsJSON(accessLog)
|
// logs.PrintAsJSON(accessLog)
|
||||||
|
|
||||||
//t.Log(strings.ToValidUTF8(string(utf8Bytes), ""))
|
//t.Log(strings.ToValidUTF8(string(utf8Bytes), ""))
|
||||||
_, err = client.HTTPAccessLogRPC().CreateHTTPAccessLogs(client.Context(), &pb.CreateHTTPAccessLogsRequest{HttpAccessLogs: []*pb.HTTPAccessLog{
|
_, err = client.HTTPAccessLogRPC.CreateHTTPAccessLogs(client.Context(), &pb.CreateHTTPAccessLogsRequest{HttpAccessLogs: []*pb.HTTPAccessLog{
|
||||||
accessLog,
|
accessLog,
|
||||||
}})
|
}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -99,7 +99,7 @@ func TestHTTPAccessLogQueue_Push2(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
_, err = client.HTTPAccessLogRPC().CreateHTTPAccessLogs(client.Context(), &pb.CreateHTTPAccessLogsRequest{HttpAccessLogs: []*pb.HTTPAccessLog{
|
_, err = client.HTTPAccessLogRPC.CreateHTTPAccessLogs(client.Context(), &pb.CreateHTTPAccessLogsRequest{HttpAccessLogs: []*pb.HTTPAccessLog{
|
||||||
accessLog,
|
accessLog,
|
||||||
}})
|
}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ func (this *HTTPCacheTaskManager) Start() {
|
|||||||
|
|
||||||
if rpcClient != nil {
|
if rpcClient != nil {
|
||||||
for taskReq := range this.taskQueue {
|
for taskReq := range this.taskQueue {
|
||||||
_, err := rpcClient.ServerRPC().PurgeServerCache(rpcClient.Context(), taskReq)
|
_, err := rpcClient.ServerRPC.PurgeServerCache(rpcClient.Context(), taskReq)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
remotelogs.Error("HTTP_CACHE_TASK_MANAGER", "create purge task failed: "+err.Error())
|
remotelogs.Error("HTTP_CACHE_TASK_MANAGER", "create purge task failed: "+err.Error())
|
||||||
}
|
}
|
||||||
@@ -104,8 +104,12 @@ func (this *HTTPCacheTaskManager) Loop() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := rpcClient.HTTPCacheTaskKeyRPC().FindDoingHTTPCacheTaskKeys(rpcClient.Context(), &pb.FindDoingHTTPCacheTaskKeysRequest{})
|
resp, err := rpcClient.HTTPCacheTaskKeyRPC.FindDoingHTTPCacheTaskKeys(rpcClient.Context(), &pb.FindDoingHTTPCacheTaskKeysRequest{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// 忽略连接错误
|
||||||
|
if rpc.IsConnError(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,7 +135,7 @@ func (this *HTTPCacheTaskManager) Loop() error {
|
|||||||
pbResults = append(pbResults, pbResult)
|
pbResults = append(pbResults, pbResult)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = rpcClient.HTTPCacheTaskKeyRPC().UpdateHTTPCacheTaskKeysStatus(rpcClient.Context(), &pb.UpdateHTTPCacheTaskKeysStatusRequest{KeyResults: pbResults})
|
_, err = rpcClient.HTTPCacheTaskKeyRPC.UpdateHTTPCacheTaskKeysStatus(rpcClient.Context(), &pb.UpdateHTTPCacheTaskKeysStatusRequest{KeyResults: pbResults})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
|
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
|
||||||
|
iplib "github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||||
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/iplibrary"
|
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/metrics"
|
"github.com/TeaOSLab/EdgeNode/internal/metrics"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/stats"
|
"github.com/TeaOSLab/EdgeNode/internal/stats"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
@@ -68,6 +68,7 @@ type HTTPRequest struct {
|
|||||||
filePath string // 请求的文件名,仅在读取Root目录下的内容时不为空
|
filePath string // 请求的文件名,仅在读取Root目录下的内容时不为空
|
||||||
origin *serverconfigs.OriginConfig // 源站
|
origin *serverconfigs.OriginConfig // 源站
|
||||||
originAddr string // 源站实际地址
|
originAddr string // 源站实际地址
|
||||||
|
originStatus int32 // 源站响应代码
|
||||||
errors []string // 错误信息
|
errors []string // 错误信息
|
||||||
rewriteRule *serverconfigs.HTTPRewriteRule // 匹配到的重写规则
|
rewriteRule *serverconfigs.HTTPRewriteRule // 匹配到的重写规则
|
||||||
rewriteReplace string // 重写规则的目标
|
rewriteReplace string // 重写规则的目标
|
||||||
@@ -228,6 +229,14 @@ func (this *HTTPRequest) Do() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 防盗链
|
||||||
|
if !this.isSubRequest && this.web.Referers != nil && this.web.Referers.IsOn {
|
||||||
|
if this.doCheckReferers() {
|
||||||
|
this.doEnd()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 访问控制
|
// 访问控制
|
||||||
if !this.isSubRequest && this.web.Auth != nil && this.web.Auth.IsOn {
|
if !this.isSubRequest && this.web.Auth != nil && this.web.Auth.IsOn {
|
||||||
if this.doAuth() {
|
if this.doAuth() {
|
||||||
@@ -512,6 +521,11 @@ func (this *HTTPRequest) configureWeb(web *serverconfigs.HTTPWebConfig, isTop bo
|
|||||||
this.web.Auth = web.Auth
|
this.web.Auth = web.Auth
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// referers
|
||||||
|
if web.Referers != nil && (web.Referers.IsPrior || isTop) {
|
||||||
|
this.web.Referers = web.Referers
|
||||||
|
}
|
||||||
|
|
||||||
// request limit
|
// request limit
|
||||||
if web.RequestLimit != nil && (web.RequestLimit.IsPrior || isTop) {
|
if web.RequestLimit != nil && (web.RequestLimit.IsPrior || isTop) {
|
||||||
this.web.RequestLimit = web.RequestLimit
|
this.web.RequestLimit = web.RequestLimit
|
||||||
@@ -677,7 +691,7 @@ func (this *HTTPRequest) Format(source string) string {
|
|||||||
case "remoteAddrValue":
|
case "remoteAddrValue":
|
||||||
return this.requestRemoteAddr(false)
|
return this.requestRemoteAddr(false)
|
||||||
case "rawRemoteAddr":
|
case "rawRemoteAddr":
|
||||||
addr := this.RawReq.RemoteAddr
|
var addr = this.RawReq.RemoteAddr
|
||||||
host, _, err := net.SplitHostPort(addr)
|
host, _, err := net.SplitHostPort(addr)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
addr = host
|
addr = host
|
||||||
@@ -928,42 +942,47 @@ func (this *HTTPRequest) Format(source string) string {
|
|||||||
|
|
||||||
// geo
|
// geo
|
||||||
if prefix == "geo" {
|
if prefix == "geo" {
|
||||||
result, _ := iplibrary.SharedLibrary.Lookup(this.requestRemoteAddr(true))
|
var result = iplib.LookupIP(this.requestRemoteAddr(true))
|
||||||
|
|
||||||
switch suffix {
|
switch suffix {
|
||||||
case "country.name":
|
case "country.name":
|
||||||
if result != nil {
|
if result != nil && result.IsOk() {
|
||||||
return result.Country
|
return result.CountryName()
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
case "country.id":
|
case "country.id":
|
||||||
if result != nil {
|
if result != nil && result.IsOk() {
|
||||||
return types.String(iplibrary.SharedCountryManager.Lookup(result.Country))
|
return types.String(result.CountryId())
|
||||||
}
|
}
|
||||||
return "0"
|
return "0"
|
||||||
case "province.name":
|
case "province.name":
|
||||||
if result != nil {
|
if result != nil && result.IsOk() {
|
||||||
return result.Province
|
return result.ProvinceName()
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
case "province.id":
|
case "province.id":
|
||||||
if result != nil {
|
if result != nil && result.IsOk() {
|
||||||
return types.String(iplibrary.SharedProvinceManager.Lookup(result.Province))
|
return types.String(result.ProvinceId())
|
||||||
}
|
}
|
||||||
return "0"
|
return "0"
|
||||||
case "city.name":
|
case "city.name":
|
||||||
if result != nil {
|
if result != nil && result.IsOk() {
|
||||||
return result.City
|
return result.CityName()
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
case "city.id":
|
case "city.id":
|
||||||
if result != nil {
|
if result != nil && result.IsOk() {
|
||||||
var provinceId = iplibrary.SharedProvinceManager.Lookup(result.Province)
|
return types.String(result.CityId())
|
||||||
if provinceId > 0 {
|
}
|
||||||
return types.String(iplibrary.SharedCityManager.Lookup(provinceId, result.City))
|
return "0"
|
||||||
} else {
|
case "town.name":
|
||||||
return "0"
|
if result != nil && result.IsOk() {
|
||||||
}
|
return result.TownName()
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
case "town.id":
|
||||||
|
if result != nil && result.IsOk() {
|
||||||
|
return types.String(result.TownId())
|
||||||
}
|
}
|
||||||
return "0"
|
return "0"
|
||||||
}
|
}
|
||||||
@@ -971,16 +990,16 @@ func (this *HTTPRequest) Format(source string) string {
|
|||||||
|
|
||||||
// ips
|
// ips
|
||||||
if prefix == "isp" {
|
if prefix == "isp" {
|
||||||
result, _ := iplibrary.SharedLibrary.Lookup(this.requestRemoteAddr(true))
|
var result = iplib.LookupIP(this.requestRemoteAddr(true))
|
||||||
|
|
||||||
switch suffix {
|
switch suffix {
|
||||||
case "name":
|
case "name":
|
||||||
if result != nil {
|
if result != nil && result.IsOk() {
|
||||||
return result.ISP
|
return result.ProviderName()
|
||||||
}
|
}
|
||||||
case "id":
|
case "id":
|
||||||
if result != nil {
|
if result != nil && result.IsOk() {
|
||||||
return types.String(iplibrary.SharedProviderManager.Lookup(result.ISP))
|
return types.String(result.ProviderId())
|
||||||
}
|
}
|
||||||
return "0"
|
return "0"
|
||||||
}
|
}
|
||||||
@@ -1098,7 +1117,7 @@ func (this *HTTPRequest) requestRemoteAddr(supportVar bool) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Remote-Addr
|
// Remote-Addr
|
||||||
remoteAddr := this.RawReq.RemoteAddr
|
var remoteAddr = this.RawReq.RemoteAddr
|
||||||
host, _, err := net.SplitHostPort(remoteAddr)
|
host, _, err := net.SplitHostPort(remoteAddr)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if supportVar {
|
if supportVar {
|
||||||
@@ -1167,7 +1186,7 @@ func (this *HTTPRequest) requestRemoteUser() string {
|
|||||||
|
|
||||||
// Path 请求的URL中路径部分
|
// Path 请求的URL中路径部分
|
||||||
func (this *HTTPRequest) Path() string {
|
func (this *HTTPRequest) Path() string {
|
||||||
uri, err := url.ParseRequestURI(this.rawURI)
|
uri, err := url.ParseRequestURI(this.uri)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
@@ -1315,7 +1334,7 @@ func (this *HTTPRequest) RemoteAddr() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *HTTPRequest) RawRemoteAddr() string {
|
func (this *HTTPRequest) RawRemoteAddr() string {
|
||||||
addr := this.RawReq.RemoteAddr
|
var addr = this.RawReq.RemoteAddr
|
||||||
host, _, err := net.SplitHostPort(addr)
|
host, _, err := net.SplitHostPort(addr)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
addr = host
|
addr = host
|
||||||
@@ -1423,18 +1442,21 @@ func (this *HTTPRequest) Done() {
|
|||||||
func (this *HTTPRequest) Close() {
|
func (this *HTTPRequest) Close() {
|
||||||
this.Done()
|
this.Done()
|
||||||
|
|
||||||
requestConn := this.RawReq.Context().Value(HTTPConnContextKey)
|
var requestConn = this.RawReq.Context().Value(HTTPConnContextKey)
|
||||||
if requestConn == nil {
|
if requestConn == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lingerConn, ok := requestConn.(LingerConn)
|
||||||
|
if ok {
|
||||||
|
_ = lingerConn.SetLinger(0)
|
||||||
|
}
|
||||||
|
|
||||||
conn, ok := requestConn.(net.Conn)
|
conn, ok := requestConn.(net.Conn)
|
||||||
if ok {
|
if ok {
|
||||||
_ = conn.Close()
|
_ = conn.Close()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allow 放行
|
// Allow 放行
|
||||||
@@ -1589,9 +1611,7 @@ func (this *HTTPRequest) fixRequestHeader(header http.Header) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 处理自定义Response Header
|
// 处理自定义Response Header
|
||||||
func (this *HTTPRequest) processResponseHeaders(statusCode int) {
|
func (this *HTTPRequest) processResponseHeaders(responseHeader http.Header, statusCode int) {
|
||||||
var responseHeader = this.writer.Header()
|
|
||||||
|
|
||||||
// 删除/添加/替换Header
|
// 删除/添加/替换Header
|
||||||
// TODO 实现AddTrailers
|
// TODO 实现AddTrailers
|
||||||
if this.web.ResponseHeaderPolicy != nil && this.web.ResponseHeaderPolicy.IsOn {
|
if this.web.ResponseHeaderPolicy != nil && this.web.ResponseHeaderPolicy.IsOn {
|
||||||
@@ -1721,7 +1741,7 @@ func (this *HTTPRequest) canIgnore(err error) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// HTTP/2流错误
|
// HTTP/2流错误
|
||||||
if err.Error() == "http2: stream closed" || err.Error() == "client disconnected" { // errStreamClosed, errClientDisconnected
|
if err.Error() == "http2: stream closed" || strings.Contains(err.Error(), "stream error") || err.Error() == "client disconnected" { // errStreamClosed, errClientDisconnected
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ func (this *HTTPRequest) doACME() (shouldStop bool) {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
keyResp, err := rpcClient.ACMEAuthenticationRPC().FindACMEAuthenticationKeyWithToken(rpcClient.Context(), &pb.FindACMEAuthenticationKeyWithTokenRequest{Token: token})
|
keyResp, err := rpcClient.ACMEAuthenticationRPC.FindACMEAuthenticationKeyWithToken(rpcClient.Context(), &pb.FindACMEAuthenticationKeyWithTokenRequest{Token: token})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
remotelogs.Error("RPC", "[ACME]read key for token failed: "+err.Error())
|
remotelogs.Error("RPC", "[ACME]read key for token failed: "+err.Error())
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -19,7 +19,10 @@ func (this *HTTPRequest) doAuth() (shouldStop bool) {
|
|||||||
if !ref.IsOn || ref.AuthPolicy == nil || !ref.AuthPolicy.IsOn {
|
if !ref.IsOn || ref.AuthPolicy == nil || !ref.AuthPolicy.IsOn {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
b, err := ref.AuthPolicy.Filter(this.RawReq, func(subReq *http.Request) (status int, err error) {
|
if !ref.AuthPolicy.MatchRequest(this.RawReq) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ok, newURI, uriChanged, err := ref.AuthPolicy.Filter(this.RawReq, func(subReq *http.Request) (status int, err error) {
|
||||||
subReq.TLS = this.RawReq.TLS
|
subReq.TLS = this.RawReq.TLS
|
||||||
subReq.RemoteAddr = this.RawReq.RemoteAddr
|
subReq.RemoteAddr = this.RawReq.RemoteAddr
|
||||||
subReq.Host = this.RawReq.Host
|
subReq.Host = this.RawReq.Host
|
||||||
@@ -36,24 +39,32 @@ func (this *HTTPRequest) doAuth() (shouldStop bool) {
|
|||||||
this.write50x(err, http.StatusInternalServerError, "Failed to execute the AuthPolicy", "认证策略执行失败", false)
|
this.write50x(err, http.StatusInternalServerError, "Failed to execute the AuthPolicy", "认证策略执行失败", false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if b {
|
if ok {
|
||||||
|
if uriChanged {
|
||||||
|
this.uri = newURI
|
||||||
|
}
|
||||||
|
this.tags = append(this.tags, ref.AuthPolicy.Type)
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
|
// Basic Auth比较特殊
|
||||||
if ref.AuthPolicy.Type == serverconfigs.HTTPAuthTypeBasicAuth {
|
if ref.AuthPolicy.Type == serverconfigs.HTTPAuthTypeBasicAuth {
|
||||||
var method = ref.AuthPolicy.Method().(*serverconfigs.HTTPAuthBasicMethod)
|
method, ok := ref.AuthPolicy.Method().(*serverconfigs.HTTPAuthBasicMethod)
|
||||||
var headerValue = "Basic realm=\""
|
if ok {
|
||||||
if len(method.Realm) > 0 {
|
var headerValue = "Basic realm=\""
|
||||||
headerValue += method.Realm
|
if len(method.Realm) > 0 {
|
||||||
} else {
|
headerValue += method.Realm
|
||||||
headerValue += this.ReqHost
|
} else {
|
||||||
|
headerValue += this.ReqHost
|
||||||
|
}
|
||||||
|
headerValue += "\""
|
||||||
|
if len(method.Charset) > 0 {
|
||||||
|
headerValue += ", charset=\"" + method.Charset + "\""
|
||||||
|
}
|
||||||
|
this.writer.Header()["WWW-Authenticate"] = []string{headerValue}
|
||||||
}
|
}
|
||||||
headerValue += "\""
|
|
||||||
if len(method.Charset) > 0 {
|
|
||||||
headerValue += ", charset=\"" + method.Charset + "\""
|
|
||||||
}
|
|
||||||
this.writer.Header()["WWW-Authenticate"] = []string{headerValue}
|
|
||||||
}
|
}
|
||||||
this.writer.WriteHeader(http.StatusUnauthorized)
|
this.writer.WriteHeader(http.StatusUnauthorized)
|
||||||
|
this.tags = append(this.tags, ref.AuthPolicy.Type)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,12 +44,11 @@ func (this *HTTPRequest) doCacheRead(useStale bool) (shouldStop bool) {
|
|||||||
// 检查服务独立的缓存条件
|
// 检查服务独立的缓存条件
|
||||||
refType := ""
|
refType := ""
|
||||||
for _, cacheRef := range this.web.Cache.CacheRefs {
|
for _, cacheRef := range this.web.Cache.CacheRefs {
|
||||||
if !cacheRef.IsOn ||
|
if !cacheRef.IsOn {
|
||||||
cacheRef.Conds == nil ||
|
|
||||||
!cacheRef.Conds.HasRequestConds() {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if cacheRef.Conds.MatchRequest(this.Format) {
|
if (cacheRef.Conds != nil && cacheRef.Conds.HasRequestConds() && cacheRef.Conds.MatchRequest(this.Format)) ||
|
||||||
|
(cacheRef.SimpleCond != nil && cacheRef.SimpleCond.Match(this.Format)) {
|
||||||
if cacheRef.IsReverse {
|
if cacheRef.IsReverse {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -61,12 +60,11 @@ func (this *HTTPRequest) doCacheRead(useStale bool) (shouldStop bool) {
|
|||||||
if this.cacheRef == nil && !this.web.Cache.DisablePolicyRefs {
|
if this.cacheRef == nil && !this.web.Cache.DisablePolicyRefs {
|
||||||
// 检查策略默认的缓存条件
|
// 检查策略默认的缓存条件
|
||||||
for _, cacheRef := range cachePolicy.CacheRefs {
|
for _, cacheRef := range cachePolicy.CacheRefs {
|
||||||
if !cacheRef.IsOn ||
|
if !cacheRef.IsOn {
|
||||||
cacheRef.Conds == nil ||
|
|
||||||
!cacheRef.Conds.HasRequestConds() {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if cacheRef.Conds.MatchRequest(this.Format) {
|
if (cacheRef.Conds != nil && cacheRef.Conds.HasRequestConds() && cacheRef.Conds.MatchRequest(this.Format)) ||
|
||||||
|
(cacheRef.SimpleCond != nil && cacheRef.SimpleCond.Match(this.Format)) {
|
||||||
if cacheRef.IsReverse {
|
if cacheRef.IsReverse {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -157,7 +155,7 @@ func (this *HTTPRequest) doCacheRead(useStale bool) (shouldStop bool) {
|
|||||||
for _, subKey := range subKeys {
|
for _, subKey := range subKeys {
|
||||||
err := storage.Delete(subKey)
|
err := storage.Delete(subKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
remotelogs.Error("HTTP_REQUEST_CACHE", "purge failed: "+err.Error())
|
remotelogs.ErrorServer("HTTP_REQUEST_CACHE", "purge failed: "+err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,7 +260,7 @@ func (this *HTTPRequest) doCacheRead(useStale bool) (shouldStop bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !this.canIgnore(err) {
|
if !this.canIgnore(err) {
|
||||||
remotelogs.Warn("HTTP_REQUEST_CACHE", this.URL()+": read from cache failed: open cache failed: "+err.Error())
|
remotelogs.WarnServer("HTTP_REQUEST_CACHE", this.URL()+": read from cache failed: open cache failed: "+err.Error())
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -322,7 +320,7 @@ func (this *HTTPRequest) doCacheRead(useStale bool) (shouldStop bool) {
|
|||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !this.canIgnore(err) {
|
if !this.canIgnore(err) {
|
||||||
remotelogs.Warn("HTTP_REQUEST_CACHE", this.URL()+": read from cache failed: read header failed: "+err.Error())
|
remotelogs.WarnServer("HTTP_REQUEST_CACHE", this.URL()+": read from cache failed: read header failed: "+err.Error())
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -374,7 +372,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(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
|
||||||
@@ -386,7 +384,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(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
|
||||||
@@ -395,7 +393,7 @@ func (this *HTTPRequest) doCacheRead(useStale bool) (shouldStop bool) {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
this.processResponseHeaders(reader.Status())
|
this.processResponseHeaders(this.writer.Header(), reader.Status())
|
||||||
this.addExpiresHeader(reader.ExpiresAt())
|
this.addExpiresHeader(reader.ExpiresAt())
|
||||||
|
|
||||||
// 返回上级节点过期时间
|
// 返回上级节点过期时间
|
||||||
@@ -424,7 +422,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(http.StatusRequestedRangeNotSatisfiable)
|
this.processResponseHeaders(this.writer.Header(), http.StatusRequestedRangeNotSatisfiable)
|
||||||
this.writer.WriteHeader(http.StatusRequestedRangeNotSatisfiable)
|
this.writer.WriteHeader(http.StatusRequestedRangeNotSatisfiable)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -432,7 +430,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(http.StatusRequestedRangeNotSatisfiable)
|
this.processResponseHeaders(this.writer.Header(), http.StatusRequestedRangeNotSatisfiable)
|
||||||
this.writer.WriteHeader(http.StatusRequestedRangeNotSatisfiable)
|
this.writer.WriteHeader(http.StatusRequestedRangeNotSatisfiable)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -441,7 +439,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(http.StatusRequestedRangeNotSatisfiable)
|
this.processResponseHeaders(this.writer.Header(), http.StatusRequestedRangeNotSatisfiable)
|
||||||
this.writer.WriteHeader(http.StatusRequestedRangeNotSatisfiable)
|
this.writer.WriteHeader(http.StatusRequestedRangeNotSatisfiable)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -468,12 +466,12 @@ 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(http.StatusRequestedRangeNotSatisfiable)
|
this.processResponseHeaders(this.writer.Header(), http.StatusRequestedRangeNotSatisfiable)
|
||||||
this.writer.WriteHeader(http.StatusRequestedRangeNotSatisfiable)
|
this.writer.WriteHeader(http.StatusRequestedRangeNotSatisfiable)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if !this.canIgnore(err) {
|
if !this.canIgnore(err) {
|
||||||
remotelogs.Warn("HTTP_REQUEST_CACHE", this.URL()+": read from cache failed: "+err.Error())
|
remotelogs.WarnServer("HTTP_REQUEST_CACHE", this.URL()+": read from cache failed: "+err.Error())
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -519,7 +517,7 @@ func (this *HTTPRequest) doCacheRead(useStale bool) (shouldStop bool) {
|
|||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !this.canIgnore(err) {
|
if !this.canIgnore(err) {
|
||||||
remotelogs.Warn("HTTP_REQUEST_CACHE", this.URL()+": read from cache failed: "+err.Error())
|
remotelogs.WarnServer("HTTP_REQUEST_CACHE", this.URL()+": read from cache failed: "+err.Error())
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -556,7 +554,7 @@ func (this *HTTPRequest) doCacheRead(useStale bool) (shouldStop bool) {
|
|||||||
this.varMapping["cache.status"] = "MISS"
|
this.varMapping["cache.status"] = "MISS"
|
||||||
|
|
||||||
if !this.canIgnore(err) {
|
if !this.canIgnore(err) {
|
||||||
remotelogs.Warn("HTTP_REQUEST_CACHE", this.URL()+": read from cache failed: read body failed: "+err.Error())
|
remotelogs.WarnServer("HTTP_REQUEST_CACHE", this.URL()+": read from cache failed: read body failed: "+err.Error())
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,10 +25,10 @@ const httpStatusPageTemplate = `<!DOCTYPE html>
|
|||||||
</html>`
|
</html>`
|
||||||
|
|
||||||
func (this *HTTPRequest) write404() {
|
func (this *HTTPRequest) write404() {
|
||||||
this.writeCode(http.StatusNotFound)
|
this.writeCode(http.StatusNotFound, "", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *HTTPRequest) writeCode(statusCode int) {
|
func (this *HTTPRequest) writeCode(statusCode int, enMessage string, zhMessage string) {
|
||||||
if this.doPage(statusCode) {
|
if this.doPage(statusCode) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -42,12 +42,22 @@ func (this *HTTPRequest) writeCode(statusCode int) {
|
|||||||
case "requestId":
|
case "requestId":
|
||||||
return this.requestId
|
return this.requestId
|
||||||
case "message":
|
case "message":
|
||||||
return "" // 空
|
var acceptLanguages = this.RawReq.Header.Get("Accept-Language")
|
||||||
|
if len(acceptLanguages) > 0 {
|
||||||
|
var index = strings.Index(acceptLanguages, ",")
|
||||||
|
if index > 0 {
|
||||||
|
var firstLanguage = acceptLanguages[:index]
|
||||||
|
if firstLanguage == "zh-CN" {
|
||||||
|
return zhMessage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return enMessage
|
||||||
}
|
}
|
||||||
return "${" + varName + "}"
|
return "${" + varName + "}"
|
||||||
})
|
})
|
||||||
|
|
||||||
this.processResponseHeaders(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))
|
||||||
@@ -100,7 +110,7 @@ func (this *HTTPRequest) write50x(err error, statusCode int, enMessage string, z
|
|||||||
return "${" + varName + "}"
|
return "${" + varName + "}"
|
||||||
})
|
})
|
||||||
|
|
||||||
this.processResponseHeaders(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))
|
||||||
|
|||||||
@@ -187,7 +187,7 @@ func (this *HTTPRequest) doFastcgi() (shouldStop bool) {
|
|||||||
|
|
||||||
// 响应Header
|
// 响应Header
|
||||||
this.writer.AddHeaders(resp.Header)
|
this.writer.AddHeaders(resp.Header)
|
||||||
this.processResponseHeaders(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)
|
||||||
|
|||||||
@@ -34,10 +34,10 @@ func (this *HTTPRequest) doHostRedirect() (blocked bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if u.Status <= 0 {
|
if u.Status <= 0 {
|
||||||
this.processResponseHeaders(http.StatusTemporaryRedirect)
|
this.processResponseHeaders(this.writer.Header(), http.StatusTemporaryRedirect)
|
||||||
http.Redirect(this.RawWriter, this.RawReq, afterURL, http.StatusTemporaryRedirect)
|
http.Redirect(this.RawWriter, this.RawReq, afterURL, http.StatusTemporaryRedirect)
|
||||||
} else {
|
} else {
|
||||||
this.processResponseHeaders(u.Status)
|
this.processResponseHeaders(this.writer.Header(), u.Status)
|
||||||
http.Redirect(this.RawWriter, this.RawReq, afterURL, u.Status)
|
http.Redirect(this.RawWriter, this.RawReq, afterURL, u.Status)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
@@ -81,10 +81,10 @@ func (this *HTTPRequest) doHostRedirect() (blocked bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if u.Status <= 0 {
|
if u.Status <= 0 {
|
||||||
this.processResponseHeaders(http.StatusTemporaryRedirect)
|
this.processResponseHeaders(this.writer.Header(), http.StatusTemporaryRedirect)
|
||||||
http.Redirect(this.RawWriter, this.RawReq, afterURL, http.StatusTemporaryRedirect)
|
http.Redirect(this.RawWriter, this.RawReq, afterURL, http.StatusTemporaryRedirect)
|
||||||
} else {
|
} else {
|
||||||
this.processResponseHeaders(u.Status)
|
this.processResponseHeaders(this.writer.Header(), u.Status)
|
||||||
http.Redirect(this.RawWriter, this.RawReq, afterURL, u.Status)
|
http.Redirect(this.RawWriter, this.RawReq, afterURL, u.Status)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
@@ -104,10 +104,10 @@ func (this *HTTPRequest) doHostRedirect() (blocked bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if u.Status <= 0 {
|
if u.Status <= 0 {
|
||||||
this.processResponseHeaders(http.StatusTemporaryRedirect)
|
this.processResponseHeaders(this.writer.Header(), http.StatusTemporaryRedirect)
|
||||||
http.Redirect(this.RawWriter, this.RawReq, afterURL, http.StatusTemporaryRedirect)
|
http.Redirect(this.RawWriter, this.RawReq, afterURL, http.StatusTemporaryRedirect)
|
||||||
} else {
|
} else {
|
||||||
this.processResponseHeaders(u.Status)
|
this.processResponseHeaders(this.writer.Header(), u.Status)
|
||||||
http.Redirect(this.RawWriter, this.RawReq, afterURL, u.Status)
|
http.Redirect(this.RawWriter, this.RawReq, afterURL, u.Status)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ func (this *HTTPRequest) doRequestLimit() (shouldStop bool) {
|
|||||||
// TODO 处理分片提交的内容
|
// TODO 处理分片提交的内容
|
||||||
if this.web.RequestLimit.MaxBodyBytes() > 0 &&
|
if this.web.RequestLimit.MaxBodyBytes() > 0 &&
|
||||||
this.RawReq.ContentLength > this.web.RequestLimit.MaxBodyBytes() {
|
this.RawReq.ContentLength > this.web.RequestLimit.MaxBodyBytes() {
|
||||||
this.writeCode(http.StatusRequestEntityTooLarge)
|
this.writeCode(http.StatusRequestEntityTooLarge, "", "")
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ func (this *HTTPRequest) doRequestLimit() (shouldStop bool) {
|
|||||||
clientConn, ok := requestConn.(ClientConnInterface)
|
clientConn, ok := requestConn.(ClientConnInterface)
|
||||||
if ok && !clientConn.IsBound() {
|
if ok && !clientConn.IsBound() {
|
||||||
if !clientConn.Bind(this.ReqServer.Id, this.requestRemoteAddr(true), this.web.RequestLimit.MaxConns, this.web.RequestLimit.MaxConnsPerIP) {
|
if !clientConn.Bind(this.ReqServer.Id, this.requestRemoteAddr(true), this.web.RequestLimit.MaxConns, this.web.RequestLimit.MaxConnsPerIP) {
|
||||||
this.writeCode(http.StatusTooManyRequests)
|
this.writeCode(http.StatusTooManyRequests, "", "")
|
||||||
this.Close()
|
this.Close()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -146,6 +146,7 @@ func (this *HTTPRequest) log() {
|
|||||||
if this.origin != nil {
|
if this.origin != nil {
|
||||||
accessLog.OriginId = this.origin.Id
|
accessLog.OriginId = this.origin.Id
|
||||||
accessLog.OriginAddress = this.originAddr
|
accessLog.OriginAddress = this.originAddr
|
||||||
|
accessLog.OriginStatus = this.originStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
// 请求Body
|
// 请求Body
|
||||||
|
|||||||
@@ -34,17 +34,7 @@ func (this *HTTPRequest) MetricValue(value string) (result int64, ok bool) {
|
|||||||
}
|
}
|
||||||
return this.RawReq.ContentLength + hl, true
|
return this.RawReq.ContentLength + hl, true
|
||||||
case "${countConnection}":
|
case "${countConnection}":
|
||||||
metricNewConnMapLocker.Lock()
|
return 1, true
|
||||||
_, ok := metricNewConnMap[this.RawReq.RemoteAddr]
|
|
||||||
if ok {
|
|
||||||
delete(metricNewConnMap, this.RawReq.RemoteAddr)
|
|
||||||
}
|
|
||||||
metricNewConnMapLocker.Unlock()
|
|
||||||
if ok {
|
|
||||||
return 1, true
|
|
||||||
} else {
|
|
||||||
return 0, false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return 0, false
|
return 0, false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ func (this *HTTPRequest) doMismatch() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 根据配置进行相应的处理
|
// 根据配置进行相应的处理
|
||||||
if sharedNodeConfig.GlobalConfig != nil && sharedNodeConfig.GlobalConfig.HTTPAll.MatchDomainStrictly {
|
if sharedNodeConfig.GlobalServerConfig != nil && sharedNodeConfig.GlobalServerConfig.HTTPAll.MatchDomainStrictly {
|
||||||
// 检查cc
|
// 检查cc
|
||||||
// TODO 可以在管理端配置是否开启以及最多尝试次数
|
// TODO 可以在管理端配置是否开启以及最多尝试次数
|
||||||
if len(remoteIP) > 0 {
|
if len(remoteIP) > 0 {
|
||||||
@@ -46,7 +46,7 @@ func (this *HTTPRequest) doMismatch() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 处理当前连接
|
// 处理当前连接
|
||||||
var httpAllConfig = sharedNodeConfig.GlobalConfig.HTTPAll
|
var httpAllConfig = sharedNodeConfig.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 {
|
||||||
|
|||||||
@@ -60,11 +60,11 @@ func (this *HTTPRequest) doPage(status int) (shouldStop bool) {
|
|||||||
// 修改状态码
|
// 修改状态码
|
||||||
if page.NewStatus > 0 {
|
if page.NewStatus > 0 {
|
||||||
// 自定义响应Headers
|
// 自定义响应Headers
|
||||||
this.processResponseHeaders(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(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)
|
||||||
}
|
}
|
||||||
@@ -99,11 +99,11 @@ func (this *HTTPRequest) doPage(status int) (shouldStop bool) {
|
|||||||
// 修改状态码
|
// 修改状态码
|
||||||
if page.NewStatus > 0 {
|
if page.NewStatus > 0 {
|
||||||
// 自定义响应Headers
|
// 自定义响应Headers
|
||||||
this.processResponseHeaders(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(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(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.
|
|||||||
}
|
}
|
||||||
|
|
||||||
newURL := "https://" + host + this.RawReq.RequestURI
|
newURL := "https://" + host + this.RawReq.RequestURI
|
||||||
this.processResponseHeaders(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
|
||||||
|
|||||||
45
internal/nodes/http_request_referers.go
Normal file
45
internal/nodes/http_request_referers.go
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||||
|
|
||||||
|
package nodes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (this *HTTPRequest) doCheckReferers() (shouldStop bool) {
|
||||||
|
if this.web.Referers == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var refererURL = this.RawReq.Header.Get("Referer")
|
||||||
|
if len(refererURL) == 0 {
|
||||||
|
if this.web.Referers.MatchDomain(this.ReqHost, "") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.tags = append(this.tags, "refererCheck")
|
||||||
|
this.writeCode(http.StatusForbidden, "The referer has been blocked.", "当前访问已被防盗链系统拦截。")
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
u, err := url.Parse(refererURL)
|
||||||
|
if err != nil {
|
||||||
|
if this.web.Referers.MatchDomain(this.ReqHost, "") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.tags = append(this.tags, "refererCheck")
|
||||||
|
this.writeCode(http.StatusForbidden, "The referer has been blocked.", "当前访问已被防盗链系统拦截。")
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if !this.web.Referers.MatchDomain(this.ReqHost, u.Host) {
|
||||||
|
this.tags = append(this.tags, "refererCheck")
|
||||||
|
this.writeCode(http.StatusForbidden, "The referer has been blocked.", "当前访问已被防盗链系统拦截。")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -116,7 +116,7 @@ func (this *HTTPRequest) doOriginRequest(failedOriginIds []int64, failedLnNodeId
|
|||||||
// 处理Scheme
|
// 处理Scheme
|
||||||
if origin.Addr == nil {
|
if origin.Addr == nil {
|
||||||
err := errors.New(this.URL() + ": Origin '" + strconv.FormatInt(origin.Id, 10) + "' does not has a address")
|
err := errors.New(this.URL() + ": Origin '" + strconv.FormatInt(origin.Id, 10) + "' does not has a address")
|
||||||
remotelogs.Error("HTTP_REQUEST_REVERSE_PROXY", err.Error())
|
remotelogs.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
|
||||||
}
|
}
|
||||||
@@ -168,7 +168,7 @@ func (this *HTTPRequest) doOriginRequest(failedOriginIds []int64, failedLnNodeId
|
|||||||
var originHostIndex = strings.Index(originAddr, ":")
|
var originHostIndex = strings.Index(originAddr, ":")
|
||||||
if originHostIndex < 0 {
|
if originHostIndex < 0 {
|
||||||
var originErr = errors.New(this.URL() + ": Invalid origin address '" + originAddr + "', lacking port")
|
var originErr = errors.New(this.URL() + ": Invalid origin address '" + originAddr + "', lacking port")
|
||||||
remotelogs.Error("HTTP_REQUEST_REVERSE_PROXY", originErr.Error())
|
remotelogs.ErrorServer("HTTP_REQUEST_REVERSE_PROXY", originErr.Error())
|
||||||
this.write50x(originErr, http.StatusBadGateway, "No port in origin site address", "源站地址中没有配置端口", true)
|
this.write50x(originErr, http.StatusBadGateway, "No port in origin site address", "源站地址中没有配置端口", true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -240,24 +240,18 @@ func (this *HTTPRequest) doOriginRequest(failedOriginIds []int64, failedLnNodeId
|
|||||||
|
|
||||||
// 判断是否为Websocket请求
|
// 判断是否为Websocket请求
|
||||||
if this.RawReq.Header.Get("Upgrade") == "websocket" {
|
if this.RawReq.Header.Get("Upgrade") == "websocket" {
|
||||||
this.doWebsocket(requestHost)
|
shouldRetry = this.doWebsocket(requestHost, isLastRetry)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取请求客户端
|
// 获取请求客户端
|
||||||
client, err := SharedHTTPClientPool.Client(this, origin, originAddr, this.reverseProxy.ProxyProtocol, this.reverseProxy.FollowRedirects)
|
client, err := SharedHTTPClientPool.Client(this, origin, originAddr, this.reverseProxy.ProxyProtocol, this.reverseProxy.FollowRedirects)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
remotelogs.Error("HTTP_REQUEST_REVERSE_PROXY", this.URL()+": Create client failed: "+err.Error())
|
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)
|
this.write50x(err, http.StatusBadGateway, "Failed to create origin site client", "构造源站客户端失败", true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 在HTTP/2下需要防止因为requestBody而导致Content-Length为空的问题
|
|
||||||
if this.RawReq.ProtoMajor == 2 && this.RawReq.ContentLength == 0 {
|
|
||||||
_ = this.RawReq.Body.Close()
|
|
||||||
this.RawReq.Body = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 开始请求
|
// 开始请求
|
||||||
resp, err := client.Do(this.RawReq)
|
resp, err := client.Do(this.RawReq)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -268,7 +262,7 @@ func (this *HTTPRequest) doOriginRequest(failedOriginIds []int64, failedLnNodeId
|
|||||||
this.reverseProxy.ResetScheduling()
|
this.reverseProxy.ResetScheduling()
|
||||||
})
|
})
|
||||||
this.write50x(err, http.StatusBadGateway, "Failed to read origin site", "源站读取失败", true)
|
this.write50x(err, http.StatusBadGateway, "Failed to read origin site", "源站读取失败", true)
|
||||||
remotelogs.Warn("HTTP_REQUEST_REVERSE_PROXY", this.RawReq.URL.String()+": Request origin server failed: "+err.Error())
|
remotelogs.WarnServer("HTTP_REQUEST_REVERSE_PROXY", this.RawReq.URL.String()+": Request origin server failed: "+err.Error())
|
||||||
} else if httpErr.Err != context.Canceled {
|
} else if httpErr.Err != context.Canceled {
|
||||||
SharedOriginStateManager.Fail(origin, requestHost, this.reverseProxy, func() {
|
SharedOriginStateManager.Fail(origin, requestHost, this.reverseProxy, func() {
|
||||||
this.reverseProxy.ResetScheduling()
|
this.reverseProxy.ResetScheduling()
|
||||||
@@ -284,7 +278,7 @@ func (this *HTTPRequest) doOriginRequest(failedOriginIds []int64, failedLnNodeId
|
|||||||
}
|
}
|
||||||
|
|
||||||
if httpErr.Err != io.EOF {
|
if httpErr.Err != io.EOF {
|
||||||
remotelogs.Warn("HTTP_REQUEST_REVERSE_PROXY", this.URL()+": Request origin server failed: "+err.Error())
|
remotelogs.WarnServer("HTTP_REQUEST_REVERSE_PROXY", this.URL()+": Request origin server failed: "+err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
@@ -298,7 +292,7 @@ func (this *HTTPRequest) doOriginRequest(failedOriginIds []int64, failedLnNodeId
|
|||||||
this.write50x(err, http.StatusBadGateway, "Failed to read origin site", "源站读取失败", true)
|
this.write50x(err, http.StatusBadGateway, "Failed to read origin site", "源站读取失败", true)
|
||||||
}
|
}
|
||||||
if httpErr.Err != io.EOF {
|
if httpErr.Err != io.EOF {
|
||||||
remotelogs.Warn("HTTP_REQUEST_REVERSE_PROXY", this.URL()+": Request origin server failed: "+err.Error())
|
remotelogs.WarnServer("HTTP_REQUEST_REVERSE_PROXY", this.URL()+": Request origin server failed: "+err.Error())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 是否为客户端方面的错误
|
// 是否为客户端方面的错误
|
||||||
@@ -326,6 +320,11 @@ func (this *HTTPRequest) doOriginRequest(failedOriginIds []int64, failedLnNodeId
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 记录相关数据
|
||||||
|
this.originStatus = int32(resp.StatusCode)
|
||||||
|
|
||||||
|
// 恢复源站状态
|
||||||
if !origin.IsOk {
|
if !origin.IsOk {
|
||||||
SharedOriginStateManager.Success(origin, func() {
|
SharedOriginStateManager.Success(origin, func() {
|
||||||
this.reverseProxy.ResetScheduling()
|
this.reverseProxy.ResetScheduling()
|
||||||
@@ -337,7 +336,7 @@ func (this *HTTPRequest) doOriginRequest(failedOriginIds []int64, failedLnNodeId
|
|||||||
if this.doWAFResponse(resp) {
|
if this.doWAFResponse(resp) {
|
||||||
err = resp.Body.Close()
|
err = resp.Body.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
remotelogs.Warn("HTTP_REQUEST_REVERSE_PROXY", this.URL()+": Closing Error (WAF): "+err.Error())
|
remotelogs.WarnServer("HTTP_REQUEST_REVERSE_PROXY", this.URL()+": Closing Error (WAF): "+err.Error())
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -347,7 +346,7 @@ 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.Warn("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
|
||||||
}
|
}
|
||||||
@@ -398,7 +397,7 @@ func (this *HTTPRequest) doOriginRequest(failedOriginIds []int64, failedLnNodeId
|
|||||||
|
|
||||||
// 响应Header
|
// 响应Header
|
||||||
this.writer.AddHeaders(resp.Header)
|
this.writer.AddHeaders(resp.Header)
|
||||||
this.processResponseHeaders(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 || this.RawReq.Header.Get("Accept") == "text/event-stream"
|
||||||
@@ -449,13 +448,13 @@ func (this *HTTPRequest) doOriginRequest(failedOriginIds []int64, failedLnNodeId
|
|||||||
var closeErr = resp.Body.Close()
|
var closeErr = resp.Body.Close()
|
||||||
if closeErr != nil {
|
if closeErr != nil {
|
||||||
if !this.canIgnore(closeErr) {
|
if !this.canIgnore(closeErr) {
|
||||||
remotelogs.Warn("HTTP_REQUEST_REVERSE_PROXY", this.URL()+": Closing error: "+closeErr.Error())
|
remotelogs.WarnServer("HTTP_REQUEST_REVERSE_PROXY", this.URL()+": Closing error: "+closeErr.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil && err != io.EOF {
|
if err != nil && err != io.EOF {
|
||||||
if !this.canIgnore(err) {
|
if !this.canIgnore(err) {
|
||||||
remotelogs.Warn("HTTP_REQUEST_REVERSE_PROXY", this.URL()+": Writing error: "+err.Error())
|
remotelogs.WarnServer("HTTP_REQUEST_REVERSE_PROXY", this.URL()+": Writing error: "+err.Error())
|
||||||
this.addError(err)
|
this.addError(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.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(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(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(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(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(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(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(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(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(http.StatusRequestedRangeNotSatisfiable)
|
this.processResponseHeaders(this.writer.Header(), http.StatusRequestedRangeNotSatisfiable)
|
||||||
this.writer.WriteHeader(http.StatusRequestedRangeNotSatisfiable)
|
this.writer.WriteHeader(http.StatusRequestedRangeNotSatisfiable)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,10 +28,10 @@ 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(shutdown.Status)
|
this.processResponseHeaders(this.writer.Header(), shutdown.Status)
|
||||||
this.writer.WriteHeader(shutdown.Status)
|
this.writer.WriteHeader(shutdown.Status)
|
||||||
} else {
|
} else {
|
||||||
this.processResponseHeaders(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.")
|
_, err := this.writer.WriteString("The site have been shutdown.")
|
||||||
@@ -59,10 +59,10 @@ func (this *HTTPRequest) doShutdown() {
|
|||||||
|
|
||||||
// 自定义响应Headers
|
// 自定义响应Headers
|
||||||
if shutdown.Status > 0 {
|
if shutdown.Status > 0 {
|
||||||
this.processResponseHeaders(shutdown.Status)
|
this.processResponseHeaders(this.writer.Header(), shutdown.Status)
|
||||||
this.writer.WriteHeader(shutdown.Status)
|
this.writer.WriteHeader(shutdown.Status)
|
||||||
} else {
|
} else {
|
||||||
this.processResponseHeaders(http.StatusOK)
|
this.processResponseHeaders(this.writer.Header(), http.StatusOK)
|
||||||
this.writer.WriteHeader(http.StatusOK)
|
this.writer.WriteHeader(http.StatusOK)
|
||||||
}
|
}
|
||||||
buf := utils.BytePool1k.Get()
|
buf := utils.BytePool1k.Get()
|
||||||
@@ -85,10 +85,10 @@ func (this *HTTPRequest) doShutdown() {
|
|||||||
} else if shutdown.BodyType == shared.BodyTypeHTML {
|
} else if shutdown.BodyType == shared.BodyTypeHTML {
|
||||||
// 自定义响应Headers
|
// 自定义响应Headers
|
||||||
if shutdown.Status > 0 {
|
if shutdown.Status > 0 {
|
||||||
this.processResponseHeaders(shutdown.Status)
|
this.processResponseHeaders(this.writer.Header(), shutdown.Status)
|
||||||
this.writer.WriteHeader(shutdown.Status)
|
this.writer.WriteHeader(shutdown.Status)
|
||||||
} else {
|
} else {
|
||||||
this.processResponseHeaders(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(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(resp.StatusCode)
|
this.processResponseHeaders(this.writer.Header(), resp.StatusCode)
|
||||||
} else {
|
} else {
|
||||||
this.processResponseHeaders(statusCode)
|
this.processResponseHeaders(this.writer.Header(), statusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
if supportVariables {
|
if supportVariables {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package nodes
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
iplib "github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/firewallconfigs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/iplibrary"
|
"github.com/TeaOSLab/EdgeNode/internal/iplibrary"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
@@ -161,56 +162,48 @@ func (this *HTTPRequest) checkWAFRequest(firewallPolicy *firewallconfigs.HTTPFir
|
|||||||
|
|
||||||
// 检查地区封禁
|
// 检查地区封禁
|
||||||
if firewallPolicy.Mode == firewallconfigs.FirewallModeDefend {
|
if firewallPolicy.Mode == firewallconfigs.FirewallModeDefend {
|
||||||
if iplibrary.SharedLibrary != nil {
|
if firewallPolicy.Inbound.Region != nil && firewallPolicy.Inbound.Region.IsOn {
|
||||||
if firewallPolicy.Inbound.Region != nil && firewallPolicy.Inbound.Region.IsOn {
|
regionConfig := firewallPolicy.Inbound.Region
|
||||||
regionConfig := firewallPolicy.Inbound.Region
|
if regionConfig.IsNotEmpty() {
|
||||||
if regionConfig.IsNotEmpty() {
|
for _, remoteAddr := range remoteAddrs {
|
||||||
for _, remoteAddr := range remoteAddrs {
|
var result = iplib.LookupIP(remoteAddr)
|
||||||
result, err := iplibrary.SharedLibrary.Lookup(remoteAddr)
|
if result != nil && result.IsOk() {
|
||||||
if err != nil {
|
// 检查国家/地区级别封禁
|
||||||
remotelogs.Error("HTTP_REQUEST_WAF", "iplibrary lookup failed: "+err.Error())
|
var countryId = result.CountryId()
|
||||||
} else if result != nil {
|
if countryId > 0 && lists.ContainsInt64(regionConfig.DenyCountryIds, countryId) {
|
||||||
// 检查国家级别封禁
|
this.firewallPolicyId = firewallPolicy.Id
|
||||||
if len(regionConfig.DenyCountryIds) > 0 && len(result.Country) > 0 {
|
|
||||||
countryId := iplibrary.SharedCountryManager.Lookup(result.Country)
|
|
||||||
if countryId > 0 && lists.ContainsInt64(regionConfig.DenyCountryIds, countryId) {
|
|
||||||
this.firewallPolicyId = firewallPolicy.Id
|
|
||||||
|
|
||||||
this.writeCode(http.StatusForbidden)
|
this.writeCode(http.StatusForbidden, "", "")
|
||||||
this.writer.Flush()
|
this.writer.Flush()
|
||||||
this.writer.Close()
|
this.writer.Close()
|
||||||
|
|
||||||
// 停止日志
|
// 停止日志
|
||||||
if !logDenying {
|
if !logDenying {
|
||||||
this.disableLog = true
|
this.disableLog = true
|
||||||
} else {
|
} else {
|
||||||
this.tags = append(this.tags, "denyCountry")
|
this.tags = append(this.tags, "denyCountry")
|
||||||
}
|
|
||||||
|
|
||||||
return true, false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查省份封禁
|
return true, false
|
||||||
if len(regionConfig.DenyProvinceIds) > 0 && len(result.Province) > 0 {
|
}
|
||||||
var provinceId = iplibrary.SharedProvinceManager.Lookup(result.Province)
|
|
||||||
if provinceId > 0 && lists.ContainsInt64(regionConfig.DenyProvinceIds, provinceId) {
|
|
||||||
this.firewallPolicyId = firewallPolicy.Id
|
|
||||||
|
|
||||||
this.writeCode(http.StatusForbidden)
|
// 检查省份封禁
|
||||||
this.writer.Flush()
|
var provinceId = result.ProvinceId()
|
||||||
this.writer.Close()
|
if provinceId > 0 && lists.ContainsInt64(regionConfig.DenyProvinceIds, provinceId) {
|
||||||
|
this.firewallPolicyId = firewallPolicy.Id
|
||||||
|
|
||||||
// 停止日志
|
this.writeCode(http.StatusForbidden, "", "")
|
||||||
if !logDenying {
|
this.writer.Flush()
|
||||||
this.disableLog = true
|
this.writer.Close()
|
||||||
} else {
|
|
||||||
this.tags = append(this.tags, "denyProvince")
|
|
||||||
}
|
|
||||||
|
|
||||||
return true, false
|
// 停止日志
|
||||||
}
|
if !logDenying {
|
||||||
|
this.disableLog = true
|
||||||
|
} else {
|
||||||
|
this.tags = append(this.tags, "denyProvince")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true, false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -379,6 +372,8 @@ func (this *HTTPRequest) WAFServerId() int64 {
|
|||||||
// WAFClose 关闭连接
|
// WAFClose 关闭连接
|
||||||
func (this *HTTPRequest) WAFClose() {
|
func (this *HTTPRequest) WAFClose() {
|
||||||
this.Close()
|
this.Close()
|
||||||
|
|
||||||
|
// 这里不要强关IP所有连接,避免因为单个服务而影响所有
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *HTTPRequest) WAFOnAction(action interface{}) (goNext bool) {
|
func (this *HTTPRequest) WAFOnAction(action interface{}) (goNext bool) {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package nodes
|
package nodes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
"io"
|
"io"
|
||||||
@@ -9,7 +10,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// 处理Websocket请求
|
// 处理Websocket请求
|
||||||
func (this *HTTPRequest) doWebsocket(requestHost string) {
|
func (this *HTTPRequest) doWebsocket(requestHost string, isLastRetry bool) (shouldRetry bool) {
|
||||||
if this.web.WebsocketRef == nil || !this.web.WebsocketRef.IsOn || this.web.Websocket == nil || !this.web.Websocket.IsOn {
|
if this.web.WebsocketRef == nil || !this.web.WebsocketRef.IsOn || this.web.Websocket == nil || !this.web.Websocket.IsOn {
|
||||||
this.writer.WriteHeader(http.StatusForbidden)
|
this.writer.WriteHeader(http.StatusForbidden)
|
||||||
this.addError(errors.New("websocket have not been enabled yet"))
|
this.addError(errors.New("websocket have not been enabled yet"))
|
||||||
@@ -43,13 +44,16 @@ func (this *HTTPRequest) doWebsocket(requestHost string) {
|
|||||||
// TODO 增加N次错误重试,重试的时候需要尝试不同的源站
|
// TODO 增加N次错误重试,重试的时候需要尝试不同的源站
|
||||||
originConn, _, err := OriginConnect(this.origin, this.requestServerPort(), this.RawReq.RemoteAddr, requestHost)
|
originConn, _, err := OriginConnect(this.origin, this.requestServerPort(), this.RawReq.RemoteAddr, requestHost)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
this.write50x(err, http.StatusBadGateway, "Failed to connect origin site", "源站连接失败", false)
|
if isLastRetry {
|
||||||
|
this.write50x(err, http.StatusBadGateway, "Failed to connect origin site", "源站连接失败", false)
|
||||||
|
}
|
||||||
|
|
||||||
// 增加失败次数
|
// 增加失败次数
|
||||||
SharedOriginStateManager.Fail(this.origin, requestHost, this.reverseProxy, func() {
|
SharedOriginStateManager.Fail(this.origin, requestHost, this.reverseProxy, func() {
|
||||||
this.reverseProxy.ResetScheduling()
|
this.reverseProxy.ResetScheduling()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
shouldRetry = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,6 +83,33 @@ func (this *HTTPRequest) doWebsocket(requestHost string) {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
|
// 读取第一个响应
|
||||||
|
resp, err := http.ReadResponse(bufio.NewReader(originConn), this.RawReq)
|
||||||
|
if err != nil {
|
||||||
|
_ = clientConn.Close()
|
||||||
|
_ = originConn.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.processResponseHeaders(resp.Header, resp.StatusCode)
|
||||||
|
|
||||||
|
// 将响应写回客户端
|
||||||
|
err = resp.Write(clientConn)
|
||||||
|
if err != nil {
|
||||||
|
if resp.Body != nil {
|
||||||
|
_ = resp.Body.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = clientConn.Close()
|
||||||
|
_ = originConn.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.Body != nil {
|
||||||
|
_ = resp.Body.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 复制剩余的数据
|
||||||
var buf = utils.BytePool4k.Get()
|
var buf = utils.BytePool4k.Get()
|
||||||
defer utils.BytePool4k.Put(buf)
|
defer utils.BytePool4k.Put(buf)
|
||||||
for {
|
for {
|
||||||
@@ -98,4 +129,6 @@ func (this *HTTPRequest) doWebsocket(requestHost string) {
|
|||||||
_ = originConn.Close()
|
_ = originConn.Close()
|
||||||
}()
|
}()
|
||||||
_, _ = io.Copy(originConn, clientConn)
|
_, _ = io.Copy(originConn, clientConn)
|
||||||
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1007,30 +1007,35 @@ func (this *HTTPWriter) finishWebP() {
|
|||||||
Exact: true,
|
Exact: true,
|
||||||
})
|
})
|
||||||
} else if gifImage != nil {
|
} else if gifImage != nil {
|
||||||
anim := gowebp.NewWebpAnimation(gifImage.Config.Width, gifImage.Config.Height, gifImage.LoopCount)
|
var anim = gowebp.NewWebpAnimation(gifImage.Config.Width, gifImage.Config.Height, gifImage.LoopCount)
|
||||||
|
|
||||||
anim.WebPAnimEncoderOptions.SetKmin(9)
|
anim.WebPAnimEncoderOptions.SetKmin(9)
|
||||||
anim.WebPAnimEncoderOptions.SetKmax(17)
|
anim.WebPAnimEncoderOptions.SetKmax(17)
|
||||||
defer anim.ReleaseMemory()
|
var webpConfig = gowebp.NewWebpConfig()
|
||||||
webpConfig := gowebp.NewWebpConfig()
|
|
||||||
//webpConfig.SetLossless(1)
|
//webpConfig.SetLossless(1)
|
||||||
webpConfig.SetQuality(f)
|
webpConfig.SetQuality(f)
|
||||||
|
|
||||||
var timeline = 0
|
var timeline = 0
|
||||||
|
var lastErr error
|
||||||
for i, img := range gifImage.Image {
|
for i, img := range gifImage.Image {
|
||||||
err = anim.AddFrame(img, timeline, webpConfig)
|
err = anim.AddFrame(img, timeline, webpConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
break
|
// 有错误直接跳过
|
||||||
|
lastErr = err
|
||||||
|
err = nil
|
||||||
}
|
}
|
||||||
timeline += gifImage.Delay[i] * 10
|
timeline += gifImage.Delay[i] * 10
|
||||||
}
|
}
|
||||||
if err == nil {
|
if lastErr != nil {
|
||||||
err = anim.AddFrame(nil, timeline, webpConfig)
|
remotelogs.Error("HTTP_WRITER", "'"+this.req.URL()+"' encode webp failed: "+lastErr.Error())
|
||||||
|
|
||||||
if err == nil {
|
|
||||||
err = anim.Encode(this.writer)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
err = anim.AddFrame(nil, timeline, webpConfig)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
err = anim.Encode(this.writer)
|
||||||
|
}
|
||||||
|
|
||||||
|
anim.ReleaseMemory()
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil && !this.req.canIgnore(err) {
|
if err != nil && !this.req.canIgnore(err) {
|
||||||
|
|||||||
103
internal/nodes/ip_library_updater.go
Normal file
103
internal/nodes/ip_library_updater.go
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
||||||
|
|
||||||
|
package nodes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
||||||
|
"github.com/iwind/TeaGo/Tea"
|
||||||
|
"github.com/iwind/TeaGo/types"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type IPLibraryUpdater struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewIPLibraryUpdater() *IPLibraryUpdater {
|
||||||
|
return &IPLibraryUpdater{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DataDir 文件目录
|
||||||
|
func (this *IPLibraryUpdater) DataDir() string {
|
||||||
|
// data/
|
||||||
|
var dir = Tea.Root + "/data"
|
||||||
|
stat, err := os.Stat(dir)
|
||||||
|
if err == nil && stat.IsDir() {
|
||||||
|
return dir
|
||||||
|
}
|
||||||
|
|
||||||
|
err = os.Mkdir(dir, 0666)
|
||||||
|
if err == nil {
|
||||||
|
return dir
|
||||||
|
}
|
||||||
|
|
||||||
|
remotelogs.Error("IP_LIBRARY_UPDATER", "create directory '"+dir+"' failed: "+err.Error())
|
||||||
|
|
||||||
|
// 如果不能创建 data/ 目录,那么使用临时目录
|
||||||
|
return os.TempDir()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindLatestFile 检查最新的IP库文件
|
||||||
|
func (this *IPLibraryUpdater) FindLatestFile() (code string, fileId int64, err error) {
|
||||||
|
rpcClient, err := rpc.SharedRPC()
|
||||||
|
if err != nil {
|
||||||
|
return "", 0, err
|
||||||
|
}
|
||||||
|
resp, err := rpcClient.IPLibraryArtifactRPC.FindPublicIPLibraryArtifact(rpcClient.Context(), &pb.FindPublicIPLibraryArtifactRequest{})
|
||||||
|
if err != nil {
|
||||||
|
return "", 0, err
|
||||||
|
}
|
||||||
|
var artifact = resp.IpLibraryArtifact
|
||||||
|
if artifact == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return artifact.Code, artifact.FileId, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DownloadFile 下载文件
|
||||||
|
func (this *IPLibraryUpdater) DownloadFile(fileId int64, writer io.Writer) error {
|
||||||
|
if fileId <= 0 {
|
||||||
|
return errors.New("invalid fileId: " + types.String(fileId))
|
||||||
|
}
|
||||||
|
|
||||||
|
rpcClient, err := rpc.SharedRPC()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
chunkIdsResp, err := rpcClient.FileChunkRPC.FindAllFileChunkIds(rpcClient.Context(), &pb.FindAllFileChunkIdsRequest{FileId: fileId})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, chunkId := range chunkIdsResp.FileChunkIds {
|
||||||
|
chunkResp, err := rpcClient.FileChunkRPC.DownloadFileChunk(rpcClient.Context(), &pb.DownloadFileChunkRequest{FileChunkId: chunkId})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var chunk = chunkResp.FileChunk
|
||||||
|
if chunk == nil {
|
||||||
|
return errors.New("can not find file chunk with chunk id '" + types.String(chunkId) + "'")
|
||||||
|
}
|
||||||
|
_, err = writer.Write(chunk.Data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogInfo 普通日志
|
||||||
|
func (this *IPLibraryUpdater) LogInfo(message string) {
|
||||||
|
remotelogs.Println("IP_LIBRARY_UPDATER", message)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogError 错误日志
|
||||||
|
func (this *IPLibraryUpdater) LogError(err error) {
|
||||||
|
if err == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
remotelogs.Error("IP_LIBRARY_UPDATER", err.Error())
|
||||||
|
}
|
||||||
@@ -7,14 +7,16 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeNode/internal/events"
|
"github.com/TeaOSLab/EdgeNode/internal/events"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
|
"golang.org/x/net/ipv4"
|
||||||
|
"golang.org/x/net/ipv6"
|
||||||
"net"
|
"net"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Listener struct {
|
type Listener struct {
|
||||||
group *serverconfigs.ServerAddressGroup
|
group *serverconfigs.ServerAddressGroup
|
||||||
isListening bool
|
listener ListenerInterface // 监听器
|
||||||
listener ListenerInterface // 监听器
|
|
||||||
|
|
||||||
locker sync.RWMutex
|
locker sync.RWMutex
|
||||||
}
|
}
|
||||||
@@ -118,18 +120,64 @@ func (this *Listener) listenTCP() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *Listener) listenUDP() error {
|
func (this *Listener) listenUDP() error {
|
||||||
listener, err := this.createUDPListener()
|
var addr = this.group.Addr()
|
||||||
|
|
||||||
|
var ipv4PacketListener *ipv4.PacketConn
|
||||||
|
var ipv6PacketListener *ipv6.PacketConn
|
||||||
|
|
||||||
|
host, _, err := net.SplitHostPort(addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(host) == 0 {
|
||||||
|
// ipv4
|
||||||
|
ipv4Listener, err := this.createUDPIPv4Listener()
|
||||||
|
if err == nil {
|
||||||
|
ipv4PacketListener = ipv4.NewPacketConn(ipv4Listener)
|
||||||
|
} else {
|
||||||
|
remotelogs.Error("LISTENER", "create udp ipv4 listener '"+addr+"': "+err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// ipv6
|
||||||
|
ipv6Listener, err := this.createUDPIPv6Listener()
|
||||||
|
if err == nil {
|
||||||
|
ipv6PacketListener = ipv6.NewPacketConn(ipv6Listener)
|
||||||
|
} else {
|
||||||
|
remotelogs.Error("LISTENER", "create udp ipv6 listener '"+addr+"': "+err.Error())
|
||||||
|
}
|
||||||
|
} else if strings.Contains(host, ":") { // ipv6
|
||||||
|
ipv6Listener, err := this.createUDPIPv6Listener()
|
||||||
|
if err == nil {
|
||||||
|
ipv6PacketListener = ipv6.NewPacketConn(ipv6Listener)
|
||||||
|
} else {
|
||||||
|
remotelogs.Error("LISTENER", "create udp ipv6 listener '"+addr+"': "+err.Error())
|
||||||
|
}
|
||||||
|
} else { // ipv4
|
||||||
|
ipv4Listener, err := this.createUDPIPv4Listener()
|
||||||
|
if err == nil {
|
||||||
|
ipv4PacketListener = ipv4.NewPacketConn(ipv4Listener)
|
||||||
|
} else {
|
||||||
|
remotelogs.Error("LISTENER", "create udp ipv4 listener '"+addr+"': "+err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
events.OnKey(events.EventQuit, this, func() {
|
events.OnKey(events.EventQuit, this, func() {
|
||||||
remotelogs.Println("LISTENER", "quit "+this.group.FullAddr())
|
remotelogs.Println("LISTENER", "quit "+this.group.FullAddr())
|
||||||
_ = listener.Close()
|
|
||||||
|
if ipv4PacketListener != nil {
|
||||||
|
_ = ipv4PacketListener.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
if ipv6PacketListener != nil {
|
||||||
|
_ = ipv6PacketListener.Close()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
this.listener = &UDPListener{
|
this.listener = &UDPListener{
|
||||||
BaseListener: BaseListener{Group: this.group},
|
BaseListener: BaseListener{Group: this.group},
|
||||||
Listener: listener,
|
IPv4Listener: ipv4PacketListener,
|
||||||
|
IPv6Listener: ipv6PacketListener,
|
||||||
}
|
}
|
||||||
|
|
||||||
goman.New(func() {
|
goman.New(func() {
|
||||||
@@ -168,12 +216,20 @@ func (this *Listener) createTCPListener() (net.Listener, error) {
|
|||||||
return listenConfig.Listen(context.Background(), "tcp", this.group.Addr())
|
return listenConfig.Listen(context.Background(), "tcp", this.group.Addr())
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建UDP监听器
|
// 创建UDP IPv4监听器
|
||||||
func (this *Listener) createUDPListener() (*net.UDPConn, error) {
|
func (this *Listener) createUDPIPv4Listener() (*net.UDPConn, error) {
|
||||||
// TODO 将来支持udp4/udp6
|
|
||||||
addr, err := net.ResolveUDPAddr("udp", this.group.Addr())
|
addr, err := net.ResolveUDPAddr("udp", this.group.Addr())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return net.ListenUDP("udp", addr)
|
return net.ListenUDP("udp4", addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建UDP监听器
|
||||||
|
func (this *Listener) createUDPIPv6Listener() (*net.UDPConn, error) {
|
||||||
|
addr, err := net.ResolveUDPAddr("udp", this.group.Addr())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return net.ListenUDP("udp6", addr)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,12 @@ package nodes
|
|||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"errors"
|
"errors"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/sslconfigs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
"github.com/iwind/TeaGo/lists"
|
|
||||||
"github.com/iwind/TeaGo/types"
|
"github.com/iwind/TeaGo/types"
|
||||||
|
"net"
|
||||||
)
|
)
|
||||||
|
|
||||||
type BaseListener struct {
|
type BaseListener struct {
|
||||||
@@ -75,7 +76,7 @@ func (this *BaseListener) matchSSL(domain string) (*sslconfigs.SSLPolicy, *tls.C
|
|||||||
// 如果域名为空,则取第一个
|
// 如果域名为空,则取第一个
|
||||||
// 通常域名为空是因为是直接通过IP访问的
|
// 通常域名为空是因为是直接通过IP访问的
|
||||||
if len(domain) == 0 {
|
if len(domain) == 0 {
|
||||||
if group.IsHTTPS() && sharedNodeConfig.GlobalConfig != nil && sharedNodeConfig.GlobalConfig.HTTPAll.MatchDomainStrictly {
|
if group.IsHTTPS() && sharedNodeConfig.GlobalServerConfig != nil && sharedNodeConfig.GlobalServerConfig.HTTPAll.MatchDomainStrictly {
|
||||||
return nil, nil, errors.New("no tls server name matched")
|
return nil, nil, errors.New("no tls server name matched")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,19 +132,19 @@ func (this *BaseListener) findNamedServer(name string) (serverConfig *serverconf
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var matchDomainStrictly = sharedNodeConfig.GlobalConfig != nil && sharedNodeConfig.GlobalConfig.HTTPAll.MatchDomainStrictly
|
var matchDomainStrictly = sharedNodeConfig.GlobalServerConfig != nil && sharedNodeConfig.GlobalServerConfig.HTTPAll.MatchDomainStrictly
|
||||||
|
|
||||||
if sharedNodeConfig.GlobalConfig != nil &&
|
if sharedNodeConfig.GlobalServerConfig != nil &&
|
||||||
len(sharedNodeConfig.GlobalConfig.HTTPAll.DefaultDomain) > 0 &&
|
len(sharedNodeConfig.GlobalServerConfig.HTTPAll.DefaultDomain) > 0 &&
|
||||||
(!matchDomainStrictly || lists.ContainsString(sharedNodeConfig.GlobalConfig.HTTPAll.AllowMismatchDomains, name)) {
|
(!matchDomainStrictly || configutils.MatchDomains(sharedNodeConfig.GlobalServerConfig.HTTPAll.AllowMismatchDomains, name) || (sharedNodeConfig.GlobalServerConfig.HTTPAll.AllowNodeIP && net.ParseIP(name) != nil)) {
|
||||||
defaultDomain := sharedNodeConfig.GlobalConfig.HTTPAll.DefaultDomain
|
var defaultDomain = sharedNodeConfig.GlobalServerConfig.HTTPAll.DefaultDomain
|
||||||
serverConfig, serverName = this.findNamedServerMatched(defaultDomain)
|
serverConfig, serverName = this.findNamedServerMatched(defaultDomain)
|
||||||
if serverConfig != nil {
|
if serverConfig != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if matchDomainStrictly && !lists.ContainsString(sharedNodeConfig.GlobalConfig.HTTPAll.AllowMismatchDomains, name) {
|
if matchDomainStrictly && !configutils.MatchDomains(sharedNodeConfig.GlobalServerConfig.HTTPAll.AllowMismatchDomains, name) && (!sharedNodeConfig.GlobalServerConfig.HTTPAll.AllowNodeIP || net.ParseIP(name) == nil) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,7 +171,7 @@ func (this *BaseListener) findNamedServerMatched(name string) (serverConfig *ser
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 是否严格匹配域名
|
// 是否严格匹配域名
|
||||||
matchDomainStrictly := sharedNodeConfig.GlobalConfig != nil && sharedNodeConfig.GlobalConfig.HTTPAll.MatchDomainStrictly
|
var matchDomainStrictly = sharedNodeConfig.GlobalServerConfig != nil && sharedNodeConfig.GlobalServerConfig.HTTPAll.MatchDomainStrictly
|
||||||
|
|
||||||
// 如果只有一个server,则默认为这个
|
// 如果只有一个server,则默认为这个
|
||||||
var currentServers = group.Servers()
|
var currentServers = group.Servers()
|
||||||
@@ -181,23 +182,3 @@ func (this *BaseListener) findNamedServerMatched(name string) (serverConfig *ser
|
|||||||
|
|
||||||
return nil, name
|
return nil, name
|
||||||
}
|
}
|
||||||
|
|
||||||
// 使用CNAME来查找服务
|
|
||||||
// TODO 防止单IP随机生成域名攻击
|
|
||||||
func (this *BaseListener) findServerWithCNAME(domain string) *serverconfigs.ServerConfig {
|
|
||||||
if !sharedNodeConfig.SupportCNAME {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var realName = sharedCNAMEManager.Lookup(domain)
|
|
||||||
if len(realName) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
group := this.Group
|
|
||||||
if group == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return group.MatchServerCNAME(realName)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/zero"
|
|
||||||
"github.com/iwind/TeaGo/Tea"
|
"github.com/iwind/TeaGo/Tea"
|
||||||
"golang.org/x/net/http2"
|
"golang.org/x/net/http2"
|
||||||
"io"
|
"io"
|
||||||
@@ -13,14 +12,11 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var httpErrorLogger = log.New(io.Discard, "", 0)
|
var httpErrorLogger = log.New(io.Discard, "", 0)
|
||||||
var metricNewConnMap = map[string]zero.Zero{} // remoteAddr => bool
|
|
||||||
var metricNewConnMapLocker = &sync.Mutex{}
|
|
||||||
|
|
||||||
type contextKey struct {
|
type contextKey struct {
|
||||||
key string
|
key string
|
||||||
@@ -55,23 +51,10 @@ func (this *HTTPListener) Serve() error {
|
|||||||
switch state {
|
switch state {
|
||||||
case http.StateNew:
|
case http.StateNew:
|
||||||
atomic.AddInt64(&this.countActiveConnections, 1)
|
atomic.AddInt64(&this.countActiveConnections, 1)
|
||||||
|
|
||||||
// 为指标存储连接信息
|
|
||||||
if sharedNodeConfig.HasHTTPConnectionMetrics() {
|
|
||||||
metricNewConnMapLocker.Lock()
|
|
||||||
metricNewConnMap[conn.RemoteAddr().String()] = zero.New()
|
|
||||||
metricNewConnMapLocker.Unlock()
|
|
||||||
}
|
|
||||||
case http.StateActive, http.StateIdle, http.StateHijacked:
|
case http.StateActive, http.StateIdle, http.StateHijacked:
|
||||||
// Nothing to do
|
// Nothing to do
|
||||||
case http.StateClosed:
|
case http.StateClosed:
|
||||||
atomic.AddInt64(&this.countActiveConnections, -1)
|
atomic.AddInt64(&this.countActiveConnections, -1)
|
||||||
|
|
||||||
// 移除指标存储连接信息
|
|
||||||
// 因为中途配置可能有改变,所以暂时不添加条件
|
|
||||||
metricNewConnMapLocker.Lock()
|
|
||||||
delete(metricNewConnMap, conn.RemoteAddr().String())
|
|
||||||
metricNewConnMapLocker.Unlock()
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ConnContext: func(ctx context.Context, conn net.Conn) context.Context {
|
ConnContext: func(ctx context.Context, conn net.Conn) context.Context {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package nodes
|
package nodes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||||
@@ -9,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/TeaOSLab/EdgeNode/internal/utils"
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
|
executils "github.com/TeaOSLab/EdgeNode/internal/utils/exec"
|
||||||
"github.com/iwind/TeaGo/Tea"
|
"github.com/iwind/TeaGo/Tea"
|
||||||
"github.com/iwind/TeaGo/lists"
|
"github.com/iwind/TeaGo/lists"
|
||||||
"github.com/iwind/TeaGo/maps"
|
"github.com/iwind/TeaGo/maps"
|
||||||
@@ -213,15 +213,14 @@ func (this *ListenerManager) findProcessNameWithPort(isUdp bool, port string) st
|
|||||||
option = "u"
|
option = "u"
|
||||||
}
|
}
|
||||||
|
|
||||||
var cmd = exec.Command(path, "-"+option+"lpn", "sport = :"+port)
|
var cmd = executils.NewTimeoutCmd(10*time.Second, path, "-"+option+"lpn", "sport = :"+port)
|
||||||
var output = &bytes.Buffer{}
|
cmd.WithStdout()
|
||||||
cmd.Stdout = output
|
|
||||||
err = cmd.Run()
|
err = cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
var matches = regexp.MustCompile(`(?U)\(\("(.+)",pid=\d+,fd=\d+\)\)`).FindStringSubmatch(output.String())
|
var matches = regexp.MustCompile(`(?U)\(\("(.+)",pid=\d+,fd=\d+\)\)`).FindStringSubmatch(cmd.Stdout())
|
||||||
if len(matches) > 1 {
|
if len(matches) > 1 {
|
||||||
return matches[1]
|
return matches[1]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
"github.com/iwind/TeaGo/types"
|
"github.com/iwind/TeaGo/types"
|
||||||
"github.com/pires/go-proxyproto"
|
"github.com/pires/go-proxyproto"
|
||||||
|
"golang.org/x/net/ipv4"
|
||||||
|
"golang.org/x/net/ipv6"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -19,10 +21,57 @@ const (
|
|||||||
UDPConnLifeSeconds = 30
|
UDPConnLifeSeconds = 30
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type UDPPacketListener interface {
|
||||||
|
ReadFrom(b []byte) (n int, cm any, src net.Addr, err error)
|
||||||
|
WriteTo(b []byte, cm any, dst net.Addr) (n int, err error)
|
||||||
|
LocalAddr() net.Addr
|
||||||
|
}
|
||||||
|
|
||||||
|
type UDPIPv4Listener struct {
|
||||||
|
rawListener *ipv4.PacketConn
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUDPIPv4Listener(rawListener *ipv4.PacketConn) *UDPIPv4Listener {
|
||||||
|
return &UDPIPv4Listener{rawListener: rawListener}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *UDPIPv4Listener) ReadFrom(b []byte) (n int, cm any, src net.Addr, err error) {
|
||||||
|
return this.rawListener.ReadFrom(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *UDPIPv4Listener) WriteTo(b []byte, cm any, dst net.Addr) (n int, err error) {
|
||||||
|
return this.rawListener.WriteTo(b, cm.(*ipv4.ControlMessage), dst)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *UDPIPv4Listener) LocalAddr() net.Addr {
|
||||||
|
return this.rawListener.LocalAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
type UDPIPv6Listener struct {
|
||||||
|
rawListener *ipv6.PacketConn
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUDPIPv6Listener(rawListener *ipv6.PacketConn) *UDPIPv6Listener {
|
||||||
|
return &UDPIPv6Listener{rawListener: rawListener}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *UDPIPv6Listener) ReadFrom(b []byte) (n int, cm any, src net.Addr, err error) {
|
||||||
|
return this.rawListener.ReadFrom(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *UDPIPv6Listener) WriteTo(b []byte, cm any, dst net.Addr) (n int, err error) {
|
||||||
|
return this.rawListener.WriteTo(b, cm.(*ipv6.ControlMessage), dst)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *UDPIPv6Listener) LocalAddr() net.Addr {
|
||||||
|
return this.rawListener.LocalAddr()
|
||||||
|
}
|
||||||
|
|
||||||
type UDPListener struct {
|
type UDPListener struct {
|
||||||
BaseListener
|
BaseListener
|
||||||
|
|
||||||
Listener *net.UDPConn
|
IPv4Listener *ipv4.PacketConn
|
||||||
|
IPv6Listener *ipv6.PacketConn
|
||||||
|
|
||||||
connMap map[string]*UDPConn
|
connMap map[string]*UDPConn
|
||||||
connLocker sync.Mutex
|
connLocker sync.Mutex
|
||||||
@@ -36,6 +85,60 @@ type UDPListener struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *UDPListener) Serve() error {
|
func (this *UDPListener) Serve() error {
|
||||||
|
if this.Group == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var server = this.Group.FirstServer()
|
||||||
|
if server == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var serverId = server.Id
|
||||||
|
|
||||||
|
var wg = &sync.WaitGroup{}
|
||||||
|
wg.Add(2) // 2 = ipv4 + ipv6
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
if this.IPv4Listener != nil {
|
||||||
|
err := this.IPv4Listener.SetControlMessage(ipv4.FlagDst, true)
|
||||||
|
if err != nil {
|
||||||
|
remotelogs.ServerError(serverId, "UDP_LISTENER", "can not serve ipv4 listener: "+err.Error(), "", nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = this.servePacketListener(NewUDPIPv4Listener(this.IPv4Listener))
|
||||||
|
if err != nil {
|
||||||
|
remotelogs.ServerError(serverId, "UDP_LISTENER", "can not serve ipv4 listener: "+err.Error(), "", nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
if this.IPv6Listener != nil {
|
||||||
|
err := this.IPv6Listener.SetControlMessage(ipv6.FlagDst, true)
|
||||||
|
if err != nil {
|
||||||
|
remotelogs.ServerError(serverId, "UDP_LISTENER", "can not serve ipv6 listener: "+err.Error(), "", nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = this.servePacketListener(NewUDPIPv6Listener(this.IPv6Listener))
|
||||||
|
if err != nil {
|
||||||
|
remotelogs.ServerError(serverId, "UDP_LISTENER", "can not serve ipv6 listener: "+err.Error(), "", nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *UDPListener) servePacketListener(listener UDPPacketListener) error {
|
||||||
// 获取分组端口
|
// 获取分组端口
|
||||||
var groupAddr = this.Group.Addr()
|
var groupAddr = this.Group.Addr()
|
||||||
var portIndex = strings.LastIndex(groupAddr, ":")
|
var portIndex = strings.LastIndex(groupAddr, ":")
|
||||||
@@ -67,7 +170,7 @@ func (this *UDPListener) Serve() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
n, addr, err := this.Listener.ReadFrom(buffer)
|
n, cm, clientAddr, err := listener.ReadFrom(buffer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if this.isClosed {
|
if this.isClosed {
|
||||||
return nil
|
return nil
|
||||||
@@ -77,14 +180,14 @@ func (this *UDPListener) Serve() error {
|
|||||||
|
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
this.connLocker.Lock()
|
this.connLocker.Lock()
|
||||||
conn, ok := this.connMap[addr.String()]
|
conn, ok := this.connMap[clientAddr.String()]
|
||||||
this.connLocker.Unlock()
|
this.connLocker.Unlock()
|
||||||
if ok && !conn.IsOk() {
|
if ok && !conn.IsOk() {
|
||||||
_ = conn.Close()
|
_ = conn.Close()
|
||||||
ok = false
|
ok = false
|
||||||
}
|
}
|
||||||
if !ok {
|
if !ok {
|
||||||
originConn, err := this.connectOrigin(firstServer.Id, this.reverseProxy, addr)
|
originConn, err := this.connectOrigin(firstServer.Id, this.reverseProxy, listener.LocalAddr(), clientAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
remotelogs.Error("UDP_LISTENER", "unable to connect to origin server: "+err.Error())
|
remotelogs.Error("UDP_LISTENER", "unable to connect to origin server: "+err.Error())
|
||||||
continue
|
continue
|
||||||
@@ -93,9 +196,9 @@ func (this *UDPListener) Serve() error {
|
|||||||
remotelogs.Error("UDP_LISTENER", "unable to find a origin server")
|
remotelogs.Error("UDP_LISTENER", "unable to find a origin server")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
conn = NewUDPConn(firstServer, addr, this.Listener, originConn.(*net.UDPConn))
|
conn = NewUDPConn(firstServer, clientAddr, listener, cm, originConn.(*net.UDPConn))
|
||||||
this.connLocker.Lock()
|
this.connLocker.Lock()
|
||||||
this.connMap[addr.String()] = conn
|
this.connMap[clientAddr.String()] = conn
|
||||||
this.connLocker.Unlock()
|
this.connLocker.Unlock()
|
||||||
}
|
}
|
||||||
_, _ = conn.Write(buffer[:n])
|
_, _ = conn.Write(buffer[:n])
|
||||||
@@ -117,7 +220,26 @@ func (this *UDPListener) Close() error {
|
|||||||
}
|
}
|
||||||
this.connLocker.Unlock()
|
this.connLocker.Unlock()
|
||||||
|
|
||||||
return this.Listener.Close()
|
var errorStrings = []string{}
|
||||||
|
if this.IPv4Listener != nil {
|
||||||
|
err := this.IPv4Listener.Close()
|
||||||
|
if err != nil {
|
||||||
|
errorStrings = append(errorStrings, err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if this.IPv6Listener != nil {
|
||||||
|
err := this.IPv6Listener.Close()
|
||||||
|
if err != nil {
|
||||||
|
errorStrings = append(errorStrings, err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(errorStrings) > 0 {
|
||||||
|
return errors.New(errorStrings[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *UDPListener) Reload(group *serverconfigs.ServerAddressGroup) {
|
func (this *UDPListener) Reload(group *serverconfigs.ServerAddressGroup) {
|
||||||
@@ -132,7 +254,7 @@ func (this *UDPListener) Reload(group *serverconfigs.ServerAddressGroup) {
|
|||||||
this.reverseProxy = firstServer.ReverseProxy
|
this.reverseProxy = firstServer.ReverseProxy
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *UDPListener) connectOrigin(serverId int64, reverseProxy *serverconfigs.ReverseProxyConfig, remoteAddr net.Addr) (conn net.Conn, err error) {
|
func (this *UDPListener) connectOrigin(serverId int64, reverseProxy *serverconfigs.ReverseProxyConfig, localAddr net.Addr, remoteAddr net.Addr) (conn net.Conn, err error) {
|
||||||
if reverseProxy == nil {
|
if reverseProxy == nil {
|
||||||
return nil, errors.New("no reverse proxy config")
|
return nil, errors.New("no reverse proxy config")
|
||||||
}
|
}
|
||||||
@@ -181,12 +303,12 @@ func (this *UDPListener) connectOrigin(serverId int64, reverseProxy *serverconfi
|
|||||||
if strings.Contains(remoteAddr.String(), "[") {
|
if strings.Contains(remoteAddr.String(), "[") {
|
||||||
transportProtocol = proxyproto.UDPv6
|
transportProtocol = proxyproto.UDPv6
|
||||||
}
|
}
|
||||||
header := proxyproto.Header{
|
var header = proxyproto.Header{
|
||||||
Version: byte(reverseProxy.ProxyProtocol.Version),
|
Version: byte(reverseProxy.ProxyProtocol.Version),
|
||||||
Command: proxyproto.PROXY,
|
Command: proxyproto.PROXY,
|
||||||
TransportProtocol: transportProtocol,
|
TransportProtocol: transportProtocol,
|
||||||
SourceAddr: remoteAddr,
|
SourceAddr: remoteAddr,
|
||||||
DestinationAddr: this.Listener.LocalAddr(),
|
DestinationAddr: localAddr,
|
||||||
}
|
}
|
||||||
_, err = header.WriteTo(conn)
|
_, err = header.WriteTo(conn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -224,21 +346,21 @@ func (this *UDPListener) gcConns() {
|
|||||||
|
|
||||||
// UDPConn 自定义的UDP连接管理
|
// UDPConn 自定义的UDP连接管理
|
||||||
type UDPConn struct {
|
type UDPConn struct {
|
||||||
addr net.Addr
|
addr net.Addr
|
||||||
proxyConn net.Conn
|
proxyListener UDPPacketListener
|
||||||
serverConn net.Conn
|
serverConn net.Conn
|
||||||
activatedAt int64
|
activatedAt int64
|
||||||
isOk bool
|
isOk bool
|
||||||
isClosed bool
|
isClosed bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewUDPConn(server *serverconfigs.ServerConfig, addr net.Addr, proxyConn *net.UDPConn, serverConn *net.UDPConn) *UDPConn {
|
func NewUDPConn(server *serverconfigs.ServerConfig, addr net.Addr, proxyListener UDPPacketListener, cm any, serverConn *net.UDPConn) *UDPConn {
|
||||||
var conn = &UDPConn{
|
var conn = &UDPConn{
|
||||||
addr: addr,
|
addr: addr,
|
||||||
proxyConn: proxyConn,
|
proxyListener: proxyListener,
|
||||||
serverConn: serverConn,
|
serverConn: serverConn,
|
||||||
activatedAt: time.Now().Unix(),
|
activatedAt: time.Now().Unix(),
|
||||||
isOk: true,
|
isOk: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
// 统计
|
// 统计
|
||||||
@@ -246,6 +368,14 @@ func NewUDPConn(server *serverconfigs.ServerConfig, addr net.Addr, proxyConn *ne
|
|||||||
stats.SharedTrafficStatManager.Add(server.Id, "", 0, 0, 1, 0, 0, 0, server.ShouldCheckTrafficLimit(), server.PlanId())
|
stats.SharedTrafficStatManager.Add(server.Id, "", 0, 0, 1, 0, 0, 0, server.ShouldCheckTrafficLimit(), server.PlanId())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 处理ControlMessage
|
||||||
|
switch controlMessage := cm.(type) {
|
||||||
|
case *ipv4.ControlMessage:
|
||||||
|
controlMessage.Src = controlMessage.Dst
|
||||||
|
case *ipv6.ControlMessage:
|
||||||
|
controlMessage.Src = controlMessage.Dst
|
||||||
|
}
|
||||||
|
|
||||||
goman.New(func() {
|
goman.New(func() {
|
||||||
var buffer = utils.BytePool4k.Get()
|
var buffer = utils.BytePool4k.Get()
|
||||||
defer func() {
|
defer func() {
|
||||||
@@ -256,7 +386,8 @@ func NewUDPConn(server *serverconfigs.ServerConfig, addr net.Addr, proxyConn *ne
|
|||||||
n, err := serverConn.Read(buffer)
|
n, err := serverConn.Read(buffer)
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
conn.activatedAt = time.Now().Unix()
|
conn.activatedAt = time.Now().Unix()
|
||||||
_, writingErr := proxyConn.WriteTo(buffer[:n], addr)
|
|
||||||
|
_, writingErr := proxyListener.WriteTo(buffer[:n], cm, addr)
|
||||||
if writingErr != nil {
|
if writingErr != nil {
|
||||||
conn.isOk = false
|
conn.isOk = false
|
||||||
break
|
break
|
||||||
|
|||||||
@@ -2,14 +2,17 @@ package nodes
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
iplib "github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/ddosconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/ddosconfigs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/caches"
|
"github.com/TeaOSLab/EdgeNode/internal/caches"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/configs"
|
"github.com/TeaOSLab/EdgeNode/internal/configs"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/conns"
|
||||||
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/events"
|
"github.com/TeaOSLab/EdgeNode/internal/events"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/firewalls"
|
"github.com/TeaOSLab/EdgeNode/internal/firewalls"
|
||||||
@@ -21,6 +24,7 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeNode/internal/stats"
|
"github.com/TeaOSLab/EdgeNode/internal/stats"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/trackers"
|
"github.com/TeaOSLab/EdgeNode/internal/trackers"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
|
_ "github.com/TeaOSLab/EdgeNode/internal/utils/clock" // 触发时钟更新
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/waf"
|
"github.com/TeaOSLab/EdgeNode/internal/waf"
|
||||||
"github.com/andybalholm/brotli"
|
"github.com/andybalholm/brotli"
|
||||||
"github.com/iwind/TeaGo/Tea"
|
"github.com/iwind/TeaGo/Tea"
|
||||||
@@ -78,7 +82,7 @@ func (this *Node) Test() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("test rpc failed: " + err.Error())
|
return errors.New("test rpc failed: " + err.Error())
|
||||||
}
|
}
|
||||||
_, err = rpcClient.APINodeRPC().FindCurrentAPINodeVersion(rpcClient.Context(), &pb.FindCurrentAPINodeVersionRequest{})
|
_, err = rpcClient.APINodeRPC.FindCurrentAPINodeVersion(rpcClient.Context(), &pb.FindCurrentAPINodeVersionRequest{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("test rpc failed: " + err.Error())
|
return errors.New("test rpc failed: " + err.Error())
|
||||||
}
|
}
|
||||||
@@ -88,6 +92,10 @@ func (this *Node) Test() error {
|
|||||||
|
|
||||||
// Start 启动
|
// Start 启动
|
||||||
func (this *Node) Start() {
|
func (this *Node) Start() {
|
||||||
|
// 设置netdns
|
||||||
|
// 这个需要放在所有网络访问的最前面
|
||||||
|
_ = os.Setenv("GODEBUG", "netdns=go")
|
||||||
|
|
||||||
_, ok := os.LookupEnv("EdgeDaemon")
|
_, ok := os.LookupEnv("EdgeDaemon")
|
||||||
if ok {
|
if ok {
|
||||||
remotelogs.Println("NODE", "start from daemon")
|
remotelogs.Println("NODE", "start from daemon")
|
||||||
@@ -101,9 +109,6 @@ func (this *Node) Start() {
|
|||||||
// 监听signal
|
// 监听signal
|
||||||
this.listenSignals()
|
this.listenSignals()
|
||||||
|
|
||||||
// 启动事件
|
|
||||||
events.Notify(events.EventStart)
|
|
||||||
|
|
||||||
// 本地Sock
|
// 本地Sock
|
||||||
err := this.listenSock()
|
err := this.listenSock()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -111,9 +116,19 @@ func (this *Node) Start() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 启动IP库
|
||||||
|
remotelogs.Println("NODE", "initializing ip library ...")
|
||||||
|
err = iplib.InitDefault()
|
||||||
|
if err != nil {
|
||||||
|
remotelogs.Error("NODE", "initialize ip library failed: "+err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
// 检查硬盘类型
|
// 检查硬盘类型
|
||||||
this.checkDisk()
|
this.checkDisk()
|
||||||
|
|
||||||
|
// 启动事件
|
||||||
|
events.Notify(events.EventStart)
|
||||||
|
|
||||||
// 读取API配置
|
// 读取API配置
|
||||||
remotelogs.Println("NODE", "init config ...")
|
remotelogs.Println("NODE", "init config ...")
|
||||||
err = this.syncConfig(0)
|
err = this.syncConfig(0)
|
||||||
@@ -146,7 +161,12 @@ func (this *Node) Start() {
|
|||||||
// 启动同步计时器
|
// 启动同步计时器
|
||||||
this.startSyncTimer()
|
this.startSyncTimer()
|
||||||
|
|
||||||
// 状态变更计时器
|
// 更新IP库
|
||||||
|
goman.New(func() {
|
||||||
|
iplib.NewUpdater(NewIPLibraryUpdater(), 10*time.Minute).Start()
|
||||||
|
})
|
||||||
|
|
||||||
|
// 监控节点运行状态
|
||||||
goman.New(func() {
|
goman.New(func() {
|
||||||
NewNodeStatusExecutor().Listen()
|
NewNodeStatusExecutor().Listen()
|
||||||
})
|
})
|
||||||
@@ -294,7 +314,7 @@ func (this *Node) loop() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var nodeCtx = rpcClient.Context()
|
var nodeCtx = rpcClient.Context()
|
||||||
tasksResp, err := rpcClient.NodeTaskRPC().FindNodeTasks(nodeCtx, &pb.FindNodeTasksRequest{})
|
tasksResp, err := rpcClient.NodeTaskRPC.FindNodeTasks(nodeCtx, &pb.FindNodeTasksRequest{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if rpc.IsConnError(err) && !Tea.IsTesting() {
|
if rpc.IsConnError(err) && !Tea.IsTesting() {
|
||||||
return nil
|
return nil
|
||||||
@@ -302,139 +322,157 @@ func (this *Node) loop() error {
|
|||||||
return errors.New("read node tasks failed: " + err.Error())
|
return errors.New("read node tasks failed: " + err.Error())
|
||||||
}
|
}
|
||||||
for _, task := range tasksResp.NodeTasks {
|
for _, task := range tasksResp.NodeTasks {
|
||||||
switch task.Type {
|
err := this.execTask(rpcClient, nodeCtx, task)
|
||||||
case "ipItemChanged":
|
this.finishTask(task.Id, err)
|
||||||
// 防止阻塞
|
|
||||||
select {
|
|
||||||
case iplibrary.IPListUpdateNotify <- true:
|
|
||||||
default:
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// 修改为已同步
|
|
||||||
_, err = rpcClient.NodeTaskRPC().ReportNodeTaskDone(nodeCtx, &pb.ReportNodeTaskDoneRequest{
|
|
||||||
NodeTaskId: task.Id,
|
|
||||||
IsOk: true,
|
|
||||||
Error: "",
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
case "configChanged":
|
|
||||||
if task.ServerId > 0 {
|
|
||||||
err = this.syncServerConfig(task.ServerId)
|
|
||||||
} else {
|
|
||||||
if !task.IsPrimary {
|
|
||||||
// 我们等等主节点配置准备完毕
|
|
||||||
time.Sleep(2 * time.Second)
|
|
||||||
}
|
|
||||||
err = this.syncConfig(task.Version)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
_, err = rpcClient.NodeTaskRPC().ReportNodeTaskDone(nodeCtx, &pb.ReportNodeTaskDoneRequest{
|
|
||||||
NodeTaskId: task.Id,
|
|
||||||
IsOk: false,
|
|
||||||
Error: err.Error(),
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
_, err = rpcClient.NodeTaskRPC().ReportNodeTaskDone(nodeCtx, &pb.ReportNodeTaskDoneRequest{
|
|
||||||
NodeTaskId: task.Id,
|
|
||||||
IsOk: true,
|
|
||||||
Error: "",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
case "nodeVersionChanged":
|
|
||||||
if !sharedUpgradeManager.IsInstalling() {
|
|
||||||
goman.New(func() {
|
|
||||||
sharedUpgradeManager.Start()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
case "scriptsChanged":
|
|
||||||
err = this.reloadCommonScripts()
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("reload common scripts failed: " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
// 修改为已同步
|
|
||||||
_, err = rpcClient.NodeTaskRPC().ReportNodeTaskDone(nodeCtx, &pb.ReportNodeTaskDoneRequest{
|
|
||||||
NodeTaskId: task.Id,
|
|
||||||
IsOk: true,
|
|
||||||
Error: "",
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
case "nodeLevelChanged":
|
|
||||||
levelInfoResp, err := rpcClient.NodeRPC().FindNodeLevelInfo(nodeCtx, &pb.FindNodeLevelInfoRequest{})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
sharedNodeConfig.Level = levelInfoResp.Level
|
|
||||||
|
|
||||||
var parentNodes = map[int64][]*nodeconfigs.ParentNodeConfig{}
|
|
||||||
if len(levelInfoResp.ParentNodesMapJSON) > 0 {
|
|
||||||
err = json.Unmarshal(levelInfoResp.ParentNodesMapJSON, &parentNodes)
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("decode level info failed: " + err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sharedNodeConfig.ParentNodes = parentNodes
|
|
||||||
|
|
||||||
// 修改为已同步
|
|
||||||
_, err = rpcClient.NodeTaskRPC().ReportNodeTaskDone(nodeCtx, &pb.ReportNodeTaskDoneRequest{
|
|
||||||
NodeTaskId: task.Id,
|
|
||||||
IsOk: true,
|
|
||||||
Error: "",
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
case "ddosProtectionChanged":
|
|
||||||
resp, err := rpcClient.NodeRPC().FindNodeDDoSProtection(nodeCtx, &pb.FindNodeDDoSProtectionRequest{})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if len(resp.DdosProtectionJSON) == 0 {
|
|
||||||
if sharedNodeConfig != nil {
|
|
||||||
sharedNodeConfig.DDoSProtection = nil
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
var ddosProtectionConfig = &ddosconfigs.ProtectionConfig{}
|
|
||||||
err = json.Unmarshal(resp.DdosProtectionJSON, ddosProtectionConfig)
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("decode DDoS protection config failed: " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if sharedNodeConfig != nil {
|
|
||||||
sharedNodeConfig.DDoSProtection = ddosProtectionConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
err = firewalls.SharedDDoSProtectionManager.Apply(ddosProtectionConfig)
|
|
||||||
if err != nil {
|
|
||||||
// 不阻塞
|
|
||||||
remotelogs.Error("NODE", "apply DDoS protection failed: "+err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 修改为已同步
|
|
||||||
_, err = rpcClient.NodeTaskRPC().ReportNodeTaskDone(nodeCtx, &pb.ReportNodeTaskDoneRequest{
|
|
||||||
NodeTaskId: task.Id,
|
|
||||||
IsOk: true,
|
|
||||||
Error: "",
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 执行任务
|
||||||
|
func (this *Node) execTask(rpcClient *rpc.RPCClient, nodeCtx context.Context, task *pb.NodeTask) error {
|
||||||
|
switch task.Type {
|
||||||
|
case "ipItemChanged":
|
||||||
|
// 防止阻塞
|
||||||
|
select {
|
||||||
|
case iplibrary.IPListUpdateNotify <- true:
|
||||||
|
default:
|
||||||
|
|
||||||
|
}
|
||||||
|
case "configChanged":
|
||||||
|
if task.ServerId > 0 {
|
||||||
|
return this.syncServerConfig(task.ServerId)
|
||||||
|
}
|
||||||
|
if !task.IsPrimary {
|
||||||
|
// 我们等等主节点配置准备完毕
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
}
|
||||||
|
return this.syncConfig(task.Version)
|
||||||
|
case "nodeVersionChanged":
|
||||||
|
if !sharedUpgradeManager.IsInstalling() {
|
||||||
|
goman.New(func() {
|
||||||
|
sharedUpgradeManager.Start()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
case "scriptsChanged":
|
||||||
|
err := this.reloadCommonScripts()
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("reload common scripts failed: " + err.Error())
|
||||||
|
}
|
||||||
|
case "nodeLevelChanged":
|
||||||
|
levelInfoResp, err := rpcClient.NodeRPC.FindNodeLevelInfo(nodeCtx, &pb.FindNodeLevelInfoRequest{})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if sharedNodeConfig != nil {
|
||||||
|
sharedNodeConfig.Level = levelInfoResp.Level
|
||||||
|
}
|
||||||
|
|
||||||
|
var parentNodes = map[int64][]*nodeconfigs.ParentNodeConfig{}
|
||||||
|
if len(levelInfoResp.ParentNodesMapJSON) > 0 {
|
||||||
|
err = json.Unmarshal(levelInfoResp.ParentNodesMapJSON, &parentNodes)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("decode level info failed: " + err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if sharedNodeConfig != nil {
|
||||||
|
sharedNodeConfig.ParentNodes = parentNodes
|
||||||
|
}
|
||||||
|
case "ddosProtectionChanged":
|
||||||
|
resp, err := rpcClient.NodeRPC.FindNodeDDoSProtection(nodeCtx, &pb.FindNodeDDoSProtectionRequest{})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(resp.DdosProtectionJSON) == 0 {
|
||||||
|
if sharedNodeConfig != nil {
|
||||||
|
sharedNodeConfig.DDoSProtection = nil
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var ddosProtectionConfig = &ddosconfigs.ProtectionConfig{}
|
||||||
|
err = json.Unmarshal(resp.DdosProtectionJSON, ddosProtectionConfig)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("decode DDoS protection config failed: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if ddosProtectionConfig != nil && sharedNodeConfig != nil {
|
||||||
|
sharedNodeConfig.DDoSProtection = ddosProtectionConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
err = firewalls.SharedDDoSProtectionManager.Apply(ddosProtectionConfig)
|
||||||
|
if err != nil {
|
||||||
|
// 不阻塞
|
||||||
|
remotelogs.Warn("NODE", "apply DDoS protection failed: "+err.Error())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
case "globalServerConfigChanged":
|
||||||
|
resp, err := rpcClient.NodeRPC.FindNodeGlobalServerConfig(nodeCtx, &pb.FindNodeGlobalServerConfigRequest{})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(resp.GlobalServerConfigJSON) > 0 {
|
||||||
|
var globalServerConfig = serverconfigs.DefaultGlobalServerConfig()
|
||||||
|
err = json.Unmarshal(resp.GlobalServerConfigJSON, globalServerConfig)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("decode global server config failed: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if globalServerConfig != nil {
|
||||||
|
err = globalServerConfig.Init()
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("validate global server config failed: " + err.Error())
|
||||||
|
}
|
||||||
|
if sharedNodeConfig != nil {
|
||||||
|
sharedNodeConfig.GlobalServerConfig = globalServerConfig
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
remotelogs.Error("NODE", "task '"+types.String(task.Id)+"', type '"+task.Type+"' has not been handled")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 标记任务完成
|
||||||
|
func (this *Node) finishTask(taskId int64, err error) {
|
||||||
|
if taskId <= 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rpcClient, err := rpc.SharedRPC()
|
||||||
|
if err != nil {
|
||||||
|
logs.Println("[NODE]", "create rpc client failed: "+err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var nodeCtx = rpcClient.Context()
|
||||||
|
|
||||||
|
var isOk = err == nil
|
||||||
|
var errMsg = ""
|
||||||
|
if err != nil {
|
||||||
|
errMsg = err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = rpcClient.NodeTaskRPC.ReportNodeTaskDone(nodeCtx, &pb.ReportNodeTaskDoneRequest{
|
||||||
|
NodeTaskId: taskId,
|
||||||
|
IsOk: isOk,
|
||||||
|
Error: errMsg,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
// 不需要上报到服务中心
|
||||||
|
if rpc.IsConnError(err) {
|
||||||
|
logs.Println("[NODE]", "report task done failed: "+err.Error())
|
||||||
|
} else {
|
||||||
|
remotelogs.Error("NODE", "report task done failed: "+err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 读取API配置
|
// 读取API配置
|
||||||
func (this *Node) syncConfig(taskVersion int64) error {
|
func (this *Node) syncConfig(taskVersion int64) error {
|
||||||
this.locker.Lock()
|
this.locker.Lock()
|
||||||
@@ -463,10 +501,10 @@ func (this *Node) syncConfig(taskVersion int64) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取同步任务
|
// 获取同步任务
|
||||||
nodeCtx := rpcClient.Context()
|
var nodeCtx = rpcClient.Context()
|
||||||
|
|
||||||
// TODO 这里考虑只同步版本号有变更的
|
// TODO 这里考虑只同步版本号有变更的
|
||||||
configResp, err := rpcClient.NodeRPC().FindCurrentNodeConfig(nodeCtx, &pb.FindCurrentNodeConfigRequest{
|
configResp, err := rpcClient.NodeRPC.FindCurrentNodeConfig(nodeCtx, &pb.FindCurrentNodeConfigRequest{
|
||||||
Version: -1, // 更新所有版本
|
Version: -1, // 更新所有版本
|
||||||
Compress: true,
|
Compress: true,
|
||||||
NodeTaskVersion: taskVersion,
|
NodeTaskVersion: taskVersion,
|
||||||
@@ -557,7 +595,7 @@ func (this *Node) syncServerConfig(serverId int64) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
resp, err := rpcClient.ServerRPC().ComposeServerConfig(rpcClient.Context(), &pb.ComposeServerConfigRequest{ServerId: serverId})
|
resp, err := rpcClient.ServerRPC.ComposeServerConfig(rpcClient.Context(), &pb.ComposeServerConfigRequest{ServerId: serverId})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -639,7 +677,7 @@ func (this *Node) checkClusterConfig() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
logs.Println("[NODE]registering node to cluster ...")
|
logs.Println("[NODE]registering node to cluster ...")
|
||||||
resp, err := rpcClient.NodeRPC().RegisterClusterNode(rpcClient.ClusterContext(config.ClusterId, config.Secret), &pb.RegisterClusterNodeRequest{Name: HOSTNAME})
|
resp, err := rpcClient.NodeRPC.RegisterClusterNode(rpcClient.ClusterContext(config.ClusterId, config.Secret), &pb.RegisterClusterNodeRequest{Name: HOSTNAME})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -778,13 +816,16 @@ func (this *Node) listenSock() error {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
case "conns":
|
case "conns":
|
||||||
ipConns, serverConns := sharedClientConnLimiter.Conns()
|
var addrs = []string{}
|
||||||
|
var connMap = conns.SharedMap.AllConns()
|
||||||
|
for _, conn := range connMap {
|
||||||
|
addrs = append(addrs, conn.RemoteAddr().String())
|
||||||
|
}
|
||||||
|
|
||||||
_ = cmd.Reply(&gosock.Command{
|
_ = cmd.Reply(&gosock.Command{
|
||||||
Params: map[string]interface{}{
|
Params: map[string]interface{}{
|
||||||
"ipConns": ipConns,
|
"addrs": addrs,
|
||||||
"serverConns": serverConns,
|
"total": len(addrs),
|
||||||
"total": sharedListenerManager.TotalActiveConnections(),
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
case "dropIP":
|
case "dropIP":
|
||||||
@@ -856,6 +897,11 @@ func (this *Node) listenSock() error {
|
|||||||
} else {
|
} else {
|
||||||
_ = cmd.ReplyOk()
|
_ = cmd.ReplyOk()
|
||||||
}
|
}
|
||||||
|
case "bandwidth":
|
||||||
|
var m = stats.SharedBandwidthStatManager.Map()
|
||||||
|
_ = cmd.Reply(&gosock.Command{Params: maps.Map{
|
||||||
|
"stats": m,
|
||||||
|
}})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -866,7 +912,7 @@ func (this *Node) listenSock() error {
|
|||||||
})
|
})
|
||||||
|
|
||||||
events.OnKey(events.EventQuit, this, func() {
|
events.OnKey(events.EventQuit, this, func() {
|
||||||
logs.Println("NODE", "quit unix sock")
|
remotelogs.Println("NODE", "quit unix sock")
|
||||||
_ = this.sock.Close()
|
_ = this.sock.Close()
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -1010,10 +1056,13 @@ func (this *Node) reloadServer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *Node) checkDisk() {
|
func (this *Node) checkDisk() {
|
||||||
if runtime.GOOS == "linux" {
|
if runtime.GOOS != "linux" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for n := 'a'; n <= 'z'; n++ {
|
||||||
for _, path := range []string{
|
for _, path := range []string{
|
||||||
"/sys/block/vda/queue/rotational",
|
"/sys/block/vd" + string(n) + "/queue/rotational",
|
||||||
"/sys/block/sda/queue/rotational",
|
"/sys/block/sd" + string(n) + "/queue/rotational",
|
||||||
} {
|
} {
|
||||||
data, err := os.ReadFile(path)
|
data, err := os.ReadFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -1022,7 +1071,7 @@ func (this *Node) checkDisk() {
|
|||||||
if string(data) == "0" {
|
if string(data) == "0" {
|
||||||
teaconst.DiskIsFast = true
|
teaconst.DiskIsFast = true
|
||||||
}
|
}
|
||||||
break
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ func (this *NodeStatusExecutor) update() {
|
|||||||
remotelogs.Error("NODE_STATUS", "failed to open rpc: "+err.Error())
|
remotelogs.Error("NODE_STATUS", "failed to open rpc: "+err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_, err = rpcClient.NodeRPC().UpdateNodeStatus(rpcClient.Context(), &pb.UpdateNodeStatusRequest{
|
_, err = rpcClient.NodeRPC.UpdateNodeStatus(rpcClient.Context(), &pb.UpdateNodeStatusRequest{
|
||||||
StatusJSON: jsonData,
|
StatusJSON: jsonData,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -1,48 +0,0 @@
|
|||||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
|
||||||
|
|
||||||
package nodes
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/ttlcache"
|
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
|
||||||
"github.com/iwind/TeaGo/types"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var sharedCNAMEManager = NewServerCNAMEManager()
|
|
||||||
|
|
||||||
// ServerCNAMEManager 服务CNAME管理
|
|
||||||
// TODO 需要自动更新缓存里的记录
|
|
||||||
type ServerCNAMEManager struct {
|
|
||||||
ttlCache *ttlcache.Cache
|
|
||||||
|
|
||||||
locker sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewServerCNAMEManager() *ServerCNAMEManager {
|
|
||||||
return &ServerCNAMEManager{
|
|
||||||
ttlCache: ttlcache.NewCache(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *ServerCNAMEManager) Lookup(domain string) string {
|
|
||||||
if len(domain) == 0 {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
var item = this.ttlCache.Read(domain)
|
|
||||||
if item != nil {
|
|
||||||
return types.String(item.Value)
|
|
||||||
}
|
|
||||||
|
|
||||||
cname, _ := utils.LookupCNAME(domain)
|
|
||||||
if len(cname) > 0 {
|
|
||||||
cname = strings.TrimSuffix(cname, ".")
|
|
||||||
}
|
|
||||||
|
|
||||||
this.ttlCache.Write(domain, cname, time.Now().Unix()+600)
|
|
||||||
|
|
||||||
return cname
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
|
||||||
|
|
||||||
package nodes
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestServerCNameManager_Lookup(t *testing.T) {
|
|
||||||
var cnameManager = NewServerCNAMEManager()
|
|
||||||
t.Log(cnameManager.Lookup("www.yun4s.cn"))
|
|
||||||
|
|
||||||
var before = time.Now()
|
|
||||||
defer func() {
|
|
||||||
t.Log(time.Since(before).Seconds()*1000, "ms")
|
|
||||||
}()
|
|
||||||
t.Log(cnameManager.Lookup("www.yun4s.cn"))
|
|
||||||
}
|
|
||||||
@@ -10,6 +10,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/TeaOSLab/EdgeNode/internal/utils"
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
|
executils "github.com/TeaOSLab/EdgeNode/internal/utils/exec"
|
||||||
"github.com/iwind/TeaGo/maps"
|
"github.com/iwind/TeaGo/maps"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
@@ -20,15 +21,18 @@ import (
|
|||||||
func init() {
|
func init() {
|
||||||
var manager = NewSystemServiceManager()
|
var manager = NewSystemServiceManager()
|
||||||
events.On(events.EventReload, func() {
|
events.On(events.EventReload, func() {
|
||||||
err := manager.Setup()
|
goman.New(func() {
|
||||||
if err != nil {
|
err := manager.Setup()
|
||||||
remotelogs.Error("SYSTEM_SERVICE", "setup system services failed: "+err.Error())
|
if err != nil {
|
||||||
}
|
remotelogs.Error("SYSTEM_SERVICE", "setup system services failed: "+err.Error())
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// SystemServiceManager 系统服务管理
|
// SystemServiceManager 系统服务管理
|
||||||
type SystemServiceManager struct {
|
type SystemServiceManager struct {
|
||||||
|
lastIsOn int // -1, 0, 1
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSystemServiceManager() *SystemServiceManager {
|
func NewSystemServiceManager() *SystemServiceManager {
|
||||||
@@ -68,7 +72,8 @@ func (this *SystemServiceManager) setupSystemd(params maps.Map) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
config := &nodeconfigs.SystemdServiceConfig{}
|
|
||||||
|
var config = &nodeconfigs.SystemdServiceConfig{}
|
||||||
err = json.Unmarshal(data, config)
|
err = json.Unmarshal(data, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -82,42 +87,60 @@ func (this *SystemServiceManager) setupSystemd(params maps.Map) error {
|
|||||||
if len(systemctl) == 0 {
|
if len(systemctl) == 0 {
|
||||||
return errors.New("can not find 'systemctl' on the system")
|
return errors.New("can not find 'systemctl' on the system")
|
||||||
}
|
}
|
||||||
cmd := utils.NewCommandExecutor()
|
|
||||||
shortName := teaconst.SystemdServiceName
|
// 记录上次状态
|
||||||
cmd.Add(systemctl, "is-enabled", shortName)
|
var isOnInt int
|
||||||
output, err := cmd.Run()
|
if config.IsOn {
|
||||||
if err != nil {
|
isOnInt = 1
|
||||||
return err
|
} else {
|
||||||
|
isOnInt = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if this.lastIsOn == isOnInt {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
this.lastIsOn = isOnInt
|
||||||
|
}()
|
||||||
|
|
||||||
|
var shortName = teaconst.SystemdServiceName
|
||||||
|
var cmd = executils.NewTimeoutCmd(10*time.Second, systemctl, "is-enabled", shortName)
|
||||||
|
cmd.WithStdout()
|
||||||
|
err = cmd.Run()
|
||||||
|
var hasInstalled = err == nil
|
||||||
if config.IsOn {
|
if config.IsOn {
|
||||||
exe, err := os.Executable()
|
exe, err := os.Executable()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 启动Service
|
// 检查文件路径是否变化
|
||||||
goman.New(func() {
|
if hasInstalled && cmd.Stdout() == "enabled" {
|
||||||
time.Sleep(5 * time.Second)
|
|
||||||
_ = exec.Command(systemctl, "start", teaconst.SystemdServiceName).Start()
|
|
||||||
})
|
|
||||||
|
|
||||||
if output == "enabled" {
|
|
||||||
// 检查文件路径是否变化
|
|
||||||
data, err := os.ReadFile("/etc/systemd/system/" + teaconst.SystemdServiceName + ".service")
|
data, err := os.ReadFile("/etc/systemd/system/" + teaconst.SystemdServiceName + ".service")
|
||||||
if err == nil && bytes.Index(data, []byte(exe)) > 0 {
|
if err == nil && bytes.Index(data, []byte(exe)) > 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
manager := utils.NewServiceManager(shortName, teaconst.ProductName)
|
|
||||||
|
// 安装服务
|
||||||
|
var manager = utils.NewServiceManager(shortName, teaconst.ProductName)
|
||||||
err = manager.Install(exe, []string{})
|
err = manager.Install(exe, []string{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 启动服务
|
||||||
|
goman.New(func() {
|
||||||
|
time.Sleep(5 * time.Second)
|
||||||
|
_ = executils.NewTimeoutCmd(30*time.Second, systemctl, "start", teaconst.SystemdServiceName).Start()
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
manager := utils.NewServiceManager(shortName, teaconst.ProductName)
|
if hasInstalled {
|
||||||
err = manager.Uninstall()
|
var manager = utils.NewServiceManager(shortName, teaconst.ProductName)
|
||||||
if err != nil {
|
err = manager.Uninstall()
|
||||||
return err
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ func (this *OCSPUpdateTask) Loop() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := rpcClient.SSLCertRPC().ListUpdatedSSLCertOCSP(rpcClient.Context(), &pb.ListUpdatedSSLCertOCSPRequest{
|
resp, err := rpcClient.SSLCertRPC.ListUpdatedSSLCertOCSP(rpcClient.Context(), &pb.ListUpdatedSSLCertOCSPRequest{
|
||||||
Version: this.version,
|
Version: this.version,
|
||||||
Size: 100,
|
Size: 100,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
"github.com/iwind/TeaGo/logs"
|
"github.com/iwind/TeaGo/logs"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/credentials"
|
"google.golang.org/grpc/credentials"
|
||||||
|
"google.golang.org/grpc/credentials/insecure"
|
||||||
"net/url"
|
"net/url"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -81,7 +82,7 @@ func (this *SyncAPINodesTask) Loop() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
resp, err := rpcClient.APINodeRPC().FindAllEnabledAPINodes(rpcClient.Context(), &pb.FindAllEnabledAPINodesRequest{})
|
resp, err := rpcClient.APINodeRPC.FindAllEnabledAPINodes(rpcClient.Context(), &pb.FindAllEnabledAPINodesRequest{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -152,7 +153,7 @@ func (this *SyncAPINodesTask) testEndpoints(endpoints []string) bool {
|
|||||||
}()
|
}()
|
||||||
var conn *grpc.ClientConn
|
var conn *grpc.ClientConn
|
||||||
if u.Scheme == "http" {
|
if u.Scheme == "http" {
|
||||||
conn, err = grpc.DialContext(ctx, u.Host, grpc.WithInsecure(), grpc.WithBlock())
|
conn, err = grpc.DialContext(ctx, u.Host, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock())
|
||||||
} else if u.Scheme == "https" {
|
} else if u.Scheme == "https" {
|
||||||
conn, err = grpc.DialContext(ctx, u.Host, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{
|
conn, err = grpc.DialContext(ctx, u.Host, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{
|
||||||
InsecureSkipVerify: true,
|
InsecureSkipVerify: true,
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
package nodes
|
package nodes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/events"
|
"github.com/TeaOSLab/EdgeNode/internal/events"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
"github.com/TeaOSLab/EdgeNode/internal/goman"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
|
||||||
|
executils "github.com/TeaOSLab/EdgeNode/internal/utils/exec"
|
||||||
"github.com/iwind/TeaGo/Tea"
|
"github.com/iwind/TeaGo/Tea"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -61,12 +62,16 @@ func (this *TOAManager) Run(config *nodeconfigs.TOAConfig) error {
|
|||||||
}
|
}
|
||||||
remotelogs.Println("TOA", "starting ...")
|
remotelogs.Println("TOA", "starting ...")
|
||||||
remotelogs.Println("TOA", "args: "+strings.Join(config.AsArgs(), " "))
|
remotelogs.Println("TOA", "args: "+strings.Join(config.AsArgs(), " "))
|
||||||
cmd := exec.Command(binPath, config.AsArgs()...)
|
cmd := executils.NewCmd(binPath, config.AsArgs()...)
|
||||||
err = cmd.Start()
|
err = cmd.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
this.pid = cmd.Process.Pid
|
var process = cmd.Process()
|
||||||
|
if process == nil {
|
||||||
|
return errors.New("start failed")
|
||||||
|
}
|
||||||
|
this.pid = process.Pid
|
||||||
|
|
||||||
goman.New(func() {
|
goman.New(func() {
|
||||||
_ = cmd.Wait()
|
_ = cmd.Wait()
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user