英文:
Reverse Proxy in Gin Returning 502 Always
问题
我需要一些关于我正在使用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)。我主要是想找出请求体在关闭后被读取的原因。
英文:
I'm in need of some advice when it comes to this service I am writing in go. In summary, the service uses Gin along with a custom NewSingleHostReverseProxy middleware for proxying file upload request to S3. Here is an example of the code for the proxy middleware:
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)
}
}
And here is the output I'm getting:
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
Any idea as to what could be causing the 502 to caused by the read on closed body. As you see, the response from s3 is successful in the ModifyResponse callback.
Currently the code I put in my post is executing the file upload successfully (I see it in s3 and the response coming back is 200). I'm mainly trying to uncover the cause for the body read after close.
答案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()
}
希望对你有所帮助!
英文:
The proxy alone is okay. I guess what's wrong is the handler or other middlewares executed after it attempts to read the request body, which has already been read and closed by the proxy.
Since you haven't provided other code, we can not tell what's wrong. Below is a demo to show what could be wrong. Use it like this:
$ 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()
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论