This commit is contained in:
程广 2023-12-13 17:59:14 +08:00
parent 5a60e898ed
commit 6e63b5f0af
8 changed files with 223 additions and 15 deletions

111
README.md
View File

@ -8,10 +8,119 @@
- 支持静态文件 - 支持静态文件
- Proxy - Proxy
- 支持rewrite - 支持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 ## Packages
### Server ### 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
```

View File

@ -1,12 +1,19 @@
package admin package admin
import ( import (
"encoding/json"
"net/http" "net/http"
"os"
"runtime"
"git.pyer.club/kingecg/gohttpd/model" "git.pyer.club/kingecg/gohttpd/model"
"git.pyer.club/kingecg/gohttpd/server" "git.pyer.club/kingecg/gohttpd/server"
) )
type RunStatus struct {
Goroutines int `json:"goroutines"`
}
func about(w http.ResponseWriter, r *http.Request) { func about(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
w.Write([]byte("About Page")) w.Write([]byte("About Page"))
@ -22,8 +29,41 @@ func setConfig(w http.ResponseWriter, r *http.Request) {
return return
} }
t := data.(model.HttpServerConfig) 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 var AdminServerMux *server.RestMux
@ -34,5 +74,7 @@ func init() {
AdminServerMux.HandleFunc("GET", "/about", http.HandlerFunc(about)) AdminServerMux.HandleFunc("GET", "/about", http.HandlerFunc(about))
postConfigRoute := AdminServerMux.HandleFunc("POST", "/config", http.HandlerFunc(setConfig)) postConfigRoute := AdminServerMux.HandleFunc("POST", "/config", http.HandlerFunc(setConfig))
postConfigRoute.Add(server.Parse[model.HttpServerConfig]) 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)
} }

View File

@ -9,6 +9,7 @@ import (
handler "git.pyer.club/kingecg/gohttpd/hander" handler "git.pyer.club/kingecg/gohttpd/hander"
"git.pyer.club/kingecg/gohttpd/model" "git.pyer.club/kingecg/gohttpd/model"
"git.pyer.club/kingecg/gohttpd/server" "git.pyer.club/kingecg/gohttpd/server"
"git.pyer.club/kingecg/gohttpd/utils"
"git.pyer.club/kingecg/gologger" "git.pyer.club/kingecg/gologger"
) )
@ -72,11 +73,8 @@ func (g *GoHttp) assembleServerMux(p []model.HttpPath) http.Handler {
func (g *GoHttp) Stop() {} func (g *GoHttp) Stop() {}
func LoadConfig(configPath string) { func LoadConfig() {
cpath := configPath cpath := utils.NormalizePath("./config.json")
if cpath == "" {
cpath = GetExecDir() + "/config.json"
}
// read content from cpath // read content from cpath
content, _ := os.ReadFile(cpath) content, _ := os.ReadFile(cpath)
@ -84,7 +82,7 @@ func LoadConfig(configPath string) {
//normalize path in 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"] = NormalizePath(appender.Options["file"].(string)) appender.Options["file"] = utils.NormalizePath(appender.Options["file"].(string))
} }
} }
normalizeServer(model.Config.Admin) normalizeServer(model.Config.Admin)

View File

@ -10,7 +10,7 @@ import (
) )
func main() { func main() {
LoadConfig("") LoadConfig()
l := gologger.GetLogger("main") l := gologger.GetLogger("main")
defer func() { defer func() {
if err := recover(); err != nil { if err := recover(); err != nil {

View File

@ -1,6 +1,12 @@
package model 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 { type HttpPath struct {
Path string `json:"path"` Path string `json:"path"`
@ -44,14 +50,31 @@ func GetConfig() *GoHttpdConfig {
return &Config return &Config
} }
func SetServerConfig(c *HttpServerConfig) { func SetServerConfig(c *HttpServerConfig) error {
l := gologger.GetLogger("model")
updated := false
for i, s := range Config.Servers { for i, s := range Config.Servers {
if s.Name == c.Name { if s.Name == c.Name {
Config.Servers[i] = c 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 { func GetServerConfig(name string) *HttpServerConfig {

36
server/result.go Normal file
View File

@ -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
}

View File

@ -1,4 +1,4 @@
package main package utils
import ( import (
"os" "os"

View File

@ -1,4 +1,4 @@
package main package utils
import ( import (
"path/filepath" "path/filepath"