From f9fae10c76bc1570c79c634b3cd18a1bbe349b92 Mon Sep 17 00:00:00 2001 From: black1552 Date: Fri, 6 Mar 2026 10:31:06 +0800 Subject: [PATCH] =?UTF-8?q?feat(server):=20=E6=B7=BB=E5=8A=A0=E7=BD=91?= =?UTF-8?q?=E5=85=B3=E4=BB=A3=E7=90=86=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 实现了 HTTP 请求转发功能,支持请求头和响应头的完整复制 - 添加了目标 URL 构建逻辑,处理路径和查询参数的正确拼接 - 集成了响应体读取和状态码设置功能 - 实现了 WebSocket 连接的反向代理处理 - 添加了缓存机制用于临时存储主机信息 - 集成了日志记录功能用于错误跟踪和调试 --- server/gateway.go | 76 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 server/gateway.go diff --git a/server/gateway.go b/server/gateway.go new file mode 100644 index 0000000..39ea1d4 --- /dev/null +++ b/server/gateway.go @@ -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) +}