612d68f56f
Go + Vue 管理台,SQLite 持久化;支持隧道/域名穿透、域名转发插件链、访客认证、 IP 规则、API Key、Docker 单镜像部署;配置通过 Web 系统设置管理,无需 YAML 文件。 Co-authored-by: Cursor <cursoragent@cursor.com>
116 lines
2.5 KiB
Go
116 lines
2.5 KiB
Go
package proxy
|
|
|
|
import (
|
|
"bytes"
|
|
"io"
|
|
"net/http"
|
|
|
|
"github.com/wormhole/wormhole/internal/forwardplugin"
|
|
"github.com/wormhole/wormhole/internal/store"
|
|
)
|
|
|
|
func pluginEntriesFromStore(plugins store.ForwardPlugins) []forwardplugin.PluginEntry {
|
|
if len(plugins) == 0 {
|
|
return nil
|
|
}
|
|
out := make([]forwardplugin.PluginEntry, len(plugins))
|
|
for i, p := range plugins {
|
|
out[i] = forwardplugin.PluginEntry{
|
|
Type: p.Type,
|
|
Enabled: p.Enabled,
|
|
Config: p.Config,
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
func runForwardPluginPipeline(
|
|
w http.ResponseWriter,
|
|
clientReq *http.Request,
|
|
df *store.DomainForward,
|
|
upstreamReq *http.Request,
|
|
transport http.RoundTripper,
|
|
) error {
|
|
chain, err := forwardplugin.BuildChain(pluginEntriesFromStore(df.Plugins))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
forwardplugin.BindForwardID(chain, df.ID)
|
|
|
|
ctx := &forwardplugin.Context{
|
|
ForwardID: df.ID,
|
|
ClientReq: clientReq,
|
|
}
|
|
|
|
outReq := upstreamReq.Clone(clientReq.Context())
|
|
if chain.NeedsBody() {
|
|
outReq.Header.Del("Accept-Encoding")
|
|
}
|
|
|
|
short, err := chain.HandleRequest(ctx, outReq)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var resp *forwardplugin.BufferedResponse
|
|
if short && ctx.CachedResp != nil {
|
|
resp = ctx.CachedResp.Clone()
|
|
} else {
|
|
resp, err = fetchUpstreamResponse(outReq, transport, chain.NeedsBody())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
chain.StoreRawResponses(ctx, resp)
|
|
}
|
|
|
|
if err := chain.HandleResponse(ctx, resp); err != nil {
|
|
return err
|
|
}
|
|
writeBufferedResponse(w, resp)
|
|
return nil
|
|
}
|
|
|
|
func fetchUpstreamResponse(req *http.Request, transport http.RoundTripper, _ bool) (*forwardplugin.BufferedResponse, error) {
|
|
client := &http.Client{Transport: transport}
|
|
upstreamResp, err := client.Do(req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer upstreamResp.Body.Close()
|
|
|
|
body, err := io.ReadAll(upstreamResp.Body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &forwardplugin.BufferedResponse{
|
|
StatusCode: upstreamResp.StatusCode,
|
|
Header: upstreamResp.Header.Clone(),
|
|
Body: body,
|
|
}, nil
|
|
}
|
|
|
|
func writeBufferedResponse(w http.ResponseWriter, resp *forwardplugin.BufferedResponse) {
|
|
for k, vals := range resp.Header {
|
|
for _, v := range vals {
|
|
w.Header().Add(k, v)
|
|
}
|
|
}
|
|
if resp.StatusCode == 0 {
|
|
resp.StatusCode = http.StatusOK
|
|
}
|
|
w.WriteHeader(resp.StatusCode)
|
|
if len(resp.Body) > 0 {
|
|
_, _ = io.Copy(w, bytes.NewReader(resp.Body))
|
|
}
|
|
}
|
|
|
|
func hasEnabledPlugins(plugins store.ForwardPlugins) bool {
|
|
for _, p := range plugins {
|
|
if p.Enabled {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|