refactor and add static

This commit is contained in:
kingecg 2025-02-18 00:54:50 +08:00
parent f6f31e56c4
commit f7df73ee54
7 changed files with 239 additions and 104 deletions

View File

@ -87,5 +87,5 @@ func init() {
postConfigRoute.Add(server.Parse[model.HttpServerConfig]) postConfigRoute.Add(server.Parse[model.HttpServerConfig])
AdminServerMux.HandleFunc("GET", "/config/:id", http.HandlerFunc(getServerConfigure)) AdminServerMux.HandleFunc("GET", "/config/:id", http.HandlerFunc(getServerConfigure))
AdminServerMux.HandleFunc("GET", "/status", http.HandlerFunc(getStatus)) AdminServerMux.HandleFunc("GET", "/status", http.HandlerFunc(getStatus))
AdminServerMux.Use(server.BasicAuth) // AdminServerMux.Use(server.BasicAuth)
} }

View File

@ -16,25 +16,46 @@ type GoHttp struct {
logger gologger.Logger logger gologger.Logger
} }
// Start 是 GoHttp 服务的启动入口。
// 它初始化配置、日志记录器,并根据配置设置必要的服务器。
func (g *GoHttp) Start() { func (g *GoHttp) Start() {
// 获取系统配置
conf := model.GetConfig() conf := model.GetConfig()
// 初始化服务器的日志记录器
g.logger = gologger.GetLogger("Server") g.logger = gologger.GetLogger("Server")
// 记录 GoHttp 服务启动信息
g.logger.Info("start gohttpd") g.logger.Info("start gohttpd")
// if g.conf != nil { // if g.conf != nil {
// 设置管理员处理器并使用管理员服务配置
adminHandler := server.NewServeMux(conf.Admin) adminHandler := server.NewServeMux(conf.Admin)
adminHandler.Handle("/api/", http.StripPrefix("/api", admin.AdminServerMux)) adminHandler.Handle("/api/", http.StripPrefix("/api", admin.AdminServerMux))
// 创建并启动管理员服务器
g.makeServer(conf.Admin, adminHandler) g.makeServer(conf.Admin, adminHandler)
// 遍历配置中的服务器列表,为每个服务器设置处理器并启动
for _, s := range conf.Servers { for _, s := range conf.Servers {
// 为每个服务器创建处理器
sHandler := server.NewServeMux(s) sHandler := server.NewServeMux(s)
// 创建并启动该服务器
g.makeServer(s, sHandler) g.makeServer(s, sHandler)
} }
// 遍历所有已初始化的服务器,开始监听并处理传入的连接
for _, listener := range server.ServerManager { for _, listener := range server.ServerManager {
// 启动服务器
listener.Start() listener.Start()
listener.Serve()
// 开始服务,处理传入请求
listener.Serve()
} }
// 记录 GoHttp 服务成功启动的信息
g.logger.Info("gohttpd start success") g.logger.Info("gohttpd start success")
} }
@ -53,24 +74,45 @@ func (g *GoHttp) makeServer(conf *model.HttpServerConfig, mux http.Handler) {
func (g *GoHttp) Stop() {} func (g *GoHttp) Stop() {}
// LoadConfig 函数用于加载配置文件并对配置中的路径进行规范化处理。
// 它读取指定的 JSON 配置文件,将其内容解析到 model.Config 结构体中,
// 然后对配置中的文件路径进行规范化处理,最后配置日志记录器。
func LoadConfig() { func LoadConfig() {
// 规范化配置文件的路径,确保路径格式统一
cpath := utils.NormalizePath("./config.json") cpath := utils.NormalizePath("./config.json")
// read content from cpath // 从指定路径读取配置文件的内容
// 忽略可能的错误,实际应用中应处理错误
content, _ := os.ReadFile(cpath) content, _ := os.ReadFile(cpath)
// 将读取的 JSON 内容解析到 model.Config 结构体中
json.Unmarshal(content, &model.Config) json.Unmarshal(content, &model.Config)
//normalize path in config
// 规范化配置中日志追加器的文件路径
// 遍历配置中的所有日志追加器
for _, appender := range model.Config.Logging.Appenders { for _, appender := range model.Config.Logging.Appenders {
// 检查追加器类型是否为文件类型
if appender.Type == "file" { if appender.Type == "file" {
// 规范化文件路径
appender.Options["file"] = utils.NormalizePath(appender.Options["file"].(string)) appender.Options["file"] = utils.NormalizePath(appender.Options["file"].(string))
} }
} }
// 规范化管理员服务器配置中的路径
normalizeServer(model.Config.Admin) normalizeServer(model.Config.Admin)
// 遍历配置中的所有服务器,规范化每个服务器配置中的路径
for _, server := range model.Config.Servers { for _, server := range model.Config.Servers {
normalizeServer(server) normalizeServer(server)
} }
// 根据配置中的日志设置配置日志记录器
gologger.Configure(model.Config.Logging) gologger.Configure(model.Config.Logging)
// 获取名为 "Server" 的日志记录器实例
logger := gologger.GetLogger("Server") logger := gologger.GetLogger("Server")
// 记录配置加载成功的信息
logger.Info("Load config success") logger.Info("Load config success")
} }

19
main.go
View File

@ -9,33 +9,48 @@ import (
"git.pyer.club/kingecg/gologger" "git.pyer.club/kingecg/gologger"
) )
// main 函数是程序的入口点
func main() { func main() {
// 加载配置文件
LoadConfig() LoadConfig()
// 获取日志记录器
l := gologger.GetLogger("main") l := gologger.GetLogger("main")
// 使用 defer 和 recover 捕获 panic确保程序不会因未处理的异常而崩溃
defer func() { defer func() {
if err := recover(); err != nil { if err := recover(); err != nil {
l.Error("panic", err) l.Error("panic", err)
} }
}() }()
// 创建守护进程实例
daemon := godaemon.NewGoDaemon(start, stop) daemon := godaemon.NewGoDaemon(start, stop)
// 启动守护进程
daemon.Start() daemon.Start()
} }
// waiter 用于接收系统信号
var waiter chan os.Signal var waiter chan os.Signal
// start 函数是守护进程启动时执行的函数
func start(g *godaemon.GoDaemon) { func start(g *godaemon.GoDaemon) {
waiter = make(chan os.Signal, 1) // buffered channel // 创建缓冲通道用于接收系统信号
waiter = make(chan os.Signal, 1)
// 监听 SIGTERM 和 SIGINT 信号
signal.Notify(waiter, syscall.SIGTERM, syscall.SIGINT) signal.Notify(waiter, syscall.SIGTERM, syscall.SIGINT)
// 创建 HTTP 服务器实例
httpd := &GoHttp{} httpd := &GoHttp{}
// 启动 HTTP 服务器
httpd.Start() httpd.Start()
// blocks here until there's a signal // 阻塞等待信号
<-waiter <-waiter
// 记录退出日志
httpd.logger.Info("Exit") httpd.logger.Info("Exit")
} }
// stop 函数是守护进程停止时执行的函数
func stop(g *godaemon.GoDaemon) { func stop(g *godaemon.GoDaemon) {
// 发送 SIGTERM 信号给当前进程
g.Running.Process.Signal(syscall.SIGTERM) g.Running.Process.Signal(syscall.SIGTERM)
} }

21
model/data.go Normal file
View File

@ -0,0 +1,21 @@
package model
type ServerStatic struct {
Count int64 `json:"count"`
}
// 声明一个全局变量map
var serverStaticMap = make(map[string]*ServerStatic)
func Incr(key string) {
if _, ok := serverStaticMap[key]; !ok {
serverStaticMap[key] = &ServerStatic{
Count: 0,
}
}
serverStaticMap[key].Count++
}
func GetServerStatic() map[string]*ServerStatic {
return serverStaticMap
}

View File

@ -5,49 +5,58 @@ import (
"net/http" "net/http"
"strings" "strings"
// "compress/gzip"
"git.pyer.club/kingecg/gohttpd/model"
"git.pyer.club/kingecg/gologger" "git.pyer.club/kingecg/gologger"
"github.com/nanmu42/gzip"
) )
type Directive func(args ...string) Middleware type Directive func(args ...string) Middleware
var Add_Header Directive = func(args ...string) Middleware { var Add_Header Directive = func(args ...string) Middleware {
return func(w http.ResponseWriter, r *http.Request, next func()) { return func(w http.ResponseWriter, r *http.Request, next http.Handler) {
l := gologger.GetLogger("Directive") l := gologger.GetLogger("Directive")
p := args[1:] p := args[1:]
params := strings.Join(p, " ") params := strings.Join(p, " ")
l.Debug(fmt.Sprintf("Add-Header %s:%s", args[0], params)) l.Debug(fmt.Sprintf("Add-Header %s:%s", args[0], params))
w.Header().Add(args[0], args[1]) w.Header().Add(args[0], args[1])
next() next.ServeHTTP(w, r)
} }
} }
var Set_Header Directive = func(args ...string) Middleware { var Set_Header Directive = func(args ...string) Middleware {
return func(w http.ResponseWriter, r *http.Request, next func()) { return func(w http.ResponseWriter, r *http.Request, next http.Handler) {
l := gologger.GetLogger("Directive") l := gologger.GetLogger("Directive")
p := args[1:] p := args[1:]
params := strings.Join(p, " ") params := strings.Join(p, " ")
l.Debug(fmt.Sprintf("Set-Header %s:%s", args[0], params)) l.Debug(fmt.Sprintf("Set-Header %s:%s", args[0], params))
w.Header().Set(args[0], params) w.Header().Set(args[0], params)
next() next.ServeHTTP(w, r)
} }
} }
var Gzip_Response Directive = func(args ...string) Middleware { var Gzip_Response Directive = func(args ...string) Middleware {
return func(w http.ResponseWriter, r *http.Request, next func()) { var gzipHandler http.Handler = nil
return func(w http.ResponseWriter, r *http.Request, next http.Handler) {
l := gologger.GetLogger("Directive") l := gologger.GetLogger("Directive")
l.Debug("Gzip-Response") l.Debug("Gzip-Response")
// if filepath.Ext(r.URL.Path) != "" { if gzipHandler == nil {
ctx := r.Context() gzipHandler = gzip.DefaultHandler().WrapHandler(next)
m := ctx.Value(RequestCtxKey("data")).(map[string]interface{}) }
m["Tg"] = "gzip" gzipHandler.ServeHTTP(w, r)
}
// } }
var DRecordAccess Directive = func(args ...string) Middleware {
next() return func(w http.ResponseWriter, r *http.Request, next http.Handler) {
l := gologger.GetLogger("Directive")
l.Debug("Record-Access")
model.Incr(r.URL.Host)
next.ServeHTTP(w, r)
} }
} }
var DirectiveMap = map[string]Directive{ var DirectiveMap = map[string]Directive{
"Set-Header": Set_Header, "Set-Header": Set_Header,
"Add-Header": Add_Header, "Add-Header": Add_Header,
"Gzip_Response": Gzip_Response, "Gzip_Response": Gzip_Response,
"Record-Access": DRecordAccess,
} }

View File

@ -10,7 +10,7 @@ import (
"git.pyer.club/kingecg/gohttpd/model" "git.pyer.club/kingecg/gohttpd/model"
) )
type Middleware func(w http.ResponseWriter, r *http.Request, next func()) type Middleware func(w http.ResponseWriter, r *http.Request, next http.Handler)
type MiddlewareLink struct { type MiddlewareLink struct {
*list.List *list.List
@ -41,19 +41,28 @@ func (ml *MiddlewareLink) Add(m Middleware) {
} }
} }
func (ml *MiddlewareLink) ServeHTTP(w http.ResponseWriter, r *http.Request) bool { // func (ml *MiddlewareLink) ServeHTTP(w http.ResponseWriter, r *http.Request) bool {
canContinue := true // canContinue := true
next := func() { // next := func() {
canContinue = true // canContinue = true
} // }
for e := ml.Front(); e != nil && canContinue; e = e.Next() { // for e := ml.Front(); e != nil && canContinue; e = e.Next() {
canContinue = false // canContinue = false
// e.Value.(Middleware)(w, r, next)
// if !canContinue {
// break
// }
// }
// return canContinue
// }
func (ml *MiddlewareLink) WrapHandler(next http.Handler) http.Handler {
for e := ml.Back(); e != nil; e = e.Prev() {
next = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
e.Value.(Middleware)(w, r, next) e.Value.(Middleware)(w, r, next)
if !canContinue { })
break
} }
} return next
return canContinue
} }
func NewMiddlewareLink() *MiddlewareLink { func NewMiddlewareLink() *MiddlewareLink {
ml := &MiddlewareLink{list.New()} ml := &MiddlewareLink{list.New()}
@ -61,36 +70,37 @@ func NewMiddlewareLink() *MiddlewareLink {
return ml return ml
} }
func BasicAuth(w http.ResponseWriter, r *http.Request, next func()) { func BasicAuth(w http.ResponseWriter, r *http.Request, next http.Handler) {
config := model.GetConfig() config := model.GetConfig()
if config.Admin.Username == "" || config.Admin.Password == "" { if config.Admin.Username == "" || config.Admin.Password == "" {
next() next.ServeHTTP(w, r)
return return
} }
user, pass, ok := r.BasicAuth() user, pass, ok := r.BasicAuth()
if ok && user == config.Admin.Username && pass == config.Admin.Password { if ok && user == config.Admin.Username && pass == config.Admin.Password {
next() next.ServeHTTP(w, r)
} else { } else {
w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`) w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
http.Error(w, "Unauthorized.", http.StatusUnauthorized) http.Error(w, "Unauthorized.", http.StatusUnauthorized)
} }
} }
func Parse[T any](w http.ResponseWriter, r *http.Request, next func()) { func RecordAccess(w http.ResponseWriter, r *http.Request, next http.Handler) {
model.Incr(r.Host)
next.ServeHTTP(w, r)
}
func Parse[T any](w http.ResponseWriter, r *http.Request, next http.Handler) {
if r.Method == "POST" || r.Method == "PUT" { if r.Method == "POST" || r.Method == "PUT" {
//判断r的content-type是否是application/x-www-form-urlencoded //判断r的content-type是否是application/x-www-form-urlencoded
if r.Header.Get("Content-Type") == "application/x-www-form-urlencoded" { if r.Header.Get("Content-Type") == "application/x-www-form-urlencoded" {
r.ParseForm() r.ParseForm()
next()
return } else if r.Header.Get("Content-Type") == "multipart/form-data" {
}
if r.Header.Get("Content-Type") == "multipart/form-data" {
r.ParseMultipartForm(r.ContentLength) r.ParseMultipartForm(r.ContentLength)
next()
return } else {
}
// 判断r的content-type是否是application/json // 判断r的content-type是否是application/json
contentType := r.Header.Get("Content-Type") contentType := r.Header.Get("Content-Type")
if strings.Contains(contentType, "application/json") { if strings.Contains(contentType, "application/json") {
@ -105,13 +115,14 @@ func Parse[T any](w http.ResponseWriter, r *http.Request, next func()) {
if m != nil { if m != nil {
m["data"] = t m["data"] = t
} }
next()
return
} }
} }
next()
}
next.ServeHTTP(w, r)
} }
func Done(w http.ResponseWriter, r *http.Request, next func()) { func Done(w http.ResponseWriter, r *http.Request, next http.Handler) {
next() next.ServeHTTP(w, r)
} }

View File

@ -10,7 +10,6 @@ import (
handler "git.pyer.club/kingecg/gohttpd/handler" handler "git.pyer.club/kingecg/gohttpd/handler"
"git.pyer.club/kingecg/gohttpd/model" "git.pyer.club/kingecg/gohttpd/model"
logger "git.pyer.club/kingecg/gologger" logger "git.pyer.club/kingecg/gologger"
"github.com/nanmu42/gzip"
) )
type RequestCtxKey string type RequestCtxKey string
@ -21,11 +20,13 @@ type Route struct {
Handler http.Handler Handler http.Handler
matcher *UrlParamMatcher matcher *UrlParamMatcher
middles *MiddlewareLink middles *MiddlewareLink
wrapped bool
} }
func (route *Route) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (route *Route) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if route.middles.Len() > 0 && !route.middles.ServeHTTP(w, r) { if route.wrapped == false {
return route.Handler = route.middles.WrapHandler(route.Handler)
route.wrapped = true
} }
route.Handler.ServeHTTP(w, r) route.Handler.ServeHTTP(w, r)
} }
@ -73,6 +74,7 @@ func NewRoute(method string, path string, handleFn http.Handler) *Route {
Method: method, Method: method,
Path: path, Path: path,
middles: NewMiddlewareLink(), middles: NewMiddlewareLink(),
wrapped: false,
} }
p := ParseUrl(path) p := ParseUrl(path)
// 使用handleFn构建handler // 使用handleFn构建handler
@ -113,35 +115,42 @@ type RestMux struct {
Path string Path string
routes Routes routes Routes
middlewares *MiddlewareLink middlewares *MiddlewareLink
wrapperHandler http.Handler
} }
func (mux *RestMux) Use(m Middleware) { func (mux *RestMux) Use(m Middleware) {
mux.middlewares.Add(m) mux.middlewares.Add(m)
} }
func (mux *RestMux) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (mux *RestMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if mux.wrapperHandler == nil {
c := r.Context() mux.wrapperHandler = mux.middlewares.WrapHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
newRequest := r
if t := c.Value("data"); t == nil {
data := map[string]interface{}{}
cn := context.WithValue(c, RequestCtxKey("data"), data)
newRequest = r.WithContext(cn)
}
if mux.middlewares.Len() > 0 && !mux.middlewares.ServeHTTP(w, newRequest) {
return
}
// 根据r 从routes中找到匹配的路由
for _, route := range mux.routes { for _, route := range mux.routes {
if route.Match(newRequest) { if route.Match(r) {
route.ServeHTTP(w, newRequest) route.ServeHTTP(w, r)
return return
} }
} }
http.NotFound(w, r) http.NotFound(w, r)
}))
return
}
mux.wrapperHandler.ServeHTTP(w, r)
// c := r.Context()
// newRequest := r
// if t := c.Value("data"); t == nil {
// data := map[string]interface{}{}
// cn := context.WithValue(c, RequestCtxKey("data"), data)
// newRequest = r.WithContext(cn)
// }
// if mux.middlewares.Len() > 0 && !mux.middlewares.ServeHTTP(w, newRequest) {
// return
// }
// 根据r 从routes中找到匹配的路由
} }
func (mux *RestMux) HandleFunc(method string, path string, f func(http.ResponseWriter, *http.Request)) *Route { func (mux *RestMux) HandleFunc(method string, path string, f func(http.ResponseWriter, *http.Request)) *Route {
@ -208,7 +217,7 @@ func (s *ServerMux) Handle(pattern string, handler http.Handler) {
if s.handlers == nil { if s.handlers == nil {
s.handlers = make(map[string]http.Handler) s.handlers = make(map[string]http.Handler)
} }
s.handlers[pattern] = handler s.handlers[pattern] = s.directiveHandlers.WrapHandler(handler)
s.paths = append(s.paths, pattern) s.paths = append(s.paths, pattern)
// 自定义比较函数排序s.paths // 自定义比较函数排序s.paths
sort.Slice(s.paths, func(i, j int) bool { sort.Slice(s.paths, func(i, j int) bool {
@ -240,20 +249,21 @@ func (s *ServerMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
for _, p := range s.paths { for _, p := range s.paths {
if strings.HasPrefix(r.URL.Path, p) { if strings.HasPrefix(r.URL.Path, p) {
l.Info(fmt.Sprintf("match path: %s", p)) l.Info(fmt.Sprintf("match path: %s", p))
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) 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 return
} }
@ -262,32 +272,59 @@ func (s *ServerMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
http.NotFound(w, newRequest) http.NotFound(w, newRequest)
} }
// NewServeMux 根据提供的 HTTP 服务器配置创建并返回一个新的 ServerMux 实例。
// 参数:
// - c: 指向 model.HttpServerConfig 结构体的指针,包含服务器的配置信息。
// 返回值:
// - *ServerMux: 一个指向 ServerMux 的指针,用于处理 HTTP 请求。
func NewServeMux(c *model.HttpServerConfig) *ServerMux { func NewServeMux(c *model.HttpServerConfig) *ServerMux {
// 获取名为 "ServerMux" 的日志记录器实例
l := logger.GetLogger("ServerMux") l := logger.GetLogger("ServerMux")
// 记录创建 ServerMux 的调试信息
l.Debug("NewServeMux") l.Debug("NewServeMux")
// 初始化一个新的 ServerMux 实例
s := &ServerMux{ s := &ServerMux{
// 初始化指令处理中间件链
directiveHandlers: NewMiddlewareLink(), directiveHandlers: NewMiddlewareLink(),
// 初始化处理程序映射
handlers: make(map[string]http.Handler), handlers: make(map[string]http.Handler),
// 初始化路径列表
paths: []string{}, paths: []string{},
// 初始化包装处理程序映射
wrappedHandler: make(map[string]http.Handler), wrappedHandler: make(map[string]http.Handler),
} }
s.AddDirective("Record-Access")
// 遍历配置中的所有指令
for _, directive := range c.Directives { for _, directive := range c.Directives {
// 将指令添加到 ServerMux 的指令处理中间件链中
s.AddDirective(string(directive)) s.AddDirective(string(directive))
} }
// 遍历配置中的所有 HTTP 路径
for _, httpPath := range c.Paths { for _, httpPath := range c.Paths {
// 检查路径配置中是否指定了根目录
if httpPath.Root != "" { if httpPath.Root != "" {
// 创建一个新的文件处理程序
fileHandler := handler.NewFileHandler(handler.FileHandler{ fileHandler := handler.NewFileHandler(handler.FileHandler{
// 设置文件处理程序的根目录
Root: httpPath.Root, Root: httpPath.Root,
// 设置文件处理程序的默认文件
Default: httpPath.Default, Default: httpPath.Default,
}) })
// 将文件处理程序注册到 ServerMux 中
s.Handle(httpPath.Path, fileHandler) s.Handle(httpPath.Path, fileHandler)
// 检查路径配置中是否指定了上游服务器
} else if len(httpPath.Upstreams) != 0 { } else if len(httpPath.Upstreams) != 0 {
// 创建一个新的代理处理程序
proxyHandler := handler.NewProxyHandler(&httpPath) proxyHandler := handler.NewProxyHandler(&httpPath)
// 将代理处理程序注册到 ServerMux 中
s.Handle(httpPath.Path, proxyHandler) s.Handle(httpPath.Path, proxyHandler)
// 如果既没有指定根目录也没有指定上游服务器
} else { } else {
// 记录不支持的路径类型错误信息
l.Error("Not supportted server path type :", httpPath.Path) l.Error("Not supportted server path type :", httpPath.Path)
} }
} }
// 返回初始化好的 ServerMux 实例
return s return s
} }