Write to response body waits for client to read in Go?

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

Write to response body waits for client to read in Go?

问题

我正在使用Go语言的httptest包来测试我的应用程序。最近我注意到我的一个测试无法完成,因为我的测试没有读取响应的主体。

上面的代码在延迟调用s.Close()时被阻塞,因为测试服务器上有未完成的HTTP连接。我的服务器有一些代码,使用http.ResponseWriter接口将数据写入主体。结果发现,这段代码实际上会阻塞,直到我在测试中读取主体。

像这样的调用可以解决问题。

ioutil.ReadAll(res.Body)

我不介意在我的测试中进行这个调用,但我担心一个行为不端的客户端可能会导致这种行为并消耗服务器资源。有人知道这里发生了什么吗?这是在实际环境中预期的行为,还是只是测试框架的问题?

谢谢!

英文:

I'm using the httptest package in Go to test my application. Recently I noticed that one of my tests was failing to finish because my test wasn't reading the body of the response

func Test_AnyTest(t *testing.T) {
    serve := newServer()
    s := httptest.NewServer(serve)
    defer s.Close()

    testUrl := "/any/old/url"

    c := &http.Client{}
    r, _ := http.NewRequest("GET", s.URL+testUrl, new(bytes.Buffer))
    r.Header.Add("If-None-Match", cacheVersion)
    res, _ := c.Do(r)

    if res.StatusCode == 304 {
	    t.Errorf("Should not have got 304")
    }
}

The above code was blocking on the deferred call to s.Close() because there were outstanding http connections on the test server. My server has some code that was writing to the body (using the http.ResponseWriter interface). Turns out that this code was actually blocking until I read the body in my test.

A call like this did the trick.

ioutil.ReadAll(res.Body)

I don't mind making this call in my tests but I am concerned that a client that misbehaves could cause this behaviour and consume server resources. Does anyone know what's happening here? Is this expected behaviour in the wild or is it just a problem with the test framework?

Thanks!

答案1

得分: 2

http文档中:

客户端在使用完响应体后必须关闭它:

resp, err := http.Get("http://example.com/")
if err != nil {
	// 处理错误
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
// ...

所以你的测试违反了http.Client的约定;添加一个defer res.Close()应该让服务器知道响应通道已关闭,并停止向其写入。你的ioutil.ReadAll的效果是一样的,因为它在达到EOF时关闭了Reader

在实际情况中,如果客户端打开连接并发送请求,服务器只发送响应,然后关闭连接。它不会等待客户端关闭连接。如果客户端在服务器发送的IP数据包上ACK的速度太慢,操作系统最终会处理超时连接,并且http.Server会通过退出为请求提供服务的goroutine来响应,没有任何损害。

英文:

From the http docs:

> The client must close the response body when finished with it:
>
>
> resp, err := http.Get("http://example.com/")
> if err != nil {
> // handle error
> }
> defer resp.Body.Close()
> body, err := ioutil.ReadAll(resp.Body)
> // ...
>

So your test is violating the contract of http.Client; adding a defer res.Close() should let the server know that the response channel is closed, and stop writing to it. The effect of your ioutil.ReadAll was the same, as it closes the Reader once it reaches EOF.

In the wild, if a client opens a connection and sends a request, the server just sends the response and then closes the connection. It doesn't wait around for the client to close it. If the client is too slow to ACK the IP packets that the server sends, the OS will eventually take care of timing out the connection, and the http.Server will respond by exiting the goroutine that served the request, no kittens harmed.

huangapple
  • 本文由 发表于 2014年11月18日 04:27:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/26981344.html
匿名

发表评论

匿名网友

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

确定