feat(server): 添加网关代理功能

- 实现了 HTTP 请求转发功能,支持请求头和响应头的完整复制
- 添加了目标 URL 构建逻辑,处理路径和查询参数的正确拼接
- 集成了响应体读取和状态码设置功能
- 实现了 WebSocket 连接的反向代理处理
- 添加了缓存机制用于临时存储主机信息
- 集成了日志记录功能用于错误跟踪和调试
main
black1552 2026-03-06 10:31:06 +08:00
parent f130e18b8f
commit f9fae10c76
1 changed files with 76 additions and 0 deletions

76
server/gateway.go Normal file
View File

@ -0,0 +1,76 @@
package server
import (
"fmt"
"io"
"net/http"
"net/http/httputil"
"net/url"
"time"
"git.magicany.cc/black1552/gf-common/log"
"github.com/gogf/gf/v2/net/gclient"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/os/gcache"
"github.com/gogf/gf/v2/os/gctx"
)
func BuildRequest(r *ghttp.Request, host string) {
client := gclient.New()
// 构建目标URL而不是直接复制RequestURI
targetURL := host + r.URL.Path
if r.URL.RawQuery != "" {
targetURL += "?" + r.URL.RawQuery
}
// 复制请求头
for key, values := range r.Header {
for _, value := range values {
client.SetHeader(key, value)
}
}
response, err := client.DoRequest(gctx.New(), r.Method, targetURL, r.GetBody())
if err != nil {
log.Error(gctx.New(), "request error:", err)
panic(fmt.Sprintf("request error: %v", err))
}
defer response.Body.Close()
// 读取响应体
respBody, err := io.ReadAll(response.Body)
if err != nil {
log.Error(gctx.New(), "read response body error:", err)
panic(fmt.Sprintf("read response body error: %v", err))
}
// 复制响应头
for key, values := range response.Header {
for _, value := range values {
r.Response.Header().Add(key, value)
}
}
gcache.Set(gctx.New(), "host", host, 1*time.Second)
// 设置响应状态码并写入响应体
r.Response.Status = response.StatusCode
r.Response.Write(respBody)
}
// ProxyWebSocket 处理 WebSocket 连接的代理
func ProxyWebSocket(r *ghttp.Request, targetHost string) {
// 解析目标主机 URL
targetURL, err := url.Parse(targetHost)
if err != nil {
log.Error(gctx.New(), "parse target host error:", err)
r.Response.WriteStatus(http.StatusInternalServerError)
r.Response.Write([]byte("Invalid target host"))
return
}
// 创建反向代理
proxy := httputil.NewSingleHostReverseProxy(targetURL)
// 修改请求 URL保留原始路径和查询参数
r.URL.Scheme = targetURL.Scheme
r.URL.Host = targetURL.Host
log.Info(gctx.New(), r.GetBodyString())
// 处理 WebSocket 连接
proxy.ServeHTTP(r.Response.Writer, r.Request)
}