add api
This commit is contained in:
parent
5a60e898ed
commit
6e63b5f0af
111
README.md
111
README.md
|
@ -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
|
||||||
|
```
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
10
gohttp.go
10
gohttp.go
|
@ -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)
|
||||||
|
|
2
main.go
2
main.go
|
@ -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 {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package main
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
|
@ -1,4 +1,4 @@
|
||||||
package main
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"path/filepath"
|
"path/filepath"
|
Loading…
Reference in New Issue