mirror of
				https://github.com/TeaOSLab/EdgeNode.git
				synced 2025-11-04 16:00:25 +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:
 | 
			
		||||
  endpoints: [ "127.0.0.1:8003" ]
 | 
			
		||||
  nodeId: ""
 | 
			
		||||
  secret: ""
 | 
			
		||||
  endpoints: [ ${endpoints} ]
 | 
			
		||||
nodeId: "${nodeId}"
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeNode/internal/apps"
 | 
			
		||||
	teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeNode/internal/nodes"
 | 
			
		||||
	_ "github.com/iwind/TeaGo/bootstrap"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
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.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
 | 
			
		||||
 | 
			
		||||
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/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/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=
 | 
			
		||||
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/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/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 | 
			
		||||
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/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.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/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.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/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-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=
 | 
			
		||||
@@ -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.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
 | 
			
		||||
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/iwind/TeaGo v0.0.0-20200722010955-47dd648dc761 h1:70Iaf6iVF4CiH1P5Au3hqile/2Ea0XIkR4SPpdiaKI0=
 | 
			
		||||
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 h1:V7HA0wUOdmZbXJTVpiUEvSD4ARKHwMLMmiCccfkqf24=
 | 
			
		||||
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/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
 | 
			
		||||
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/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
 | 
			
		||||
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.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 | 
			
		||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
 | 
			
		||||
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
 | 
			
		||||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 | 
			
		||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
 | 
			
		||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
 | 
			
		||||
github.com/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/go.mod h1:YARuvh7BUWHNhzDq2OM5tzR2RiCcN2D7sapiKyCel/M=
 | 
			
		||||
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-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-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-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
 | 
			
		||||
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-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-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/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/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
 | 
			
		||||
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-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-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-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-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-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
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-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-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/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
			
		||||
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/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-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/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
 | 
			
		||||
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/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
 | 
			
		||||
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/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
			
		||||
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/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.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
			
		||||
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.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-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 {
 | 
			
		||||
		Endpoints []string `yaml:"endpoints"`
 | 
			
		||||
	} `yaml:"rpc"`
 | 
			
		||||
	NodeId string `yaml:"nodeId"`
 | 
			
		||||
	Secret string `yaml:"secret"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func LoadAPIConfig() (*APIConfig, error) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
package configs
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/TeaOSLab/EdgeNode/internal/configs/serverconfigs"
 | 
			
		||||
	"github.com/go-yaml/yaml"
 | 
			
		||||
	"github.com/iwind/TeaGo/Tea"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
@@ -9,10 +10,13 @@ import (
 | 
			
		||||
var sharedNodeConfig *NodeConfig = nil
 | 
			
		||||
 | 
			
		||||
type NodeConfig struct {
 | 
			
		||||
	Id      string          `yaml:"id"`
 | 
			
		||||
	Servers []*ServerConfig `yaml:"servers"`
 | 
			
		||||
	Id      string                        `yaml:"id" json:"id"`
 | 
			
		||||
	IsOn    bool                          `yaml:"isOn" json:"isOn"`
 | 
			
		||||
	Servers []*serverconfigs.ServerConfig `yaml:"servers" json:"servers"`
 | 
			
		||||
	Version int                           `yaml:"version" json:"version"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 取得当前节点配置单例
 | 
			
		||||
func SharedNodeConfig() (*NodeConfig, error) {
 | 
			
		||||
	sharedLocker.Lock()
 | 
			
		||||
	defer sharedLocker.Unlock()
 | 
			
		||||
@@ -36,9 +40,19 @@ func SharedNodeConfig() (*NodeConfig, error) {
 | 
			
		||||
	return config, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 刷新当前节点配置
 | 
			
		||||
func ReloadNodeConfig() error {
 | 
			
		||||
	sharedLocker.Lock()
 | 
			
		||||
	sharedNodeConfig = nil
 | 
			
		||||
	sharedLocker.Unlock()
 | 
			
		||||
 | 
			
		||||
	_, err := SharedNodeConfig()
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 根据网络地址和协议分组
 | 
			
		||||
func (this *NodeConfig) AvailableGroups() []*ServerGroup {
 | 
			
		||||
	groupMapping := map[string]*ServerGroup{} // protocol://addr => Server Group
 | 
			
		||||
func (this *NodeConfig) AvailableGroups() []*serverconfigs.ServerGroup {
 | 
			
		||||
	groupMapping := map[string]*serverconfigs.ServerGroup{} // protocol://addr => Server Group
 | 
			
		||||
	for _, server := range this.Servers {
 | 
			
		||||
		if !server.IsOn {
 | 
			
		||||
			continue
 | 
			
		||||
@@ -48,15 +62,39 @@ func (this *NodeConfig) AvailableGroups() []*ServerGroup {
 | 
			
		||||
			if ok {
 | 
			
		||||
				group.Add(server)
 | 
			
		||||
			} else {
 | 
			
		||||
				group = NewServerGroup(addr)
 | 
			
		||||
				group = serverconfigs.NewServerGroup(addr)
 | 
			
		||||
				group.Add(server)
 | 
			
		||||
			}
 | 
			
		||||
			groupMapping[addr] = group
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	result := []*ServerGroup{}
 | 
			
		||||
	result := []*serverconfigs.ServerGroup{}
 | 
			
		||||
	for _, group := range groupMapping {
 | 
			
		||||
		result = append(result, group)
 | 
			
		||||
	}
 | 
			
		||||
	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
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/TeaOSLab/EdgeNode/internal/configs/serverconfigs"
 | 
			
		||||
	_ "github.com/iwind/TeaGo/bootstrap"
 | 
			
		||||
	"github.com/iwind/TeaGo/logs"
 | 
			
		||||
	"testing"
 | 
			
		||||
@@ -27,18 +28,37 @@ func TestSharedNodeConfig(t *testing.T) {
 | 
			
		||||
 | 
			
		||||
func TestNodeConfig_Groups(t *testing.T) {
 | 
			
		||||
	config := &NodeConfig{}
 | 
			
		||||
	config.Servers = []*ServerConfig{
 | 
			
		||||
	config.Servers = []*serverconfigs.ServerConfig{
 | 
			
		||||
		{
 | 
			
		||||
			IsOn: true,
 | 
			
		||||
			HTTP: &HTTPProtocolConfig{
 | 
			
		||||
			HTTP: &serverconfigs.HTTPProtocolConfig{
 | 
			
		||||
				BaseProtocol: serverconfigs.BaseProtocol{
 | 
			
		||||
					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,
 | 
			
		||||
				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 {
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										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 {
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
package configs
 | 
			
		||||
package serverconfigs
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										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 (
 | 
			
		||||
	"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 (
 | 
			
		||||
	"context"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeNode/internal/configs"
 | 
			
		||||
	"github.com/TeaOSLab/EdgeNode/internal/configs/serverconfigs"
 | 
			
		||||
	"github.com/iwind/TeaGo/logs"
 | 
			
		||||
	"net"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"sync"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Listener struct {
 | 
			
		||||
	group       *configs.ServerGroup
 | 
			
		||||
	group       *serverconfigs.ServerGroup
 | 
			
		||||
	isListening bool
 | 
			
		||||
	listener    interface{} // 监听器
 | 
			
		||||
	listener    ListenerImpl // 监听器
 | 
			
		||||
 | 
			
		||||
	locker sync.RWMutex
 | 
			
		||||
}
 | 
			
		||||
@@ -22,7 +21,7 @@ func NewListener() *Listener {
 | 
			
		||||
	return &Listener{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *Listener) Reload(group *configs.ServerGroup) {
 | 
			
		||||
func (this *Listener) Reload(group *serverconfigs.ServerGroup) {
 | 
			
		||||
	this.locker.Lock()
 | 
			
		||||
	defer this.locker.Unlock()
 | 
			
		||||
	this.group = group
 | 
			
		||||
@@ -40,78 +39,67 @@ func (this *Listener) Listen() error {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	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 {
 | 
			
		||||
	// TODO 需要实现
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *Listener) listenHTTP() error {
 | 
			
		||||
	listener, err := this.createListener()
 | 
			
		||||
	netListener, err := this.createListener()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	mux := http.NewServeMux()
 | 
			
		||||
	mux.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
 | 
			
		||||
		_, _ = writer.Write([]byte("Hello, World"))
 | 
			
		||||
	})
 | 
			
		||||
	server := &http.Server{
 | 
			
		||||
		Addr:    this.group.Addr(),
 | 
			
		||||
		Handler: mux,
 | 
			
		||||
	switch protocol {
 | 
			
		||||
	case serverconfigs.ProtocolHTTP, serverconfigs.ProtocolHTTP4, serverconfigs.ProtocolHTTP6:
 | 
			
		||||
		this.listener = &HTTPListener{
 | 
			
		||||
			Group:    this.group,
 | 
			
		||||
			Listener: netListener,
 | 
			
		||||
		}
 | 
			
		||||
	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() {
 | 
			
		||||
		err = server.Serve(listener)
 | 
			
		||||
		err := this.listener.Serve()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			logs.Println("[LISTENER]" + err.Error())
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (this *Listener) ListenHTTPS() error {
 | 
			
		||||
	// TODO 需要实现
 | 
			
		||||
func (this *Listener) Close() error {
 | 
			
		||||
	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 this.listener.Close()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 创建监听器
 | 
			
		||||
func (this *Listener) createListener() (net.Listener, error) {
 | 
			
		||||
	listenConfig := net.ListenConfig{
 | 
			
		||||
		Control:   nil,
 | 
			
		||||
@@ -119,9 +107,9 @@ func (this *Listener) createListener() (net.Listener, error) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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())
 | 
			
		||||
	case configs.ProtocolHTTP6, configs.ProtocolHTTPS6, configs.ProtocolTLS6:
 | 
			
		||||
	case serverconfigs.ProtocolHTTP6, serverconfigs.ProtocolHTTPS6, serverconfigs.ProtocolTLS6:
 | 
			
		||||
		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/iwind/TeaGo/lists"
 | 
			
		||||
	"github.com/iwind/TeaGo/logs"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"sync"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -12,6 +14,7 @@ var sharedListenerManager = NewListenerManager()
 | 
			
		||||
type ListenerManager struct {
 | 
			
		||||
	listenersMap map[string]*Listener // addr => *Listener
 | 
			
		||||
	locker       sync.Mutex
 | 
			
		||||
	lastConfig   *configs.NodeConfig
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewListenerManager() *ListenerManager {
 | 
			
		||||
@@ -24,6 +27,18 @@ func (this *ListenerManager) Start(node *configs.NodeConfig) error {
 | 
			
		||||
	this.locker.Lock()
 | 
			
		||||
	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{}
 | 
			
		||||
	for _, group := range node.AvailableGroups() {
 | 
			
		||||
@@ -45,15 +60,16 @@ func (this *ListenerManager) Start(node *configs.NodeConfig) error {
 | 
			
		||||
		addr := group.FullAddr()
 | 
			
		||||
		listener, ok := this.listenersMap[addr]
 | 
			
		||||
		if ok {
 | 
			
		||||
			logs.Println("[LISTENER_MANAGER]reload '" + addr + "'")
 | 
			
		||||
			logs.Println("[LISTENER_MANAGER]reload '" + this.prettyAddress(addr) + "'")
 | 
			
		||||
			listener.Reload(group)
 | 
			
		||||
		} else {
 | 
			
		||||
			logs.Println("[LISTENER_MANAGER]listen '" + addr + "'")
 | 
			
		||||
			logs.Println("[LISTENER_MANAGER]listen '" + this.prettyAddress(addr) + "'")
 | 
			
		||||
			listener = NewListener()
 | 
			
		||||
			listener.Reload(group)
 | 
			
		||||
			err := listener.Listen()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
				logs.Println("[LISTENER_MANAGER]" + err.Error())
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			this.listenersMap[addr] = listener
 | 
			
		||||
		}
 | 
			
		||||
@@ -61,3 +77,14 @@ func (this *ListenerManager) Start(node *configs.NodeConfig) error {
 | 
			
		||||
 | 
			
		||||
	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,
 | 
			
		||||
				HTTP: &configs.HTTPProtocolConfig{
 | 
			
		||||
					BaseProtocol: configs.BaseProtocol{
 | 
			
		||||
						IsOn: true,
 | 
			
		||||
					Listen: []string{"127.0.0.1:1234"},
 | 
			
		||||
						Listen: []*configs.NetworkAddressConfig{
 | 
			
		||||
							{
 | 
			
		||||
								Protocol:  configs.ProtocolHTTP,
 | 
			
		||||
								PortRange: "1234",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				IsOn: true,
 | 
			
		||||
				HTTP: &configs.HTTPProtocolConfig{
 | 
			
		||||
					BaseProtocol: configs.BaseProtocol{
 | 
			
		||||
						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,
 | 
			
		||||
				HTTP: &configs.HTTPProtocolConfig{
 | 
			
		||||
					BaseProtocol: configs.BaseProtocol{
 | 
			
		||||
						IsOn: true,
 | 
			
		||||
					Listen: []string{"127.0.0.1:1234"},
 | 
			
		||||
						Listen: []*configs.NetworkAddressConfig{
 | 
			
		||||
							{
 | 
			
		||||
								Protocol:  configs.ProtocolHTTP,
 | 
			
		||||
								PortRange: "1234",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				IsOn: true,
 | 
			
		||||
				HTTP: &configs.HTTPProtocolConfig{
 | 
			
		||||
					BaseProtocol: configs.BaseProtocol{
 | 
			
		||||
						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
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"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/iwind/TeaGo/logs"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var sharedNodeConfig *configs.NodeConfig = nil
 | 
			
		||||
var stop = make(chan bool)
 | 
			
		||||
var lastVersion = -1
 | 
			
		||||
 | 
			
		||||
// 节点
 | 
			
		||||
type Node struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -17,13 +23,24 @@ func NewNode() *Node {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logs.Println("[NODE]start failed: read node config failed: " + err.Error())
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	sharedNodeConfig = nodeConfig
 | 
			
		||||
 | 
			
		||||
	// 设置rlimit
 | 
			
		||||
	_ = utils.SetRLimit(1024 * 1024)
 | 
			
		||||
@@ -37,3 +54,59 @@ func (this *Node) Start() {
 | 
			
		||||
	// hold住进程
 | 
			
		||||
	<-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