我试图调试我收到了一个简单的REST库我一个非常不寻常的错误中写道.
我使用标准的net/http包来制作Get,Post,Put,Delete请求,但是当我连续发出多个请求时,我的测试偶尔会失败.我的测试看起来像这样:
func TestGetObject(t *testing.T) {
firebaseRoot := New(firebase_url)
body, err := firebaseRoot.Get("1")
if err != nil {
t.Errorf("Error: %s", err)
}
t.Logf("%q", body)
}
func TestPushObject(t *testing.T) {
firebaseRoot := New(firebase_url)
msg := Message{"testing", "1..2..3"}
body, err := firebaseRoot.Push("/", msg)
if err != nil {
t.Errorf("Error: %s", err)
}
t.Logf("%q", body)
}
Run Code Online (Sandbox Code Playgroud)
而我正在提出这样的要求:
// Send HTTP Request, return data
func (f *firebaseRoot) SendRequest(method string, path string, body io.Reader) ([]byte, error) {
url := f.BuildURL(path)
// create a request
req, err := http.NewRequest(method, url, body)
if err != nil {
return nil, err
}
// send JSON to firebase
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("Bad HTTP Response: %v", resp.Status)
}
defer resp.Body.Close()
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
return b, nil
}
Run Code Online (Sandbox Code Playgroud)
有时它可以工作,但大多数时候我得到1或2个失败:
--- FAIL: TestGetObject (0.00 seconds)
firebase_test.go:53: Error: Get https://go-firebase-test.firebaseio.com/1.json: EOF
firebase_test.go:55: ""
--- FAIL: TestPushObject (0.00 seconds)
firebase_test.go:63: Error: Post https://go-firebase-test.firebaseio.com/.json: EOF
firebase_test.go:65: ""
FAIL
exit status 1
FAIL github.com/chourobin/go.firebase 3.422s
Run Code Online (Sandbox Code Playgroud)
当我发出超过1个请求时,会发生故障.如果我注释掉除PUT请求之外的所有内容,则测试会一直通过.一旦我包括第二个测试,例如GET,一个或另一个失败(有时两个都通过).
任何帮助表示赞赏,谢谢!
小智 45
我可靠地体验了这一点 您需要将Req.Close设置为true(对示例中使用的resp.Body.Close()语法的延迟是不够的).像这样:
client := &http.Client{}
req, err := http.NewRequest(method, url, httpBody)
// NOTE this !!
req.Close = true
req.Header.Set("Content-Type", "application/json")
req.SetBasicAuth("user", "pass")
resp, err := client.Do(req)
if err != nil {
// whatever
}
defer resp.Body.Close()
response, err = ioutil.ReadAll(resp.Body)
if err != nil {
// Whatever
}
Run Code Online (Sandbox Code Playgroud)
cgi*_*ing 21
我同意你不应该在单元测试中击中外部服务器的断言,为什么不使用内置的http.Server并提供你想要测试的内容.(实际上有httptest包可以帮助解决这个问题)
我最近在尝试抓取站点地图时遇到了同样的问题,这是我到目前为止所发现的:
默认情况下,Go将发送带有标头Connection: Keep-Alive
和持久连接的请求以便重复使用.我遇到的问题是服务器Connection: Keep-Alive
在响应头中响应,然后立即关闭连接.
作为在这种情况下如何实现连接的一个小背景(您可以查看net/http/transport.go中的完整代码).有两个goroutine,一个负责写入,一个负责读取(readLoop
和writeLoop
)在大多数情况下readLoop
将检测到套接字上的关闭,并关闭连接.当您在readLoop实际检测到关闭之前启动另一个请求时,会发生此问题,并且它读取的EOF将被解释为该新请求的错误,而不是请求之前发生的关闭.
鉴于这种情况,在请求之间休眠的原因是,它提供了readLoop时间来检测新请求之前的连接关闭并将其关闭,以便新请求将启动新连接.(并且它会间歇性地失败的原因是因为在您的请求之间运行了一些代码,并且根据goroutine的调度,有时EOF将在您的下一个请求之前正确处理,有时不会.)并且req.Close = true
,解决方案有效,因为它可以防止连接被重复使用.
有一个与此情况相关的故障单:https://code.google.com/p/go/issues/detail? id = 4677(以及我创建的欺骗票,可让我可靠地重现此信息:https:// code.google.com/p/go/issues/detail?id=8122)
Jer*_*all 17
我猜你的代码没问题.导致问题的最可能原因是服务器正在关闭连接.速率限制是一个可能的原因.
您的测试不应该依赖于非常脆弱且不密封的外部服务.相反,你应该考虑在本地启动测试服务器.