gin-gonic / gin

Gin is a HTTP web framework written in Go (Golang). It features a Martini-like API with much better performance -- up to 40 times faster. If you need smashing performance, get yourself some Gin.
https://gin-gonic.com/
MIT License
78.47k stars 8k forks source link

Reverse proxy not working #821

Open MrXu opened 7 years ago

MrXu commented 7 years ago

I wrote a simple reverse proxy with Gin. The code is listed below.

func NewMultiHostProxy(target_urls []string) gin.HandlerFunc {
    var urls []*url.URL
    for i := 0; i < len(target_urls); i++ {
        target, err := url.Parse(target_urls[i])
        if err != nil {
            fmt.Errorf("Error parsing url")
            return nil
        }
        urls = append(urls, target)
    }
    return func(c *gin.Context) {
        director := func(req *http.Request) {
            target := urls[rand.Int()%len(urls)]
            req.URL.Scheme = target.Scheme
            req.URL.Host = target.Host
            req.URL.Path = target.Path
            req.Header.Set("X-GoProxy", "GoProxy")
            if target.RawQuery == "" || req.URL.RawQuery == "" {
                req.URL.RawQuery = target.RawQuery + req.URL.RawQuery
            } else {
                req.URL.RawQuery = target.RawQuery + "&" + req.URL.RawQuery
            }
            log.Print(req.URL)
        }
        proxy := &httputil.ReverseProxy{Director: director}
        proxy.ServeHTTP(c.Writer, c.Request)
    }
}
  1. When I try to send GET request to it, the request is successfully sent over to a remote server. However, although the url is correct, it returns 404 and Nginx in remote server log 404 as well.
  2. When I try to send POST request to it, golang complains http: proxy error: http: ContentLength=452831 with Body length 0

May I know what may cause this problem?

tsirolnik commented 7 years ago

Seems like you're sending an incorrect Content-Length value

Dieterbe commented 7 years ago

Hi, I don't use gin but i'm troubleshooting the same problem as your number 2, with macaron. I think in my case (and likely yours) the problem is that the macaron/gin/whatever reads the request body for post requests (it does this to load in the form variables) (and likely closes it too), and so when proxying the request, there's nothing to read in the body anymore. so body length becomes 0, but the header is still the original header. so the options are A) do proxying in such a way that the request body isn't already read when you try to proxy. B) recreate the body based on the form data C) switch the request body before it gets read the first time, by something that can be read multiple times. something like:

        buffer, _ := ioutil.ReadAll(c.Req.Request.Body)
        c.Req.Request.Body = ioutil.NopCloser(bytes.NewBuffer(buffer))

I'm attempting C right now.