diff --git a/admin/admin.go b/admin/admin.go index 04a2699..a313216 100644 --- a/admin/admin.go +++ b/admin/admin.go @@ -87,5 +87,5 @@ func init() { postConfigRoute.Add(server.Parse[model.HttpServerConfig]) AdminServerMux.HandleFunc("GET", "/config/:id", http.HandlerFunc(getServerConfigure)) AdminServerMux.HandleFunc("GET", "/status", http.HandlerFunc(getStatus)) - AdminServerMux.Use(server.BasicAuth) + // AdminServerMux.Use(server.BasicAuth) } diff --git a/gohttp.go b/gohttp.go index c14c348..edd3450 100644 --- a/gohttp.go +++ b/gohttp.go @@ -16,25 +16,46 @@ type GoHttp struct { logger gologger.Logger } +// Start 是 GoHttp 服务的启动入口。 +// 它初始化配置、日志记录器,并根据配置设置必要的服务器。 func (g *GoHttp) Start() { + // 获取系统配置 conf := model.GetConfig() + + // 初始化服务器的日志记录器 g.logger = gologger.GetLogger("Server") + + // 记录 GoHttp 服务启动信息 g.logger.Info("start gohttpd") + // if g.conf != nil { + + // 设置管理员处理器并使用管理员服务配置 adminHandler := server.NewServeMux(conf.Admin) adminHandler.Handle("/api/", http.StripPrefix("/api", admin.AdminServerMux)) + + // 创建并启动管理员服务器 g.makeServer(conf.Admin, adminHandler) + // 遍历配置中的服务器列表,为每个服务器设置处理器并启动 for _, s := range conf.Servers { + // 为每个服务器创建处理器 sHandler := server.NewServeMux(s) + + // 创建并启动该服务器 g.makeServer(s, sHandler) } + // 遍历所有已初始化的服务器,开始监听并处理传入的连接 for _, listener := range server.ServerManager { + // 启动服务器 listener.Start() - listener.Serve() + // 开始服务,处理传入请求 + listener.Serve() } + + // 记录 GoHttp 服务成功启动的信息 g.logger.Info("gohttpd start success") } @@ -53,24 +74,45 @@ func (g *GoHttp) makeServer(conf *model.HttpServerConfig, mux http.Handler) { func (g *GoHttp) Stop() {} +// LoadConfig 函数用于加载配置文件并对配置中的路径进行规范化处理。 +// 它读取指定的 JSON 配置文件,将其内容解析到 model.Config 结构体中, +// 然后对配置中的文件路径进行规范化处理,最后配置日志记录器。 func LoadConfig() { + // 规范化配置文件的路径,确保路径格式统一 cpath := utils.NormalizePath("./config.json") - // read content from cpath + // 从指定路径读取配置文件的内容 + // 忽略可能的错误,实际应用中应处理错误 content, _ := os.ReadFile(cpath) + + // 将读取的 JSON 内容解析到 model.Config 结构体中 json.Unmarshal(content, &model.Config) - //normalize path in config + + // 规范化配置中日志追加器的文件路径 + // 遍历配置中的所有日志追加器 for _, appender := range model.Config.Logging.Appenders { + // 检查追加器类型是否为文件类型 if appender.Type == "file" { + // 规范化文件路径 appender.Options["file"] = utils.NormalizePath(appender.Options["file"].(string)) } } + + // 规范化管理员服务器配置中的路径 normalizeServer(model.Config.Admin) + + // 遍历配置中的所有服务器,规范化每个服务器配置中的路径 for _, server := range model.Config.Servers { normalizeServer(server) } + + // 根据配置中的日志设置配置日志记录器 gologger.Configure(model.Config.Logging) + + // 获取名为 "Server" 的日志记录器实例 logger := gologger.GetLogger("Server") + + // 记录配置加载成功的信息 logger.Info("Load config success") } diff --git a/main.go b/main.go index 3744630..f22e47c 100644 --- a/main.go +++ b/main.go @@ -9,33 +9,48 @@ import ( "git.pyer.club/kingecg/gologger" ) +// main 函数是程序的入口点 func main() { + // 加载配置文件 LoadConfig() + // 获取日志记录器 l := gologger.GetLogger("main") + // 使用 defer 和 recover 捕获 panic,确保程序不会因未处理的异常而崩溃 defer func() { if err := recover(); err != nil { l.Error("panic", err) } }() + // 创建守护进程实例 daemon := godaemon.NewGoDaemon(start, stop) + // 启动守护进程 daemon.Start() } +// waiter 用于接收系统信号 var waiter chan os.Signal +// start 函数是守护进程启动时执行的函数 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) + // 创建 HTTP 服务器实例 httpd := &GoHttp{} + // 启动 HTTP 服务器 httpd.Start() - // blocks here until there's a signal + // 阻塞等待信号 <-waiter + // 记录退出日志 httpd.logger.Info("Exit") } +// stop 函数是守护进程停止时执行的函数 func stop(g *godaemon.GoDaemon) { + // 发送 SIGTERM 信号给当前进程 g.Running.Process.Signal(syscall.SIGTERM) -} +} \ No newline at end of file diff --git a/model/data.go b/model/data.go new file mode 100644 index 0000000..ee5a499 --- /dev/null +++ b/model/data.go @@ -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 +} diff --git a/server/directive.go b/server/directive.go index ace6f8f..75139fb 100644 --- a/server/directive.go +++ b/server/directive.go @@ -5,49 +5,58 @@ import ( "net/http" "strings" + // "compress/gzip" + "git.pyer.club/kingecg/gohttpd/model" "git.pyer.club/kingecg/gologger" + "github.com/nanmu42/gzip" ) type 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") p := args[1:] params := strings.Join(p, " ") l.Debug(fmt.Sprintf("Add-Header %s:%s", args[0], params)) w.Header().Add(args[0], args[1]) - next() + next.ServeHTTP(w, r) } } 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") p := args[1:] params := strings.Join(p, " ") l.Debug(fmt.Sprintf("Set-Header %s:%s", args[0], params)) w.Header().Set(args[0], params) - next() + next.ServeHTTP(w, r) } } 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.Debug("Gzip-Response") - // if filepath.Ext(r.URL.Path) != "" { - ctx := r.Context() - m := ctx.Value(RequestCtxKey("data")).(map[string]interface{}) - m["Tg"] = "gzip" - - // } - - next() + if gzipHandler == nil { + gzipHandler = gzip.DefaultHandler().WrapHandler(next) + } + gzipHandler.ServeHTTP(w, r) + } +} +var DRecordAccess Directive = func(args ...string) Middleware { + 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{ "Set-Header": Set_Header, "Add-Header": Add_Header, "Gzip_Response": Gzip_Response, + "Record-Access": DRecordAccess, } diff --git a/server/middleware.go b/server/middleware.go index 05bee28..d356c92 100644 --- a/server/middleware.go +++ b/server/middleware.go @@ -10,7 +10,7 @@ import ( "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 { *list.List @@ -41,19 +41,28 @@ func (ml *MiddlewareLink) Add(m Middleware) { } } -func (ml *MiddlewareLink) ServeHTTP(w http.ResponseWriter, r *http.Request) bool { - canContinue := true - next := func() { - canContinue = true +// 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) 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) + }) } - for e := ml.Front(); e != nil && canContinue; e = e.Next() { - canContinue = false - e.Value.(Middleware)(w, r, next) - if !canContinue { - break - } - } - return canContinue + return next } func NewMiddlewareLink() *MiddlewareLink { ml := &MiddlewareLink{list.New()} @@ -61,57 +70,59 @@ func NewMiddlewareLink() *MiddlewareLink { 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() if config.Admin.Username == "" || config.Admin.Password == "" { - next() + next.ServeHTTP(w, r) return } user, pass, ok := r.BasicAuth() if ok && user == config.Admin.Username && pass == config.Admin.Password { - next() + next.ServeHTTP(w, r) } else { w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`) 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" { //判断r的content-type是否是application/x-www-form-urlencoded if r.Header.Get("Content-Type") == "application/x-www-form-urlencoded" { r.ParseForm() - next() - return - } - if r.Header.Get("Content-Type") == "multipart/form-data" { + + } else if r.Header.Get("Content-Type") == "multipart/form-data" { r.ParseMultipartForm(r.ContentLength) - next() - return - } - // 判断r的content-type是否是application/json - contentType := r.Header.Get("Content-Type") - if strings.Contains(contentType, "application/json") { - var t T - // 读取json数据 - if err := json.NewDecoder(r.Body).Decode(&t); err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return + + } else { + // 判断r的content-type是否是application/json + contentType := r.Header.Get("Content-Type") + if strings.Contains(contentType, "application/json") { + var t T + // 读取json数据 + if err := json.NewDecoder(r.Body).Decode(&t); err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + ctx := r.Context() + m := ctx.Value(RequestCtxKey("data")).(map[string]interface{}) + if m != nil { + m["data"] = t + } + } - ctx := r.Context() - m := ctx.Value(RequestCtxKey("data")).(map[string]interface{}) - if m != nil { - m["data"] = t - } - next() - return } + } - next() + next.ServeHTTP(w, r) } -func Done(w http.ResponseWriter, r *http.Request, next func()) { - next() +func Done(w http.ResponseWriter, r *http.Request, next http.Handler) { + next.ServeHTTP(w, r) } diff --git a/server/server.go b/server/server.go index 1a59ce6..3be0dee 100644 --- a/server/server.go +++ b/server/server.go @@ -10,7 +10,6 @@ import ( handler "git.pyer.club/kingecg/gohttpd/handler" "git.pyer.club/kingecg/gohttpd/model" logger "git.pyer.club/kingecg/gologger" - "github.com/nanmu42/gzip" ) type RequestCtxKey string @@ -21,11 +20,13 @@ type Route struct { Handler http.Handler matcher *UrlParamMatcher middles *MiddlewareLink + wrapped bool } func (route *Route) ServeHTTP(w http.ResponseWriter, r *http.Request) { - if route.middles.Len() > 0 && !route.middles.ServeHTTP(w, r) { - return + if route.wrapped == false { + route.Handler = route.middles.WrapHandler(route.Handler) + route.wrapped = true } route.Handler.ServeHTTP(w, r) } @@ -73,6 +74,7 @@ func NewRoute(method string, path string, handleFn http.Handler) *Route { Method: method, Path: path, middles: NewMiddlewareLink(), + wrapped: false, } p := ParseUrl(path) // 使用handleFn构建handler @@ -110,38 +112,45 @@ func (rs Routes) Swap(i, j int) { // 可以嵌套的Rest http server mux type RestMux struct { - Path string - routes Routes - middlewares *MiddlewareLink + Path string + routes Routes + middlewares *MiddlewareLink + wrapperHandler http.Handler } func (mux *RestMux) Use(m Middleware) { mux.middlewares.Add(m) } func (mux *RestMux) ServeHTTP(w http.ResponseWriter, r *http.Request) { + if mux.wrapperHandler == nil { + mux.wrapperHandler = mux.middlewares.WrapHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + for _, route := range mux.routes { + if route.Match(r) { + route.ServeHTTP(w, r) + return + } + } - 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) { + 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中找到匹配的路由 - for _, route := range mux.routes { - if route.Match(newRequest) { - route.ServeHTTP(w, newRequest) - return - } - } - http.NotFound(w, r) } 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 { 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 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 { if strings.HasPrefix(r.URL.Path, 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 } @@ -262,32 +272,59 @@ func (s *ServerMux) ServeHTTP(w http.ResponseWriter, r *http.Request) { http.NotFound(w, newRequest) } +// NewServeMux 根据提供的 HTTP 服务器配置创建并返回一个新的 ServerMux 实例。 +// 参数: +// - c: 指向 model.HttpServerConfig 结构体的指针,包含服务器的配置信息。 +// 返回值: +// - *ServerMux: 一个指向 ServerMux 的指针,用于处理 HTTP 请求。 func NewServeMux(c *model.HttpServerConfig) *ServerMux { + // 获取名为 "ServerMux" 的日志记录器实例 l := logger.GetLogger("ServerMux") + // 记录创建 ServerMux 的调试信息 l.Debug("NewServeMux") + // 初始化一个新的 ServerMux 实例 s := &ServerMux{ + // 初始化指令处理中间件链 directiveHandlers: NewMiddlewareLink(), - handlers: make(map[string]http.Handler), - paths: []string{}, - wrappedHandler: make(map[string]http.Handler), + // 初始化处理程序映射 + handlers: make(map[string]http.Handler), + // 初始化路径列表 + paths: []string{}, + // 初始化包装处理程序映射 + wrappedHandler: make(map[string]http.Handler), } + s.AddDirective("Record-Access") + // 遍历配置中的所有指令 for _, directive := range c.Directives { + // 将指令添加到 ServerMux 的指令处理中间件链中 s.AddDirective(string(directive)) } + // 遍历配置中的所有 HTTP 路径 for _, httpPath := range c.Paths { + // 检查路径配置中是否指定了根目录 if httpPath.Root != "" { + // 创建一个新的文件处理程序 fileHandler := handler.NewFileHandler(handler.FileHandler{ - Root: httpPath.Root, + // 设置文件处理程序的根目录 + Root: httpPath.Root, + // 设置文件处理程序的默认文件 Default: httpPath.Default, }) + // 将文件处理程序注册到 ServerMux 中 s.Handle(httpPath.Path, fileHandler) + // 检查路径配置中是否指定了上游服务器 } else if len(httpPath.Upstreams) != 0 { + // 创建一个新的代理处理程序 proxyHandler := handler.NewProxyHandler(&httpPath) + // 将代理处理程序注册到 ServerMux 中 s.Handle(httpPath.Path, proxyHandler) + // 如果既没有指定根目录也没有指定上游服务器 } else { + // 记录不支持的路径类型错误信息 l.Error("Not supportted server path type :", httpPath.Path) } } + // 返回初始化好的 ServerMux 实例 return s }