doujiao6507
2013-07-18 04:22
浏览 1.8k
已采纳

当连续发出多个请求时,Golang http请求导致EOF错误

I am trying to debug a very unusual error I am receiving for a simple REST library I wrote.

I am using the standard net/http package to make Get, Post, Put, Delete requests but my tests occasionally fail when I make multiple requests successively. My test looks like this:

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

And I am making the request like this:

// 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
} 

Sometimes it works, but most of the time I get 1 or 2 failures:

--- 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

The failures happen when I make more than 1 request. If I comment out everything except for the PUT request, the tests consistently pass. Once I include a second test, such as GET, one or the other fails (sometimes both pass).

Any help appreciated, and thanks!

Link to the source: http://github.com/chourobin/go.firebase

图片转代码服务由CSDN问答提供 功能建议

我正在尝试调试一个非常罕见的错误,该错误是我为

我正在使用标准的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)
} 
} n  TestPushObject(t * testing.T){
 firebaseRoot:= New(firebase_url)
 msg:=消息{“ testing”,“ 1..2..3”} 
 body,err:= firebaseRoot.Push(  “ /”,msg)
 if err!= nil {
 t.Errorf(“错误:%s”,err)
} 
 t.Logf(“%q”,body)
} 
    
 
 

我正在这样发出请求:

  //发送HTTP请求,返回数据
func(f  * firebaseRoot)SendRequest(方法字符串,路径字符串,主体io.Reader)([] byte,错误){
url:= f.BuildURL(path)
 
 //创建请求
req,err:= http  .NewRequest(方法,URL,正文)
if err!= nil {
返回nil,err 
} 
 
 ///将JSON发送到firebase 
resp,err:= http.DefaultClient.Do(req)\  nif err!= nil {
 return nil,err 
} 
 
if resp.StatusCode!= http.Status 确定{
返回nil,fmt.Errorf(“错误的HTTP响应:%v”,分别为状态)
} 
 
defer.Body.Close()
b,错误:= ioutil.ReadAll(resp。  Body)
if err!= nil {
 return nil,err 
} 
 
return b,nil 
} 
   
 
 

有时可以,但是 大多数情况下,我会遇到1或2个失败:

  ---失败:TestGetObject(0.00秒)
firebase_test.go:53:错误:获取https:// go  -firebase-test.firebaseio.com/1.json:EOF 
firebase_test.go:55:“” 
 
 ---失败:TestPushObject(0.00秒)
firebase_test.go:63:错误:发布https://  /go-firebase-test.firebaseio.com/.json:EOF 
firebase_test.go:65:“” 
FAIL 
exit状态1 
FAIL github.com/chourobin/go.firebase 3.422s 
  <  / pre> 
 
 

当我发出多个请求时,将发生失败。 如果我注释掉除PUT请求之外的所有内容,则测试将始终通过。 一旦我添加了第二个测试(例如GET),一个或另一个失败(有时都通过了)。

任何帮助表示感谢,谢谢!

链接到源: http://github.com/chourobin/go.firebase < / DIV>

  • 写回答
  • 好问题 提建议
  • 关注问题
  • 收藏
  • 邀请回答

3条回答 默认 最新

  • douxue4395 2013-07-18 22:56
    已采纳

    I'm going to guess there is no problem with your code. The most likely cause of your problem is because the server is closing the connection. Rate limiting is one possible reason for this.

    Your test shouldn't be relying on an external service that's very brittle and not hermetic. Instead you should think about spinning up a test server locally.

    已采纳该答案
    评论
    解决 无用
    打赏 举报
  • dongshao1156 2013-09-25 13:17

    I experienced this reliably. You need to set Req.Close to true (the defer on resp.Body.Close() syntax used in the examples is not enough). Like this:

    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
    }
    
    评论
    解决 无用
    打赏 举报
  • dpbsy60000 2014-05-30 20:52

    I agree with the assertion that you shouldn't be hitting outside servers in your unit tests, why not just use the built-in http.Server and serve up the content that you want to test. (There is actually the httptest package to help with this)

    I recently ran into this same problem while trying to crawl sitemaps, and this is what I have found so far:

    Go by default will send requests with the header Connection: Keep-Alive and persist connections for re-use. The problem that I ran into is that the server is responding with Connection: Keep-Alive in the response header and then immediately closing the connection.

    As a little background as to how go implements connections in this case (you can look at the full code in net/http/transport.go). There are two goroutines, one responsible for writing and one responsible for reading (readLoop and writeLoop) In most circumstances readLoop will detect a close on the socket, and close down the connection. The problem here occurs when you initiate another request before the readLoop actually detects the close, and the EOF that it reads get interpreted as an error for that new request rather than a close that occurred prior to the request.

    Given that this is the case the reason why sleeping in between requests works is that it gives readLoop time to detect the close on the connection before your new request and shut it down, so that your new request will initiate a new connection. (And the reason why it would intermittently fail is because there is some amount code running between your requests and depending of scheduling of goroutines, sometimes the EOF will be properly handled before your next request, sometimes not). And the req.Close = true, solution works because it prevents the connection from being re-used.

    There is a ticket related to this situation: https://code.google.com/p/go/issues/detail?id=4677 (and a dupe ticket that I created that allowed me to reliably reproduce this: https://code.google.com/p/go/issues/detail?id=8122)

    评论
    解决 无用
    打赏 举报

相关推荐 更多相似问题