change and correct
This commit is contained in:
parent
ea38f85fb7
commit
0a667bea96
17
README.md
17
README.md
|
@ -116,11 +116,7 @@ paths 配置
|
|||
"servers":[{
|
||||
"port" : 8080,
|
||||
"name":"test",
|
||||
"health_check": {
|
||||
"interval": "10s", // 每10秒检查一次
|
||||
"timeout": "5s", // 每次检查5秒超时
|
||||
"retries": 3 // 失败3次才认为是不健康
|
||||
},
|
||||
|
||||
"paths":[{
|
||||
"path": "/",
|
||||
"root": "/home/kingecg/code/gohttp/public/",
|
||||
|
@ -133,7 +129,12 @@ paths 配置
|
|||
"HeaderOrigin",
|
||||
"Path /ws /",
|
||||
"RemoveCookie token"
|
||||
]
|
||||
],
|
||||
"health_check": {
|
||||
"interval": "10s", // 每10秒检查一次
|
||||
"timeout": "5s", // 每次检查5秒超时
|
||||
"retries": 3 // 失败3次才认为是不健康
|
||||
},
|
||||
}]
|
||||
}]
|
||||
}
|
||||
|
@ -158,6 +159,10 @@ paths 配置
|
|||
- 返回的response中设置header
|
||||
- 反向代理时,修改发送到上游服务器的请求
|
||||
|
||||
命名规则:
|
||||
模块支持的指令,以模块名作为前缀,如:Proxy_Path 是反向代理模块的Path指令。
|
||||
没有前缀的,是全局指令,如:Set-Header 是设置header的指令。
|
||||
|
||||
## Packages
|
||||
|
||||
### Server
|
||||
|
|
Binary file not shown.
|
@ -77,16 +77,53 @@ func getStatus(w http.ResponseWriter, r *http.Request) {
|
|||
w.Write(server.NewSuccessResult(ret))
|
||||
}
|
||||
|
||||
func stop(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
ctxData := ctx.Value(server.RequestCtxKey("data")).(map[string]interface{})
|
||||
name, ok := ctxData["name"]
|
||||
if ok {
|
||||
serverConf := model.GetServerConfig(name.(string))
|
||||
server.StopServer(name.(string), serverConf.Port)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
data := "stopped"
|
||||
w.Write(server.NewSuccessResult(data))
|
||||
return
|
||||
}
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
|
||||
func start(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
ctxData := ctx.Value(server.RequestCtxKey("data")).(map[string]interface{})
|
||||
name, ok := ctxData["name"]
|
||||
if ok {
|
||||
serverConf := model.GetServerConfig(name.(string))
|
||||
server.StartServer(name.(string), serverConf.Port)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
data := "started"
|
||||
w.Write(server.NewSuccessResult(data))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var AdminServerMux *server.RestMux
|
||||
|
||||
func init() {
|
||||
func InitAdminApi(conf *model.HttpServerConfig) {
|
||||
|
||||
AdminServerMux = server.NewRestMux("/api")
|
||||
AdminServerMux.Use(server.JwtAuth)
|
||||
if conf.AuthType == "jwt" {
|
||||
AdminServerMux.Use(server.JwtAuth)
|
||||
} else if conf.AuthType == "basic" {
|
||||
AdminServerMux.Use(server.BasicAuth)
|
||||
}
|
||||
// AdminServerMux.Use(server.JwtAuth)
|
||||
AdminServerMux.HandleFunc("GET", "/about", http.HandlerFunc(about))
|
||||
postConfigRoute := AdminServerMux.HandleFunc("POST", "/config", http.HandlerFunc(setConfig))
|
||||
postConfigRoute.Add(server.Parse[model.HttpServerConfig])
|
||||
AdminServerMux.HandleFunc("GET", "/config/:id", http.HandlerFunc(getServerConfigure))
|
||||
AdminServerMux.HandleFunc("GET", "/status", http.HandlerFunc(getStatus))
|
||||
AdminServerMux.HandleFunc("Post", "/stop/:name", http.HandlerFunc(stop))
|
||||
AdminServerMux.HandleFunc("Post", "/start/:name", http.HandlerFunc(start))
|
||||
loginRoute := AdminServerMux.HandleFunc("POST", "/login", http.HandlerFunc(login))
|
||||
loginRoute.Add(server.Parse[LoginModel])
|
||||
// AdminServerMux.Use(server.BasicAuth)
|
||||
|
|
179
config.json
179
config.json
|
@ -23,6 +23,7 @@
|
|||
},
|
||||
"admin": {
|
||||
"name": "admin",
|
||||
"server":"admin",
|
||||
"port": 8088,
|
||||
"username": "admin",
|
||||
"password": "admin",
|
||||
|
@ -40,183 +41,15 @@
|
|||
]
|
||||
},
|
||||
"servers": [
|
||||
|
||||
{
|
||||
"port": 3000,
|
||||
"name": "cloudops",
|
||||
|
||||
"name": "teststatic",
|
||||
"server":"www.teststatic.com",
|
||||
"port": 8088,
|
||||
"paths": [
|
||||
|
||||
{
|
||||
"path": "/static",
|
||||
"root": "/home/kingecg/work/angular13/cloudops/static",
|
||||
"default": "index.html"
|
||||
},
|
||||
{
|
||||
"path": "/",
|
||||
"upstreams": [
|
||||
"http://localhost:4200"
|
||||
],
|
||||
"directives": [
|
||||
"HostSchemas $target"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/ssologin",
|
||||
"upstreams": [
|
||||
"http://localhost:4200"
|
||||
],
|
||||
"directives": [
|
||||
"HostSchemas $target"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/themes",
|
||||
"upstreams": [
|
||||
"http://192.168.12.157:9880"
|
||||
],
|
||||
"directives": [
|
||||
"HostSchemas $target"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/security",
|
||||
"upstreams": [
|
||||
"http://192.168.12.157:9880"
|
||||
],
|
||||
"directives": [
|
||||
"HostSchemas $target"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/v1/relational/assets",
|
||||
"upstreams": [
|
||||
"http://192.168.12.157:9880"
|
||||
],
|
||||
"directives": [
|
||||
"HostSchemas $target"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/platform/version",
|
||||
"upstreams": [
|
||||
"http://192.168.12.157:9880"
|
||||
],
|
||||
"directives": [
|
||||
"HostSchemas $target"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/v1/patrolrecords",
|
||||
"upstreams": [
|
||||
"http://192.168.12.157:9880"
|
||||
],
|
||||
"directives": [
|
||||
"HostSchemas $target"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/v1/standingbook",
|
||||
"upstreams": [
|
||||
"http://192.168.12.157:9880"
|
||||
],
|
||||
"directives": [
|
||||
"HostSchemas $target"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/v1/resource",
|
||||
"upstreams": [
|
||||
"http://192.168.12.157:9880"
|
||||
],
|
||||
"directives": [
|
||||
"HostSchemas $target"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/v1/config",
|
||||
"upstreams": [
|
||||
"http://192.168.12.157:9880"
|
||||
],
|
||||
"directives": [
|
||||
"HostSchemas $target"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/v1/workflow",
|
||||
"upstreams": [
|
||||
"http://192.168.12.157:9880"
|
||||
],
|
||||
"directives": [
|
||||
"HostSchemas $target"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/token/check",
|
||||
"upstreams": [
|
||||
"http://192.168.12.157:9880"
|
||||
],
|
||||
"directives": [
|
||||
"HostSchemas $target"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/v1/messages",
|
||||
"upstreams": [
|
||||
"http://192.168.12.157:9880"
|
||||
],
|
||||
"directives": [
|
||||
"HostSchemas $target"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/cloudops-state/",
|
||||
"upstreams": [
|
||||
"http://192.168.12.157:9880"
|
||||
],
|
||||
"directives": [
|
||||
"HostSchemas $target"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/socket.io/",
|
||||
"upstreams": [
|
||||
"http://192.168.12.157:9880"
|
||||
],
|
||||
"directives": [
|
||||
"HostSchemas $target"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/v1/stream",
|
||||
"upstreams": [
|
||||
"http://192.168.12.157:9880"
|
||||
],
|
||||
"directives": [
|
||||
"HostSchemas $target"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/thing",
|
||||
"upstreams": [
|
||||
"http://192.168.12.204:1662"
|
||||
],
|
||||
"directives": [
|
||||
"HostSchemas $target",
|
||||
"HeaderOrigin",
|
||||
"RemoveCookie token"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path":"/ib",
|
||||
"upstreams": [
|
||||
"http://localhost:8080"
|
||||
],
|
||||
"directives": [
|
||||
"HostSchemas $target",
|
||||
"HeaderOrigin",
|
||||
"RemoveCookie token"
|
||||
]
|
||||
"root": "./example",
|
||||
"default": "index.html"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -0,0 +1,224 @@
|
|||
{
|
||||
"logging": {
|
||||
"appenders": {
|
||||
"out": {
|
||||
"type": "file",
|
||||
"options": {
|
||||
"file": "gohttpd.log"
|
||||
}
|
||||
},
|
||||
"stdout": {
|
||||
"type": "console"
|
||||
}
|
||||
},
|
||||
"categories": {
|
||||
"default": {
|
||||
"appenders": [
|
||||
"out",
|
||||
"stdout"
|
||||
],
|
||||
"level": "debug"
|
||||
}
|
||||
}
|
||||
},
|
||||
"admin": {
|
||||
"name": "admin",
|
||||
"port": 8088,
|
||||
"username": "admin",
|
||||
"password": "admin",
|
||||
"directives": [
|
||||
"Set-Header Access-Control-Allow-Origin *",
|
||||
"Set-Header Access-Control-Allow-Methods GET, POST, PUT, DELETE, OPTIONS",
|
||||
"Set-Header Access-Control-Allow-Headers Content-Type, Authorization, Content-Length, X-Requested-With"
|
||||
],
|
||||
"paths": [
|
||||
{
|
||||
"path": "/",
|
||||
"root": "./adminui",
|
||||
"default": "index.html"
|
||||
}
|
||||
]
|
||||
},
|
||||
"servers": [
|
||||
|
||||
{
|
||||
"port": 3000,
|
||||
"name": "cloudops",
|
||||
|
||||
"paths": [
|
||||
|
||||
{
|
||||
"path": "/static",
|
||||
"root": "/home/kingecg/work/angular13/cloudops/static",
|
||||
"default": "index.html"
|
||||
},
|
||||
{
|
||||
"path": "/",
|
||||
"upstreams": [
|
||||
"http://localhost:4200"
|
||||
],
|
||||
"directives": [
|
||||
"HostSchemas $target"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/ssologin",
|
||||
"upstreams": [
|
||||
"http://localhost:4200"
|
||||
],
|
||||
"directives": [
|
||||
"HostSchemas $target"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/themes",
|
||||
"upstreams": [
|
||||
"http://192.168.12.157:9880"
|
||||
],
|
||||
"directives": [
|
||||
"HostSchemas $target"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/security",
|
||||
"upstreams": [
|
||||
"http://192.168.12.157:9880"
|
||||
],
|
||||
"directives": [
|
||||
"HostSchemas $target"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/v1/relational/assets",
|
||||
"upstreams": [
|
||||
"http://192.168.12.157:9880"
|
||||
],
|
||||
"directives": [
|
||||
"HostSchemas $target"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/platform/version",
|
||||
"upstreams": [
|
||||
"http://192.168.12.157:9880"
|
||||
],
|
||||
"directives": [
|
||||
"HostSchemas $target"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/v1/patrolrecords",
|
||||
"upstreams": [
|
||||
"http://192.168.12.157:9880"
|
||||
],
|
||||
"directives": [
|
||||
"HostSchemas $target"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/v1/standingbook",
|
||||
"upstreams": [
|
||||
"http://192.168.12.157:9880"
|
||||
],
|
||||
"directives": [
|
||||
"HostSchemas $target"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/v1/resource",
|
||||
"upstreams": [
|
||||
"http://192.168.12.157:9880"
|
||||
],
|
||||
"directives": [
|
||||
"HostSchemas $target"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/v1/config",
|
||||
"upstreams": [
|
||||
"http://192.168.12.157:9880"
|
||||
],
|
||||
"directives": [
|
||||
"HostSchemas $target"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/v1/workflow",
|
||||
"upstreams": [
|
||||
"http://192.168.12.157:9880"
|
||||
],
|
||||
"directives": [
|
||||
"HostSchemas $target"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/token/check",
|
||||
"upstreams": [
|
||||
"http://192.168.12.157:9880"
|
||||
],
|
||||
"directives": [
|
||||
"HostSchemas $target"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/v1/messages",
|
||||
"upstreams": [
|
||||
"http://192.168.12.157:9880"
|
||||
],
|
||||
"directives": [
|
||||
"HostSchemas $target"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/cloudops-state/",
|
||||
"upstreams": [
|
||||
"http://192.168.12.157:9880"
|
||||
],
|
||||
"directives": [
|
||||
"HostSchemas $target"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/socket.io/",
|
||||
"upstreams": [
|
||||
"http://192.168.12.157:9880"
|
||||
],
|
||||
"directives": [
|
||||
"HostSchemas $target"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/v1/stream",
|
||||
"upstreams": [
|
||||
"http://192.168.12.157:9880"
|
||||
],
|
||||
"directives": [
|
||||
"HostSchemas $target"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/thing",
|
||||
"upstreams": [
|
||||
"http://192.168.12.204:1662"
|
||||
],
|
||||
"directives": [
|
||||
"HostSchemas $target",
|
||||
"HeaderOrigin",
|
||||
"RemoveCookie token"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path":"/ib",
|
||||
"upstreams": [
|
||||
"http://localhost:8080"
|
||||
],
|
||||
"directives": [
|
||||
"HostSchemas $target",
|
||||
"HeaderOrigin",
|
||||
"RemoveCookie token"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head><body>
|
||||
<h1>Hello World!</h1>
|
||||
<p>Welcome to my website.</p>
|
||||
|
||||
</body></html>
|
|
@ -32,7 +32,8 @@ func (g *GoHttp) Start() {
|
|||
|
||||
// 设置管理员处理器并使用管理员服务配置
|
||||
adminHandler := server.NewServeMux(conf.Admin)
|
||||
adminHandler.Handle("/api/", http.StripPrefix("/api", admin.AdminServerMux))
|
||||
admin.InitAdminApi(conf.Admin)
|
||||
adminHandler.Handle("/api/", http.StripPrefix("/api", admin.AdminServerMux), []string{})
|
||||
|
||||
// 创建并启动管理员服务器
|
||||
g.makeServer(conf.Admin, adminHandler)
|
||||
|
|
1527
gohttpd.log
1527
gohttpd.log
File diff suppressed because it is too large
Load Diff
|
@ -62,7 +62,8 @@ func makeProxy(upstream string, path *model.HttpPath, index int) *httputil.Rever
|
|||
directiveHandlers := []func(r *http.Request){}
|
||||
if len(path.Directives) > 0 {
|
||||
for _, directive := range path.Directives {
|
||||
d := strings.Replace(string(directive), "$target", upstream, 1)
|
||||
ndirective := strings.TrimPrefix(directive, "Proxy_")
|
||||
d := strings.Replace(string(ndirective), "$target", upstream, 1)
|
||||
dh := GetUpdaterFn(d)
|
||||
if dh != nil {
|
||||
directiveHandlers = append(directiveHandlers, dh)
|
||||
|
@ -106,48 +107,45 @@ func NewProxyHandler(p *model.HttpPath) *ProxyHandler {
|
|||
}
|
||||
ph.proxy = make([]*httputil.ReverseProxy, upstreamCount)
|
||||
|
||||
// 从配置中获取健康检查参数,如果不存在则使用默认值
|
||||
var interval time.Duration = 10 * time.Second
|
||||
var timeout time.Duration = 5 * time.Second
|
||||
var retries int = 3
|
||||
|
||||
// 使用服务器配置中的健康检查参数
|
||||
config := model.GetConfig()
|
||||
if config != nil && config.Admin != nil && config.Admin.HealthCheck != nil {
|
||||
if config.Admin.HealthCheck.Interval != "" {
|
||||
var err error
|
||||
interval, err = time.ParseDuration(config.Admin.HealthCheck.Interval)
|
||||
if err != nil {
|
||||
interval = 10 * time.Second // 默认值
|
||||
}
|
||||
}
|
||||
if config.Admin.HealthCheck.Timeout != "" {
|
||||
var err error
|
||||
timeout, err = time.ParseDuration(config.Admin.HealthCheck.Timeout)
|
||||
if err != nil {
|
||||
timeout = 5 * time.Second // 默认值
|
||||
}
|
||||
}
|
||||
if config.Admin.HealthCheck.Retries > 0 {
|
||||
retries = config.Admin.HealthCheck.Retries
|
||||
}
|
||||
}
|
||||
|
||||
for index, upstream := range p.Upstreams {
|
||||
ph.proxy[index] = makeProxy(upstream, p, index)
|
||||
}
|
||||
|
||||
// 使用配置参数创建健康检查器
|
||||
ph.checker = healthcheck.NewHealthChecker(interval, timeout, retries)
|
||||
ph.checker.StartHealthCheck(ph.Upstreams, func(upstream string, healthy bool) {
|
||||
// 当上游服务器状态变化时的回调函数
|
||||
logger := gologger.GetLogger("Proxy")
|
||||
if !healthy {
|
||||
logger.Warn(fmt.Sprintf("Upstream %s is now unhealthy", upstream))
|
||||
} else {
|
||||
logger.Info(fmt.Sprintf("Upstream %s is now healthy", upstream))
|
||||
if len(p.Upstreams) > 1 && p.HealthCheck != nil {
|
||||
// 只有上游服务器数目大于1时才需要进行健康检查
|
||||
// 从配置中获取健康检查参数,如果不存在则使用默认值
|
||||
var interval time.Duration = 10 * time.Second
|
||||
var timeout time.Duration = 5 * time.Second
|
||||
var retries int = 3
|
||||
if p.HealthCheck.Interval != "" {
|
||||
var err error
|
||||
interval, err = time.ParseDuration(p.HealthCheck.Interval)
|
||||
if err != nil {
|
||||
interval = 10 * time.Second // 默认值
|
||||
}
|
||||
}
|
||||
})
|
||||
if p.HealthCheck.Timeout != "" {
|
||||
var err error
|
||||
timeout, err = time.ParseDuration(p.HealthCheck.Timeout)
|
||||
if err != nil {
|
||||
timeout = 5 * time.Second // 默认值
|
||||
}
|
||||
}
|
||||
if p.HealthCheck.Retries > 0 {
|
||||
retries = p.HealthCheck.Retries
|
||||
}
|
||||
// 使用配置参数创建健康检查器
|
||||
ph.checker = healthcheck.NewHealthChecker(interval, timeout, retries)
|
||||
ph.checker.StartHealthCheck(ph.Upstreams, func(upstream string, healthy bool) {
|
||||
// 当上游服务器状态变化时的回调函数
|
||||
logger := gologger.GetLogger("Proxy")
|
||||
if !healthy {
|
||||
logger.Warn(fmt.Sprintf("Upstream %s is now unhealthy", upstream))
|
||||
} else {
|
||||
logger.Info(fmt.Sprintf("Upstream %s is now healthy", upstream))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return ph
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,16 +12,17 @@ import (
|
|||
type HealthCheckConfig struct {
|
||||
Interval string `json:"interval,omitempty"` // 检查间隔时间
|
||||
Timeout string `json:"timeout,omitempty"` // 单次检查超时时间
|
||||
Retries int `json:"retries,omitempty"` // 健康检查失败重试次数
|
||||
Retries int `json:"retries,omitempty"` // 健康检查失败重试次数
|
||||
}
|
||||
|
||||
// HttpPath 定义HTTP路径配置
|
||||
type HttpPath struct {
|
||||
Path string `json:"path"`
|
||||
Root string `json:"root"`
|
||||
Default string `json:"default"`
|
||||
Upstreams []string `json:"upstreams"`
|
||||
Directives []string `json:"directives"`
|
||||
Path string `json:"path"`
|
||||
Root string `json:"root"`
|
||||
Default string `json:"default"`
|
||||
Upstreams []string `json:"upstreams"`
|
||||
Directives []string `json:"directives"`
|
||||
HealthCheck *HealthCheckConfig `json:"health_check,omitempty"`
|
||||
}
|
||||
|
||||
type HeaderValueConst string
|
||||
|
@ -32,10 +33,10 @@ const (
|
|||
|
||||
// HttpServerConfig 定义HTTP服务器配置
|
||||
type HttpServerConfig struct {
|
||||
Name string `json:"name"`
|
||||
ServerName string `json:"server"`
|
||||
Port int `json:"port"`
|
||||
Host string `json:"host"`
|
||||
Name string `json:"name"`
|
||||
ServerName string `json:"server"`
|
||||
Port int `json:"port"`
|
||||
Host string `json:"host"`
|
||||
Paths []HttpPath `json:"paths"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
|
@ -45,10 +46,10 @@ type HttpServerConfig struct {
|
|||
AuthType string `json:"auth_type"`
|
||||
Jwt *JwtConfig `json:"jwt"`
|
||||
// 健康检查配置
|
||||
HealthCheck *HealthCheckConfig `json:"health_check,omitempty"`
|
||||
|
||||
// 访问控制配置
|
||||
AllowIPs []string `json:"allow_ips,omitempty"` // 允许访问的IP地址列表
|
||||
DenyIPs []string `json:"deny_ips,omitempty"` // 禁止访问的IP地址列表
|
||||
AllowIPs []string `json:"allow_ips,omitempty"` // 允许访问的IP地址列表
|
||||
DenyIPs []string `json:"deny_ips,omitempty"` // 禁止访问的IP地址列表
|
||||
}
|
||||
|
||||
type JwtConfig struct {
|
||||
|
|
|
@ -71,7 +71,7 @@ var BasicAuthDirective Directive = func(args ...string) Middleware {
|
|||
var DirectiveMap = map[string]Directive{
|
||||
"Set-Header": Set_Header,
|
||||
"Add-Header": Add_Header,
|
||||
"Gzip_Response": Gzip_Response,
|
||||
"Gzip-Response": Gzip_Response,
|
||||
"Record-Access": DRecordAccess,
|
||||
"Jwt-Auth": JWTDirective,
|
||||
"Basic-Auth": BasicAuthDirective,
|
||||
|
|
|
@ -2,10 +2,12 @@ package server
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"bufio"
|
||||
"io"
|
||||
|
@ -30,10 +32,37 @@ func makeMatcher(name string, s *ServerListener) cmux.Matcher {
|
|||
}
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
Conf *model.HttpServerConfig
|
||||
*http.Server
|
||||
type sl struct {
|
||||
net.Listener
|
||||
closed bool
|
||||
}
|
||||
|
||||
func (sl *sl) Accept() (net.Conn, error) {
|
||||
if sl.closed {
|
||||
return nil, errors.New("closed")
|
||||
}
|
||||
|
||||
return sl.Listener.Accept()
|
||||
}
|
||||
|
||||
func (sl *sl) Close() error {
|
||||
sl.closed = true
|
||||
return nil
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
Conf *model.HttpServerConfig
|
||||
Closed bool
|
||||
*http.Server
|
||||
l *sl
|
||||
}
|
||||
|
||||
func (s *Server) Renew() {
|
||||
s.Server = &http.Server{
|
||||
Handler: s.Handler,
|
||||
}
|
||||
}
|
||||
|
||||
type ServerListener struct {
|
||||
port int
|
||||
listener cmux.CMux
|
||||
|
@ -48,28 +77,64 @@ func (s *ServerListener) ServerCount() int {
|
|||
return len(s.servers)
|
||||
}
|
||||
|
||||
func (s *ServerListener) StartServer(name string) error {
|
||||
func (s *ServerListener) StartServer(name string) {
|
||||
server, ok := s.servers[name]
|
||||
serverName := server.Conf.ServerName
|
||||
if !ok {
|
||||
panic("No named server")
|
||||
}
|
||||
|
||||
l := s.listener.Match(makeMatcher(name, s))
|
||||
if server.Conf.CertFile != "" && server.Conf.KeyFile != "" {
|
||||
return server.ServeTLS(l, server.Conf.CertFile, server.Conf.KeyFile)
|
||||
}
|
||||
return server.Serve(l)
|
||||
go func() {
|
||||
var err error
|
||||
defer func() {
|
||||
l := logger.GetLogger("ServerListener")
|
||||
if err != nil {
|
||||
if err == http.ErrServerClosed {
|
||||
server.Closed = true
|
||||
|
||||
} else {
|
||||
l.Error("Server error:", err)
|
||||
}
|
||||
|
||||
}
|
||||
l.Info("Server stopped:", name)
|
||||
}()
|
||||
if server.Closed {
|
||||
server.Renew()
|
||||
}
|
||||
if server.l == nil {
|
||||
server.l = &sl{
|
||||
Listener: s.listener.Match(makeMatcher(serverName, s)),
|
||||
}
|
||||
}
|
||||
|
||||
if server.Conf.CertFile != "" && server.Conf.KeyFile != "" {
|
||||
err = server.ServeTLS(server.l, server.Conf.CertFile, server.Conf.KeyFile)
|
||||
} else {
|
||||
err = server.Serve(server.l)
|
||||
}
|
||||
}()
|
||||
|
||||
// return server.Serve(l)
|
||||
}
|
||||
|
||||
func (s *ServerListener) StopServer(name string) error {
|
||||
server, ok := s.servers[name]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
return server.Shutdown(ctx)
|
||||
}
|
||||
|
||||
// Start 方法用于并发启动 ServerListener 中所有注册的服务器。
|
||||
// 它会为每个服务器创建一个新的 goroutine 来启动,以确保各个服务器可以独立运行。
|
||||
func (s *ServerListener) Start() {
|
||||
// 遍历 ServerListener 中所有注册的服务器
|
||||
for name, _ := range s.servers {
|
||||
go func(name string) {
|
||||
err := s.StartServer(name)
|
||||
if err != nil {
|
||||
l := logger.GetLogger("ServerListener")
|
||||
l.Error("Start server error:", name, err)
|
||||
}
|
||||
}(name)
|
||||
// 为每个服务器启动一个新的 goroutine 来执行启动操作
|
||||
s.StartServer(name)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -109,3 +174,35 @@ func AddServer(name string, server *Server, port int) {
|
|||
}
|
||||
listener.AddServer(name, server)
|
||||
}
|
||||
|
||||
func AddServerWithConfig(conf *model.HttpServerConfig) {
|
||||
mux := NewServeMux(conf)
|
||||
s := &http.Server{
|
||||
Handler: mux,
|
||||
}
|
||||
name := conf.Name
|
||||
port := conf.Port
|
||||
ss := &Server{
|
||||
Server: s,
|
||||
Conf: conf,
|
||||
}
|
||||
AddServer(name, ss, port)
|
||||
}
|
||||
|
||||
func StopServer(name string, port int) {
|
||||
listenStr := fmt.Sprintf(":%d", port)
|
||||
listener, ok := ServerManager[listenStr]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
listener.StopServer(name)
|
||||
}
|
||||
|
||||
func StartServer(name string, port int) {
|
||||
listenStr := fmt.Sprintf(":%d", port)
|
||||
listener, ok := ServerManager[listenStr]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
listener.StartServer(name)
|
||||
}
|
||||
|
|
|
@ -46,21 +46,6 @@ func (ml *MiddlewareLink) Add(m Middleware) {
|
|||
}
|
||||
}
|
||||
|
||||
// func (ml *MiddlewareLink) ServeHTTP(w http.ResponseWriter, r *http.Request) bool {
|
||||
// canContinue := true
|
||||
// next := func() {
|
||||
// canContinue = true
|
||||
// }
|
||||
// for e := ml.Front(); e != nil && canContinue; e = e.Next() {
|
||||
// canContinue = false
|
||||
// e.Value.(Middleware)(w, r, next)
|
||||
// if !canContinue {
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
// return canContinue
|
||||
// }
|
||||
|
||||
func (ml *MiddlewareLink) wrap(m Middleware, next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
m(w, r, next)
|
||||
|
@ -84,6 +69,18 @@ func (ml *MiddlewareLink) WrapHandler(next http.Handler) http.Handler {
|
|||
}
|
||||
return handler
|
||||
}
|
||||
|
||||
func (ml *MiddlewareLink) Clone() *MiddlewareLink {
|
||||
ret := NewMiddlewareLink()
|
||||
for e := ml.Back(); e != nil; e = e.Prev() {
|
||||
middleware, ok := e.Value.(Middleware)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
ret.Add(middleware)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
func NewMiddlewareLink() *MiddlewareLink {
|
||||
ml := &MiddlewareLink{list.New()}
|
||||
ml.Add(Done)
|
||||
|
@ -107,8 +104,12 @@ func BasicAuth(w http.ResponseWriter, r *http.Request, next http.Handler) {
|
|||
}
|
||||
func JwtAuth(w http.ResponseWriter, r *http.Request, next http.Handler) {
|
||||
l := gologger.GetLogger("JwtAuth")
|
||||
config := model.GetConfig()
|
||||
jwtConfig := config.Admin.Jwt
|
||||
config := getServerConfig(r)
|
||||
if config == nil || config.Jwt == nil {
|
||||
http.Error(w, "Jwt config error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
jwtConfig := config.Jwt
|
||||
if jwtConfig.Secret == "" || path.Base(r.URL.Path) == "login" {
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
|
@ -178,16 +179,22 @@ func Parse[T any](w http.ResponseWriter, r *http.Request, next http.Handler) {
|
|||
next.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
// IPAccessControl 中间件实现IP访问控制
|
||||
func IPAccessControl(w http.ResponseWriter, r *http.Request, next http.Handler) {
|
||||
|
||||
// get serverName from request context
|
||||
func getServerConfig(r *http.Request) *model.HttpServerConfig {
|
||||
ctx := r.Context()
|
||||
serverName, ok := ctx.Value(RequestCtxKey("serverName")).(string)
|
||||
if !ok {
|
||||
serverName = ""
|
||||
return nil
|
||||
}
|
||||
config := model.GetServerConfig(serverName)
|
||||
return config
|
||||
}
|
||||
|
||||
// IPAccessControl 中间件实现IP访问控制
|
||||
func IPAccessControl(w http.ResponseWriter, r *http.Request, next http.Handler) {
|
||||
|
||||
// get serverName from request context
|
||||
config := getServerConfig(r)
|
||||
if config != nil {
|
||||
allowedIPs := config.AllowIPs
|
||||
deniedIPs := config.DenyIPs
|
||||
|
@ -215,6 +222,8 @@ func IPAccessControl(w http.ResponseWriter, r *http.Request, next http.Handler)
|
|||
return
|
||||
}
|
||||
}
|
||||
} else {
|
||||
http.Error(w, "Server Config Error", http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
|
|
|
@ -34,7 +34,7 @@ func (route *Route) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||
func (route *Route) Match(r *http.Request) bool {
|
||||
l := logger.GetLogger("Route")
|
||||
l.Debug(fmt.Sprintf("matching route: %s %s with %s %s", r.Method, r.URL.Path, route.Method, route.Path))
|
||||
if route.Method != "" && route.Method != r.Method {
|
||||
if route.Method != "" && strings.ToLower(route.Method) != strings.ToLower(r.Method) {
|
||||
l.Debug("method not match")
|
||||
return false
|
||||
}
|
||||
|
@ -216,11 +216,26 @@ type ServerMux struct {
|
|||
wrappedHandler map[string]http.Handler
|
||||
}
|
||||
|
||||
func (s *ServerMux) Handle(pattern string, handler http.Handler) {
|
||||
func (s *ServerMux) Handle(pattern string, handler http.Handler, directives []string) {
|
||||
if s.handlers == nil {
|
||||
s.handlers = make(map[string]http.Handler)
|
||||
}
|
||||
s.handlers[pattern] = s.directiveHandlers.WrapHandler(handler)
|
||||
nMiddleWareLink := s.directiveHandlers.Clone()
|
||||
for _, directive := range directives {
|
||||
strs := strings.Split(directive, " ")
|
||||
directiveName := strs[0]
|
||||
p := strings.Split(directiveName, "_")
|
||||
if len(p) > 1 {
|
||||
continue // 跳过自定义指令
|
||||
}
|
||||
params := strs[1:]
|
||||
directive, ok := DirectiveMap[directiveName]
|
||||
if ok {
|
||||
nMiddleWareLink.Add(directive(params...))
|
||||
}
|
||||
// nMiddleWareLink.Add(GetDirective(directive))
|
||||
}
|
||||
s.handlers[pattern] = nMiddleWareLink.WrapHandler(handler)
|
||||
s.paths = append(s.paths, pattern)
|
||||
// 自定义比较函数排序s.paths
|
||||
sort.Slice(s.paths, func(i, j int) bool {
|
||||
|
@ -263,23 +278,7 @@ func (s *ServerMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||
fhandler.ServeHTTP(w, newRequest)
|
||||
return
|
||||
}
|
||||
// s.handlers[p].ServeHTTP(w, newRequest)
|
||||
// s.directiveHandlers.ServeHTTP(w, newRequest)
|
||||
// ctx := newRequest.Context()
|
||||
// m := ctx.Value(RequestCtxKey("data")).(map[string]interface{})
|
||||
// _, ok := m["Tg"]
|
||||
// if ok {
|
||||
// wrappedHandler := s.wrappedHandler[p]
|
||||
// if wrappedHandler == nil {
|
||||
// s.wrappedHandler[p] = gzip.DefaultHandler().WrapHandler(s.handlers[p])
|
||||
// wrappedHandler = s.wrappedHandler[p]
|
||||
// }
|
||||
// wrappedHandler.ServeHTTP(w, newRequest)
|
||||
// } else {
|
||||
// s.handlers[p].ServeHTTP(w, newRequest)
|
||||
// }
|
||||
|
||||
// return
|
||||
}
|
||||
}
|
||||
l.Error(fmt.Sprintf("404: %s", r.URL.Path))
|
||||
|
@ -318,18 +317,19 @@ func NewServeMux(c *model.HttpServerConfig) *ServerMux {
|
|||
// 将指令添加到 ServerMux 的指令处理中间件链中
|
||||
s.AddDirective(string(directive))
|
||||
}
|
||||
if c.AuthType == "jwt" {
|
||||
s.AddDirective("Jwt-Auth")
|
||||
}
|
||||
if c.AuthType == "basic" {
|
||||
s.AddDirective("Basic-Auth")
|
||||
}
|
||||
// if c.AuthType == "jwt" {
|
||||
// s.AddDirective("Jwt-Auth")
|
||||
// }
|
||||
// if c.AuthType == "basic" {
|
||||
// s.AddDirective("Basic-Auth")
|
||||
// }
|
||||
// 遍历配置中的所有 HTTP 路径
|
||||
for _, httpPath := range c.Paths {
|
||||
// 检查路径配置中是否指定了根目录
|
||||
var fhandler http.Handler = nil
|
||||
if httpPath.Root != "" {
|
||||
// 创建一个新的文件处理程序
|
||||
fileHandler := handler.NewFileHandler(handler.FileHandler{
|
||||
fhandler = handler.NewFileHandler(handler.FileHandler{
|
||||
WPath: httpPath.Path,
|
||||
// 设置文件处理程序的根目录
|
||||
Root: httpPath.Root,
|
||||
|
@ -337,18 +337,23 @@ func NewServeMux(c *model.HttpServerConfig) *ServerMux {
|
|||
Default: httpPath.Default,
|
||||
})
|
||||
// 将文件处理程序注册到 ServerMux 中
|
||||
s.Handle(httpPath.Path, fileHandler)
|
||||
|
||||
// 检查路径配置中是否指定了上游服务器
|
||||
} else if len(httpPath.Upstreams) != 0 {
|
||||
// 创建一个新的代理处理程序
|
||||
proxyHandler := handler.NewProxyHandler(&httpPath)
|
||||
fhandler = handler.NewProxyHandler(&httpPath)
|
||||
// 将代理处理程序注册到 ServerMux 中
|
||||
s.Handle(httpPath.Path, proxyHandler)
|
||||
// s.Handle(httpPath.Path, proxyHandler)
|
||||
// 如果既没有指定根目录也没有指定上游服务器
|
||||
} else {
|
||||
// 记录不支持的路径类型错误信息
|
||||
l.Error("Not supportted server path type :", httpPath.Path)
|
||||
}
|
||||
if fhandler != nil {
|
||||
directives := httpPath.Directives
|
||||
s.Handle(httpPath.Path, fhandler, directives)
|
||||
}
|
||||
// s.Handle(httpPath.Path, fhandler)
|
||||
}
|
||||
// 返回初始化好的 ServerMux 实例
|
||||
return s
|
||||
|
|
Loading…
Reference in New Issue