Golang ServeHTTP在处理POST请求时出现代理错误EOF。

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

Golang ServeHTTP make proxy error EOF on POST request

问题

我正在使用gin和ServeHTTP开发代理服务器。实际上,GET和OPTIONS请求都能正常工作。但是当我尝试发送多个POST请求时,每两个请求中就会出现EOF错误。
我已经测试过在没有代理服务的情况下重复发送请求,结果是正常工作的,所以问题应该出在我的代码中。

编辑:

  1. 我已经使用https://ptsv2.com/测试了POST代理,并且所有请求的响应都返回200状态码。
// main.go

package main

import (
	"fmt"
	"../pkg/api"
)

func main() {
	fmt.Println("Starting server")
	api.InitServer()
}

// routes.go
package api

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

const serviceUrl = "http://localhost:8732"

func InitServer() {
	router := gin.Default()
	defineRoutes(router)
	router.Run()
}

func defineRoutes(router *gin.Engine) {
	router.GET("/ping", Ping)
	router.POST("/*any", Proxy(serviceUrl))
}
// controller.go

package api

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httputil"
	"net/url"
	"strconv"
	"github.com/gin-gonic/gin"
)

type transport struct {
    http.RoundTripper
}

func (t *transport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
    resp, err = t.RoundTripper.RoundTrip(req)
    if err != nil {
        // 在这里出现EOF错误
        return nil, err
    }
    b, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        return nil, err
    }
    err = resp.Body.Close()
    if err != nil {
        return nil, err
    }
    b = bytes.Replace(b, []byte("server"), []byte("schmerver"), -1)
    body := ioutil.NopCloser(bytes.NewReader(b))

    response := resp
    response.Body = body


    response.ContentLength = int64(len(b))
    response.Header.Set("Content-Length", strconv.Itoa(len(b)))
    response.Header.Set("Access-Control-Allow-Origin", "*")

    return response, nil
}

func Proxy(targetUrl string) gin.HandlerFunc {
    fn := func(c *gin.Context) {
        remote, err := url.Parse(targetUrl)
        if err != nil {
            panic(err)
        }
        proxy := httputil.NewSingleHostReverseProxy(remote)

        proxy.Director = func(req *http.Request) {
            req.Header = c.Request.Header
            req.Host = remote.Host
            req.URL.Scheme = remote.Scheme
            req.URL.Host = remote.Host
            req.Close = true // True / False same result
        }
        proxy.Transport = &transport{http.DefaultTransport}
        proxy.ServeHTTP(c.Writer, c.Request)
    }
    return fn
}
```

<details>
<summary>英文:</summary>

I’m working on proxy server with gin and ServeHTTP. 
Actually GET and OPTIONS request works well. But when I trying multiples POST request I get EOF error one in two request.
I’ve test to make **repeat request without proxy service and its work** well, so there is something not working in my code. 

Edit : 
1. I have test POST proxy with https://ptsv2.com/ and all request response return 200 status code.



// main.go

package main

import (
"fmt"
"../pkg/api"
)

func main() {
fmt.Println("Starting server")
api.InitServer()
}


// routes.go
package api

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

const serviceUrl = "http://localhost:8732"

func InitServer() {
router := gin.Default()
defineRoutes(router)
router.Run()
}

func defineRoutes(router *gin.Engine) {
router.GET("/ping", Ping)
router.POST("/*any", Proxy(serviceUrl))
}


// controller.go

package api

import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
"net/http/httputil"
"net/url"
"strconv"
"github.com/gin-gonic/gin"
)

type transport struct {
http.RoundTripper
}

func (t *transport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
resp, err = t.RoundTripper.RoundTrip(req)
if err != nil {
// EOF ERROR HERE
return nil, err
}
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
err = resp.Body.Close()
if err != nil {
return nil, err
}
b = bytes.Replace(b, []byte("server"), []byte("schmerver"), -1)
body := ioutil.NopCloser(bytes.NewReader(b))

response := resp
response.Body = body


response.ContentLength = int64(len(b))
response.Header.Set(&quot;Content-Length&quot;, strconv.Itoa(len(b)))
response.Header.Set(&quot;Access-Control-Allow-Origin&quot;, &quot;*&quot;)

return response, nil

}

func Proxy(targetUrl string) gin.HandlerFunc {
fn := func(c *gin.Context) {
remote, err := url.Parse(targetUrl)
if err != nil {
panic(err)
}
proxy := httputil.NewSingleHostReverseProxy(remote)

    proxy.Director = func(req *http.Request) {
        req.Header = c.Request.Header
        req.Host = remote.Host
        req.URL.Scheme = remote.Scheme
        req.URL.Host = remote.Host
        req.Close = true // True / False same result
    }
    proxy.Transport = &amp;transport{http.DefaultTransport}
    proxy.ServeHTTP(c.Writer, c.Request)
}
return fn

}


</details>


# 答案1
**得分**: 1

## 结论

你的代码没有 bug,它是有效的。可能是你的网络设置有问题。

## 解释

我下载了你的代码,并在本地后端服务器上进行了测试。它可以正常工作。

[![enter image description here][1]][1]

[![enter image description here][2]][2]

## 附录

后端服务器代码

```go
package main

import (
	"fmt"

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

func main() {
	fmt.Println("Starting server")
	router := gin.Default()
	router.POST("/*any", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"mes": "hello",
		})
	})
	router.Run(":8888")
}

```


  [1]: https://i.stack.imgur.com/kzP50.png
  [2]: https://i.stack.imgur.com/Vp18s.png

<details>
<summary>英文:</summary>


## conclusion

your code have no bug. it works. maybe your network setting is wrong.

## explain 

I download your code and test it with a local backend server. It works.

[![enter image description here][1]][1]

[![enter image description here][2]][2]

## appendix

backend server code 

```
package main

import (
	&quot;fmt&quot;

	&quot;github.com/gin-gonic/gin&quot;
)

func main() {
	fmt.Println(&quot;Starting server&quot;)
	router := gin.Default()
	router.POST(&quot;/*any&quot;, func(c *gin.Context) {
		c.JSON(200, gin.H{
			&quot;mes&quot;: &quot;hello&quot;,
		})
	})
	router.Run(&quot;:8888&quot;)
}

```


  [1]: https://i.stack.imgur.com/kzP50.png
  [2]: https://i.stack.imgur.com/Vp18s.png

</details>



huangapple
  • 本文由 发表于 2022年1月29日 02:48:07
  • 转载请务必保留本文链接:https://go.coder-hub.com/70898805.html
匿名

发表评论

匿名网友

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

确定