关于我在 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 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()
}