http.Request.Clone()不是深度克隆吗?

时间:2020-05-26 07:53:34

标签: http go

我想要一个Request,并使用ParseForm检查一些数据,然后将相同的数据发送给proxy,如果我这样做,我们有问题

这样的错误日志

2020/05/26 15:34:47 http:代理错误:net / http:HTTP / 1.x传输连接断开:http:ContentLength = 32,主体长度为0

所以我终于弄清楚了,ParseForm()很好地关闭了Request .body

这是工作代理代码

    url, _ := url.Parse(config.GetGameHost())

    proxy := httputil.NewSingleHostReverseProxy(url)
    r.URL.Host = url.Host
    r.URL.Scheme = url.Scheme
    r.Header.Set("X-Forwarded-Host", r.Header.Get("Host"))
    r.Host = url.Host
    proxy.ServeHTTP(w, r)

所以,我想我也需要克隆深度克隆请求,也要获取我的数据,也要发送原始请求,也要代理 我也编辑我的代码

nr := r.Clone(r.Context())
nr.ParseForm()

url, _ := url.Parse(config.GetGameHost())

proxy := httputil.NewSingleHostReverseProxy(url)

r.URL.Host = url.Host
r.URL.Scheme = url.Scheme
r.Header.Set("X-Forwarded-Host", r.Header.Get("Host"))
r.Host = url.Host

proxy.ServeHTTP(w, r)

并再次显示错误日志

2020/05/26 15:49:29 http:代理错误:net / http:HTTP / 1.x传输连接断开:http:ContentLength = 32,主体长度为0

Clone()不是深度克隆,还是我做错了?

------------这项工作------------

body, err := ioutil.ReadAll(r.Body)
if err != nil {
    // ...
}
url, _ := url.Parse(config.GetGameHost())

r2 := r.Clone(r.Context())

r.Body = ioutil.NopCloser(bytes.NewReader(body))
r2.Body = ioutil.NopCloser(bytes.NewReader(body))

r.ParseForm()

proxy := httputil.NewSingleHostReverseProxy(url)
proxy.ServeHTTP(w, r2)

1 个答案:

答案 0 :(得分:5)

http.Request.Body只能读取一次,需要复制一个新的正文。

body,err := ioutil.ReadAll(r)
if err != nil {
    // ...
}
r2 := r.Clone(r.Context())
// clone body
r.Body = ioutil.NopCloser(bytes.NewReader(body))
r2.Body = ioutil.NopCloser(bytes.NewReader(body))

// parse r1, proxy r2
r.ParseForm()
proxy.ServerHTTP(w, r2)

body对象默认为net.Conn多层封装。每次使用io.Reader接口读取4kb时,都可以节省内存使用量,通常在从网络读取时使用。

因此,只能从网络读取一次正文。如果要重复读取主体对象,则应全部读取,保存并使用。