package server import ( "net/http" "net/http/httputil" "net/url" "strings" "git.magicany.cc/black1552/gf-common/log" "github.com/gogf/gf/v2/net/ghttp" "github.com/gogf/gf/v2/os/gctx" ) // hasProtocol 检查字符串是否包含协议前缀 func hasProtocol(s string) bool { return strings.HasPrefix(s, "http://") || strings.HasPrefix(s, "https://") || strings.HasPrefix(s, "ws://") || strings.HasPrefix(s, "wss://") } // 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 } } 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) // 自定义 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()) }