写入响应正文等待客户端读取Go?

时间:2014-11-17 20:27:08

标签: http go

我在Go中使用httptest包来测试我的应用程序。最近我注意到我的一个测试未能完成,因为我的测试没有阅读回复的主体

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")
    }
}

上面的代码阻止了对s.Close()的延迟调用,因为测试服务器上有未完成的http连接。我的服务器有一些写入正文的代码(使用http.ResponseWriter接口)。原来这个代码实际上是阻塞的,直到我在测试中读取了正文。

这样的电话就行了。

ioutil.ReadAll(res.Body)

我不介意在我的测试中进行此调用,但我担心行为不当的客户端可能会导致此行为并消耗服务器资源。有谁知道这里发生了什么?这是预期的行为还是仅仅是测试框架的问题?

谢谢!

1 个答案:

答案 0 :(得分:2)

来自http文档:

  

完成后,客户端必须关闭响应正文:

     

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

因此,您的测试违反了http.Client的合同;添加defer res.Close()应该让服务器知道响应通道已关闭,并停止写入它。 ioutil.ReadAll的效果是相同的,因为一旦达到EOF就会关闭Reader

在野外,如果客户端打开连接并发送请求,则服务器只发送响应然后关闭连接。它不会等待客户端关闭它。如果客户端太慢而无法确认服务器发送的IP数据包,操作系统最终会关闭连接超时,http.Server将通过退出服务请求的goroutine来响应,没有小猫受到伤害。