From 95623f3802e0d0f6b7696c33c03ad76ab58dd833 Mon Sep 17 00:00:00 2001 From: black1552 Date: Fri, 6 Mar 2026 17:00:37 +0800 Subject: [PATCH] =?UTF-8?q?refactor(gateway):=20=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E5=8F=8D=E5=90=91=E4=BB=A3=E7=90=86=E5=AE=9E=E7=8E=B0=E4=BB=A5?= =?UTF-8?q?=E6=94=AF=E6=8C=81=20WebSocket=20=E5=92=8C=20HTTP=20=E8=AF=B7?= =?UTF-8?q?=E6=B1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加 gclient 和 gstr 依赖包用于 HTTP 客户端操作 - 实现新的 BuildRequest 函数,使用 gclient 处理普通 HTTP 请求 - 分离 WebSocket 请求处理到独立的 proxyWebSocket 函数 - 移除旧的 hasProtocol 函数和相关逻辑 - 添加完整的请求头和响应头复制机制 - 实现响应状态码和响应体的正确传递 - 简化 WebSocket 代理逻辑,使用标准反向代理处理 --- server/gateway.go | 86 ++++++++++++++++++++++++++++------------------- 1 file changed, 51 insertions(+), 35 deletions(-) diff --git a/server/gateway.go b/server/gateway.go index de71bea..f609fe2 100644 --- a/server/gateway.go +++ b/server/gateway.go @@ -1,14 +1,18 @@ package server import ( + "fmt" + "io" "net/http" "net/http/httputil" "net/url" "strings" "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/gctx" + "github.com/gogf/gf/v2/text/gstr" ) // hasProtocol 检查字符串是否包含协议前缀 @@ -22,17 +26,49 @@ func hasProtocol(s string) bool { // BuildRequest 反向代理请求到指定主机 // 自动支持所有 HTTP 方法及 WebSocket 连接 func BuildRequest(r *ghttp.Request, host string) { - // 自动添加协议前缀 - targetHost := host - if !hasProtocol(host) { - // 根据请求判断协议:WebSocket 用 ws,普通 HTTP 用 http - if r.Header.Get("Upgrade") == "websocket" { - targetHost = "ws://" + host - } else { - targetHost = "http://" + host + if gstr.Contains(r.RequestURI, "/ws") { + proxyWebSocket(r, host) + return + } + 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) + } + } + // 设置响应状态码并写入响应体 + 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) @@ -41,33 +77,13 @@ func BuildRequest(r *ghttp.Request, host string) { return } + // 创建反向代理 proxy := httputil.NewSingleHostReverseProxy(targetURL) - // 自定义 Director 来设置目标 URL,保留原始请求的所有参数 - proxy.Director = func(req *http.Request) { - req.URL.Scheme = targetURL.Scheme - req.URL.Host = targetURL.Host - req.URL.Path = r.URL.Path - req.URL.RawQuery = r.URL.RawQuery - // 保留原始 Host 头 - req.Host = r.Host - } - - // 错误处理 - proxy.ErrorHandler = func(w http.ResponseWriter, req *http.Request, err error) { - log.Error(gctx.New(), "proxy error:", err) - w.WriteHeader(http.StatusBadGateway) - w.Write([]byte("Bad Gateway")) - } - proxy.ModifyResponse = func(resp *http.Response) error { - // 获取原始响应的 Header - for k, v := range resp.Header { - r.Response.Header().Set(k, v[0]) - } - return nil - } - // ServeHTTP 会自动处理 WebSocket 升级和所有 HTTP 方法 - // 使用 RawWriter() 获取原始的 http.ResponseWriter,避免 gf 框架的封装影响 - proxy.ServeHTTP(r.Response.RawWriter(), r.Request) - r.Response.WriteJson(r.Response.RawWriter()) + // 修改请求 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) }