Reverse Proxy in Gin Returning 502 Always

huangapple go评论83阅读模式
英文:

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()
}

huangapple
  • 本文由 发表于 2023年4月14日 09:26:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/76010943.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定