diff --git a/README.md b/README.md index d626194..1d42e14 100644 --- a/README.md +++ b/README.md @@ -8,10 +8,119 @@ - 支持静态文件 - Proxy - 支持rewrite +- 支持TLS +- 支持端口复用 + +## 配置 +```json +{ + "logging" :{ + "appenders": { + "out" :{ + "type": "file", + "options":{ + "file": "gohttpd.log" + } + } + }, + "categories": { + "default": { + "appenders": [ "out" ], + "level": "debug" + } + } + }, + "admin" : { + "name": "admin", + "port" : 8088, + "username": "admin", + "password": "admin", + "paths": [ + { + "path": "/", + "root": "./adminui", + "default": "index.html" + } + ] + }, + "servers":[{ + "port" : 8080, + "name":"test", + "paths":[ + { + "path": "/", + "root": "/home/kingecg/code/gohttp/public/", + "default": "index.html" + }, + { + "path": "/ws", + "upstreams":["http://localhost:3000"], + "pathrewrite": { + "replace": "/ws", + "with": "/" + } + } + ] + }] +} +``` +- logging 日志配置 +- admin 管理后台配置 +- servers 服务器配置 + +日志采用自己实现的类log4j库,目前只支持console 和file两种appeder + +servers 配置 + +- port 端口 +- name 服务器名称 +- paths 路径配置 +- certfile 证书文件 +- keyfile 证书密钥文件 + +paths 配置 + +- path 路径 +- root 根目录 +- default 默认文件 +- upstreams 代理地址 +- pathrewrite 路径重写 + + ## Packages ### Server -RestMux 提供Restful API注册功能的 ServerMux +RestMux 提供 +- Restful API注册功能的 ServerMux +- Route +- Url路径参数解析(形如:/user/:id) +- 中间件 +- server管理 +### model +提供模型定义 + +### admin +管理后台api + +### handler + +目录 hander + +提供文件和代理两种handler +其中proxy handler 提供简单的负载均衡和会话粘滞功能 + +## 构建 + +```bash +make clean && make build +``` +在target目录下生成可执行文件 + +## 运行 + +```bash +./gohttpd +``` diff --git a/admin/admin.go b/admin/admin.go index cf53a80..1d16944 100644 --- a/admin/admin.go +++ b/admin/admin.go @@ -1,12 +1,19 @@ package admin import ( + "encoding/json" "net/http" + "os" + "runtime" "git.pyer.club/kingecg/gohttpd/model" "git.pyer.club/kingecg/gohttpd/server" ) +type RunStatus struct { + Goroutines int `json:"goroutines"` +} + func about(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) w.Write([]byte("About Page")) @@ -22,8 +29,41 @@ func setConfig(w http.ResponseWriter, r *http.Request) { return } t := data.(model.HttpServerConfig) - model.SetServerConfig(&t) + err := model.SetServerConfig(&t) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + w.Write(server.NewErrorResult(err)) + return + } + defer func() { + os.Exit(0) + }() + w.WriteHeader(http.StatusOK) + w.Write(server.NewSuccessResult(t)) +} +func getServerConfigure(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + ctxData := ctx.Value(server.RequestCtxKey("ctxData")).(map[string]interface{}) + id, ok := ctxData["id"] + if ok { + data := model.GetServerConfig(id.(string)) + configContent, _ := json.Marshal(data) + w.WriteHeader(http.StatusOK) + w.Write(server.NewSuccessResult(configContent)) + } else { + http.NotFound(w, r) + } +} + +func getStatus(w http.ResponseWriter, r *http.Request) { + //获取当前进程的goroutines数量 + ret := RunStatus{ + Goroutines: runtime.NumGoroutine(), + } + rs, _ := json.Marshal(ret) + w.WriteHeader(http.StatusOK) + w.Write(rs) } var AdminServerMux *server.RestMux @@ -34,5 +74,7 @@ func init() { 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.Use(server.BasicAuth) } diff --git a/gohttp.go b/gohttp.go index 19472e8..e0cf1ee 100644 --- a/gohttp.go +++ b/gohttp.go @@ -9,6 +9,7 @@ import ( handler "git.pyer.club/kingecg/gohttpd/hander" "git.pyer.club/kingecg/gohttpd/model" "git.pyer.club/kingecg/gohttpd/server" + "git.pyer.club/kingecg/gohttpd/utils" "git.pyer.club/kingecg/gologger" ) @@ -72,11 +73,8 @@ func (g *GoHttp) assembleServerMux(p []model.HttpPath) http.Handler { func (g *GoHttp) Stop() {} -func LoadConfig(configPath string) { - cpath := configPath - if cpath == "" { - cpath = GetExecDir() + "/config.json" - } +func LoadConfig() { + cpath := utils.NormalizePath("./config.json") // read content from cpath content, _ := os.ReadFile(cpath) @@ -84,7 +82,7 @@ func LoadConfig(configPath string) { //normalize path in config for _, appender := range model.Config.Logging.Appenders { if appender.Type == "file" { - appender.Options["file"] = NormalizePath(appender.Options["file"].(string)) + appender.Options["file"] = utils.NormalizePath(appender.Options["file"].(string)) } } normalizeServer(model.Config.Admin) diff --git a/main.go b/main.go index 52b5d7c..3744630 100644 --- a/main.go +++ b/main.go @@ -10,7 +10,7 @@ import ( ) func main() { - LoadConfig("") + LoadConfig() l := gologger.GetLogger("main") defer func() { if err := recover(); err != nil { diff --git a/model/model.go b/model/model.go index 8979ef5..396d306 100644 --- a/model/model.go +++ b/model/model.go @@ -1,6 +1,12 @@ package model -import "git.pyer.club/kingecg/gologger" +import ( + "encoding/json" + "os" + + "git.pyer.club/kingecg/gohttpd/utils" + "git.pyer.club/kingecg/gologger" +) type HttpPath struct { Path string `json:"path"` @@ -44,14 +50,31 @@ func GetConfig() *GoHttpdConfig { return &Config } -func SetServerConfig(c *HttpServerConfig) { +func SetServerConfig(c *HttpServerConfig) error { + l := gologger.GetLogger("model") + updated := false for i, s := range Config.Servers { if s.Name == c.Name { Config.Servers[i] = c - return + updated = true + break } } - Config.Servers = append(Config.Servers, c) + if !updated { + Config.Servers = append(Config.Servers, c) + } + configFile := utils.NormalizePath("./config.json") + configsStr, err := json.Marshal(Config) + if err != nil { + l.Error("Save config error:", err) + return err + } + werr := os.WriteFile(configFile, configsStr, 0644) + if werr != nil { + l.Error("Save config error:", werr) + return werr + } + return nil } func GetServerConfig(name string) *HttpServerConfig { diff --git a/server/result.go b/server/result.go new file mode 100644 index 0000000..470f38f --- /dev/null +++ b/server/result.go @@ -0,0 +1,36 @@ +package server + +import "encoding/json" + +type RestResult struct { + Code int `json:"code"` + Message string `json:"message"` + Error string `json:"error"` + Data interface{} `json:"data"` +} + +var ResultSuccess = RestResult{ + Code: 200, + Message: "success", + Data: nil, +} + +var ResultError = RestResult{ + Code: 500, + Message: "error", + Error: "error", +} + +func NewErrorResult(err error) []byte { + result := ResultError + result.Error = err.Error() + ret, _ := json.Marshal(result) + return ret +} + +func NewSuccessResult(data interface{}) []byte { + result := ResultSuccess + result.Data = data + ret, _ := json.Marshal(result) + return ret +} diff --git a/util.go b/utils/util.go similarity index 96% rename from util.go rename to utils/util.go index 106eaa2..b39e99e 100644 --- a/util.go +++ b/utils/util.go @@ -1,4 +1,4 @@ -package main +package utils import ( "os" diff --git a/util_test.go b/utils/util_test.go similarity index 98% rename from util_test.go rename to utils/util_test.go index d571aa0..3f003d1 100644 --- a/util_test.go +++ b/utils/util_test.go @@ -1,4 +1,4 @@ -package main +package utils import ( "path/filepath"