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

AM0*_*447 -1 http go

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

像这样的错误日志

2020/05/26 15:34:47 http: 代理错误: net/http: HTTP/1.x 传输连接中断: http: ContentLength=32 with Body length 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)
Run Code Online (Sandbox Code Playgroud)

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

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)
Run Code Online (Sandbox Code Playgroud)

并且错误日志再次显示

2020/05/26 15:49:29 http: 代理错误: net/http: HTTP/1.x 传输连接中断: http: ContentLength=32 with Body length 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)
Run Code Online (Sandbox Code Playgroud)

eud*_*ore 6

http.Request.Body 只能读取一次,需要复制一个新的 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)
Run Code Online (Sandbox Code Playgroud)

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

因此,身体只能从网络中读取一次。如果要重复读取 body 对象,则应全部读取、保存并使用它。