Gin 中的反向代理总是返回 502

问题描述 投票:0回答:1

关于我在 go 中编写的这项服务,我需要一些建议。总之,该服务使用 Gin 和自定义的 NewSingleHostReverseProxy 中间件来将文件上传请求代理到 S3。这是代理中间件的代码示例:

package middleware

import (
    "crypto/tls"
    "fmt"
    "log"
    "net/http"
    "net/http/httputil"
    "net/url"

    "github.com/gin-gonic/gin"

    "my-proxy/internal/service"
)

func Proxy(proxyService *service.ProxyService) gin.HandlerFunc {
    return func(c *gin.Context) {
        proxyHeaders, _ := proxyService.NewProxyHeaders(c, bucket, key)

        remote, err := url.Parse(fmt.Sprintf("https://%s.myendpoint.com", bucket))
        if err != nil {
            panic(err)
        }

        proxy := httputil.NewSingleHostReverseProxy(remote)

        proxy.Transport = &http.Transport{
            TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
        }

        proxy.Director = func(req *http.Request) {
            req.Header = proxyHeaders
            req.Host = remote.Host
            req.URL.Scheme = remote.Scheme
            req.URL.Host = remote.Host
            req.URL.Path = key
        }

        proxy.ModifyResponse = func(r *http.Response) error {
            log.Println(r.Status + "\n")
            return nil
        }

        proxy.ServeHTTP(c.Writer, c.Request)
    }
}

这是我得到的输出:

2023/04/13 15:05:56 200 OK

2023/04/13 15:05:56 http: proxy error: http: invalid Read on closed Body

\[GIN\] 2023/04/13 - 15:05:56 | 502 | 865.355034ms | ::1 | PUT

关于什么可能导致 502 由封闭体上的读取引起的任何想法。如您所见,s3 的响应在 ModifyResponse 回调中成功。

目前我在帖子中的代码正在成功执行文件上传(我在 s3 中看到它并且返回的响应是 200)。我主要是想找出关闭后读取身体的原因。

go amazon-s3 webserver reverse-proxy
1个回答
1
投票

单独使用代理是可以的。我猜是处理程序或其他中间件在尝试读取已被代理读取并关闭的请求主体后执行的错误。

由于您没有提供其他代码,我们无法判断出什么问题。下面是一个演示,以显示可能出错的地方。像这样使用它:

$ go run main.go
$ curl -i -X POST http://localhost:8080/upload-bad -d "hello"
$ curl -i -X POST http://localhost:8080/upload-good -d "hello"
package main

import (
    "fmt"
    "log"
    "net/http"
    "net/http/httptest"
    "net/http/httputil"
    "net/url"

    "github.com/gin-gonic/gin"
)

func Proxy(target *url.URL) gin.HandlerFunc {
    return func(c *gin.Context) {
        proxy := httputil.NewSingleHostReverseProxy(target)

        proxy.ServeHTTP(c.Writer, c.Request)
    }
}

func main() {
    backendServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        buf, err := httputil.DumpRequest(r, true)
        if err != nil {
            log.Printf("[backend] failed to dump request: %v", err)
        } else {
            log.Printf("[backend]\n%s", buf)
        }
        fmt.Fprintln(w, "this call was relayed by the reverse proxy")
    }))
    defer backendServer.Close()

    rpURL, err := url.Parse(backendServer.URL)
    if err != nil {
        log.Fatal(err)
    }

    r := gin.Default()

    // == begin wrong setup
    // To apply the middleware on "/upload-bad" only.
    // It's weird to use Proxy as a middleware.
    r.Use(gin.HandlerFunc(func(c *gin.Context) {
        if c.Request.URL.Path == "/upload-bad" {
            Proxy(rpURL)(c)
        }
    }))
    // This handler tries to read the request body which already been
    // closed by the proxy middleware.
    r.POST("/upload-bad", func(c *gin.Context) {
        // err will be: failed to dump request: http: invalid Read on closed Body
        buf, err := httputil.DumpRequest(c.Request, true)
        if err != nil {
            log.Printf("[handler] failed to dump request: %v", err)
        } else {
            log.Printf("[handler]: %s", buf)
        }
    })
    // == end wrong setup

    // I think it should be used as a normal handler instead.
    r.POST("/upload-good", Proxy(rpURL))
    _ = r.Run()
}
© www.soinside.com 2019 - 2024. All rights reserved.