英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论