612d68f56f
Go + Vue 管理台,SQLite 持久化;支持隧道/域名穿透、域名转发插件链、访客认证、 IP 规则、API Key、Docker 单镜像部署;配置通过 Web 系统设置管理,无需 YAML 文件。 Co-authored-by: Cursor <cursoragent@cursor.com>
119 lines
2.2 KiB
Go
119 lines
2.2 KiB
Go
package forwardplugin
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"sync"
|
|
)
|
|
|
|
type PluginEntry struct {
|
|
Type string `json:"type"`
|
|
Enabled bool `json:"enabled"`
|
|
Config json.RawMessage `json:"config"`
|
|
}
|
|
|
|
type Plugin interface {
|
|
Type() string
|
|
ValidateConfig(raw json.RawMessage) error
|
|
NeedsBody() bool
|
|
HandleRequest(ctx *Context, req *http.Request) (shortCircuit bool, err error)
|
|
HandleResponse(ctx *Context, resp *BufferedResponse) error
|
|
}
|
|
|
|
type PluginFactory func(config json.RawMessage) (Plugin, error)
|
|
|
|
type RawStorer interface {
|
|
StoreRaw(ctx *Context, resp *BufferedResponse)
|
|
}
|
|
|
|
var (
|
|
registryMu sync.RWMutex
|
|
registry = map[string]PluginFactory{}
|
|
)
|
|
|
|
func Register(typ string, factory PluginFactory) {
|
|
registryMu.Lock()
|
|
defer registryMu.Unlock()
|
|
registry[typ] = factory
|
|
}
|
|
|
|
func getFactory(typ string) (PluginFactory, bool) {
|
|
registryMu.RLock()
|
|
defer registryMu.RUnlock()
|
|
f, ok := registry[typ]
|
|
return f, ok
|
|
}
|
|
|
|
type Context struct {
|
|
ForwardID uint
|
|
ClientReq *http.Request
|
|
CachedResp *BufferedResponse
|
|
}
|
|
|
|
type BufferedResponse struct {
|
|
StatusCode int
|
|
Header http.Header
|
|
Body []byte
|
|
FromCache bool
|
|
}
|
|
|
|
func (r *BufferedResponse) Clone() *BufferedResponse {
|
|
if r == nil {
|
|
return nil
|
|
}
|
|
h := r.Header.Clone()
|
|
return &BufferedResponse{
|
|
StatusCode: r.StatusCode,
|
|
Header: h,
|
|
Body: append([]byte(nil), r.Body...),
|
|
FromCache: r.FromCache,
|
|
}
|
|
}
|
|
|
|
type Chain struct {
|
|
plugins []Plugin
|
|
}
|
|
|
|
func (c *Chain) Empty() bool {
|
|
return c == nil || len(c.plugins) == 0
|
|
}
|
|
|
|
func (c *Chain) NeedsBody() bool {
|
|
for _, p := range c.plugins {
|
|
if p.NeedsBody() {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (c *Chain) HandleRequest(ctx *Context, req *http.Request) (bool, error) {
|
|
for _, p := range c.plugins {
|
|
short, err := p.HandleRequest(ctx, req)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
if short {
|
|
return true, nil
|
|
}
|
|
}
|
|
return false, nil
|
|
}
|
|
|
|
func (c *Chain) StoreRawResponses(ctx *Context, resp *BufferedResponse) {
|
|
for _, p := range c.plugins {
|
|
if st, ok := p.(RawStorer); ok {
|
|
st.StoreRaw(ctx, resp)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (c *Chain) HandleResponse(ctx *Context, resp *BufferedResponse) error {
|
|
for _, p := range c.plugins {
|
|
if err := p.HandleResponse(ctx, resp); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|