testing - How to test for leaking connections in a Go test? -


after i've found leaks in program, i've solved problem. however, now i'm trying find out how " test " leaking connections in go test? question.

i've tried change number of requests in test, didn't matter. no matter do, current number of tcp connections in test stay same.

func testleakingconnections(t *testing.t) {     getter := myhttp.new()      s := newserver(ok)     defer s.close()      cur := tcps(t)     := 0; < 1000; i++ {         r, _ := getter.getwithtimeout(s.url, time.millisecond*10)         r.body.close()     }      tries := 10; tries >= 0; tries-- {         growth := tcps(t) - cur         if growth > 5 {             t.error("leaked")             return         }     } }  // find tcp connections func tcps(t *testing.t) (conns int) {     lsof, err := exec.command("lsof", "-n", "-p", strconv.itoa(os.getpid())).output()     if err != nil {         t.skip("skipping test; error finding or running lsof")     }      _, ls := range strings.split(string(lsof), "\n") {         if strings.contains(ls, "tcp") {             conns++         }     }     return }  func newserver(f http.handlerfunc) *httptest.server {     return httptest.newserver(http.handlerfunc(f)) }  func ok(w http.responsewriter, r *http.request) {     w.header().add("content-type", "application/xml")     io.writestring(w, "<xml></xml>") }  // myhttp package  // ...other code omitted clarification  func (g *getter) getwithtimeout(     url string,     timeout time.duration, ) (     *http.response, error, ) {     // leaking part     // moving out of here stop leaks     transport := http.transport{         dialcontext: (&net.dialer{             timeout: timeout,         }).dialcontext,         tlshandshaketimeout:   timeout,         responseheadertimeout: timeout,         expectcontinuetimeout: timeout,     }      client := http.client{         timeout:   timeout,         transport: &transport,     }      return client.get(url) }  // fixture worker package  // outside code injects getter fixture_worker this: getter := myhttp.new()  // newwithtimeout creates new fetcher timeout threshold func newwithtimeout(     getter myhttp.httpgetter,     fetchurl string,     timeout time.duration, ) *fetcher {     return &fetcher{getter, fetchurl, timeout} }  // fetch fetches fixture xml func (f *fetcher) fetch() (*parser.fixturexml, error) {     res, err := f.getter.getwithtimeout(f.fetchurl, f.timeout)     if err != nil {         if res != nil && res.body != nil {             res.body.close()         }         return nil, errors.wrap(err, errfetch.error())     }     defer res.body.close()      ioutil.readall(res.body)      return &parser.fixturexml{}, nil } 

leaking conns gif


output of fixture worker lsof: https://pastebin.com/fdukpyse

output of test: https://pastebin.com/ugpk0czn

test never leaks whereas fixture worker leaks.

fixture worker using same code test, request http gets using myhttp package.

if want test represent reality, need use in same manner outside of tests. in case you're not reading of responses, , transport happens preventing lost connections because it's discarding them possible since can't reused.

reading response use connection, , state it's "leaked". need handle errors in cases, , need close() response body. pattern handle http response , make sure it's closed simple, , doesn't require testing (see what happen if don't close response.body in golang?)

    resp, err := getwithtimeout(s.url)     if err != nil {         t.fatal(err)     }     ioutil.readall(resp.body)     resp.body.close() 

this arguably of limited usefulness, since common bugs result improper error , response handling, , you're not testing because test needs correctly in first place.

the remaining problem here getwithtimeout method returns error value and valid http response, contradicts http package documentation user's expectations. if you're going insert error, better handle response @ same point ensure body closed , discarded.

finally, of getwithtimeout superfluous, since not creating transports every time incorrect, creating http.client every request unnecessary meant reused , safe concurrent use.


Comments

Popular posts from this blog

Is there a better way to structure post methods in Class Based Views -

performance - Why is XCHG reg, reg a 3 micro-op instruction on modern Intel architectures? -

jquery - Responsive Navbar with Sub Navbar -