mirror of
https://github.com/TeaOSLab/EdgeNode.git
synced 2025-11-07 10:40:26 +08:00
阶段性提交
This commit is contained in:
51
build/build.sh
Executable file
51
build/build.sh
Executable file
@@ -0,0 +1,51 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
function build() {
|
||||||
|
VERSION_DATA=$(cat ../internal/const/const.go)
|
||||||
|
re="Version[ ]+=[ ]+\"([0-9.]+)\""
|
||||||
|
if [[ $VERSION_DATA =~ $re ]]; then
|
||||||
|
VERSION=${BASH_REMATCH[1]}
|
||||||
|
else
|
||||||
|
echo "could not match version"
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "checking ..."
|
||||||
|
ZIP_PATH=$(which zip)
|
||||||
|
if [ -z $ZIP_PATH ]; then
|
||||||
|
echo "we need 'zip' command to compress files"
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "building v${VERSION}/${1}/${2} ..."
|
||||||
|
NAME="edge-node"
|
||||||
|
DIST="../dist/${NAME}"
|
||||||
|
ZIP="${NAME}-${1}-${2}-v${VERSION}.zip"
|
||||||
|
|
||||||
|
echo "copying ..."
|
||||||
|
if [ ! -d $DIST ]; then
|
||||||
|
mkdir $DIST
|
||||||
|
mkdir $DIST/bin
|
||||||
|
mkdir $DIST/configs
|
||||||
|
mkdir $DIST/logs
|
||||||
|
fi
|
||||||
|
|
||||||
|
cp configs/api.template.yaml $DIST/configs
|
||||||
|
cp -R www $DIST/
|
||||||
|
|
||||||
|
echo "building ..."
|
||||||
|
env GOOS=${1} GOARCH=${2} go build -o $DIST/bin/${NAME} -ldflags="-s -w" ../cmd/edge-node/main.go
|
||||||
|
|
||||||
|
echo "zip files"
|
||||||
|
cd "${DIST}/../" || exit
|
||||||
|
if [ -f "${ZIP}" ]; then
|
||||||
|
rm -f "${ZIP}"
|
||||||
|
fi
|
||||||
|
zip -r -X -q "${ZIP}" ${NAME}/
|
||||||
|
rm -rf ${NAME}
|
||||||
|
cd - || exit
|
||||||
|
|
||||||
|
echo "OK"
|
||||||
|
}
|
||||||
|
|
||||||
|
build "linux" "amd64"
|
||||||
8
build/config.sh
Executable file
8
build/config.sh
Executable file
@@ -0,0 +1,8 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
TARGET=../../EdgeAdmin/internal/serverconfigs
|
||||||
|
if [ -d ${TARGET} ]
|
||||||
|
then
|
||||||
|
rm -rf ../../EdgeAdmin/internal/serverconfigs
|
||||||
|
fi
|
||||||
|
cp -R ../internal/configs/serverconfigs ../../EdgeAdmin/internal/configs/
|
||||||
1
build/configs/README.md
Normal file
1
build/configs/README.md
Normal file
@@ -0,0 +1 @@
|
|||||||
|
* `global.yaml` - 全局配置
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
rpc:
|
rpc:
|
||||||
endpoints: [ "127.0.0.1:8003" ]
|
endpoints: [ ${endpoints} ]
|
||||||
nodeId: ""
|
nodeId: "${nodeId}"
|
||||||
secret: ""
|
secret: "${nodeSecret}"
|
||||||
1
build/logs/.gitignore
vendored
Normal file
1
build/logs/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
*.log
|
||||||
@@ -1,11 +1,29 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/apps"
|
||||||
|
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/nodes"
|
"github.com/TeaOSLab/EdgeNode/internal/nodes"
|
||||||
_ "github.com/iwind/TeaGo/bootstrap"
|
_ "github.com/iwind/TeaGo/bootstrap"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
app := apps.NewAppCmd().
|
||||||
|
Version(teaconst.Version).
|
||||||
|
Product(teaconst.ProductName).
|
||||||
|
Usage(teaconst.ProcessName + " [-v|start|stop|restart|sync|update]")
|
||||||
|
|
||||||
|
app.On("sync", func() {
|
||||||
|
// TODO
|
||||||
|
fmt.Println("not implemented yet")
|
||||||
|
})
|
||||||
|
app.On("update", func() {
|
||||||
|
// TODO
|
||||||
|
fmt.Println("not implemented yet")
|
||||||
|
})
|
||||||
|
app.Run(func() {
|
||||||
node := nodes.NewNode()
|
node := nodes.NewNode()
|
||||||
node.Start()
|
node.Start()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
1
dist/.gitignore
vendored
Normal file
1
dist/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
*.zip
|
||||||
12
go.mod
12
go.mod
@@ -3,9 +3,19 @@ module github.com/TeaOSLab/EdgeNode
|
|||||||
go 1.14
|
go 1.14
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect
|
||||||
|
github.com/go-ole/go-ole v1.2.4 // indirect
|
||||||
|
github.com/go-redis/redis v6.15.8+incompatible // indirect
|
||||||
github.com/go-yaml/yaml v2.1.0+incompatible
|
github.com/go-yaml/yaml v2.1.0+incompatible
|
||||||
github.com/golang/protobuf v1.4.2
|
github.com/golang/protobuf v1.4.2
|
||||||
github.com/iwind/TeaGo v0.0.0-20200722010955-47dd648dc761
|
github.com/iwind/TeaGo v0.0.0-20200822074248-b1cf7248c98a
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
|
github.com/modern-go/reflect2 v1.0.1 // indirect
|
||||||
|
github.com/pquerna/ffjson v0.0.0-20190930134022-aa0246cd15f7 // indirect
|
||||||
|
github.com/shirou/gopsutil v2.20.7+incompatible
|
||||||
|
github.com/stretchr/testify v1.6.1 // indirect
|
||||||
|
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7
|
||||||
google.golang.org/grpc v1.30.0
|
google.golang.org/grpc v1.30.0
|
||||||
google.golang.org/protobuf v1.25.0
|
google.golang.org/protobuf v1.25.0
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c
|
||||||
)
|
)
|
||||||
|
|||||||
68
go.sum
68
go.sum
@@ -1,15 +1,30 @@
|
|||||||
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=
|
||||||
|
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/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/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk=
|
||||||
|
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
||||||
|
github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=
|
||||||
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=
|
||||||
|
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||||
|
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/dgryski/go-rendezvous v0.0.0-20200624174652-8d2f3be8b2d9/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=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||||
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/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||||
|
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.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI=
|
||||||
|
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
|
||||||
github.com/go-redis/redis v6.15.8+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
|
github.com/go-redis/redis v6.15.8+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
|
||||||
|
github.com/go-redis/redis/v8 v8.0.0-beta.7/go.mod h1:FGJAWDWFht1sQ4qxyJHZZbVyvnVcKQN0E3u5/5lRz+g=
|
||||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||||
github.com/go-yaml/yaml v2.1.0+incompatible h1:RYi2hDdss1u4YE7GwixGzWwVo47T8UQwnTLB6vQiq+o=
|
github.com/go-yaml/yaml v2.1.0+incompatible h1:RYi2hDdss1u4YE7GwixGzWwVo47T8UQwnTLB6vQiq+o=
|
||||||
github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0=
|
github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0=
|
||||||
@@ -33,34 +48,71 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
|
|||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
|
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
|
||||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
github.com/iwind/TeaGo v0.0.0-20200722010955-47dd648dc761 h1:70Iaf6iVF4CiH1P5Au3hqile/2Ea0XIkR4SPpdiaKI0=
|
github.com/iwind/TeaGo v0.0.0-20200727075925-7e7e67b44f2d h1:V7HA0wUOdmZbXJTVpiUEvSD4ARKHwMLMmiCccfkqf24=
|
||||||
github.com/iwind/TeaGo v0.0.0-20200722010955-47dd648dc761/go.mod h1:zjM7k+b+Jthhf0T0fKwuF0iy4TWb5SsU1gmKR2l+OmE=
|
github.com/iwind/TeaGo v0.0.0-20200727075925-7e7e67b44f2d/go.mod h1:zjM7k+b+Jthhf0T0fKwuF0iy4TWb5SsU1gmKR2l+OmE=
|
||||||
|
github.com/iwind/TeaGo v0.0.0-20200822074248-b1cf7248c98a h1:VaWcMNOzHHT1y8MeTA2fWhG6GEfAdy6CwF2tW+KiY5Y=
|
||||||
|
github.com/iwind/TeaGo v0.0.0-20200822074248-b1cf7248c98a/go.mod h1:KU4mS7QNiZ7QWEuDBk1zw0/Q2LrAPZv3tycEFBsuUwc=
|
||||||
|
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
|
||||||
|
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
|
||||||
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
|
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||||
|
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
|
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||||
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
||||||
|
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||||
|
github.com/opentracing/opentracing-go v1.1.1-0.20190913142402-a7454ce5950e/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/pquerna/ffjson v0.0.0-20190930134022-aa0246cd15f7 h1:xoIK0ctDddBMnc74udxJYBqlo9Ylnsp1waqjLsnef20=
|
github.com/pquerna/ffjson v0.0.0-20190930134022-aa0246cd15f7 h1:xoIK0ctDddBMnc74udxJYBqlo9Ylnsp1waqjLsnef20=
|
||||||
github.com/pquerna/ffjson v0.0.0-20190930134022-aa0246cd15f7/go.mod h1:YARuvh7BUWHNhzDq2OM5tzR2RiCcN2D7sapiKyCel/M=
|
github.com/pquerna/ffjson v0.0.0-20190930134022-aa0246cd15f7/go.mod h1:YARuvh7BUWHNhzDq2OM5tzR2RiCcN2D7sapiKyCel/M=
|
||||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
|
github.com/shirou/gopsutil v2.20.7+incompatible h1:Ymv4OD12d6zm+2yONe39VSmp2XooJe8za7ngOLW/o/w=
|
||||||
|
github.com/shirou/gopsutil v2.20.7+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||||
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
go.opentelemetry.io/otel v0.7.0/go.mod h1:aZMyHG5TqDOXEgH2tyLiXSUKly1jT3yqE9PmrzIeCdo=
|
||||||
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/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/exp v0.0.0-20200513190911-00229845015e/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
|
||||||
|
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/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/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-20180906233101-161cd47e91fd/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 h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
|
||||||
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-20190620200207-3b0461eec859/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-20200520004742-59133d7f0dd7 h1:AeiKBIuRw3UomYXSbLy0Mc2dDLfdtbT/IVn4keq83P0=
|
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7 h1:AeiKBIuRw3UomYXSbLy0Mc2dDLfdtbT/IVn4keq83P0=
|
||||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
@@ -71,8 +123,12 @@ golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5h
|
|||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/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 h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
|
||||||
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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/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-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-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 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4=
|
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4=
|
||||||
@@ -86,12 +142,15 @@ golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGm
|
|||||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-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-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
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-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=
|
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=
|
||||||
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/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
@@ -111,12 +170,17 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD
|
|||||||
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
|
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
|
||||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||||
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 h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
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.4/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 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
|||||||
234
internal/apps/app_cmd.go
Normal file
234
internal/apps/app_cmd.go
Normal file
@@ -0,0 +1,234 @@
|
|||||||
|
package apps
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/iwind/TeaGo/Tea"
|
||||||
|
"github.com/iwind/TeaGo/logs"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// App命令帮助
|
||||||
|
type AppCmd struct {
|
||||||
|
product string
|
||||||
|
version string
|
||||||
|
usage string
|
||||||
|
options []*CommandHelpOption
|
||||||
|
appendStrings []string
|
||||||
|
|
||||||
|
directives []*Directive
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAppCmd() *AppCmd {
|
||||||
|
return &AppCmd{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type CommandHelpOption struct {
|
||||||
|
Code string
|
||||||
|
Description string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 产品
|
||||||
|
func (this *AppCmd) Product(product string) *AppCmd {
|
||||||
|
this.product = product
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
// 版本
|
||||||
|
func (this *AppCmd) Version(version string) *AppCmd {
|
||||||
|
this.version = version
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用方法
|
||||||
|
func (this *AppCmd) Usage(usage string) *AppCmd {
|
||||||
|
this.usage = usage
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选项
|
||||||
|
func (this *AppCmd) Option(code string, description string) *AppCmd {
|
||||||
|
this.options = append(this.options, &CommandHelpOption{
|
||||||
|
Code: code,
|
||||||
|
Description: description,
|
||||||
|
})
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
// 附加内容
|
||||||
|
func (this *AppCmd) Append(appendString string) *AppCmd {
|
||||||
|
this.appendStrings = append(this.appendStrings, appendString)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打印
|
||||||
|
func (this *AppCmd) Print() {
|
||||||
|
fmt.Println(this.product + " v" + this.version)
|
||||||
|
|
||||||
|
usage := this.usage
|
||||||
|
fmt.Println("Usage:", "\n "+usage)
|
||||||
|
|
||||||
|
if len(this.options) > 0 {
|
||||||
|
fmt.Println("")
|
||||||
|
fmt.Println("Options:")
|
||||||
|
|
||||||
|
spaces := 20
|
||||||
|
max := 40
|
||||||
|
for _, option := range this.options {
|
||||||
|
l := len(option.Code)
|
||||||
|
if l < max && l > spaces {
|
||||||
|
spaces = l + 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, option := range this.options {
|
||||||
|
if len(option.Code) > max {
|
||||||
|
fmt.Println("")
|
||||||
|
fmt.Println(" " + option.Code)
|
||||||
|
option.Code = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf(" %-"+strconv.Itoa(spaces)+"s%s\n", option.Code, ": "+option.Description)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(this.appendStrings) > 0 {
|
||||||
|
fmt.Println("")
|
||||||
|
for _, s := range this.appendStrings {
|
||||||
|
fmt.Println(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加指令
|
||||||
|
func (this *AppCmd) On(arg string, callback func()) {
|
||||||
|
this.directives = append(this.directives, &Directive{
|
||||||
|
Arg: arg,
|
||||||
|
Callback: callback,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 运行
|
||||||
|
func (this *AppCmd) Run(main func()) {
|
||||||
|
// 获取参数
|
||||||
|
args := os.Args[1:]
|
||||||
|
if len(args) > 0 {
|
||||||
|
switch args[0] {
|
||||||
|
case "-v", "version", "-version", "--version":
|
||||||
|
this.runVersion()
|
||||||
|
return
|
||||||
|
case "?", "help", "-help", "h", "-h":
|
||||||
|
this.runHelp()
|
||||||
|
return
|
||||||
|
case "start":
|
||||||
|
this.runStart()
|
||||||
|
return
|
||||||
|
case "stop":
|
||||||
|
this.runStop()
|
||||||
|
return
|
||||||
|
case "restart":
|
||||||
|
this.runRestart()
|
||||||
|
return
|
||||||
|
case "status":
|
||||||
|
this.runStatus()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查找指令
|
||||||
|
for _, directive := range this.directives {
|
||||||
|
if directive.Arg == args[0] {
|
||||||
|
directive.Callback()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("unknown command '" + args[0] + "'")
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 记录PID
|
||||||
|
_ = this.writePid()
|
||||||
|
|
||||||
|
// 日志
|
||||||
|
writer := new(LogWriter)
|
||||||
|
writer.Init()
|
||||||
|
logs.SetWriter(writer)
|
||||||
|
|
||||||
|
// 运行主函数
|
||||||
|
main()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 版本号
|
||||||
|
func (this *AppCmd) runVersion() {
|
||||||
|
fmt.Println(this.product+" v"+this.version, "(build: "+runtime.Version(), runtime.GOOS, runtime.GOARCH+")")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 帮助
|
||||||
|
func (this *AppCmd) runHelp() {
|
||||||
|
this.Print()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 启动
|
||||||
|
func (this *AppCmd) runStart() {
|
||||||
|
proc := this.checkPid()
|
||||||
|
if proc != nil {
|
||||||
|
fmt.Println(this.product+" already started, pid:", proc.Pid)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := exec.Command(os.Args[0])
|
||||||
|
err := cmd.Start()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(this.product+" start failed:", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(this.product+" started ok, pid:", cmd.Process.Pid)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 停止
|
||||||
|
func (this *AppCmd) runStop() {
|
||||||
|
proc := this.checkPid()
|
||||||
|
if proc == nil {
|
||||||
|
fmt.Println(this.product + " not started yet")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 停止进程
|
||||||
|
_ = proc.Kill()
|
||||||
|
|
||||||
|
// 在Windows上经常不能及时释放资源
|
||||||
|
_ = DeletePid(Tea.Root + "/bin/pid")
|
||||||
|
fmt.Println(this.product+" stopped ok, pid:", proc.Pid)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重启
|
||||||
|
func (this *AppCmd) runRestart() {
|
||||||
|
this.runStop()
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
this.runStart()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 状态
|
||||||
|
func (this *AppCmd) runStatus() {
|
||||||
|
proc := this.checkPid()
|
||||||
|
if proc == nil {
|
||||||
|
fmt.Println(this.product + " not started yet")
|
||||||
|
} else {
|
||||||
|
fmt.Println(this.product + " is running, pid: " + fmt.Sprintf("%d", proc.Pid))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查PID
|
||||||
|
func (this *AppCmd) checkPid() *os.Process {
|
||||||
|
return CheckPid(Tea.Root + "/bin/pid")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 写入PID
|
||||||
|
func (this *AppCmd) writePid() error {
|
||||||
|
return WritePid(Tea.Root + "/bin/pid")
|
||||||
|
}
|
||||||
6
internal/apps/directive.go
Normal file
6
internal/apps/directive.go
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
package apps
|
||||||
|
|
||||||
|
type Directive struct {
|
||||||
|
Arg string
|
||||||
|
Callback func()
|
||||||
|
}
|
||||||
17
internal/apps/file_others.go
Normal file
17
internal/apps/file_others.go
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package apps
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
// lock file
|
||||||
|
func LockFile(fp *os.File) error {
|
||||||
|
return syscall.Flock(int(fp.Fd()), syscall.LOCK_EX|syscall.LOCK_NB)
|
||||||
|
}
|
||||||
|
|
||||||
|
func UnlockFile(fp *os.File) error {
|
||||||
|
return syscall.Flock(int(fp.Fd()), syscall.LOCK_UN)
|
||||||
|
}
|
||||||
17
internal/apps/file_windows.go
Normal file
17
internal/apps/file_windows.go
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
// +build windows
|
||||||
|
|
||||||
|
package apps
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// lock file
|
||||||
|
func LockFile(fp *os.File) error {
|
||||||
|
return errors.New("not implemented on windows")
|
||||||
|
}
|
||||||
|
|
||||||
|
func UnlockFile(fp *os.File) error {
|
||||||
|
return errors.New("not implemented on windows")
|
||||||
|
}
|
||||||
51
internal/apps/log_writer.go
Normal file
51
internal/apps/log_writer.go
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
package apps
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/iwind/TeaGo/Tea"
|
||||||
|
"github.com/iwind/TeaGo/files"
|
||||||
|
"github.com/iwind/TeaGo/logs"
|
||||||
|
"github.com/iwind/TeaGo/utils/time"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LogWriter struct {
|
||||||
|
fileAppender *files.Appender
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *LogWriter) Init() {
|
||||||
|
// 创建目录
|
||||||
|
dir := files.NewFile(Tea.LogDir())
|
||||||
|
if !dir.Exists() {
|
||||||
|
err := dir.Mkdir()
|
||||||
|
if err != nil {
|
||||||
|
log.Println("[error]" + err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logFile := files.NewFile(Tea.LogFile("run.log"))
|
||||||
|
|
||||||
|
// 打开要写入的日志文件
|
||||||
|
appender, err := logFile.Appender()
|
||||||
|
if err != nil {
|
||||||
|
logs.Error(err)
|
||||||
|
} else {
|
||||||
|
this.fileAppender = appender
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *LogWriter) Write(message string) {
|
||||||
|
log.Println(message)
|
||||||
|
|
||||||
|
if this.fileAppender != nil {
|
||||||
|
_, err := this.fileAppender.AppendString(timeutil.Format("Y/m/d H:i:s ") + message + "\n")
|
||||||
|
if err != nil {
|
||||||
|
log.Println("[error]" + err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *LogWriter) Close() {
|
||||||
|
if this.fileAppender != nil {
|
||||||
|
_ = this.fileAppender.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
113
internal/apps/pid.go
Normal file
113
internal/apps/pid.go
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
package apps
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/iwind/TeaGo/types"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
var pidFileList = []*os.File{}
|
||||||
|
|
||||||
|
// 检查Pid
|
||||||
|
func CheckPid(path string) *os.Process {
|
||||||
|
// windows上打开的文件是不能删除的
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
if os.Remove(path) == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
_ = file.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 是否能取得Lock
|
||||||
|
err = LockFile(file)
|
||||||
|
if err == nil {
|
||||||
|
_ = UnlockFile(file)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
pidBytes, err := ioutil.ReadAll(file)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
pid := types.Int(string(pidBytes))
|
||||||
|
|
||||||
|
if pid <= 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
proc, _ := os.FindProcess(pid)
|
||||||
|
return proc
|
||||||
|
}
|
||||||
|
|
||||||
|
// 写入Pid
|
||||||
|
func WritePid(path string) error {
|
||||||
|
fp, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY|os.O_RDONLY, 0666)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if runtime.GOOS != "windows" {
|
||||||
|
err = LockFile(fp)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pidFileList = append(pidFileList, fp) // hold the file pointers
|
||||||
|
|
||||||
|
_, err = fp.WriteString(fmt.Sprintf("%d", os.Getpid()))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 写入Ppid
|
||||||
|
func WritePpid(path string) error {
|
||||||
|
fp, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY|os.O_RDONLY, 0666)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if runtime.GOOS != "windows" {
|
||||||
|
err = LockFile(fp)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pidFileList = append(pidFileList, fp) // hold the file pointers
|
||||||
|
|
||||||
|
_, err = fp.WriteString(fmt.Sprintf("%d", os.Getppid()))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除Pid
|
||||||
|
func DeletePid(path string) error {
|
||||||
|
_, err := os.Stat(path)
|
||||||
|
if err != nil {
|
||||||
|
if !os.IsNotExist(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, fp := range pidFileList {
|
||||||
|
_ = UnlockFile(fp)
|
||||||
|
_ = fp.Close()
|
||||||
|
}
|
||||||
|
return os.Remove(path)
|
||||||
|
}
|
||||||
@@ -10,6 +10,8 @@ type APIConfig struct {
|
|||||||
RPC struct {
|
RPC struct {
|
||||||
Endpoints []string `yaml:"endpoints"`
|
Endpoints []string `yaml:"endpoints"`
|
||||||
} `yaml:"rpc"`
|
} `yaml:"rpc"`
|
||||||
|
NodeId string `yaml:"nodeId"`
|
||||||
|
Secret string `yaml:"secret"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadAPIConfig() (*APIConfig, error) {
|
func LoadAPIConfig() (*APIConfig, error) {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package configs
|
package configs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/configs/serverconfigs"
|
||||||
"github.com/go-yaml/yaml"
|
"github.com/go-yaml/yaml"
|
||||||
"github.com/iwind/TeaGo/Tea"
|
"github.com/iwind/TeaGo/Tea"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
@@ -9,10 +10,13 @@ import (
|
|||||||
var sharedNodeConfig *NodeConfig = nil
|
var sharedNodeConfig *NodeConfig = nil
|
||||||
|
|
||||||
type NodeConfig struct {
|
type NodeConfig struct {
|
||||||
Id string `yaml:"id"`
|
Id string `yaml:"id" json:"id"`
|
||||||
Servers []*ServerConfig `yaml:"servers"`
|
IsOn bool `yaml:"isOn" json:"isOn"`
|
||||||
|
Servers []*serverconfigs.ServerConfig `yaml:"servers" json:"servers"`
|
||||||
|
Version int `yaml:"version" json:"version"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 取得当前节点配置单例
|
||||||
func SharedNodeConfig() (*NodeConfig, error) {
|
func SharedNodeConfig() (*NodeConfig, error) {
|
||||||
sharedLocker.Lock()
|
sharedLocker.Lock()
|
||||||
defer sharedLocker.Unlock()
|
defer sharedLocker.Unlock()
|
||||||
@@ -36,9 +40,19 @@ func SharedNodeConfig() (*NodeConfig, error) {
|
|||||||
return config, nil
|
return config, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 刷新当前节点配置
|
||||||
|
func ReloadNodeConfig() error {
|
||||||
|
sharedLocker.Lock()
|
||||||
|
sharedNodeConfig = nil
|
||||||
|
sharedLocker.Unlock()
|
||||||
|
|
||||||
|
_, err := SharedNodeConfig()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// 根据网络地址和协议分组
|
// 根据网络地址和协议分组
|
||||||
func (this *NodeConfig) AvailableGroups() []*ServerGroup {
|
func (this *NodeConfig) AvailableGroups() []*serverconfigs.ServerGroup {
|
||||||
groupMapping := map[string]*ServerGroup{} // protocol://addr => Server Group
|
groupMapping := map[string]*serverconfigs.ServerGroup{} // protocol://addr => Server Group
|
||||||
for _, server := range this.Servers {
|
for _, server := range this.Servers {
|
||||||
if !server.IsOn {
|
if !server.IsOn {
|
||||||
continue
|
continue
|
||||||
@@ -48,15 +62,39 @@ func (this *NodeConfig) AvailableGroups() []*ServerGroup {
|
|||||||
if ok {
|
if ok {
|
||||||
group.Add(server)
|
group.Add(server)
|
||||||
} else {
|
} else {
|
||||||
group = NewServerGroup(addr)
|
group = serverconfigs.NewServerGroup(addr)
|
||||||
group.Add(server)
|
group.Add(server)
|
||||||
}
|
}
|
||||||
groupMapping[addr] = group
|
groupMapping[addr] = group
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result := []*ServerGroup{}
|
result := []*serverconfigs.ServerGroup{}
|
||||||
for _, group := range groupMapping {
|
for _, group := range groupMapping {
|
||||||
result = append(result, group)
|
result = append(result, group)
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *NodeConfig) Init() error {
|
||||||
|
for _, server := range this.Servers {
|
||||||
|
err := server.Init()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 写入到文件
|
||||||
|
func (this *NodeConfig) Save() error {
|
||||||
|
sharedLocker.Lock()
|
||||||
|
defer sharedLocker.Unlock()
|
||||||
|
|
||||||
|
data, err := yaml.Marshal(this)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ioutil.WriteFile(Tea.ConfigFile("node.yaml"), data, 0777)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package configs
|
package configs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/configs/serverconfigs"
|
||||||
_ "github.com/iwind/TeaGo/bootstrap"
|
_ "github.com/iwind/TeaGo/bootstrap"
|
||||||
"github.com/iwind/TeaGo/logs"
|
"github.com/iwind/TeaGo/logs"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -27,18 +28,37 @@ func TestSharedNodeConfig(t *testing.T) {
|
|||||||
|
|
||||||
func TestNodeConfig_Groups(t *testing.T) {
|
func TestNodeConfig_Groups(t *testing.T) {
|
||||||
config := &NodeConfig{}
|
config := &NodeConfig{}
|
||||||
config.Servers = []*ServerConfig{
|
config.Servers = []*serverconfigs.ServerConfig{
|
||||||
{
|
{
|
||||||
IsOn: true,
|
IsOn: true,
|
||||||
HTTP: &HTTPProtocolConfig{
|
HTTP: &serverconfigs.HTTPProtocolConfig{
|
||||||
|
BaseProtocol: serverconfigs.BaseProtocol{
|
||||||
IsOn: true,
|
IsOn: true,
|
||||||
Listen: []string{"127.0.0.1:1234", ":8080"},
|
Listen: []*serverconfigs.NetworkAddressConfig{
|
||||||
|
{
|
||||||
|
Protocol: serverconfigs.ProtocolHTTP,
|
||||||
|
Host: "127.0.0.1",
|
||||||
|
PortRange: "1234",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Protocol: serverconfigs.ProtocolHTTP,
|
||||||
|
PortRange: "8080",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
HTTP: &HTTPProtocolConfig{
|
HTTP: &serverconfigs.HTTPProtocolConfig{
|
||||||
|
BaseProtocol: serverconfigs.BaseProtocol{
|
||||||
IsOn: true,
|
IsOn: true,
|
||||||
Listen: []string{":8080"},
|
Listen: []*serverconfigs.NetworkAddressConfig{
|
||||||
|
{
|
||||||
|
Protocol: serverconfigs.ProtocolHTTP,
|
||||||
|
PortRange: "8080",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
package configs
|
|
||||||
|
|
||||||
type OriginServerConfig struct {
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
package configs
|
|
||||||
|
|
||||||
type HTTPProtocolConfig struct {
|
|
||||||
IsOn bool `yaml:"isOn"` // 是否开启
|
|
||||||
IPVersion IPVersion `yaml:"ipVersion"` // 4, 6
|
|
||||||
Listen []string `yaml:"listen" json:"listen"` // 监听地址
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *HTTPProtocolConfig) Addresses() []string {
|
|
||||||
result := []string{}
|
|
||||||
for _, listen := range this.Listen {
|
|
||||||
switch this.IPVersion {
|
|
||||||
case IPv4:
|
|
||||||
result = append(result, ProtocolHTTP4+"://"+listen)
|
|
||||||
case IPv6:
|
|
||||||
result = append(result, ProtocolHTTP6+"://"+listen)
|
|
||||||
default:
|
|
||||||
result = append(result, ProtocolHTTP+"://"+listen)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
package configs
|
|
||||||
|
|
||||||
type HTTPSProtocolConfig struct {
|
|
||||||
IsOn bool `yaml:"isOn"` // 是否开启
|
|
||||||
IPVersion string `yaml:"ipVersion"` // 4, 6
|
|
||||||
Listen []string `yaml:"listen" json:"listen"` // 监听地址
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *HTTPSProtocolConfig) Addresses() []string {
|
|
||||||
result := []string{}
|
|
||||||
for _, listen := range this.Listen {
|
|
||||||
switch this.IPVersion {
|
|
||||||
case IPv4:
|
|
||||||
result = append(result, ProtocolHTTPS4+"://"+listen)
|
|
||||||
case IPv6:
|
|
||||||
result = append(result, ProtocolHTTPS6+"://"+listen)
|
|
||||||
default:
|
|
||||||
result = append(result, ProtocolHTTPS+"://"+listen)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
package configs
|
|
||||||
|
|
||||||
type TCPProtocolConfig struct {
|
|
||||||
IsOn bool `yaml:"isOn"` // 是否开启
|
|
||||||
IPVersion IPVersion `yaml:"ipVersion"` // 4, 6
|
|
||||||
Listen []string `yaml:"listen" json:"listen"` // 监听地址
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *TCPProtocolConfig) Addresses() []string {
|
|
||||||
result := []string{}
|
|
||||||
for _, listen := range this.Listen {
|
|
||||||
switch this.IPVersion {
|
|
||||||
case IPv4:
|
|
||||||
result = append(result, ProtocolTCP4+"://"+listen)
|
|
||||||
case IPv6:
|
|
||||||
result = append(result, ProtocolTCP6+"://"+listen)
|
|
||||||
default:
|
|
||||||
result = append(result, ProtocolTCP+"://"+listen)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
package configs
|
|
||||||
|
|
||||||
type TLSProtocolConfig struct {
|
|
||||||
IsOn bool `yaml:"isOn"` // 是否开启
|
|
||||||
IPVersion string `yaml:"ipVersion"` // 4, 6
|
|
||||||
Listen []string `yaml:"listen" json:"listen"` // 监听地址
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *TLSProtocolConfig) Addresses() []string {
|
|
||||||
result := []string{}
|
|
||||||
for _, listen := range this.Listen {
|
|
||||||
switch this.IPVersion {
|
|
||||||
case IPv4:
|
|
||||||
result = append(result, ProtocolTLS4+"://"+listen)
|
|
||||||
case IPv6:
|
|
||||||
result = append(result, ProtocolTLS6+"://"+listen)
|
|
||||||
default:
|
|
||||||
result = append(result, ProtocolTLS+"://"+listen)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
package configs
|
|
||||||
|
|
||||||
type UDPProtocolConfig struct {
|
|
||||||
IsOn bool `yaml:"isOn"` // 是否开启
|
|
||||||
Listen []string `yaml:"listen" json:"listen"` // 监听地址
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *UDPProtocolConfig) Addresses() []string {
|
|
||||||
result := []string{}
|
|
||||||
for _, listen := range this.Listen {
|
|
||||||
result = append(result, ProtocolUDP+"://"+listen)
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
package configs
|
|
||||||
|
|
||||||
type UnixProtocolConfig struct {
|
|
||||||
IsOn bool `yaml:"isOn"` // 是否开启
|
|
||||||
Listen []string `yaml:"listen" json:"listen"` // 监听地址
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *UnixProtocolConfig) Addresses() []string {
|
|
||||||
result := []string{}
|
|
||||||
for _, listen := range this.Listen {
|
|
||||||
result = append(result, ProtocolUnix+":"+listen)
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
package configs
|
|
||||||
|
|
||||||
type ServerConfig struct {
|
|
||||||
Id string `yaml:"id"` // ID
|
|
||||||
IsOn bool `yaml:"isOn"` // 是否开启
|
|
||||||
Components []*ComponentConfig `yaml:"components"` // 组件
|
|
||||||
Filters []*FilterConfig `yaml:"filters"` // 过滤器
|
|
||||||
Name string `yaml:"name"` // 名称
|
|
||||||
Description string `yaml:"description"` // 描述
|
|
||||||
ServerNames []string `yaml:"serverNames"` // 域名
|
|
||||||
|
|
||||||
// 协议
|
|
||||||
HTTP *HTTPProtocolConfig `yaml:"http"` // HTTP配置
|
|
||||||
HTTPS *HTTPSProtocolConfig `yaml:"https"` // HTTPS配置
|
|
||||||
TCP *TCPProtocolConfig `yaml:"tcp"` // TCP配置
|
|
||||||
TLS *TLSProtocolConfig `yaml:"tls"` // TLS配置
|
|
||||||
Unix *UnixProtocolConfig `yaml:"unix"` // Unix配置
|
|
||||||
UDP *UDPProtocolConfig `yaml:"udp"` // UDP配置
|
|
||||||
|
|
||||||
// Web配置
|
|
||||||
Web *WebConfig `yaml:"web"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewServerConfig() *ServerConfig {
|
|
||||||
return &ServerConfig{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *ServerConfig) Init() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *ServerConfig) FullAddresses() []string {
|
|
||||||
result := []Protocol{}
|
|
||||||
if this.HTTP != nil && this.HTTP.IsOn {
|
|
||||||
result = append(result, this.HTTP.Addresses()...)
|
|
||||||
}
|
|
||||||
if this.HTTPS != nil && this.HTTPS.IsOn {
|
|
||||||
result = append(result, this.HTTPS.Addresses()...)
|
|
||||||
}
|
|
||||||
if this.TCP != nil && this.TCP.IsOn {
|
|
||||||
result = append(result, this.TCP.Addresses()...)
|
|
||||||
}
|
|
||||||
if this.TLS != nil && this.TLS.IsOn {
|
|
||||||
result = append(result, this.TLS.Addresses()...)
|
|
||||||
}
|
|
||||||
if this.Unix != nil && this.Unix.IsOn {
|
|
||||||
result = append(result, this.Unix.Addresses()...)
|
|
||||||
}
|
|
||||||
if this.UDP != nil && this.UDP.IsOn {
|
|
||||||
result = append(result, this.UDP.Addresses()...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
package configs
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
func TestServerConfig_Protocols(t *testing.T) {
|
|
||||||
{
|
|
||||||
server := NewServerConfig()
|
|
||||||
t.Log(server.FullAddresses())
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
server := NewServerConfig()
|
|
||||||
server.HTTP = &HTTPProtocolConfig{IsOn: true, Listen: []string{"127.0.0.1:1234"}}
|
|
||||||
server.HTTPS = &HTTPSProtocolConfig{IsOn: true, Listen: []string{"127.0.0.1:1234"}}
|
|
||||||
server.TCP = &TCPProtocolConfig{IsOn: true, Listen: []string{"127.0.0.1:1234"}}
|
|
||||||
server.TLS = &TLSProtocolConfig{IsOn: true, Listen: []string{"127.0.0.1:1234"}}
|
|
||||||
server.Unix = &UnixProtocolConfig{IsOn: true, Listen: []string{"127.0.0.1:1234"}}
|
|
||||||
server.UDP = &UDPProtocolConfig{IsOn: true, Listen: []string{"127.0.0.1:1234"}}
|
|
||||||
t.Log(server.FullAddresses())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
package configs
|
|
||||||
|
|
||||||
import "strings"
|
|
||||||
|
|
||||||
type ServerGroup struct {
|
|
||||||
fullAddr string
|
|
||||||
Servers []*ServerConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewServerGroup(fullAddr string) *ServerGroup {
|
|
||||||
return &ServerGroup{fullAddr: fullAddr}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 添加服务
|
|
||||||
func (this *ServerGroup) Add(server *ServerConfig) {
|
|
||||||
this.Servers = append(this.Servers, server)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取完整的地址
|
|
||||||
func (this *ServerGroup) FullAddr() string {
|
|
||||||
return this.fullAddr
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取当前分组的协议
|
|
||||||
func (this *ServerGroup) Protocol() Protocol {
|
|
||||||
for _, p := range AllProtocols() {
|
|
||||||
if strings.HasPrefix(this.fullAddr, p+":") {
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ProtocolHTTP
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取当前分组的地址
|
|
||||||
func (this *ServerGroup) Addr() string {
|
|
||||||
protocol := this.Protocol()
|
|
||||||
if protocol == ProtocolUnix {
|
|
||||||
return strings.TrimPrefix(this.fullAddr, protocol+":")
|
|
||||||
}
|
|
||||||
return strings.TrimPrefix(this.fullAddr, protocol+"://")
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package configs
|
package serverconfigs
|
||||||
|
|
||||||
type ComponentConfig struct {
|
type ComponentConfig struct {
|
||||||
}
|
}
|
||||||
20
internal/configs/serverconfigs/configutils/copy.go
Normal file
20
internal/configs/serverconfigs/configutils/copy.go
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package configutils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 拷贝同类型struct指针对象中的字段
|
||||||
|
func CopyStructObject(destPtr, sourcePtr interface{}) {
|
||||||
|
value := reflect.ValueOf(destPtr)
|
||||||
|
value2 := reflect.ValueOf(sourcePtr)
|
||||||
|
|
||||||
|
countFields := value2.Elem().NumField()
|
||||||
|
for i := 0; i < countFields; i++ {
|
||||||
|
v := value2.Elem().Field(i)
|
||||||
|
if !v.IsValid() || !v.CanSet() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
value.Elem().Field(i).Set(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
28
internal/configs/serverconfigs/configutils/copy_test.go
Normal file
28
internal/configs/serverconfigs/configutils/copy_test.go
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package configutils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/iwind/TeaGo/logs"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCopyStructObject(t *testing.T) {
|
||||||
|
type Book struct {
|
||||||
|
Name string
|
||||||
|
Price int
|
||||||
|
Year int
|
||||||
|
Author string
|
||||||
|
press string
|
||||||
|
}
|
||||||
|
|
||||||
|
book1 := &Book{
|
||||||
|
Name: "Hello Golang",
|
||||||
|
Price: 100,
|
||||||
|
Year: 2020,
|
||||||
|
Author: "Liu",
|
||||||
|
press: "Beijing",
|
||||||
|
}
|
||||||
|
book2 := new(Book)
|
||||||
|
CopyStructObject(book2, book1)
|
||||||
|
logs.PrintAsJSON(book2, t)
|
||||||
|
logs.PrintAsJSON(book1, t)
|
||||||
|
}
|
||||||
59
internal/configs/serverconfigs/configutils/domain.go
Normal file
59
internal/configs/serverconfigs/configutils/domain.go
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
package configutils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/iwind/TeaGo/logs"
|
||||||
|
"github.com/iwind/TeaGo/utils/string"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 从一组规则中匹配域名
|
||||||
|
// 支持的格式:example.com, www.example.com, .example.com, *.example.com, ~(\d+).example.com
|
||||||
|
// 更多参考:http://nginx.org/en/docs/http/ngx_http_core_module.html#server_name
|
||||||
|
func MatchDomains(patterns []string, domain string) (isMatched bool) {
|
||||||
|
if len(patterns) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, pattern := range patterns {
|
||||||
|
if matchDomain(pattern, domain) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 匹配单个域名规则
|
||||||
|
func matchDomain(pattern string, domain string) (isMatched bool) {
|
||||||
|
if len(pattern) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 正则表达式
|
||||||
|
if pattern[0] == '~' {
|
||||||
|
reg, err := stringutil.RegexpCompile(strings.TrimSpace(pattern[1:]))
|
||||||
|
if err != nil {
|
||||||
|
logs.Error(err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return reg.MatchString(domain)
|
||||||
|
}
|
||||||
|
|
||||||
|
if pattern[0] == '.' {
|
||||||
|
return strings.HasSuffix(domain, pattern)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 其他匹配
|
||||||
|
patternPieces := strings.Split(pattern, ".")
|
||||||
|
domainPieces := strings.Split(domain, ".")
|
||||||
|
if len(patternPieces) != len(domainPieces) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
isMatched = true
|
||||||
|
for index, patternPiece := range patternPieces {
|
||||||
|
if patternPiece == "" || patternPiece == "*" || patternPiece == domainPieces[index] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
isMatched = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return isMatched
|
||||||
|
}
|
||||||
79
internal/configs/serverconfigs/configutils/domain_test.go
Normal file
79
internal/configs/serverconfigs/configutils/domain_test.go
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
package configutils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/iwind/TeaGo/assert"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMatchDomain(t *testing.T) {
|
||||||
|
a := assert.NewAssertion(t)
|
||||||
|
{
|
||||||
|
ok := MatchDomains([]string{}, "example.com")
|
||||||
|
a.IsFalse(ok)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
ok := MatchDomains([]string{"example.com"}, "example.com")
|
||||||
|
a.IsTrue(ok)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
ok := MatchDomains([]string{"www.example.com"}, "example.com")
|
||||||
|
a.IsFalse(ok)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
ok := MatchDomains([]string{".example.com"}, "www.example.com")
|
||||||
|
a.IsTrue(ok)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
ok := MatchDomains([]string{".example.com"}, "a.www.example.com")
|
||||||
|
a.IsTrue(ok)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
ok := MatchDomains([]string{".example.com"}, "a.www.example123.com")
|
||||||
|
a.IsFalse(ok)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
ok := MatchDomains([]string{"*.example.com"}, "www.example.com")
|
||||||
|
a.IsTrue(ok)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
ok := MatchDomains([]string{"*.*.com"}, "www.example.com")
|
||||||
|
a.IsTrue(ok)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
ok := MatchDomains([]string{"www.*.com"}, "www.example.com")
|
||||||
|
a.IsTrue(ok)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
ok := MatchDomains([]string{"gallery.*.com"}, "www.example.com")
|
||||||
|
a.IsFalse(ok)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
ok := MatchDomains([]string{"~\\w+.example.com"}, "www.example.com")
|
||||||
|
a.IsTrue(ok)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
ok := MatchDomains([]string{"~\\w+.example.com"}, "a.www.example.com")
|
||||||
|
a.IsTrue(ok)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
ok := MatchDomains([]string{"~^\\d+.example.com$"}, "www.example.com")
|
||||||
|
a.IsFalse(ok)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
ok := MatchDomains([]string{"~^\\d+.example.com$"}, "123.example.com")
|
||||||
|
a.IsTrue(ok)
|
||||||
|
}
|
||||||
|
}
|
||||||
11
internal/configs/serverconfigs/configutils/log.go
Normal file
11
internal/configs/serverconfigs/configutils/log.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package configutils
|
||||||
|
|
||||||
|
import "github.com/iwind/TeaGo/logs"
|
||||||
|
|
||||||
|
// 记录错误
|
||||||
|
func LogError(arg ...interface{}) {
|
||||||
|
if len(arg) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logs.Println(arg...)
|
||||||
|
}
|
||||||
25
internal/configs/serverconfigs/configutils/match.go
Normal file
25
internal/configs/serverconfigs/configutils/match.go
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package configutils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var whitespaceReg = regexp.MustCompile(`\s+`)
|
||||||
|
|
||||||
|
// 关键词匹配
|
||||||
|
func MatchKeyword(source, keyword string) bool {
|
||||||
|
if len(keyword) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
pieces := whitespaceReg.Split(keyword, -1)
|
||||||
|
source = strings.ToLower(source)
|
||||||
|
for _, piece := range pieces {
|
||||||
|
if strings.Index(source, strings.ToLower(piece)) > -1 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
13
internal/configs/serverconfigs/configutils/match_test.go
Normal file
13
internal/configs/serverconfigs/configutils/match_test.go
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package configutils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/iwind/TeaGo/assert"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMatchKeyword(t *testing.T) {
|
||||||
|
a := assert.NewAssertion(t)
|
||||||
|
a.IsTrue(MatchKeyword("a b c", "a"))
|
||||||
|
a.IsFalse(MatchKeyword("a b c", ""))
|
||||||
|
a.IsTrue(MatchKeyword("abc", "BC"))
|
||||||
|
}
|
||||||
14
internal/configs/serverconfigs/configutils/yaml.go
Normal file
14
internal/configs/serverconfigs/configutils/yaml.go
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package configutils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/go-yaml/yaml"
|
||||||
|
"io/ioutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func UnmarshalYamlFile(file string, ptr interface{}) error {
|
||||||
|
data, err := ioutil.ReadFile(file)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return yaml.Unmarshal(data, ptr)
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package configs
|
package serverconfigs
|
||||||
|
|
||||||
type FilterConfig struct {
|
type FilterConfig struct {
|
||||||
}
|
}
|
||||||
43
internal/configs/serverconfigs/global_config.go
Normal file
43
internal/configs/serverconfigs/global_config.go
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
package serverconfigs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/configs/serverconfigs/configutils"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/configs/serverconfigs/shared"
|
||||||
|
)
|
||||||
|
|
||||||
|
var globalConfig *GlobalConfig = nil
|
||||||
|
var globalConfigFile = "global.yaml"
|
||||||
|
|
||||||
|
// 全局设置
|
||||||
|
type GlobalConfig struct {
|
||||||
|
HTTPAll struct {
|
||||||
|
MatchDomainStrictly bool `yaml:"matchDomainStrictly"`
|
||||||
|
} `yaml:"httpAll"`
|
||||||
|
HTTP struct{} `yaml:"http"`
|
||||||
|
HTTPS struct{} `yaml:"https"`
|
||||||
|
TCPAll struct{} `yaml:"tcpAll"`
|
||||||
|
TCP struct{} `yaml:"tcp"`
|
||||||
|
TLS struct{} `yaml:"tls"`
|
||||||
|
Unix struct{} `yaml:"unix"`
|
||||||
|
UDP struct{} `yaml:"udp"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func SharedGlobalConfig() *GlobalConfig {
|
||||||
|
shared.Locker.Lock()
|
||||||
|
defer shared.Locker.Unlock()
|
||||||
|
|
||||||
|
if globalConfig != nil {
|
||||||
|
return globalConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
err := configutils.UnmarshalYamlFile(globalConfigFile, globalConfig)
|
||||||
|
if err != nil {
|
||||||
|
configutils.LogError("[SharedGlobalConfig]" + err.Error())
|
||||||
|
globalConfig = &GlobalConfig{}
|
||||||
|
}
|
||||||
|
return globalConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *GlobalConfig) Init() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package configs
|
package serverconfigs
|
||||||
|
|
||||||
type IPVersion = string
|
type IPVersion = string
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package configs
|
package serverconfigs
|
||||||
|
|
||||||
type LocationConfig struct {
|
type LocationConfig struct {
|
||||||
}
|
}
|
||||||
70
internal/configs/serverconfigs/network_address_config.go
Normal file
70
internal/configs/serverconfigs/network_address_config.go
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
package serverconfigs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/iwind/TeaGo/types"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var regexpSinglePort = regexp.MustCompile(`^\d+$`)
|
||||||
|
|
||||||
|
// 网络地址配置
|
||||||
|
type NetworkAddressConfig struct {
|
||||||
|
Protocol string `yaml:"protocol" json:"protocol"` // 协议,http、tcp、tcp4、tcp6、unix、udp等
|
||||||
|
Host string `yaml:"host" json:"host"` // 主机地址或主机名
|
||||||
|
PortRange string `yaml:"portRange" json:"portRange"` // 端口范围,支持 8080、8080-8090、8080:8090
|
||||||
|
|
||||||
|
minPort int
|
||||||
|
maxPort int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *NetworkAddressConfig) Init() error {
|
||||||
|
// 8080
|
||||||
|
if regexpSinglePort.MatchString(this.PortRange) {
|
||||||
|
this.minPort = types.Int(this.PortRange)
|
||||||
|
this.maxPort = this.minPort
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8080:8090
|
||||||
|
if strings.Contains(this.PortRange, ":") {
|
||||||
|
pieces := strings.SplitN(this.PortRange, ":", 2)
|
||||||
|
minPort := types.Int(pieces[0])
|
||||||
|
maxPort := types.Int(pieces[1])
|
||||||
|
if minPort > maxPort {
|
||||||
|
minPort, maxPort = maxPort, minPort
|
||||||
|
}
|
||||||
|
this.minPort = minPort
|
||||||
|
this.maxPort = maxPort
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8080-8090
|
||||||
|
if strings.Contains(this.PortRange, "-") {
|
||||||
|
pieces := strings.SplitN(this.PortRange, "-", 2)
|
||||||
|
minPort := types.Int(pieces[0])
|
||||||
|
maxPort := types.Int(pieces[1])
|
||||||
|
if minPort > maxPort {
|
||||||
|
minPort, maxPort = maxPort, minPort
|
||||||
|
}
|
||||||
|
this.minPort = minPort
|
||||||
|
this.maxPort = maxPort
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *NetworkAddressConfig) FullAddresses() []string {
|
||||||
|
if this.Protocol == ProtocolUnix {
|
||||||
|
return []string{this.Protocol + ":" + this.Host}
|
||||||
|
}
|
||||||
|
|
||||||
|
result := []string{}
|
||||||
|
for i := this.minPort; i <= this.maxPort; i++ {
|
||||||
|
host := this.Host
|
||||||
|
result = append(result, this.Protocol+"://"+host+":"+strconv.Itoa(i))
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package serverconfigs
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestNetworkAddressConfig_FullAddresses(t *testing.T) {
|
||||||
|
{
|
||||||
|
addr := &NetworkAddressConfig{
|
||||||
|
Protocol: "http",
|
||||||
|
Host: "127.0.0.1",
|
||||||
|
PortRange: "8080",
|
||||||
|
}
|
||||||
|
err := addr.Init()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log(addr.FullAddresses())
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
addr := &NetworkAddressConfig{
|
||||||
|
Protocol: "http",
|
||||||
|
Host: "127.0.0.1",
|
||||||
|
PortRange: "8080:8090",
|
||||||
|
}
|
||||||
|
err := addr.Init()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log(addr.FullAddresses())
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
addr := &NetworkAddressConfig{
|
||||||
|
Protocol: "http",
|
||||||
|
Host: "127.0.0.1",
|
||||||
|
PortRange: "8080-8090",
|
||||||
|
}
|
||||||
|
err := addr.Init()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log(addr.FullAddresses())
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
addr := &NetworkAddressConfig{
|
||||||
|
Protocol: "http",
|
||||||
|
Host: "127.0.0.1",
|
||||||
|
PortRange: "8080-8070",
|
||||||
|
}
|
||||||
|
err := addr.Init()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log(addr.FullAddresses())
|
||||||
|
}
|
||||||
|
}
|
||||||
10
internal/configs/serverconfigs/origin_server_config.go
Normal file
10
internal/configs/serverconfigs/origin_server_config.go
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package serverconfigs
|
||||||
|
|
||||||
|
// 源站服务配置
|
||||||
|
type OriginServerConfig struct {
|
||||||
|
Id string `yaml:"id" json:"id"` // ID
|
||||||
|
IsOn bool `yaml:"isOn" json:"isOn"` // 是否启用
|
||||||
|
Name string `yaml:"name" json:"name"` // 名称 TODO
|
||||||
|
Addr *NetworkAddressConfig `yaml:"addr" json:"addr"` // 地址
|
||||||
|
Description string `yaml:"description" json:"description"` // 描述 TODO
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package serverconfigs
|
||||||
|
|
||||||
|
// TODO 需要实现
|
||||||
|
type OriginServerGroupConfig struct {
|
||||||
|
Origins []*OriginServerConfig `yaml:"origins" json:"origins"` // 源站列表
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package configs
|
package serverconfigs
|
||||||
|
|
||||||
type Protocol = string
|
type Protocol = string
|
||||||
|
|
||||||
32
internal/configs/serverconfigs/protocol_base.go
Normal file
32
internal/configs/serverconfigs/protocol_base.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package serverconfigs
|
||||||
|
|
||||||
|
// 协议基础数据结构
|
||||||
|
type BaseProtocol struct {
|
||||||
|
IsOn bool `yaml:"isOn" json:"isOn"` // 是否开启
|
||||||
|
Listen []*NetworkAddressConfig `yaml:"listen" json:"listen"` // 绑定的网络地址
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
func (this *BaseProtocol) InitBase() error {
|
||||||
|
for _, addr := range this.Listen {
|
||||||
|
err := addr.Init()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取完整的地址列表
|
||||||
|
func (this *BaseProtocol) FullAddresses() []string {
|
||||||
|
result := []string{}
|
||||||
|
for _, addr := range this.Listen {
|
||||||
|
result = append(result, addr.FullAddresses()...)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加地址
|
||||||
|
func (this *BaseProtocol) AddListen(addr ...*NetworkAddressConfig) {
|
||||||
|
this.Listen = append(this.Listen, addr...)
|
||||||
|
}
|
||||||
14
internal/configs/serverconfigs/protocol_http_config.go
Normal file
14
internal/configs/serverconfigs/protocol_http_config.go
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package serverconfigs
|
||||||
|
|
||||||
|
type HTTPProtocolConfig struct {
|
||||||
|
BaseProtocol `yaml:",inline"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *HTTPProtocolConfig) Init() error {
|
||||||
|
err := this.InitBase()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
24
internal/configs/serverconfigs/protocol_https_config.go
Normal file
24
internal/configs/serverconfigs/protocol_https_config.go
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package serverconfigs
|
||||||
|
|
||||||
|
import "github.com/TeaOSLab/EdgeNode/internal/configs/serverconfigs/sslconfigs"
|
||||||
|
|
||||||
|
// TLS Version
|
||||||
|
type TLSVersion = string
|
||||||
|
|
||||||
|
// Cipher Suites
|
||||||
|
type TLSCipherSuite = string
|
||||||
|
|
||||||
|
type HTTPSProtocolConfig struct {
|
||||||
|
BaseProtocol `yaml:",inline"`
|
||||||
|
|
||||||
|
SSL *sslconfigs.SSLConfig `yaml:"ssl"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *HTTPSProtocolConfig) Init() error {
|
||||||
|
err := this.InitBase()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
14
internal/configs/serverconfigs/protocol_tcp_config.go
Normal file
14
internal/configs/serverconfigs/protocol_tcp_config.go
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package serverconfigs
|
||||||
|
|
||||||
|
type TCPProtocolConfig struct {
|
||||||
|
BaseProtocol `yaml:",inline"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *TCPProtocolConfig) Init() error {
|
||||||
|
err := this.InitBase()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
18
internal/configs/serverconfigs/protocol_tls_config.go
Normal file
18
internal/configs/serverconfigs/protocol_tls_config.go
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package serverconfigs
|
||||||
|
|
||||||
|
import "github.com/TeaOSLab/EdgeNode/internal/configs/serverconfigs/sslconfigs"
|
||||||
|
|
||||||
|
type TLSProtocolConfig struct {
|
||||||
|
BaseProtocol `yaml:",inline"`
|
||||||
|
|
||||||
|
SSL *sslconfigs.SSLConfig `yaml:"ssl"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *TLSProtocolConfig) Init() error {
|
||||||
|
err := this.InitBase()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
14
internal/configs/serverconfigs/protocol_udp_config.go
Normal file
14
internal/configs/serverconfigs/protocol_udp_config.go
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package serverconfigs
|
||||||
|
|
||||||
|
type UDPProtocolConfig struct {
|
||||||
|
BaseProtocol `yaml:",inline"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *UDPProtocolConfig) Init() error {
|
||||||
|
err := this.InitBase()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
14
internal/configs/serverconfigs/protocol_unix_config.go
Normal file
14
internal/configs/serverconfigs/protocol_unix_config.go
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package serverconfigs
|
||||||
|
|
||||||
|
type UnixProtocolConfig struct {
|
||||||
|
BaseProtocol `yaml:",inline"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *UnixProtocolConfig) Init() error {
|
||||||
|
err := this.InitBase()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
6
internal/configs/serverconfigs/reverse_proxy_config.go
Normal file
6
internal/configs/serverconfigs/reverse_proxy_config.go
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
package serverconfigs
|
||||||
|
|
||||||
|
type ReverseProxyConfig struct {
|
||||||
|
IsOn bool `yaml:"isOn" json:"isOn"` // 是否启用
|
||||||
|
Origins []*OriginServerConfig `yaml:"origins" json:"origins"` // 源站列表
|
||||||
|
}
|
||||||
178
internal/configs/serverconfigs/server_config.go
Normal file
178
internal/configs/serverconfigs/server_config.go
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
package serverconfigs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/configs/serverconfigs/sslconfigs"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ServerConfig struct {
|
||||||
|
Id string `yaml:"id" json:"id"` // ID
|
||||||
|
IsOn bool `yaml:"isOn" json:"isOn"` // 是否开启
|
||||||
|
Components []*ComponentConfig `yaml:"components" json:"components"` // 组件
|
||||||
|
Filters []*FilterConfig `yaml:"filters" json:"filters"` // 过滤器
|
||||||
|
Name string `yaml:"name" json:"name"` // 名称
|
||||||
|
Description string `yaml:"description" json:"description"` // 描述
|
||||||
|
ServerNames []*ServerNameConfig `yaml:"serverNames" json:"serverNames"` // 域名
|
||||||
|
|
||||||
|
// 前端协议
|
||||||
|
HTTP *HTTPProtocolConfig `yaml:"http" json:"http"` // HTTP配置
|
||||||
|
HTTPS *HTTPSProtocolConfig `yaml:"https" json:"https"` // HTTPS配置
|
||||||
|
TCP *TCPProtocolConfig `yaml:"tcp" json:"tcp"` // TCP配置
|
||||||
|
TLS *TLSProtocolConfig `yaml:"tls" json:"tls"` // TLS配置
|
||||||
|
Unix *UnixProtocolConfig `yaml:"unix" json:"unix"` // Unix配置
|
||||||
|
UDP *UDPProtocolConfig `yaml:"udp" json:"udp"` // UDP配置
|
||||||
|
|
||||||
|
// Web配置
|
||||||
|
Web *WebConfig `yaml:"web" json:"web"`
|
||||||
|
|
||||||
|
// 反向代理配置
|
||||||
|
ReverseProxy *ReverseProxyConfig `yaml:"reverseProxy" json:"reverseProxy"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServerConfig() *ServerConfig {
|
||||||
|
return &ServerConfig{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *ServerConfig) Init() error {
|
||||||
|
if this.HTTP != nil {
|
||||||
|
err := this.HTTP.Init()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if this.HTTPS != nil {
|
||||||
|
err := this.HTTPS.Init()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if this.TCP != nil {
|
||||||
|
err := this.TCP.Init()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if this.TLS != nil {
|
||||||
|
err := this.TLS.Init()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if this.Unix != nil {
|
||||||
|
err := this.Unix.Init()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if this.UDP != nil {
|
||||||
|
err := this.UDP.Init()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *ServerConfig) FullAddresses() []string {
|
||||||
|
result := []Protocol{}
|
||||||
|
if this.HTTP != nil && this.HTTP.IsOn {
|
||||||
|
result = append(result, this.HTTP.FullAddresses()...)
|
||||||
|
}
|
||||||
|
if this.HTTPS != nil && this.HTTPS.IsOn {
|
||||||
|
result = append(result, this.HTTPS.FullAddresses()...)
|
||||||
|
}
|
||||||
|
if this.TCP != nil && this.TCP.IsOn {
|
||||||
|
result = append(result, this.TCP.FullAddresses()...)
|
||||||
|
}
|
||||||
|
if this.TLS != nil && this.TLS.IsOn {
|
||||||
|
result = append(result, this.TLS.FullAddresses()...)
|
||||||
|
}
|
||||||
|
if this.Unix != nil && this.Unix.IsOn {
|
||||||
|
result = append(result, this.Unix.FullAddresses()...)
|
||||||
|
}
|
||||||
|
if this.UDP != nil && this.UDP.IsOn {
|
||||||
|
result = append(result, this.UDP.FullAddresses()...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *ServerConfig) Listen() []*NetworkAddressConfig {
|
||||||
|
result := []*NetworkAddressConfig{}
|
||||||
|
if this.HTTP != nil {
|
||||||
|
result = append(result, this.HTTP.Listen...)
|
||||||
|
}
|
||||||
|
if this.HTTPS != nil {
|
||||||
|
result = append(result, this.HTTPS.Listen...)
|
||||||
|
}
|
||||||
|
if this.TCP != nil {
|
||||||
|
result = append(result, this.TCP.Listen...)
|
||||||
|
}
|
||||||
|
if this.TLS != nil {
|
||||||
|
result = append(result, this.TLS.Listen...)
|
||||||
|
}
|
||||||
|
if this.Unix != nil {
|
||||||
|
result = append(result, this.Unix.Listen...)
|
||||||
|
}
|
||||||
|
if this.UDP != nil {
|
||||||
|
result = append(result, this.UDP.Listen...)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *ServerConfig) AsJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *ServerConfig) IsHTTP() bool {
|
||||||
|
return this.HTTP != nil || this.HTTPS != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *ServerConfig) IsTCP() bool {
|
||||||
|
return this.TCP != nil || this.TLS != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *ServerConfig) IsUnix() bool {
|
||||||
|
return this.Unix != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *ServerConfig) IsUDP() bool {
|
||||||
|
return this.UDP != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断是否和域名匹配
|
||||||
|
func (this *ServerConfig) MatchName(name string) bool {
|
||||||
|
for _, serverName := range this.ServerNames {
|
||||||
|
if serverName.Match(name) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断是否严格匹配
|
||||||
|
func (this *ServerConfig) MatchNameStrictly(name string) bool {
|
||||||
|
for _, serverName := range this.ServerNames {
|
||||||
|
if serverName.Name == name {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// SSL信息
|
||||||
|
func (this *ServerConfig) SSLConfig() *sslconfigs.SSLConfig {
|
||||||
|
if this.HTTPS != nil {
|
||||||
|
return this.HTTPS.SSL
|
||||||
|
}
|
||||||
|
if this.TLS != nil {
|
||||||
|
return this.TLS.SSL
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
74
internal/configs/serverconfigs/server_config_test.go
Normal file
74
internal/configs/serverconfigs/server_config_test.go
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
package serverconfigs
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestServerConfig_Protocols(t *testing.T) {
|
||||||
|
{
|
||||||
|
server := NewServerConfig()
|
||||||
|
t.Log(server.FullAddresses())
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
server := NewServerConfig()
|
||||||
|
server.HTTP = &HTTPProtocolConfig{BaseProtocol: BaseProtocol{
|
||||||
|
IsOn: true,
|
||||||
|
Listen: []*NetworkAddressConfig{
|
||||||
|
{
|
||||||
|
Protocol: ProtocolHTTP,
|
||||||
|
PortRange: "1234",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
server.HTTPS = &HTTPSProtocolConfig{BaseProtocol: BaseProtocol{
|
||||||
|
IsOn: true,
|
||||||
|
Listen: []*NetworkAddressConfig{
|
||||||
|
{
|
||||||
|
Protocol: ProtocolUnix,
|
||||||
|
Host: "/hello.sock",
|
||||||
|
PortRange: "1235",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
server.TCP = &TCPProtocolConfig{BaseProtocol: BaseProtocol{
|
||||||
|
IsOn: true,
|
||||||
|
Listen: []*NetworkAddressConfig{
|
||||||
|
{
|
||||||
|
Protocol: ProtocolHTTPS,
|
||||||
|
PortRange: "1236",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
server.TLS = &TLSProtocolConfig{BaseProtocol: BaseProtocol{
|
||||||
|
IsOn: true,
|
||||||
|
Listen: []*NetworkAddressConfig{
|
||||||
|
{
|
||||||
|
Protocol: ProtocolTCP,
|
||||||
|
PortRange: "1234",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
server.Unix = &UnixProtocolConfig{BaseProtocol: BaseProtocol{
|
||||||
|
IsOn: true,
|
||||||
|
Listen: []*NetworkAddressConfig{
|
||||||
|
{
|
||||||
|
Protocol: ProtocolTLS,
|
||||||
|
PortRange: "1234",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
server.UDP = &UDPProtocolConfig{BaseProtocol: BaseProtocol{
|
||||||
|
IsOn: true,
|
||||||
|
Listen: []*NetworkAddressConfig{
|
||||||
|
{
|
||||||
|
Protocol: ProtocolUDP,
|
||||||
|
PortRange: "1234",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
err := server.Init()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log(server.FullAddresses())
|
||||||
|
}
|
||||||
|
}
|
||||||
85
internal/configs/serverconfigs/server_group.go
Normal file
85
internal/configs/serverconfigs/server_group.go
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
package serverconfigs
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
type ServerGroup struct {
|
||||||
|
fullAddr string
|
||||||
|
Servers []*ServerConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServerGroup(fullAddr string) *ServerGroup {
|
||||||
|
return &ServerGroup{fullAddr: fullAddr}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加服务
|
||||||
|
func (this *ServerGroup) Add(server *ServerConfig) {
|
||||||
|
this.Servers = append(this.Servers, server)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取完整的地址
|
||||||
|
func (this *ServerGroup) FullAddr() string {
|
||||||
|
return this.fullAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取当前分组的协议
|
||||||
|
func (this *ServerGroup) Protocol() Protocol {
|
||||||
|
for _, p := range AllProtocols() {
|
||||||
|
if strings.HasPrefix(this.fullAddr, p+":") {
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ProtocolHTTP
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取当前分组的地址
|
||||||
|
func (this *ServerGroup) Addr() string {
|
||||||
|
protocol := this.Protocol()
|
||||||
|
if protocol == ProtocolUnix {
|
||||||
|
return strings.TrimPrefix(this.fullAddr, protocol+":")
|
||||||
|
}
|
||||||
|
return strings.TrimPrefix(this.fullAddr, protocol+"://")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断当前分组是否为HTTP
|
||||||
|
func (this *ServerGroup) IsHTTP() bool {
|
||||||
|
p := this.Protocol()
|
||||||
|
return p == ProtocolHTTP || p == ProtocolHTTP4 || p == ProtocolHTTP6
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断当前分组是否为HTTPS
|
||||||
|
func (this *ServerGroup) IsHTTPS() bool {
|
||||||
|
p := this.Protocol()
|
||||||
|
return p == ProtocolHTTPS || p == ProtocolHTTPS4 || p == ProtocolHTTPS6
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断当前分组是否为TCP
|
||||||
|
func (this *ServerGroup) IsTCP() bool {
|
||||||
|
p := this.Protocol()
|
||||||
|
return p == ProtocolTCP || p == ProtocolTCP4 || p == ProtocolTCP6
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断当前分组是否为TLS
|
||||||
|
func (this *ServerGroup) IsTLS() bool {
|
||||||
|
p := this.Protocol()
|
||||||
|
return p == ProtocolTLS || p == ProtocolTLS4 || p == ProtocolTLS6
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断当前分组是否为Unix
|
||||||
|
func (this *ServerGroup) IsUnix() bool {
|
||||||
|
p := this.Protocol()
|
||||||
|
return p == ProtocolUnix
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断当前分组是否为UDP
|
||||||
|
func (this *ServerGroup) IsUDP() bool {
|
||||||
|
p := this.Protocol()
|
||||||
|
return p == ProtocolUDP
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取第一个Server
|
||||||
|
func (this *ServerGroup) FirstServer() *ServerConfig {
|
||||||
|
if len(this.Servers) > 0 {
|
||||||
|
return this.Servers[0]
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package configs
|
package serverconfigs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/iwind/TeaGo/assert"
|
"github.com/iwind/TeaGo/assert"
|
||||||
23
internal/configs/serverconfigs/server_name_config.go
Normal file
23
internal/configs/serverconfigs/server_name_config.go
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package serverconfigs
|
||||||
|
|
||||||
|
import "github.com/TeaOSLab/EdgeNode/internal/configs/serverconfigs/configutils"
|
||||||
|
|
||||||
|
type ServerNameType = string
|
||||||
|
|
||||||
|
const (
|
||||||
|
ServerNameTypeFull = "full" // 完整的域名,包含通配符等
|
||||||
|
ServerNameTypePrefix = "prefix" // 前缀
|
||||||
|
ServerNameTypeSuffix = "suffix" // 后缀
|
||||||
|
ServerNameTypeMatch = "match" // 正则匹配
|
||||||
|
)
|
||||||
|
|
||||||
|
// 主机名(域名)配置
|
||||||
|
type ServerNameConfig struct {
|
||||||
|
Name string `yaml:"name" json:"name"` // 名称
|
||||||
|
Type string `yaml:"type" json:"type"` // 类型
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断主机名是否匹配
|
||||||
|
func (this *ServerNameConfig) Match(name string) bool {
|
||||||
|
return configutils.MatchDomains([]string{this.Name}, name)
|
||||||
|
}
|
||||||
21
internal/configs/serverconfigs/shared/locker.go
Normal file
21
internal/configs/serverconfigs/shared/locker.go
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package shared
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
var Locker = new(FileLocker)
|
||||||
|
|
||||||
|
// global file modify locker
|
||||||
|
type FileLocker struct {
|
||||||
|
locker sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// lock
|
||||||
|
func (this *FileLocker) Lock() {
|
||||||
|
this.locker.Lock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *FileLocker) Unlock() {
|
||||||
|
this.locker.Unlock()
|
||||||
|
}
|
||||||
207
internal/configs/serverconfigs/sslconfigs/ssl.go
Normal file
207
internal/configs/serverconfigs/sslconfigs/ssl.go
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
package sslconfigs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"errors"
|
||||||
|
"github.com/iwind/TeaGo/types"
|
||||||
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TLS Version
|
||||||
|
type TLSVersion = string
|
||||||
|
|
||||||
|
// Cipher Suites
|
||||||
|
type TLSCipherSuite = string
|
||||||
|
|
||||||
|
// SSL配置
|
||||||
|
type SSLConfig struct {
|
||||||
|
IsOn bool `yaml:"isOn" json:"isOn"` // 是否开启
|
||||||
|
|
||||||
|
Certs []*SSLCertConfig `yaml:"certs" json:"certs"`
|
||||||
|
ClientAuthType SSLClientAuthType `yaml:"clientAuthType" json:"clientAuthType"` // 客户端认证类型
|
||||||
|
ClientCACertIds []string `yaml:"clientCACertIds" json:"clientCACertIds"` // 客户端认证CA
|
||||||
|
|
||||||
|
Listen []string `yaml:"listen" json:"listen"` // 网络地址
|
||||||
|
MinVersion TLSVersion `yaml:"minVersion" json:"minVersion"` // 支持的最小版本
|
||||||
|
CipherSuites []TLSCipherSuite `yaml:"cipherSuites" json:"cipherSuites"` // 加密算法套件
|
||||||
|
|
||||||
|
HSTS *HSTSConfig `yaml:"hsts2" json:"hsts"` // HSTS配置,yaml之所以使用hsts2,是因为要和以前的版本分开
|
||||||
|
HTTP2Disabled bool `yaml:"http2Disabled" json:"http2Disabled"` // 是否禁用HTTP2
|
||||||
|
|
||||||
|
nameMapping map[string]*tls.Certificate // dnsName => cert
|
||||||
|
|
||||||
|
minVersion uint16
|
||||||
|
cipherSuites []uint16
|
||||||
|
|
||||||
|
clientCAPool *x509.CertPool
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取新对象
|
||||||
|
func NewSSLConfig() *SSLConfig {
|
||||||
|
return &SSLConfig{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 校验配置
|
||||||
|
func (this *SSLConfig) Init() error {
|
||||||
|
if !this.IsOn {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(this.Certs) == 0 {
|
||||||
|
return errors.New("no certificates in https config")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, cert := range this.Certs {
|
||||||
|
err := cert.Init()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if this.Listen == nil {
|
||||||
|
this.Listen = []string{}
|
||||||
|
} else {
|
||||||
|
for index, addr := range this.Listen {
|
||||||
|
_, _, err := net.SplitHostPort(addr)
|
||||||
|
if err != nil {
|
||||||
|
this.Listen[index] = strings.TrimSuffix(addr, ":") + ":443"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// min version
|
||||||
|
this.convertMinVersion()
|
||||||
|
|
||||||
|
// cipher suite categories
|
||||||
|
this.initCipherSuites()
|
||||||
|
|
||||||
|
// hsts
|
||||||
|
if this.HSTS != nil {
|
||||||
|
err := this.HSTS.Init()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CA证书
|
||||||
|
if len(this.ClientCACertIds) > 0 && this.ClientAuthType != SSLClientAuthTypeNoClientCert {
|
||||||
|
this.clientCAPool = x509.NewCertPool()
|
||||||
|
list := SharedSSLCertList()
|
||||||
|
for _, certId := range this.ClientCACertIds {
|
||||||
|
cert := list.FindCert(certId)
|
||||||
|
if cert == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !cert.On {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
data, err := ioutil.ReadFile(cert.FullCertPath())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
this.clientCAPool.AppendCertsFromPEM(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取得最小版本
|
||||||
|
func (this *SSLConfig) TLSMinVersion() uint16 {
|
||||||
|
return this.minVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
// 套件
|
||||||
|
func (this *SSLConfig) TLSCipherSuites() []uint16 {
|
||||||
|
return this.cipherSuites
|
||||||
|
}
|
||||||
|
|
||||||
|
// 校验是否匹配某个域名
|
||||||
|
func (this *SSLConfig) MatchDomain(domain string) (cert *tls.Certificate, ok bool) {
|
||||||
|
for _, cert := range this.Certs {
|
||||||
|
if cert.MatchDomain(domain) {
|
||||||
|
return cert.CertObject(), true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取得第一个证书
|
||||||
|
func (this *SSLConfig) FirstCert() *tls.Certificate {
|
||||||
|
for _, cert := range this.Certs {
|
||||||
|
return cert.CertObject()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 是否包含某个证书或密钥路径
|
||||||
|
func (this *SSLConfig) ContainsFile(file string) bool {
|
||||||
|
for _, cert := range this.Certs {
|
||||||
|
if cert.CertFile == file || cert.KeyFile == file {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除证书文件
|
||||||
|
func (this *SSLConfig) DeleteFiles() error {
|
||||||
|
var resultErr error = nil
|
||||||
|
|
||||||
|
for _, cert := range this.Certs {
|
||||||
|
err := cert.DeleteFiles()
|
||||||
|
if err != nil {
|
||||||
|
resultErr = err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查找单个证书配置
|
||||||
|
func (this *SSLConfig) FindCert(certId string) *SSLCertConfig {
|
||||||
|
for _, cert := range this.Certs {
|
||||||
|
if cert.Id == certId {
|
||||||
|
return cert
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加证书
|
||||||
|
func (this *SSLConfig) AddCert(cert *SSLCertConfig) {
|
||||||
|
this.Certs = append(this.Certs, cert)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CA证书Pool,用于TLS对客户端进行认证
|
||||||
|
func (this *SSLConfig) CAPool() *x509.CertPool {
|
||||||
|
return this.clientCAPool
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分解所有监听地址
|
||||||
|
func (this *SSLConfig) ParseListenAddresses() []string {
|
||||||
|
result := []string{}
|
||||||
|
var reg = regexp.MustCompile(`\[\s*(\d+)\s*[,:-]\s*(\d+)\s*]$`)
|
||||||
|
for _, addr := range this.Listen {
|
||||||
|
match := reg.FindStringSubmatch(addr)
|
||||||
|
if len(match) == 0 {
|
||||||
|
result = append(result, addr)
|
||||||
|
} else {
|
||||||
|
min := types.Int(match[1])
|
||||||
|
max := types.Int(match[2])
|
||||||
|
if min > max {
|
||||||
|
min, max = max, min
|
||||||
|
}
|
||||||
|
for i := min; i <= max; i++ {
|
||||||
|
newAddr := reg.ReplaceAllString(addr, ":"+strconv.Itoa(i))
|
||||||
|
result = append(result, newAddr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
75
internal/configs/serverconfigs/sslconfigs/ssl_auth.go
Normal file
75
internal/configs/serverconfigs/sslconfigs/ssl_auth.go
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
package sslconfigs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"github.com/iwind/TeaGo/maps"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 认证类型
|
||||||
|
type SSLClientAuthType = int
|
||||||
|
|
||||||
|
const (
|
||||||
|
SSLClientAuthTypeNoClientCert SSLClientAuthType = 0
|
||||||
|
SSLClientAuthTypeRequestClientCert SSLClientAuthType = 1
|
||||||
|
SSLClientAuthTypeRequireAnyClientCert SSLClientAuthType = 2
|
||||||
|
SSLClientAuthTypeVerifyClientCertIfGiven SSLClientAuthType = 3
|
||||||
|
SSLClientAuthTypeRequireAndVerifyClientCert SSLClientAuthType = 4
|
||||||
|
)
|
||||||
|
|
||||||
|
// 所有的客户端认证类型
|
||||||
|
func AllSSLClientAuthTypes() []maps.Map {
|
||||||
|
return []maps.Map{
|
||||||
|
{
|
||||||
|
"name": "不需要客户端证书",
|
||||||
|
"type": SSLClientAuthTypeNoClientCert,
|
||||||
|
"requireCA": false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "请求客户端证书",
|
||||||
|
"type": SSLClientAuthTypeRequestClientCert,
|
||||||
|
"requireCA": true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "需要客户端证书,但不校验",
|
||||||
|
"type": SSLClientAuthTypeRequireAnyClientCert,
|
||||||
|
"requireCA": true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "有客户端证书的时候才校验",
|
||||||
|
"type": SSLClientAuthTypeVerifyClientCertIfGiven,
|
||||||
|
"requireCA": true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "校验客户端提供的证书",
|
||||||
|
"type": SSLClientAuthTypeRequireAndVerifyClientCert,
|
||||||
|
"requireCA": true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查找单个认证方式的名称
|
||||||
|
func FindSSLClientAuthTypeName(authType SSLClientAuthType) string {
|
||||||
|
for _, m := range AllSSLClientAuthTypes() {
|
||||||
|
if m.GetInt("type") == authType {
|
||||||
|
return m.GetString("name")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// 认证类型和tls包内类型的映射
|
||||||
|
func GoSSLClientAuthType(authType SSLClientAuthType) tls.ClientAuthType {
|
||||||
|
switch authType {
|
||||||
|
case SSLClientAuthTypeNoClientCert:
|
||||||
|
return tls.NoClientCert
|
||||||
|
case SSLClientAuthTypeRequestClientCert:
|
||||||
|
return tls.RequestClientCert
|
||||||
|
case SSLClientAuthTypeRequireAnyClientCert:
|
||||||
|
return tls.RequireAnyClientCert
|
||||||
|
case SSLClientAuthTypeVerifyClientCertIfGiven:
|
||||||
|
return tls.VerifyClientCertIfGiven
|
||||||
|
case SSLClientAuthTypeRequireAndVerifyClientCert:
|
||||||
|
return tls.RequireAndVerifyClientCert
|
||||||
|
}
|
||||||
|
return tls.NoClientCert
|
||||||
|
}
|
||||||
271
internal/configs/serverconfigs/sslconfigs/ssl_cert.go
Normal file
271
internal/configs/serverconfigs/sslconfigs/ssl_cert.go
Normal file
@@ -0,0 +1,271 @@
|
|||||||
|
package sslconfigs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"crypto/x509/pkix"
|
||||||
|
"encoding/pem"
|
||||||
|
"errors"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/configs/serverconfigs/configutils"
|
||||||
|
"github.com/iwind/TeaGo/Tea"
|
||||||
|
"github.com/iwind/TeaGo/files"
|
||||||
|
"github.com/iwind/TeaGo/lists"
|
||||||
|
"github.com/iwind/TeaGo/utils/string"
|
||||||
|
"io/ioutil"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SSL证书
|
||||||
|
type SSLCertConfig struct {
|
||||||
|
Id string `yaml:"id" json:"id"`
|
||||||
|
On bool `yaml:"on" json:"on"`
|
||||||
|
Description string `yaml:"description" json:"description"` // 说明
|
||||||
|
CertFile string `yaml:"certFile" json:"certFile"`
|
||||||
|
KeyFile string `yaml:"keyFile" json:"keyFile"`
|
||||||
|
IsLocal bool `yaml:"isLocal" json:"isLocal"` // 是否为本地文件
|
||||||
|
TaskId string `yaml:"taskId" json:"taskId"` // 生成证书任务ID
|
||||||
|
IsShared bool `yaml:"isShared" json:"isShared"` // 是否为公用组件
|
||||||
|
ServerName string `yaml:"serverName" json:"serverName"` // 证书使用的主机名,在请求TLS服务器时需要
|
||||||
|
IsCA bool `yaml:"isCA" json:"isCA"` // 是否为CA证书
|
||||||
|
|
||||||
|
dnsNames []string
|
||||||
|
cert *tls.Certificate
|
||||||
|
timeBefore time.Time
|
||||||
|
timeAfter time.Time
|
||||||
|
issuer pkix.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取新的SSL证书
|
||||||
|
func NewSSLCertConfig(certFile string, keyFile string) *SSLCertConfig {
|
||||||
|
return &SSLCertConfig{
|
||||||
|
On: true,
|
||||||
|
Id: stringutil.Rand(16),
|
||||||
|
CertFile: certFile,
|
||||||
|
KeyFile: keyFile,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 校验
|
||||||
|
func (this *SSLCertConfig) Init() error {
|
||||||
|
if this.IsShared {
|
||||||
|
shared := this.FindShared()
|
||||||
|
if shared == nil {
|
||||||
|
return errors.New("the shared cert has been deleted")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 拷贝之前需要保留的
|
||||||
|
serverName := this.ServerName
|
||||||
|
|
||||||
|
// copy
|
||||||
|
configutils.CopyStructObject(this, shared)
|
||||||
|
this.ServerName = serverName
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dnsNames = []string{}
|
||||||
|
|
||||||
|
if len(this.CertFile) == 0 {
|
||||||
|
return errors.New("cert file should not be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分析证书
|
||||||
|
if this.IsCA { // CA证书
|
||||||
|
data, err := ioutil.ReadFile(this.FullCertPath())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
index := -1
|
||||||
|
this.cert = &tls.Certificate{
|
||||||
|
Certificate: [][]byte{},
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
index++
|
||||||
|
|
||||||
|
block, rest := pem.Decode(data)
|
||||||
|
if block == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if len(rest) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
this.cert.Certificate = append(this.cert.Certificate, block.Bytes)
|
||||||
|
data = rest
|
||||||
|
c, err := x509.ParseCertificate(block.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if c == nil {
|
||||||
|
return errors.New("no available certificates in file")
|
||||||
|
}
|
||||||
|
|
||||||
|
dnsNames := c.DNSNames
|
||||||
|
if len(dnsNames) > 0 {
|
||||||
|
for _, dnsName := range dnsNames {
|
||||||
|
if !lists.ContainsString(this.dnsNames, dnsName) {
|
||||||
|
this.dnsNames = append(this.dnsNames, dnsName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if index == 0 {
|
||||||
|
this.timeBefore = c.NotBefore
|
||||||
|
this.timeAfter = c.NotAfter
|
||||||
|
this.issuer = c.Issuer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else { // 证书+私钥
|
||||||
|
if len(this.KeyFile) == 0 {
|
||||||
|
return errors.New("key file should not be empty")
|
||||||
|
}
|
||||||
|
cert, err := tls.LoadX509KeyPair(this.FullCertPath(), this.FullKeyPath())
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("load certificate '" + this.CertFile + "', '" + this.KeyFile + "' failed:" + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
for index, data := range cert.Certificate {
|
||||||
|
c, err := x509.ParseCertificate(data)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
dnsNames := c.DNSNames
|
||||||
|
if len(dnsNames) > 0 {
|
||||||
|
for _, dnsName := range dnsNames {
|
||||||
|
if !lists.ContainsString(this.dnsNames, dnsName) {
|
||||||
|
this.dnsNames = append(this.dnsNames, dnsName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if index == 0 {
|
||||||
|
this.timeBefore = c.NotBefore
|
||||||
|
this.timeAfter = c.NotAfter
|
||||||
|
this.issuer = c.Issuer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.cert = &cert
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查找共享的证书
|
||||||
|
func (this *SSLCertConfig) FindShared() *SSLCertConfig {
|
||||||
|
if !this.IsShared {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return SharedSSLCertList().FindCert(this.Id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 证书文件路径
|
||||||
|
func (this *SSLCertConfig) FullCertPath() string {
|
||||||
|
if len(this.CertFile) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if !strings.ContainsAny(this.CertFile, "/\\") {
|
||||||
|
return Tea.ConfigFile(this.CertFile)
|
||||||
|
}
|
||||||
|
return this.CertFile
|
||||||
|
}
|
||||||
|
|
||||||
|
// 密钥文件路径
|
||||||
|
func (this *SSLCertConfig) FullKeyPath() string {
|
||||||
|
if len(this.KeyFile) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if !strings.ContainsAny(this.KeyFile, "/\\") {
|
||||||
|
return Tea.ConfigFile(this.KeyFile)
|
||||||
|
}
|
||||||
|
return this.KeyFile
|
||||||
|
}
|
||||||
|
|
||||||
|
// 校验是否匹配某个域名
|
||||||
|
func (this *SSLCertConfig) MatchDomain(domain string) bool {
|
||||||
|
if len(this.dnsNames) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return configutils.MatchDomains(this.dnsNames, domain)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 证书中的域名
|
||||||
|
func (this *SSLCertConfig) DNSNames() []string {
|
||||||
|
return this.dnsNames
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取证书对象
|
||||||
|
func (this *SSLCertConfig) CertObject() *tls.Certificate {
|
||||||
|
return this.cert
|
||||||
|
}
|
||||||
|
|
||||||
|
// 开始时间
|
||||||
|
func (this *SSLCertConfig) TimeBefore() time.Time {
|
||||||
|
return this.timeBefore
|
||||||
|
}
|
||||||
|
|
||||||
|
// 结束时间
|
||||||
|
func (this *SSLCertConfig) TimeAfter() time.Time {
|
||||||
|
return this.timeAfter
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发行信息
|
||||||
|
func (this *SSLCertConfig) Issuer() pkix.Name {
|
||||||
|
return this.issuer
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除文件
|
||||||
|
func (this *SSLCertConfig) DeleteFiles() error {
|
||||||
|
if this.IsLocal {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var resultErr error = nil
|
||||||
|
if len(this.CertFile) > 0 && !strings.ContainsAny(this.CertFile, "/\\") {
|
||||||
|
err := files.NewFile(this.FullCertPath()).Delete()
|
||||||
|
if err != nil {
|
||||||
|
resultErr = err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(this.KeyFile) > 0 && !strings.ContainsAny(this.KeyFile, "/\\") {
|
||||||
|
err := files.NewFile(this.FullKeyPath()).Delete()
|
||||||
|
if err != nil {
|
||||||
|
resultErr = err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resultErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取证书文件
|
||||||
|
func (this *SSLCertConfig) ReadCert() ([]byte, error) {
|
||||||
|
if len(this.CertFile) == 0 {
|
||||||
|
return nil, errors.New("cert file should not be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
if this.IsLocal {
|
||||||
|
return ioutil.ReadFile(this.CertFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ioutil.ReadFile(Tea.ConfigFile(this.CertFile))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取密钥文件
|
||||||
|
func (this *SSLCertConfig) ReadKey() ([]byte, error) {
|
||||||
|
if len(this.KeyFile) == 0 {
|
||||||
|
return nil, errors.New("key file should not be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
if this.IsLocal {
|
||||||
|
return ioutil.ReadFile(this.KeyFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ioutil.ReadFile(Tea.ConfigFile(this.KeyFile))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 匹配关键词
|
||||||
|
func (this *SSLCertConfig) MatchKeyword(keyword string) (matched bool, name string, tags []string) {
|
||||||
|
if configutils.MatchKeyword(this.Description, keyword) {
|
||||||
|
matched = true
|
||||||
|
name = this.Description
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
86
internal/configs/serverconfigs/sslconfigs/ssl_cert_list.go
Normal file
86
internal/configs/serverconfigs/sslconfigs/ssl_cert_list.go
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
package sslconfigs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/configs/serverconfigs/shared"
|
||||||
|
"github.com/iwind/TeaGo/Tea"
|
||||||
|
"github.com/iwind/TeaGo/logs"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
"io/ioutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
sslCertListFilename = "ssl.certs.conf"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 获取证书列表实例
|
||||||
|
// 一定会返回不为nil的值
|
||||||
|
func SharedSSLCertList() *SSLCertList {
|
||||||
|
data, err := ioutil.ReadFile(Tea.ConfigFile(sslCertListFilename))
|
||||||
|
if err != nil {
|
||||||
|
return NewSSLCertList()
|
||||||
|
}
|
||||||
|
|
||||||
|
list := &SSLCertList{}
|
||||||
|
err = yaml.Unmarshal(data, list)
|
||||||
|
if err != nil {
|
||||||
|
logs.Error(err)
|
||||||
|
return NewSSLCertList()
|
||||||
|
}
|
||||||
|
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
// 公共的SSL证书列表
|
||||||
|
type SSLCertList struct {
|
||||||
|
Certs []*SSLCertConfig `yaml:"certs" json:"certs"` // 证书
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取新对象
|
||||||
|
func NewSSLCertList() *SSLCertList {
|
||||||
|
return &SSLCertList{
|
||||||
|
Certs: []*SSLCertConfig{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加证书
|
||||||
|
func (this *SSLCertList) AddCert(cert *SSLCertConfig) {
|
||||||
|
this.Certs = append(this.Certs, cert)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除证书
|
||||||
|
func (this *SSLCertList) RemoveCert(certId string) {
|
||||||
|
result := []*SSLCertConfig{}
|
||||||
|
for _, cert := range this.Certs {
|
||||||
|
if cert.Id == certId {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
result = append(result, cert)
|
||||||
|
}
|
||||||
|
this.Certs = result
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查找证书
|
||||||
|
func (this *SSLCertList) FindCert(certId string) *SSLCertConfig {
|
||||||
|
if len(certId) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for _, cert := range this.Certs {
|
||||||
|
if cert.Id == certId {
|
||||||
|
return cert
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存
|
||||||
|
func (this *SSLCertList) Save() error {
|
||||||
|
shared.Locker.Lock()
|
||||||
|
defer shared.Locker.Unlock()
|
||||||
|
|
||||||
|
data, err := yaml.Marshal(this)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ioutil.WriteFile(Tea.ConfigFile(sslCertListFilename), data, 0777)
|
||||||
|
}
|
||||||
124
internal/configs/serverconfigs/sslconfigs/ssl_go_1.11.go
Normal file
124
internal/configs/serverconfigs/sslconfigs/ssl_go_1.11.go
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
// +build !go1.12
|
||||||
|
|
||||||
|
package sslconfigs
|
||||||
|
|
||||||
|
import "crypto/tls"
|
||||||
|
|
||||||
|
var AllTlsVersions = []TLSVersion{"SSL 3.0", "TLS 1.0", "TLS 1.1", "TLS 1.2"}
|
||||||
|
|
||||||
|
var AllTLSCipherSuites = []TLSCipherSuite{
|
||||||
|
"TLS_RSA_WITH_RC4_128_SHA",
|
||||||
|
"TLS_RSA_WITH_3DES_EDE_CBC_SHA",
|
||||||
|
"TLS_RSA_WITH_AES_128_CBC_SHA",
|
||||||
|
"TLS_RSA_WITH_AES_256_CBC_SHA",
|
||||||
|
"TLS_RSA_WITH_AES_128_CBC_SHA256",
|
||||||
|
"TLS_RSA_WITH_AES_128_GCM_SHA256",
|
||||||
|
"TLS_RSA_WITH_AES_256_GCM_SHA384",
|
||||||
|
"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
|
||||||
|
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
|
||||||
|
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
|
||||||
|
"TLS_ECDHE_RSA_WITH_RC4_128_SHA",
|
||||||
|
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
|
||||||
|
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
|
||||||
|
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
|
||||||
|
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
|
||||||
|
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
|
||||||
|
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||||
|
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||||
|
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
|
||||||
|
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
|
||||||
|
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305",
|
||||||
|
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
|
||||||
|
}
|
||||||
|
|
||||||
|
var TLSModernCipherSuites = []string{
|
||||||
|
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305",
|
||||||
|
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
|
||||||
|
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||||
|
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||||
|
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
|
||||||
|
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
|
||||||
|
}
|
||||||
|
|
||||||
|
var TLSIntermediateCipherSuites = []string{
|
||||||
|
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305",
|
||||||
|
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
|
||||||
|
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||||
|
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||||
|
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
|
||||||
|
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
|
||||||
|
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
|
||||||
|
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
|
||||||
|
|
||||||
|
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
|
||||||
|
"TLS_RSA_WITH_3DES_EDE_CBC_SHA",
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *SSLConfig) convertMinVersion() {
|
||||||
|
switch this.MinVersion {
|
||||||
|
case "SSL 3.0":
|
||||||
|
this.minVersion = tls.VersionSSL30
|
||||||
|
case "TLS 1.0":
|
||||||
|
this.minVersion = tls.VersionTLS10
|
||||||
|
case "TLS 1.1":
|
||||||
|
this.minVersion = tls.VersionTLS11
|
||||||
|
case "TLS 1.2":
|
||||||
|
this.minVersion = tls.VersionTLS12
|
||||||
|
default:
|
||||||
|
this.minVersion = tls.VersionTLS10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *SSLConfig) initCipherSuites() {
|
||||||
|
// cipher suites
|
||||||
|
suites := []uint16{}
|
||||||
|
for _, suite := range this.CipherSuites {
|
||||||
|
switch suite {
|
||||||
|
case "TLS_RSA_WITH_RC4_128_SHA":
|
||||||
|
suites = append(suites, tls.TLS_RSA_WITH_RC4_128_SHA)
|
||||||
|
case "TLS_RSA_WITH_3DES_EDE_CBC_SHA":
|
||||||
|
suites = append(suites, tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA)
|
||||||
|
case "TLS_RSA_WITH_AES_128_CBC_SHA":
|
||||||
|
suites = append(suites, tls.TLS_RSA_WITH_AES_128_CBC_SHA)
|
||||||
|
case "TLS_RSA_WITH_AES_256_CBC_SHA":
|
||||||
|
suites = append(suites, tls.TLS_RSA_WITH_AES_256_CBC_SHA)
|
||||||
|
case "TLS_RSA_WITH_AES_128_CBC_SHA256":
|
||||||
|
suites = append(suites, tls.TLS_RSA_WITH_AES_128_CBC_SHA256)
|
||||||
|
case "TLS_RSA_WITH_AES_128_GCM_SHA256":
|
||||||
|
suites = append(suites, tls.TLS_RSA_WITH_AES_128_GCM_SHA256)
|
||||||
|
case "TLS_RSA_WITH_AES_256_GCM_SHA384":
|
||||||
|
suites = append(suites, tls.TLS_RSA_WITH_AES_256_GCM_SHA384)
|
||||||
|
case "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA":
|
||||||
|
suites = append(suites, tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA)
|
||||||
|
case "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA":
|
||||||
|
suites = append(suites, tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA)
|
||||||
|
case "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA":
|
||||||
|
suites = append(suites, tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA)
|
||||||
|
case "TLS_ECDHE_RSA_WITH_RC4_128_SHA":
|
||||||
|
suites = append(suites, tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA)
|
||||||
|
case "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA":
|
||||||
|
suites = append(suites, tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA)
|
||||||
|
case "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA":
|
||||||
|
suites = append(suites, tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA)
|
||||||
|
case "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA":
|
||||||
|
suites = append(suites, tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA)
|
||||||
|
case "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256":
|
||||||
|
suites = append(suites, tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256)
|
||||||
|
case "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256":
|
||||||
|
suites = append(suites, tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256)
|
||||||
|
case "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256":
|
||||||
|
suites = append(suites, tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256)
|
||||||
|
case "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256":
|
||||||
|
suites = append(suites, tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256)
|
||||||
|
case "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384":
|
||||||
|
suites = append(suites, tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384)
|
||||||
|
case "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384":
|
||||||
|
suites = append(suites, tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384)
|
||||||
|
case "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305":
|
||||||
|
suites = append(suites, tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305)
|
||||||
|
case "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305":
|
||||||
|
suites = append(suites, tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.cipherSuites = suites
|
||||||
|
}
|
||||||
148
internal/configs/serverconfigs/sslconfigs/ssl_go_1.12.go
Normal file
148
internal/configs/serverconfigs/sslconfigs/ssl_go_1.12.go
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
// +build go1.12
|
||||||
|
|
||||||
|
package sslconfigs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
var AllTlsVersions = []TLSVersion{"SSL 3.0", "TLS 1.0", "TLS 1.1", "TLS 1.2", "TLS 1.3"}
|
||||||
|
|
||||||
|
var AllTLSCipherSuites = []TLSCipherSuite{
|
||||||
|
"TLS_RSA_WITH_RC4_128_SHA",
|
||||||
|
"TLS_RSA_WITH_3DES_EDE_CBC_SHA",
|
||||||
|
"TLS_RSA_WITH_AES_128_CBC_SHA",
|
||||||
|
"TLS_RSA_WITH_AES_256_CBC_SHA",
|
||||||
|
"TLS_RSA_WITH_AES_128_CBC_SHA256",
|
||||||
|
"TLS_RSA_WITH_AES_128_GCM_SHA256",
|
||||||
|
"TLS_RSA_WITH_AES_256_GCM_SHA384",
|
||||||
|
"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
|
||||||
|
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
|
||||||
|
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
|
||||||
|
"TLS_ECDHE_RSA_WITH_RC4_128_SHA",
|
||||||
|
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
|
||||||
|
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
|
||||||
|
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
|
||||||
|
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
|
||||||
|
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
|
||||||
|
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||||
|
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||||
|
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
|
||||||
|
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
|
||||||
|
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305",
|
||||||
|
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
|
||||||
|
"TLS_AES_128_GCM_SHA256",
|
||||||
|
"TLS_AES_256_GCM_SHA384",
|
||||||
|
"TLS_CHACHA20_POLY1305_SHA256",
|
||||||
|
}
|
||||||
|
|
||||||
|
var TLSModernCipherSuites = []string{
|
||||||
|
"TLS_AES_128_GCM_SHA256",
|
||||||
|
"TLS_CHACHA20_POLY1305_SHA256",
|
||||||
|
"TLS_AES_256_GCM_SHA384",
|
||||||
|
|
||||||
|
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305",
|
||||||
|
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
|
||||||
|
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||||
|
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||||
|
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
|
||||||
|
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
|
||||||
|
}
|
||||||
|
|
||||||
|
var TLSIntermediateCipherSuites = []string{
|
||||||
|
"TLS_AES_128_GCM_SHA256",
|
||||||
|
"TLS_CHACHA20_POLY1305_SHA256",
|
||||||
|
"TLS_AES_256_GCM_SHA384",
|
||||||
|
|
||||||
|
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305",
|
||||||
|
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
|
||||||
|
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||||
|
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||||
|
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
|
||||||
|
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
|
||||||
|
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
|
||||||
|
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
|
||||||
|
|
||||||
|
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
|
||||||
|
"TLS_RSA_WITH_3DES_EDE_CBC_SHA",
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *SSLConfig) convertMinVersion() {
|
||||||
|
switch this.MinVersion {
|
||||||
|
case "SSL 3.0":
|
||||||
|
this.minVersion = tls.VersionSSL30
|
||||||
|
case "TLS 1.0":
|
||||||
|
this.minVersion = tls.VersionTLS10
|
||||||
|
case "TLS 1.1":
|
||||||
|
this.minVersion = tls.VersionTLS11
|
||||||
|
case "TLS 1.2":
|
||||||
|
this.minVersion = tls.VersionTLS12
|
||||||
|
case "TLS 1.3":
|
||||||
|
this.minVersion = tls.VersionTLS13
|
||||||
|
|
||||||
|
os.Setenv("GODEBUG", "tls13=1") // TODO should be removed in go 1.14, in go 1.12 tls IS NOT FULL IMPLEMENTED YET
|
||||||
|
default:
|
||||||
|
this.minVersion = tls.VersionTLS10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *SSLConfig) initCipherSuites() {
|
||||||
|
// cipher suites
|
||||||
|
suites := []uint16{}
|
||||||
|
for _, suite := range this.CipherSuites {
|
||||||
|
switch suite {
|
||||||
|
case "TLS_RSA_WITH_RC4_128_SHA":
|
||||||
|
suites = append(suites, tls.TLS_RSA_WITH_RC4_128_SHA)
|
||||||
|
case "TLS_RSA_WITH_3DES_EDE_CBC_SHA":
|
||||||
|
suites = append(suites, tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA)
|
||||||
|
case "TLS_RSA_WITH_AES_128_CBC_SHA":
|
||||||
|
suites = append(suites, tls.TLS_RSA_WITH_AES_128_CBC_SHA)
|
||||||
|
case "TLS_RSA_WITH_AES_256_CBC_SHA":
|
||||||
|
suites = append(suites, tls.TLS_RSA_WITH_AES_256_CBC_SHA)
|
||||||
|
case "TLS_RSA_WITH_AES_128_CBC_SHA256":
|
||||||
|
suites = append(suites, tls.TLS_RSA_WITH_AES_128_CBC_SHA256)
|
||||||
|
case "TLS_RSA_WITH_AES_128_GCM_SHA256":
|
||||||
|
suites = append(suites, tls.TLS_RSA_WITH_AES_128_GCM_SHA256)
|
||||||
|
case "TLS_RSA_WITH_AES_256_GCM_SHA384":
|
||||||
|
suites = append(suites, tls.TLS_RSA_WITH_AES_256_GCM_SHA384)
|
||||||
|
case "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA":
|
||||||
|
suites = append(suites, tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA)
|
||||||
|
case "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA":
|
||||||
|
suites = append(suites, tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA)
|
||||||
|
case "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA":
|
||||||
|
suites = append(suites, tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA)
|
||||||
|
case "TLS_ECDHE_RSA_WITH_RC4_128_SHA":
|
||||||
|
suites = append(suites, tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA)
|
||||||
|
case "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA":
|
||||||
|
suites = append(suites, tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA)
|
||||||
|
case "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA":
|
||||||
|
suites = append(suites, tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA)
|
||||||
|
case "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA":
|
||||||
|
suites = append(suites, tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA)
|
||||||
|
case "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256":
|
||||||
|
suites = append(suites, tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256)
|
||||||
|
case "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256":
|
||||||
|
suites = append(suites, tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256)
|
||||||
|
case "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256":
|
||||||
|
suites = append(suites, tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256)
|
||||||
|
case "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256":
|
||||||
|
suites = append(suites, tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256)
|
||||||
|
case "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384":
|
||||||
|
suites = append(suites, tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384)
|
||||||
|
case "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384":
|
||||||
|
suites = append(suites, tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384)
|
||||||
|
case "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305":
|
||||||
|
suites = append(suites, tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305)
|
||||||
|
case "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305":
|
||||||
|
suites = append(suites, tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305)
|
||||||
|
case "TLS_AES_128_GCM_SHA256":
|
||||||
|
suites = append(suites, tls.TLS_AES_128_GCM_SHA256)
|
||||||
|
case "TLS_AES_256_GCM_SHA384":
|
||||||
|
suites = append(suites, tls.TLS_AES_256_GCM_SHA384)
|
||||||
|
case "TLS_CHACHA20_POLY1305_SHA256":
|
||||||
|
suites = append(suites, tls.TLS_CHACHA20_POLY1305_SHA256)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.cipherSuites = suites
|
||||||
|
}
|
||||||
63
internal/configs/serverconfigs/sslconfigs/ssl_hsts.go
Normal file
63
internal/configs/serverconfigs/sslconfigs/ssl_hsts.go
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
package sslconfigs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/configs/serverconfigs/configutils"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HSTS设置
|
||||||
|
// 参考: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security
|
||||||
|
type HSTSConfig struct {
|
||||||
|
On bool `yaml:"on" json:"on"`
|
||||||
|
MaxAge int `yaml:"maxAge" json:"maxAge"` // 单位秒
|
||||||
|
IncludeSubDomains bool `yaml:"includeSubDomains" json:"includeSubDomains"`
|
||||||
|
Preload bool `yaml:"preload" json:"preload"`
|
||||||
|
Domains []string `yaml:"domains" json:"domains"`
|
||||||
|
|
||||||
|
hasDomains bool
|
||||||
|
headerValue string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 校验
|
||||||
|
func (this *HSTSConfig) Init() error {
|
||||||
|
this.hasDomains = len(this.Domains) > 0
|
||||||
|
this.headerValue = this.asHeaderValue()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断是否匹配域名
|
||||||
|
func (this *HSTSConfig) Match(domain string) bool {
|
||||||
|
if !this.hasDomains {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return configutils.MatchDomains(this.Domains, domain)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Header Key
|
||||||
|
func (this *HSTSConfig) HeaderKey() string {
|
||||||
|
return "Strict-Transport-Security"
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取得当前的Header值
|
||||||
|
func (this *HSTSConfig) HeaderValue() string {
|
||||||
|
return this.headerValue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 转换为Header值
|
||||||
|
func (this *HSTSConfig) asHeaderValue() string {
|
||||||
|
b := strings.Builder{}
|
||||||
|
b.WriteString("max-age=")
|
||||||
|
if this.MaxAge > 0 {
|
||||||
|
b.WriteString(strconv.Itoa(this.MaxAge))
|
||||||
|
} else {
|
||||||
|
b.WriteString("31536000") // 1 year
|
||||||
|
}
|
||||||
|
if this.IncludeSubDomains {
|
||||||
|
b.WriteString("; includeSubDomains")
|
||||||
|
}
|
||||||
|
if this.Preload {
|
||||||
|
b.WriteString("; preload")
|
||||||
|
}
|
||||||
|
return b.String()
|
||||||
|
}
|
||||||
39
internal/configs/serverconfigs/sslconfigs/ssl_hsts_test.go
Normal file
39
internal/configs/serverconfigs/sslconfigs/ssl_hsts_test.go
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
package sslconfigs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/iwind/TeaGo/assert"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHSTSConfig(t *testing.T) {
|
||||||
|
h := &HSTSConfig{}
|
||||||
|
h.Init()
|
||||||
|
t.Log(h.HeaderValue())
|
||||||
|
|
||||||
|
h.IncludeSubDomains = true
|
||||||
|
h.Init()
|
||||||
|
t.Log(h.HeaderValue())
|
||||||
|
|
||||||
|
h.Preload = true
|
||||||
|
h.Init()
|
||||||
|
t.Log(h.HeaderValue())
|
||||||
|
|
||||||
|
h.IncludeSubDomains = false
|
||||||
|
h.Init()
|
||||||
|
t.Log(h.HeaderValue())
|
||||||
|
|
||||||
|
h.MaxAge = 86400
|
||||||
|
h.Init()
|
||||||
|
t.Log(h.HeaderValue())
|
||||||
|
|
||||||
|
a := assert.NewAssertion(t)
|
||||||
|
a.IsTrue(h.Match("abc.com"))
|
||||||
|
|
||||||
|
h.Domains = []string{"abc.com"}
|
||||||
|
h.Init()
|
||||||
|
a.IsTrue(h.Match("abc.com"))
|
||||||
|
|
||||||
|
h.Domains = []string{"1.abc.com"}
|
||||||
|
h.Init()
|
||||||
|
a.IsFalse(h.Match("abc.com"))
|
||||||
|
}
|
||||||
10
internal/configs/serverconfigs/web_config.go
Normal file
10
internal/configs/serverconfigs/web_config.go
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package serverconfigs
|
||||||
|
|
||||||
|
type WebConfig struct {
|
||||||
|
IsOn bool `yaml:"isOn" json:"isOn"`
|
||||||
|
|
||||||
|
Locations []*LocationConfig `yaml:"locations" json:"locations"` // 路径规则 TODO
|
||||||
|
|
||||||
|
// 本地静态资源配置
|
||||||
|
Root string `yaml:"root" json:"root"` // 资源根目录 TODO
|
||||||
|
}
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
package configs
|
|
||||||
|
|
||||||
type WebConfig struct {
|
|
||||||
Locations []*LocationConfig `yaml:"locations"` // 路径规则
|
|
||||||
|
|
||||||
// 本地静态资源配置
|
|
||||||
Root string `yaml:"root" json:"root"` // 资源根目录
|
|
||||||
}
|
|
||||||
13
internal/const/const.go
Normal file
13
internal/const/const.go
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package teaconst
|
||||||
|
|
||||||
|
const (
|
||||||
|
Version = "0.0.1"
|
||||||
|
|
||||||
|
ProductName = "Edge Node"
|
||||||
|
ProcessName = "edge-node"
|
||||||
|
|
||||||
|
Role = "node"
|
||||||
|
|
||||||
|
EncryptKey = "8f983f4d69b83aaa0d74b21a212f6967"
|
||||||
|
EncryptMethod = "aes-256-cfb"
|
||||||
|
)
|
||||||
41
internal/encrypt/magic_key.go
Normal file
41
internal/encrypt/magic_key.go
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
package encrypt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/iwind/TeaGo/logs"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
MagicKey = "f1c8eafb543f03023e97b7be864a4e9b"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 加密特殊信息
|
||||||
|
func MagicKeyEncode(data []byte) []byte {
|
||||||
|
method, err := NewMethodInstance("aes-256-cfb", MagicKey, MagicKey[:16])
|
||||||
|
if err != nil {
|
||||||
|
logs.Println("[MagicKeyEncode]" + err.Error())
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
dst, err := method.Encrypt(data)
|
||||||
|
if err != nil {
|
||||||
|
logs.Println("[MagicKeyEncode]" + err.Error())
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解密特殊信息
|
||||||
|
func MagicKeyDecode(data []byte) []byte {
|
||||||
|
method, err := NewMethodInstance("aes-256-cfb", MagicKey, MagicKey[:16])
|
||||||
|
if err != nil {
|
||||||
|
logs.Println("[MagicKeyEncode]" + err.Error())
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
src, err := method.Decrypt(data)
|
||||||
|
if err != nil {
|
||||||
|
logs.Println("[MagicKeyEncode]" + err.Error())
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
return src
|
||||||
|
}
|
||||||
11
internal/encrypt/magic_key_test.go
Normal file
11
internal/encrypt/magic_key_test.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package encrypt
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestMagicKeyEncode(t *testing.T) {
|
||||||
|
dst := MagicKeyEncode([]byte("Hello,World"))
|
||||||
|
t.Log("dst:", string(dst))
|
||||||
|
|
||||||
|
src := MagicKeyDecode(dst)
|
||||||
|
t.Log("src:", string(src))
|
||||||
|
}
|
||||||
12
internal/encrypt/method.go
Normal file
12
internal/encrypt/method.go
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package encrypt
|
||||||
|
|
||||||
|
type MethodInterface interface {
|
||||||
|
// 初始化
|
||||||
|
Init(key []byte, iv []byte) error
|
||||||
|
|
||||||
|
// 加密
|
||||||
|
Encrypt(src []byte) (dst []byte, err error)
|
||||||
|
|
||||||
|
// 解密
|
||||||
|
Decrypt(dst []byte) (src []byte, err error)
|
||||||
|
}
|
||||||
73
internal/encrypt/method_aes_128_cfb.go
Normal file
73
internal/encrypt/method_aes_128_cfb.go
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
package encrypt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AES128CFBMethod struct {
|
||||||
|
iv []byte
|
||||||
|
block cipher.Block
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *AES128CFBMethod) Init(key, iv []byte) error {
|
||||||
|
// 判断key是否为32长度
|
||||||
|
l := len(key)
|
||||||
|
if l > 16 {
|
||||||
|
key = key[:16]
|
||||||
|
} else if l < 16 {
|
||||||
|
key = append(key, bytes.Repeat([]byte{' '}, 16-l)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断iv长度
|
||||||
|
l2 := len(iv)
|
||||||
|
if l2 > aes.BlockSize {
|
||||||
|
iv = iv[:aes.BlockSize]
|
||||||
|
} else if l2 < aes.BlockSize {
|
||||||
|
iv = append(iv, bytes.Repeat([]byte{' '}, aes.BlockSize-l2)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.iv = iv
|
||||||
|
|
||||||
|
// block
|
||||||
|
block, err := aes.NewCipher(key)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
this.block = block
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *AES128CFBMethod) Encrypt(src []byte) (dst []byte, err error) {
|
||||||
|
if len(src) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
err = RecoverMethodPanic(recover())
|
||||||
|
}()
|
||||||
|
|
||||||
|
dst = make([]byte, len(src))
|
||||||
|
encrypter := cipher.NewCFBEncrypter(this.block, this.iv)
|
||||||
|
encrypter.XORKeyStream(dst, src)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *AES128CFBMethod) Decrypt(dst []byte) (src []byte, err error) {
|
||||||
|
if len(dst) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
err = RecoverMethodPanic(recover())
|
||||||
|
}()
|
||||||
|
|
||||||
|
src = make([]byte, len(dst))
|
||||||
|
encrypter := cipher.NewCFBDecrypter(this.block, this.iv)
|
||||||
|
encrypter.XORKeyStream(src, dst)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
92
internal/encrypt/method_aes_128_cfb_test.go
Normal file
92
internal/encrypt/method_aes_128_cfb_test.go
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
package encrypt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAES128CFBMethod_Encrypt(t *testing.T) {
|
||||||
|
method, err := NewMethodInstance("aes-128-cfb", "abc", "123")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
src := []byte("Hello, World")
|
||||||
|
dst, err := method.Encrypt(src)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
dst = dst[:len(src)]
|
||||||
|
t.Log("dst:", string(dst))
|
||||||
|
|
||||||
|
src = make([]byte, len(src))
|
||||||
|
src, err = method.Decrypt(dst)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log("src:", string(src))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAES128CFBMethod_Encrypt2(t *testing.T) {
|
||||||
|
method, err := NewMethodInstance("aes-128-cfb", "abc", "123")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sources := [][]byte{}
|
||||||
|
|
||||||
|
{
|
||||||
|
a := []byte{1}
|
||||||
|
_, err = method.Encrypt(a)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
src := []byte(strings.Repeat("Hello", 1))
|
||||||
|
dst, err := method.Encrypt(src)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sources = append(sources, dst)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
a := []byte{1}
|
||||||
|
_, err = method.Decrypt(a)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, dst := range sources {
|
||||||
|
dst2 := append([]byte{}, dst...)
|
||||||
|
src2 := make([]byte, len(dst2))
|
||||||
|
src2, err := method.Decrypt(dst2)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log(string(src2))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkAES128CFBMethod_Encrypt(b *testing.B) {
|
||||||
|
runtime.GOMAXPROCS(1)
|
||||||
|
|
||||||
|
method, err := NewMethodInstance("aes-128-cfb", "abc", "123")
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
src := []byte(strings.Repeat("Hello", 1024))
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
dst, err := method.Encrypt(src)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
_ = dst
|
||||||
|
}
|
||||||
|
}
|
||||||
74
internal/encrypt/method_aes_192_cfb.go
Normal file
74
internal/encrypt/method_aes_192_cfb.go
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
package encrypt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AES192CFBMethod struct {
|
||||||
|
block cipher.Block
|
||||||
|
iv []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *AES192CFBMethod) Init(key, iv []byte) error {
|
||||||
|
// 判断key是否为24长度
|
||||||
|
l := len(key)
|
||||||
|
if l > 24 {
|
||||||
|
key = key[:24]
|
||||||
|
} else if l < 24 {
|
||||||
|
key = append(key, bytes.Repeat([]byte{' '}, 24-l)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
block, err := aes.NewCipher(key)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
this.block = block
|
||||||
|
|
||||||
|
// 判断iv长度
|
||||||
|
l2 := len(iv)
|
||||||
|
if l2 > aes.BlockSize {
|
||||||
|
iv = iv[:aes.BlockSize]
|
||||||
|
} else if l2 < aes.BlockSize {
|
||||||
|
iv = append(iv, bytes.Repeat([]byte{' '}, aes.BlockSize-l2)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.iv = iv
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *AES192CFBMethod) Encrypt(src []byte) (dst []byte, err error) {
|
||||||
|
if len(src) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
err = RecoverMethodPanic(recover())
|
||||||
|
}()
|
||||||
|
|
||||||
|
dst = make([]byte, len(src))
|
||||||
|
|
||||||
|
encrypter := cipher.NewCFBEncrypter(this.block, this.iv)
|
||||||
|
encrypter.XORKeyStream(dst, src)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *AES192CFBMethod) Decrypt(dst []byte) (src []byte, err error) {
|
||||||
|
if len(dst) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
err = RecoverMethodPanic(recover())
|
||||||
|
}()
|
||||||
|
|
||||||
|
src = make([]byte, len(dst))
|
||||||
|
|
||||||
|
decrypter := cipher.NewCFBDecrypter(this.block, this.iv)
|
||||||
|
decrypter.XORKeyStream(src, dst)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
45
internal/encrypt/method_aes_192_cfb_test.go
Normal file
45
internal/encrypt/method_aes_192_cfb_test.go
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
package encrypt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAES192CFBMethod_Encrypt(t *testing.T) {
|
||||||
|
method, err := NewMethodInstance("aes-192-cfb", "abc", "123")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
src := []byte("Hello, World")
|
||||||
|
dst, err := method.Encrypt(src)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
dst = dst[:len(src)]
|
||||||
|
t.Log("dst:", string(dst))
|
||||||
|
|
||||||
|
src, err = method.Decrypt(dst)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log("src:", string(src))
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkAES192CFBMethod_Encrypt(b *testing.B) {
|
||||||
|
runtime.GOMAXPROCS(1)
|
||||||
|
|
||||||
|
method, err := NewMethodInstance("aes-192-cfb", "abc", "123")
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
src := []byte(strings.Repeat("Hello", 1024))
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
dst, err := method.Encrypt(src)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
_ = dst
|
||||||
|
}
|
||||||
|
}
|
||||||
72
internal/encrypt/method_aes_256_cfb.go
Normal file
72
internal/encrypt/method_aes_256_cfb.go
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
package encrypt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AES256CFBMethod struct {
|
||||||
|
block cipher.Block
|
||||||
|
iv []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *AES256CFBMethod) Init(key, iv []byte) error {
|
||||||
|
// 判断key是否为32长度
|
||||||
|
l := len(key)
|
||||||
|
if l > 32 {
|
||||||
|
key = key[:32]
|
||||||
|
} else if l < 32 {
|
||||||
|
key = append(key, bytes.Repeat([]byte{' '}, 32-l)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
block, err := aes.NewCipher(key)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
this.block = block
|
||||||
|
|
||||||
|
// 判断iv长度
|
||||||
|
l2 := len(iv)
|
||||||
|
if l2 > aes.BlockSize {
|
||||||
|
iv = iv[:aes.BlockSize]
|
||||||
|
} else if l2 < aes.BlockSize {
|
||||||
|
iv = append(iv, bytes.Repeat([]byte{' '}, aes.BlockSize-l2)...)
|
||||||
|
}
|
||||||
|
this.iv = iv
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *AES256CFBMethod) Encrypt(src []byte) (dst []byte, err error) {
|
||||||
|
if len(src) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
err = RecoverMethodPanic(recover())
|
||||||
|
}()
|
||||||
|
|
||||||
|
dst = make([]byte, len(src))
|
||||||
|
|
||||||
|
encrypter := cipher.NewCFBEncrypter(this.block, this.iv)
|
||||||
|
encrypter.XORKeyStream(dst, src)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *AES256CFBMethod) Decrypt(dst []byte) (src []byte, err error) {
|
||||||
|
if len(dst) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
err = RecoverMethodPanic(recover())
|
||||||
|
}()
|
||||||
|
|
||||||
|
src = make([]byte, len(dst))
|
||||||
|
decrypter := cipher.NewCFBDecrypter(this.block, this.iv)
|
||||||
|
decrypter.XORKeyStream(src, dst)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
42
internal/encrypt/method_aes_256_cfb_test.go
Normal file
42
internal/encrypt/method_aes_256_cfb_test.go
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
package encrypt
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestAES256CFBMethod_Encrypt(t *testing.T) {
|
||||||
|
method, err := NewMethodInstance("aes-256-cfb", "abc", "123")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
src := []byte("Hello, World")
|
||||||
|
dst, err := method.Encrypt(src)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
dst = dst[:len(src)]
|
||||||
|
t.Log("dst:", string(dst))
|
||||||
|
|
||||||
|
src, err = method.Decrypt(dst)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log("src:", string(src))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAES256CFBMethod_Encrypt2(t *testing.T) {
|
||||||
|
method, err := NewMethodInstance("aes-256-cfb", "abc", "123")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
src := []byte("Hello, World")
|
||||||
|
dst, err := method.Encrypt(src)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log("dst:", string(dst))
|
||||||
|
|
||||||
|
src, err = method.Decrypt(dst)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log("src:", string(src))
|
||||||
|
}
|
||||||
26
internal/encrypt/method_raw.go
Normal file
26
internal/encrypt/method_raw.go
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
package encrypt
|
||||||
|
|
||||||
|
type RawMethod struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *RawMethod) Init(key, iv []byte) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *RawMethod) Encrypt(src []byte) (dst []byte, err error) {
|
||||||
|
if len(src) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dst = make([]byte, len(src))
|
||||||
|
copy(dst, src)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *RawMethod) Decrypt(dst []byte) (src []byte, err error) {
|
||||||
|
if len(dst) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
src = make([]byte, len(dst))
|
||||||
|
copy(src, dst)
|
||||||
|
return
|
||||||
|
}
|
||||||
23
internal/encrypt/method_raw_test.go
Normal file
23
internal/encrypt/method_raw_test.go
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package encrypt
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestRawMethod_Encrypt(t *testing.T) {
|
||||||
|
method, err := NewMethodInstance("raw", "abc", "123")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
src := []byte("Hello, World")
|
||||||
|
dst, err := method.Encrypt(src)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
dst = dst[:len(src)]
|
||||||
|
t.Log("dst:", string(dst))
|
||||||
|
|
||||||
|
src, err = method.Decrypt(dst)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log("src:", string(src))
|
||||||
|
}
|
||||||
43
internal/encrypt/method_utils.go
Normal file
43
internal/encrypt/method_utils.go
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
package encrypt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
var methods = map[string]reflect.Type{
|
||||||
|
"raw": reflect.TypeOf(new(RawMethod)).Elem(),
|
||||||
|
"aes-128-cfb": reflect.TypeOf(new(AES128CFBMethod)).Elem(),
|
||||||
|
"aes-192-cfb": reflect.TypeOf(new(AES192CFBMethod)).Elem(),
|
||||||
|
"aes-256-cfb": reflect.TypeOf(new(AES256CFBMethod)).Elem(),
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMethodInstance(method string, key string, iv string) (MethodInterface, error) {
|
||||||
|
valueType, ok := methods[method]
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("method '" + method + "' not found")
|
||||||
|
}
|
||||||
|
instance, ok := reflect.New(valueType).Interface().(MethodInterface)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("method '" + method + "' must implement MethodInterface")
|
||||||
|
}
|
||||||
|
err := instance.Init([]byte(key), []byte(iv))
|
||||||
|
return instance, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func RecoverMethodPanic(err interface{}) error {
|
||||||
|
if err != nil {
|
||||||
|
s, ok := err.(string)
|
||||||
|
if ok {
|
||||||
|
return errors.New(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
e, ok := err.(error)
|
||||||
|
if ok {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.New("unknown error")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
8
internal/encrypt/method_utils_test.go
Normal file
8
internal/encrypt/method_utils_test.go
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
package encrypt
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestFindMethodInstance(t *testing.T) {
|
||||||
|
t.Log(NewMethodInstance("a", "b", ""))
|
||||||
|
t.Log(NewMethodInstance("aes-256-cfb", "123456", ""))
|
||||||
|
}
|
||||||
@@ -3,17 +3,16 @@ package nodes
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/configs"
|
"github.com/TeaOSLab/EdgeNode/internal/configs/serverconfigs"
|
||||||
"github.com/iwind/TeaGo/logs"
|
"github.com/iwind/TeaGo/logs"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Listener struct {
|
type Listener struct {
|
||||||
group *configs.ServerGroup
|
group *serverconfigs.ServerGroup
|
||||||
isListening bool
|
isListening bool
|
||||||
listener interface{} // 监听器
|
listener ListenerImpl // 监听器
|
||||||
|
|
||||||
locker sync.RWMutex
|
locker sync.RWMutex
|
||||||
}
|
}
|
||||||
@@ -22,7 +21,7 @@ func NewListener() *Listener {
|
|||||||
return &Listener{}
|
return &Listener{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *Listener) Reload(group *configs.ServerGroup) {
|
func (this *Listener) Reload(group *serverconfigs.ServerGroup) {
|
||||||
this.locker.Lock()
|
this.locker.Lock()
|
||||||
defer this.locker.Unlock()
|
defer this.locker.Unlock()
|
||||||
this.group = group
|
this.group = group
|
||||||
@@ -40,78 +39,67 @@ func (this *Listener) Listen() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
protocol := this.group.Protocol()
|
protocol := this.group.Protocol()
|
||||||
switch protocol {
|
|
||||||
case configs.ProtocolHTTP, configs.ProtocolHTTP4, configs.ProtocolHTTP6:
|
|
||||||
return this.listenHTTP()
|
|
||||||
case configs.ProtocolHTTPS, configs.ProtocolHTTPS4, configs.ProtocolHTTPS6:
|
|
||||||
return this.ListenHTTPS()
|
|
||||||
case configs.ProtocolTCP, configs.ProtocolTCP4, configs.ProtocolTCP6:
|
|
||||||
return this.listenTCP()
|
|
||||||
case configs.ProtocolTLS, configs.ProtocolTLS4, configs.ProtocolTLS6:
|
|
||||||
return this.listenTLS()
|
|
||||||
case configs.ProtocolUnix:
|
|
||||||
return this.listenUnix()
|
|
||||||
case configs.ProtocolUDP:
|
|
||||||
return this.listenUDP()
|
|
||||||
default:
|
|
||||||
return errors.New("unknown protocol '" + protocol + "'")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *Listener) Close() error {
|
netListener, err := this.createListener()
|
||||||
// TODO 需要实现
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *Listener) listenHTTP() error {
|
|
||||||
listener, err := this.createListener()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
mux := http.NewServeMux()
|
switch protocol {
|
||||||
mux.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
|
case serverconfigs.ProtocolHTTP, serverconfigs.ProtocolHTTP4, serverconfigs.ProtocolHTTP6:
|
||||||
_, _ = writer.Write([]byte("Hello, World"))
|
this.listener = &HTTPListener{
|
||||||
})
|
Group: this.group,
|
||||||
server := &http.Server{
|
Listener: netListener,
|
||||||
Addr: this.group.Addr(),
|
}
|
||||||
Handler: mux,
|
case serverconfigs.ProtocolHTTPS, serverconfigs.ProtocolHTTPS4, serverconfigs.ProtocolHTTPS6:
|
||||||
|
this.listener = &HTTPListener{
|
||||||
|
Group: this.group,
|
||||||
|
Listener: netListener,
|
||||||
|
}
|
||||||
|
case serverconfigs.ProtocolTCP, serverconfigs.ProtocolTCP4, serverconfigs.ProtocolTCP6:
|
||||||
|
this.listener = &TCPListener{
|
||||||
|
Group: this.group,
|
||||||
|
Listener: netListener,
|
||||||
|
}
|
||||||
|
case serverconfigs.ProtocolTLS, serverconfigs.ProtocolTLS4, serverconfigs.ProtocolTLS6:
|
||||||
|
this.listener = &TCPListener{
|
||||||
|
Group: this.group,
|
||||||
|
Listener: netListener,
|
||||||
|
}
|
||||||
|
case serverconfigs.ProtocolUnix:
|
||||||
|
this.listener = &UnixListener{
|
||||||
|
Group: this.group,
|
||||||
|
Listener: netListener,
|
||||||
|
}
|
||||||
|
case serverconfigs.ProtocolUDP:
|
||||||
|
this.listener = &UDPListener{
|
||||||
|
Group: this.group,
|
||||||
|
Listener: netListener,
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return errors.New("unknown protocol '" + protocol + "'")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.listener.Init()
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
err = server.Serve(listener)
|
err := this.listener.Serve()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logs.Println("[LISTENER]" + err.Error())
|
logs.Println("[LISTENER]" + err.Error())
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *Listener) ListenHTTPS() error {
|
func (this *Listener) Close() error {
|
||||||
// TODO 需要实现
|
if this.listener == nil {
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *Listener) listenTCP() error {
|
|
||||||
// TODO 需要实现
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *Listener) listenTLS() error {
|
|
||||||
// TODO 需要实现
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *Listener) listenUnix() error {
|
|
||||||
// TODO 需要实现
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *Listener) listenUDP() error {
|
|
||||||
// TODO 需要实现
|
|
||||||
return nil
|
return nil
|
||||||
|
}
|
||||||
|
return this.listener.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 创建监听器
|
||||||
func (this *Listener) createListener() (net.Listener, error) {
|
func (this *Listener) createListener() (net.Listener, error) {
|
||||||
listenConfig := net.ListenConfig{
|
listenConfig := net.ListenConfig{
|
||||||
Control: nil,
|
Control: nil,
|
||||||
@@ -119,9 +107,9 @@ func (this *Listener) createListener() (net.Listener, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch this.group.Protocol() {
|
switch this.group.Protocol() {
|
||||||
case configs.ProtocolHTTP4, configs.ProtocolHTTPS4, configs.ProtocolTLS4:
|
case serverconfigs.ProtocolHTTP4, serverconfigs.ProtocolHTTPS4, serverconfigs.ProtocolTLS4:
|
||||||
return listenConfig.Listen(context.Background(), "tcp4", this.group.Addr())
|
return listenConfig.Listen(context.Background(), "tcp4", this.group.Addr())
|
||||||
case configs.ProtocolHTTP6, configs.ProtocolHTTPS6, configs.ProtocolTLS6:
|
case serverconfigs.ProtocolHTTP6, serverconfigs.ProtocolHTTPS6, serverconfigs.ProtocolTLS6:
|
||||||
return listenConfig.Listen(context.Background(), "tcp6", this.group.Addr())
|
return listenConfig.Listen(context.Background(), "tcp6", this.group.Addr())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
195
internal/nodes/listener_base.go
Normal file
195
internal/nodes/listener_base.go
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
package nodes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"errors"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/configs/serverconfigs"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/configs/serverconfigs/sslconfigs"
|
||||||
|
http2 "golang.org/x/net/http2"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BaseListener struct {
|
||||||
|
serversLocker sync.RWMutex
|
||||||
|
namedServersLocker sync.RWMutex
|
||||||
|
namedServers map[string]*NamedServer // 域名 => server
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
func (this *BaseListener) Init() {
|
||||||
|
this.namedServers = map[string]*NamedServer{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构造TLS配置
|
||||||
|
func (this *BaseListener) buildTLSConfig(group *serverconfigs.ServerGroup) *tls.Config {
|
||||||
|
return &tls.Config{
|
||||||
|
Certificates: nil,
|
||||||
|
GetConfigForClient: func(info *tls.ClientHelloInfo) (config *tls.Config, e error) {
|
||||||
|
ssl, _, err := this.matchSSL(group, info.ServerName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cipherSuites := ssl.TLSCipherSuites()
|
||||||
|
if len(cipherSuites) == 0 {
|
||||||
|
cipherSuites = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
nextProto := []string{}
|
||||||
|
if !ssl.HTTP2Disabled {
|
||||||
|
nextProto = []string{http2.NextProtoTLS}
|
||||||
|
}
|
||||||
|
return &tls.Config{
|
||||||
|
Certificates: nil,
|
||||||
|
MinVersion: ssl.TLSMinVersion(),
|
||||||
|
CipherSuites: cipherSuites,
|
||||||
|
GetCertificate: func(info *tls.ClientHelloInfo) (certificate *tls.Certificate, e error) {
|
||||||
|
_, cert, err := this.matchSSL(group, info.ServerName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if cert == nil {
|
||||||
|
return nil, errors.New("[proxy]no certs found for '" + info.ServerName + "'")
|
||||||
|
}
|
||||||
|
return cert, nil
|
||||||
|
},
|
||||||
|
ClientAuth: sslconfigs.GoSSLClientAuthType(ssl.ClientAuthType),
|
||||||
|
ClientCAs: ssl.CAPool(),
|
||||||
|
|
||||||
|
NextProtos: nextProto,
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
GetCertificate: func(info *tls.ClientHelloInfo) (certificate *tls.Certificate, e error) {
|
||||||
|
_, cert, err := this.matchSSL(group, info.ServerName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if cert == nil {
|
||||||
|
return nil, errors.New("[proxy]no certs found for '" + info.ServerName + "'")
|
||||||
|
}
|
||||||
|
return cert, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据域名匹配证书
|
||||||
|
func (this *BaseListener) matchSSL(group *serverconfigs.ServerGroup, domain string) (*sslconfigs.SSLConfig, *tls.Certificate, error) {
|
||||||
|
this.serversLocker.RLock()
|
||||||
|
defer this.serversLocker.RUnlock()
|
||||||
|
|
||||||
|
// 如果域名为空,则取第一个
|
||||||
|
// 通常域名为空是因为是直接通过IP访问的
|
||||||
|
if len(domain) == 0 {
|
||||||
|
if serverconfigs.SharedGlobalConfig().HTTPAll.MatchDomainStrictly {
|
||||||
|
return nil, nil, errors.New("no tls server name matched")
|
||||||
|
}
|
||||||
|
|
||||||
|
firstServer := group.FirstServer()
|
||||||
|
if firstServer == nil {
|
||||||
|
return nil, nil, errors.New("no server available")
|
||||||
|
}
|
||||||
|
sslConfig := firstServer.SSLConfig()
|
||||||
|
|
||||||
|
if sslConfig != nil {
|
||||||
|
return sslConfig, sslConfig.FirstCert(), nil
|
||||||
|
|
||||||
|
}
|
||||||
|
return nil, nil, errors.New("no tls server name found")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 通过代理服务域名配置匹配
|
||||||
|
server, _ := this.findNamedServer(group, domain)
|
||||||
|
if server == nil || server.SSLConfig() == nil || !server.SSLConfig().IsOn {
|
||||||
|
// 搜索所有的Server,通过SSL证书内容中的DNSName匹配
|
||||||
|
for _, server := range group.Servers {
|
||||||
|
if server.SSLConfig() == nil || !server.SSLConfig().IsOn {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
cert, ok := server.SSLConfig().MatchDomain(domain)
|
||||||
|
if ok {
|
||||||
|
return server.SSLConfig(), cert, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil, errors.New("[proxy]no server found for '" + domain + "'")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 证书是否匹配
|
||||||
|
sslConfig := server.SSLConfig()
|
||||||
|
cert, ok := sslConfig.MatchDomain(domain)
|
||||||
|
if ok {
|
||||||
|
return sslConfig, cert, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return sslConfig, sslConfig.FirstCert(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据域名来查找匹配的域名
|
||||||
|
func (this *BaseListener) findNamedServer(group *serverconfigs.ServerGroup, name string) (serverConfig *serverconfigs.ServerConfig, serverName string) {
|
||||||
|
// 读取缓存
|
||||||
|
this.namedServersLocker.RLock()
|
||||||
|
namedServer, found := this.namedServers[name]
|
||||||
|
if found {
|
||||||
|
this.namedServersLocker.RUnlock()
|
||||||
|
return namedServer.Server, namedServer.Name
|
||||||
|
}
|
||||||
|
this.namedServersLocker.RUnlock()
|
||||||
|
|
||||||
|
this.serversLocker.RLock()
|
||||||
|
defer this.serversLocker.RUnlock()
|
||||||
|
|
||||||
|
currentServers := group.Servers
|
||||||
|
countServers := len(currentServers)
|
||||||
|
if countServers == 0 {
|
||||||
|
return nil, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// 只记录N个记录,防止内存耗尽
|
||||||
|
maxNamedServers := 10240
|
||||||
|
|
||||||
|
// 是否严格匹配域名
|
||||||
|
matchDomainStrictly := serverconfigs.SharedGlobalConfig().HTTPAll.MatchDomainStrictly
|
||||||
|
|
||||||
|
// 如果只有一个server,则默认为这个
|
||||||
|
if countServers == 1 && !matchDomainStrictly {
|
||||||
|
return currentServers[0], name
|
||||||
|
}
|
||||||
|
|
||||||
|
// 精确查找
|
||||||
|
for _, server := range currentServers {
|
||||||
|
if server.MatchNameStrictly(name) {
|
||||||
|
this.namedServersLocker.Lock()
|
||||||
|
if len(this.namedServers) < maxNamedServers {
|
||||||
|
this.namedServers[name] = &NamedServer{
|
||||||
|
Name: name,
|
||||||
|
Server: server,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.namedServersLocker.Unlock()
|
||||||
|
return server, name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 模糊查找
|
||||||
|
for _, server := range currentServers {
|
||||||
|
if matched := server.MatchName(name); matched {
|
||||||
|
this.namedServersLocker.Lock()
|
||||||
|
if len(this.namedServers) < maxNamedServers {
|
||||||
|
this.namedServers[name] = &NamedServer{
|
||||||
|
Name: name,
|
||||||
|
Server: server,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.namedServersLocker.Unlock()
|
||||||
|
return server, name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 找不到而且域名严格匹配模式下不返回Server
|
||||||
|
if matchDomainStrictly {
|
||||||
|
return nil, name
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没有找到,则匹配到第一个
|
||||||
|
return currentServers[0], name
|
||||||
|
}
|
||||||
68
internal/nodes/listener_http.go
Normal file
68
internal/nodes/listener_http.go
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
package nodes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/configs/serverconfigs"
|
||||||
|
"github.com/iwind/TeaGo/logs"
|
||||||
|
"golang.org/x/net/http2"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HTTPListener struct {
|
||||||
|
BaseListener
|
||||||
|
|
||||||
|
Group *serverconfigs.ServerGroup
|
||||||
|
Listener net.Listener
|
||||||
|
|
||||||
|
httpServer *http.Server
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *HTTPListener) Serve() error {
|
||||||
|
handler := http.NewServeMux()
|
||||||
|
handler.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
|
||||||
|
this.handleHTTP(writer, request)
|
||||||
|
})
|
||||||
|
|
||||||
|
this.httpServer = &http.Server{
|
||||||
|
Addr: this.Group.Addr(),
|
||||||
|
Handler: handler,
|
||||||
|
IdleTimeout: 2 * time.Minute,
|
||||||
|
}
|
||||||
|
this.httpServer.SetKeepAlivesEnabled(true)
|
||||||
|
|
||||||
|
// HTTP协议
|
||||||
|
if this.Group.IsHTTP() {
|
||||||
|
err := this.httpServer.Serve(this.Listener)
|
||||||
|
if err != nil && err != http.ErrServerClosed {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTPS协议
|
||||||
|
if this.Group.IsHTTPS() {
|
||||||
|
this.httpServer.TLSConfig = this.buildTLSConfig(this.Group)
|
||||||
|
|
||||||
|
// support http/2
|
||||||
|
err := http2.ConfigureServer(this.httpServer, nil)
|
||||||
|
if err != nil {
|
||||||
|
logs.Println("[HTTP_LISTENER]configure http2 error: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
err = this.httpServer.ServeTLS(this.Listener, "", "")
|
||||||
|
if err != nil && err != http.ErrServerClosed {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *HTTPListener) Close() error {
|
||||||
|
// TODO
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *HTTPListener) handleHTTP(writer http.ResponseWriter, req *http.Request) {
|
||||||
|
writer.Write([]byte("Hello, World"))
|
||||||
|
}
|
||||||
13
internal/nodes/listener_impl.go
Normal file
13
internal/nodes/listener_impl.go
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package nodes
|
||||||
|
|
||||||
|
// 各协议监听器的具体实现
|
||||||
|
type ListenerImpl interface {
|
||||||
|
// 初始化
|
||||||
|
Init()
|
||||||
|
|
||||||
|
// 监听
|
||||||
|
Serve() error
|
||||||
|
|
||||||
|
// 关闭
|
||||||
|
Close() error
|
||||||
|
}
|
||||||
@@ -4,6 +4,8 @@ import (
|
|||||||
"github.com/TeaOSLab/EdgeNode/internal/configs"
|
"github.com/TeaOSLab/EdgeNode/internal/configs"
|
||||||
"github.com/iwind/TeaGo/lists"
|
"github.com/iwind/TeaGo/lists"
|
||||||
"github.com/iwind/TeaGo/logs"
|
"github.com/iwind/TeaGo/logs"
|
||||||
|
"net/url"
|
||||||
|
"regexp"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -12,6 +14,7 @@ var sharedListenerManager = NewListenerManager()
|
|||||||
type ListenerManager struct {
|
type ListenerManager struct {
|
||||||
listenersMap map[string]*Listener // addr => *Listener
|
listenersMap map[string]*Listener // addr => *Listener
|
||||||
locker sync.Mutex
|
locker sync.Mutex
|
||||||
|
lastConfig *configs.NodeConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewListenerManager() *ListenerManager {
|
func NewListenerManager() *ListenerManager {
|
||||||
@@ -24,6 +27,18 @@ func (this *ListenerManager) Start(node *configs.NodeConfig) error {
|
|||||||
this.locker.Lock()
|
this.locker.Lock()
|
||||||
defer this.locker.Unlock()
|
defer this.locker.Unlock()
|
||||||
|
|
||||||
|
// 检查是否有变化
|
||||||
|
if this.lastConfig != nil && this.lastConfig.Version == node.Version {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
this.lastConfig = node
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
err := node.Init()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// 所有的新地址
|
// 所有的新地址
|
||||||
groupAddrs := []string{}
|
groupAddrs := []string{}
|
||||||
for _, group := range node.AvailableGroups() {
|
for _, group := range node.AvailableGroups() {
|
||||||
@@ -45,15 +60,16 @@ func (this *ListenerManager) Start(node *configs.NodeConfig) error {
|
|||||||
addr := group.FullAddr()
|
addr := group.FullAddr()
|
||||||
listener, ok := this.listenersMap[addr]
|
listener, ok := this.listenersMap[addr]
|
||||||
if ok {
|
if ok {
|
||||||
logs.Println("[LISTENER_MANAGER]reload '" + addr + "'")
|
logs.Println("[LISTENER_MANAGER]reload '" + this.prettyAddress(addr) + "'")
|
||||||
listener.Reload(group)
|
listener.Reload(group)
|
||||||
} else {
|
} else {
|
||||||
logs.Println("[LISTENER_MANAGER]listen '" + addr + "'")
|
logs.Println("[LISTENER_MANAGER]listen '" + this.prettyAddress(addr) + "'")
|
||||||
listener = NewListener()
|
listener = NewListener()
|
||||||
listener.Reload(group)
|
listener.Reload(group)
|
||||||
err := listener.Listen()
|
err := listener.Listen()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
logs.Println("[LISTENER_MANAGER]" + err.Error())
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
this.listenersMap[addr] = listener
|
this.listenersMap[addr] = listener
|
||||||
}
|
}
|
||||||
@@ -61,3 +77,14 @@ func (this *ListenerManager) Start(node *configs.NodeConfig) error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *ListenerManager) prettyAddress(addr string) string {
|
||||||
|
u, err := url.Parse(addr)
|
||||||
|
if err != nil {
|
||||||
|
return addr
|
||||||
|
}
|
||||||
|
if regexp.MustCompile(`^:\d+$`).MatchString(u.Host) {
|
||||||
|
u.Host = "*" + u.Host
|
||||||
|
}
|
||||||
|
return u.String()
|
||||||
|
}
|
||||||
|
|||||||
@@ -12,15 +12,29 @@ func TestListenerManager_Listen(t *testing.T) {
|
|||||||
{
|
{
|
||||||
IsOn: true,
|
IsOn: true,
|
||||||
HTTP: &configs.HTTPProtocolConfig{
|
HTTP: &configs.HTTPProtocolConfig{
|
||||||
|
BaseProtocol: configs.BaseProtocol{
|
||||||
IsOn: true,
|
IsOn: true,
|
||||||
Listen: []string{"127.0.0.1:1234"},
|
Listen: []*configs.NetworkAddressConfig{
|
||||||
|
{
|
||||||
|
Protocol: configs.ProtocolHTTP,
|
||||||
|
PortRange: "1234",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
IsOn: true,
|
IsOn: true,
|
||||||
HTTP: &configs.HTTPProtocolConfig{
|
HTTP: &configs.HTTPProtocolConfig{
|
||||||
|
BaseProtocol: configs.BaseProtocol{
|
||||||
IsOn: true,
|
IsOn: true,
|
||||||
Listen: []string{"127.0.0.1:1235"},
|
Listen: []*configs.NetworkAddressConfig{
|
||||||
|
{
|
||||||
|
Protocol: configs.ProtocolHTTP,
|
||||||
|
PortRange: "1235",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -34,15 +48,29 @@ func TestListenerManager_Listen(t *testing.T) {
|
|||||||
{
|
{
|
||||||
IsOn: true,
|
IsOn: true,
|
||||||
HTTP: &configs.HTTPProtocolConfig{
|
HTTP: &configs.HTTPProtocolConfig{
|
||||||
|
BaseProtocol: configs.BaseProtocol{
|
||||||
IsOn: true,
|
IsOn: true,
|
||||||
Listen: []string{"127.0.0.1:1234"},
|
Listen: []*configs.NetworkAddressConfig{
|
||||||
|
{
|
||||||
|
Protocol: configs.ProtocolHTTP,
|
||||||
|
PortRange: "1234",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
IsOn: true,
|
IsOn: true,
|
||||||
HTTP: &configs.HTTPProtocolConfig{
|
HTTP: &configs.HTTPProtocolConfig{
|
||||||
|
BaseProtocol: configs.BaseProtocol{
|
||||||
IsOn: true,
|
IsOn: true,
|
||||||
Listen: []string{"127.0.0.1:1236"},
|
Listen: []*configs.NetworkAddressConfig{
|
||||||
|
{
|
||||||
|
Protocol: configs.ProtocolHTTP,
|
||||||
|
PortRange: "1236",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
23
internal/nodes/listener_tcp.go
Normal file
23
internal/nodes/listener_tcp.go
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package nodes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/configs/serverconfigs"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TCPListener struct {
|
||||||
|
BaseListener
|
||||||
|
|
||||||
|
Group *serverconfigs.ServerGroup
|
||||||
|
Listener net.Listener
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *TCPListener) Serve() error {
|
||||||
|
// TODO
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *TCPListener) Close() error {
|
||||||
|
// TODO
|
||||||
|
return nil
|
||||||
|
}
|
||||||
23
internal/nodes/listener_udp.go
Normal file
23
internal/nodes/listener_udp.go
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package nodes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/configs/serverconfigs"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UDPListener struct {
|
||||||
|
BaseListener
|
||||||
|
|
||||||
|
Group *serverconfigs.ServerGroup
|
||||||
|
Listener net.Listener
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *UDPListener) Serve() error {
|
||||||
|
// TODO
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *UDPListener) Close() error {
|
||||||
|
// TODO
|
||||||
|
return nil
|
||||||
|
}
|
||||||
23
internal/nodes/listener_unix.go
Normal file
23
internal/nodes/listener_unix.go
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package nodes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/configs/serverconfigs"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UnixListener struct {
|
||||||
|
BaseListener
|
||||||
|
|
||||||
|
Group *serverconfigs.ServerGroup
|
||||||
|
Listener net.Listener
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *UnixListener) Serve() error {
|
||||||
|
// TODO
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *UnixListener) Close() error {
|
||||||
|
// TODO
|
||||||
|
return nil
|
||||||
|
}
|
||||||
9
internal/nodes/named_server.go
Normal file
9
internal/nodes/named_server.go
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package nodes
|
||||||
|
|
||||||
|
import "github.com/TeaOSLab/EdgeNode/internal/configs/serverconfigs"
|
||||||
|
|
||||||
|
// 域名和服务映射
|
||||||
|
type NamedServer struct {
|
||||||
|
Name string // 匹配后的域名
|
||||||
|
Server *serverconfigs.ServerConfig // 匹配后的服务配置
|
||||||
|
}
|
||||||
@@ -1,14 +1,20 @@
|
|||||||
package nodes
|
package nodes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/configs"
|
"github.com/TeaOSLab/EdgeNode/internal/configs"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/rpc/pb"
|
||||||
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
"github.com/TeaOSLab/EdgeNode/internal/utils"
|
||||||
"github.com/iwind/TeaGo/logs"
|
"github.com/iwind/TeaGo/logs"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var sharedNodeConfig *configs.NodeConfig = nil
|
|
||||||
var stop = make(chan bool)
|
var stop = make(chan bool)
|
||||||
|
var lastVersion = -1
|
||||||
|
|
||||||
|
// 节点
|
||||||
type Node struct {
|
type Node struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -17,13 +23,24 @@ func NewNode() *Node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (this *Node) Start() {
|
func (this *Node) Start() {
|
||||||
|
// 读取API配置
|
||||||
|
err := this.syncConfig(false)
|
||||||
|
if err != nil {
|
||||||
|
logs.Println(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// 启动同步计时器
|
||||||
|
this.startSyncTimer()
|
||||||
|
|
||||||
|
// 状态变更计时器
|
||||||
|
go NewNodeStatusExecutor().Listen()
|
||||||
|
|
||||||
// 读取配置
|
// 读取配置
|
||||||
nodeConfig, err := configs.SharedNodeConfig()
|
nodeConfig, err := configs.SharedNodeConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logs.Println("[NODE]start failed: read node config failed: " + err.Error())
|
logs.Println("[NODE]start failed: read node config failed: " + err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
sharedNodeConfig = nodeConfig
|
|
||||||
|
|
||||||
// 设置rlimit
|
// 设置rlimit
|
||||||
_ = utils.SetRLimit(1024 * 1024)
|
_ = utils.SetRLimit(1024 * 1024)
|
||||||
@@ -37,3 +54,59 @@ func (this *Node) Start() {
|
|||||||
// hold住进程
|
// hold住进程
|
||||||
<-stop
|
<-stop
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 读取API配置
|
||||||
|
func (this *Node) syncConfig(isFirstTime bool) error {
|
||||||
|
rpcClient, err := rpc.SharedRPC()
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("[NODE]create rpc client failed: " + err.Error())
|
||||||
|
}
|
||||||
|
configResp, err := rpcClient.NodeRPC().ComposeNodeConfig(rpcClient.Context(), &pb.ComposeNodeConfigRequest{})
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("[NODE]read config from rpc failed: " + err.Error())
|
||||||
|
}
|
||||||
|
configBytes := configResp.ConfigJSON
|
||||||
|
nodeConfig := &configs.NodeConfig{}
|
||||||
|
err = json.Unmarshal(configBytes, nodeConfig)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("[NODE]decode config failed: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// 写入到文件中
|
||||||
|
err = nodeConfig.Save()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果版本相同,则只是保存
|
||||||
|
if lastVersion == nodeConfig.Version {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
lastVersion = nodeConfig.Version
|
||||||
|
|
||||||
|
// 刷新配置
|
||||||
|
err = configs.ReloadNodeConfig()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isFirstTime {
|
||||||
|
return sharedListenerManager.Start(nodeConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 启动同步计时器
|
||||||
|
func (this *Node) startSyncTimer() {
|
||||||
|
ticker := time.NewTicker(60 * time.Second)
|
||||||
|
go func() {
|
||||||
|
for range ticker.C {
|
||||||
|
err := this.syncConfig(false)
|
||||||
|
if err != nil {
|
||||||
|
logs.Println("[NODE]sync config error: " + err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|||||||
24
internal/nodes/node_status.go
Normal file
24
internal/nodes/node_status.go
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package nodes
|
||||||
|
|
||||||
|
// 节点状态
|
||||||
|
type NodeStatus struct {
|
||||||
|
Version string `json:"version"`
|
||||||
|
Hostname string `json:"hostname"`
|
||||||
|
HostIP string `json:"hostIP"`
|
||||||
|
CPUUsage float64 `json:"cpuUsage"`
|
||||||
|
CPULogicalCount int `json:"cpuLogicalCount"`
|
||||||
|
CPUPhysicalCount int `json:"cpuPhysicalCount"`
|
||||||
|
MemoryUsage float64 `json:"memoryUsage"`
|
||||||
|
MemoryTotal uint64 `json:"memoryTotal"`
|
||||||
|
DiskUsage float64 `json:"diskUsage"`
|
||||||
|
DiskMaxUsage float64 `json:"diskMaxUsage"`
|
||||||
|
DiskMaxUsagePartition string `json:"diskMaxUsagePartition"`
|
||||||
|
DiskTotal uint64 `json:"diskTotal"`
|
||||||
|
UpdatedAt int64 `json:"updatedAt"`
|
||||||
|
Load1m float64 `json:"load1m"`
|
||||||
|
Load5m float64 `json:"load5m"`
|
||||||
|
Load15m float64 `json:"load15m"`
|
||||||
|
|
||||||
|
IsActive bool `json:"isActive"`
|
||||||
|
Error string `json:"error"`
|
||||||
|
}
|
||||||
172
internal/nodes/node_status_executor.go
Normal file
172
internal/nodes/node_status_executor.go
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
package nodes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/rpc"
|
||||||
|
"github.com/TeaOSLab/EdgeNode/internal/rpc/pb"
|
||||||
|
"github.com/iwind/TeaGo/lists"
|
||||||
|
"github.com/iwind/TeaGo/logs"
|
||||||
|
"github.com/shirou/gopsutil/cpu"
|
||||||
|
"github.com/shirou/gopsutil/disk"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type NodeStatusExecutor struct {
|
||||||
|
isFirstTime bool
|
||||||
|
|
||||||
|
cpuUpdatedTime time.Time
|
||||||
|
cpuLogicalCount int
|
||||||
|
cpuPhysicalCount int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNodeStatusExecutor() *NodeStatusExecutor {
|
||||||
|
return &NodeStatusExecutor{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *NodeStatusExecutor) Listen() {
|
||||||
|
this.isFirstTime = true
|
||||||
|
this.cpuUpdatedTime = time.Now()
|
||||||
|
this.update()
|
||||||
|
|
||||||
|
ticker := time.NewTicker(60 * time.Second)
|
||||||
|
for range ticker.C {
|
||||||
|
this.isFirstTime = false
|
||||||
|
this.update()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *NodeStatusExecutor) update() {
|
||||||
|
status := &NodeStatus{}
|
||||||
|
status.Version = teaconst.Version
|
||||||
|
status.IsActive = true
|
||||||
|
|
||||||
|
hostname, _ := os.Hostname()
|
||||||
|
status.Hostname = hostname
|
||||||
|
|
||||||
|
this.updateCPU(status)
|
||||||
|
this.updateMem(status)
|
||||||
|
this.updateLoad(status)
|
||||||
|
this.updateDisk(status)
|
||||||
|
status.UpdatedAt = time.Now().Unix()
|
||||||
|
|
||||||
|
// 发送数据
|
||||||
|
jsonData, err := json.Marshal(status)
|
||||||
|
if err != nil {
|
||||||
|
logs.Println("[NODE]serial NodeStatus fail: " + err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rpcClient, err := rpc.SharedRPC()
|
||||||
|
if err != nil {
|
||||||
|
logs.Println("[NODE]failed to open rpc: " + err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, err = rpcClient.NodeRPC().UpdateNodeStatus(rpcClient.Context(), &pb.UpdateNodeStatusRequest{
|
||||||
|
StatusJSON: jsonData,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
logs.Println("[NODE]rpc UpdateNodeStatus() failed: " + err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新CPU
|
||||||
|
func (this *NodeStatusExecutor) updateCPU(status *NodeStatus) {
|
||||||
|
duration := time.Duration(0)
|
||||||
|
if this.isFirstTime {
|
||||||
|
duration = 100 * time.Millisecond
|
||||||
|
}
|
||||||
|
percents, err := cpu.Percent(duration, false)
|
||||||
|
if err != nil {
|
||||||
|
status.Error = err.Error()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(percents) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
status.CPUUsage = percents[0] / 100
|
||||||
|
|
||||||
|
if time.Since(this.cpuUpdatedTime) > 300*time.Second { // 每隔5分钟才会更新一次
|
||||||
|
this.cpuUpdatedTime = time.Now()
|
||||||
|
|
||||||
|
status.CPULogicalCount, err = cpu.Counts(true)
|
||||||
|
if err != nil {
|
||||||
|
status.Error = err.Error()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
status.CPUPhysicalCount, err = cpu.Counts(false)
|
||||||
|
if err != nil {
|
||||||
|
status.Error = err.Error()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.cpuLogicalCount = status.CPULogicalCount
|
||||||
|
this.cpuPhysicalCount = status.CPUPhysicalCount
|
||||||
|
} else {
|
||||||
|
status.CPULogicalCount = this.cpuLogicalCount
|
||||||
|
status.CPUPhysicalCount = this.cpuPhysicalCount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新硬盘
|
||||||
|
func (this *NodeStatusExecutor) updateDisk(status *NodeStatus) {
|
||||||
|
partitions, err := disk.Partitions(false)
|
||||||
|
if err != nil {
|
||||||
|
logs.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
lists.Sort(partitions, func(i int, j int) bool {
|
||||||
|
p1 := partitions[i]
|
||||||
|
p2 := partitions[j]
|
||||||
|
return p1.Mountpoint > p2.Mountpoint
|
||||||
|
})
|
||||||
|
|
||||||
|
// 当前TeaWeb所在的fs
|
||||||
|
rootFS := ""
|
||||||
|
rootTotal := uint64(0)
|
||||||
|
if lists.ContainsString([]string{"darwin", "linux", "freebsd"}, runtime.GOOS) {
|
||||||
|
for _, p := range partitions {
|
||||||
|
if p.Mountpoint == "/" {
|
||||||
|
rootFS = p.Fstype
|
||||||
|
usage, _ := disk.Usage(p.Mountpoint)
|
||||||
|
if usage != nil {
|
||||||
|
rootTotal = usage.Total
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
total := rootTotal
|
||||||
|
totalUsage := uint64(0)
|
||||||
|
maxUsage := float64(0)
|
||||||
|
for _, partition := range partitions {
|
||||||
|
if runtime.GOOS != "windows" && !strings.Contains(partition.Device, "/") && !strings.Contains(partition.Device, "\\") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 跳过不同fs的
|
||||||
|
if len(rootFS) > 0 && rootFS != partition.Fstype {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
usage, err := disk.Usage(partition.Mountpoint)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if partition.Mountpoint != "/" && (usage.Total != rootTotal || total == 0) {
|
||||||
|
total += usage.Total
|
||||||
|
}
|
||||||
|
totalUsage += usage.Used
|
||||||
|
if usage.UsedPercent >= maxUsage {
|
||||||
|
maxUsage = usage.UsedPercent
|
||||||
|
status.DiskMaxUsagePartition = partition.Mountpoint
|
||||||
|
}
|
||||||
|
}
|
||||||
|
status.DiskTotal = total
|
||||||
|
status.DiskUsage = float64(totalUsage) / float64(total)
|
||||||
|
status.DiskMaxUsage = maxUsage / 100
|
||||||
|
}
|
||||||
40
internal/nodes/node_status_executor_unix.go
Normal file
40
internal/nodes/node_status_executor_unix.go
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package nodes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/shirou/gopsutil/load"
|
||||||
|
"github.com/shirou/gopsutil/mem"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 更新内存
|
||||||
|
func (this *NodeStatusExecutor) updateMem(status *NodeStatus) {
|
||||||
|
stat, err := mem.VirtualMemory()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重新计算内存
|
||||||
|
if stat.Total > 0 {
|
||||||
|
stat.Used = stat.Total - stat.Free - stat.Buffers - stat.Cached
|
||||||
|
status.MemoryUsage = float64(stat.Used) / float64(stat.Total)
|
||||||
|
}
|
||||||
|
|
||||||
|
status.MemoryTotal = stat.Total
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新负载
|
||||||
|
func (this *NodeStatusExecutor) updateLoad(status *NodeStatus) {
|
||||||
|
stat, err := load.Avg()
|
||||||
|
if err != nil {
|
||||||
|
status.Error = err.Error()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if stat == nil {
|
||||||
|
status.Error = "load is nil"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
status.Load1m = stat.Load1
|
||||||
|
status.Load5m = stat.Load5
|
||||||
|
status.Load15m = stat.Load15
|
||||||
|
}
|
||||||
101
internal/nodes/node_status_executor_windows.go
Normal file
101
internal/nodes/node_status_executor_windows.go
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
// +build windows
|
||||||
|
|
||||||
|
package agent
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/shirou/gopsutil/cpu"
|
||||||
|
"github.com/shirou/gopsutil/mem"
|
||||||
|
"math"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type WindowsLoadValue struct {
|
||||||
|
Timestamp int64
|
||||||
|
Value int
|
||||||
|
}
|
||||||
|
|
||||||
|
var windowsLoadValues = []*WindowsLoadValue{}
|
||||||
|
var windowsLoadLocker = &sync.Mutex{}
|
||||||
|
|
||||||
|
// 更新内存
|
||||||
|
func (this *NodeStatusExecutor) updateMem(status *NodeStatus) {
|
||||||
|
stat, err := mem.VirtualMemory()
|
||||||
|
if err != nil {
|
||||||
|
status.Error = err.Error()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
status.MemoryUsage = stat.UsedPercent
|
||||||
|
status.MemoryTotal = stat.Total
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新负载
|
||||||
|
func (this *NodeStatusExecutor) updateLoad(status *NodeStatus) {
|
||||||
|
timestamp := time.Now().Unix()
|
||||||
|
|
||||||
|
currentLoad := 0
|
||||||
|
info, err := cpu.ProcInfo()
|
||||||
|
if err == nil && len(info) > 0 && info[0].ProcessorQueueLength < 1000 {
|
||||||
|
currentLoad = int(info[0].ProcessorQueueLength)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除15分钟之前的数据
|
||||||
|
windowsLoadLocker.Lock()
|
||||||
|
result := []*WindowsLoadValue{}
|
||||||
|
for _, v := range windowsLoadValues {
|
||||||
|
if timestamp-v.Timestamp > 15*60 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
result = append(result, v)
|
||||||
|
}
|
||||||
|
result = append(result, &WindowsLoadValue{
|
||||||
|
Timestamp: timestamp,
|
||||||
|
Value: currentLoad,
|
||||||
|
})
|
||||||
|
windowsLoadValues = result
|
||||||
|
|
||||||
|
total1 := 0
|
||||||
|
count1 := 0
|
||||||
|
total5 := 0
|
||||||
|
count5 := 0
|
||||||
|
total15 := 0
|
||||||
|
count15 := 0
|
||||||
|
for _, v := range result {
|
||||||
|
if timestamp-v.Timestamp <= 60 {
|
||||||
|
total1 += v.Value
|
||||||
|
count1++
|
||||||
|
}
|
||||||
|
|
||||||
|
if timestamp-v.Timestamp <= 300 {
|
||||||
|
total5 += v.Value
|
||||||
|
count5++
|
||||||
|
}
|
||||||
|
|
||||||
|
total15 += v.Value
|
||||||
|
count15++
|
||||||
|
}
|
||||||
|
|
||||||
|
load1 := float64(0)
|
||||||
|
load5 := float64(0)
|
||||||
|
load15 := float64(0)
|
||||||
|
if count1 > 0 {
|
||||||
|
load1 = math.Round(float64(total1*100)/float64(count1)) / 100
|
||||||
|
}
|
||||||
|
if count5 > 0 {
|
||||||
|
load5 = math.Round(float64(total5*100)/float64(count5)) / 100
|
||||||
|
}
|
||||||
|
if count15 > 0 {
|
||||||
|
load15 = math.Round(float64(total15*100)/float64(count15)) / 100
|
||||||
|
}
|
||||||
|
|
||||||
|
windowsLoadLocker.Unlock()
|
||||||
|
|
||||||
|
// 在老Windows上不显示错误
|
||||||
|
if err == context.DeadlineExceeded {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
status.Load1m = load1
|
||||||
|
status.Load5m = load5
|
||||||
|
status.Load15m = load15
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user