diff --git a/addr.go b/addr.go index f793a21..cfffc7c 100644 --- a/addr.go +++ b/addr.go @@ -18,15 +18,3 @@ func ResolveAddr(network, address string) (net.Addr, error) { return net.ResolveUnixAddr(network, address) } } - -// conn is a struct that stores a raddr to get around: -// * https://github.com/golang/go/issues/9661#issuecomment-71043147 -// * https://gist.github.com/jbenet/5c191d698fe9ec58c49d -type conn struct { - net.Conn - raddr net.Addr -} - -func (c *conn) RemoteAddr() net.Addr { - return c.raddr -} diff --git a/impl_unix.go b/impl_unix.go index 0b0943c..3d725fa 100644 --- a/impl_unix.go +++ b/impl_unix.go @@ -163,6 +163,7 @@ func dial(dialer net.Dialer, netw, addr string) (c net.Conn, err error) { if err = file.Close(); err != nil { syscall.Close(fd) + c.Close() return nil, err } @@ -170,19 +171,6 @@ func dial(dialer net.Dialer, netw, addr string) (c net.Conn, err error) { return c, err } -// there's a rare case where dial returns successfully but for some reason the -// RemoteAddr is not yet set. So, since we know what raddr should be, we just -// wrap it. This is not ideal in that sometimes getpeername() may return a -// different addr. But until this is fixed, best way to do it. -// * https://gist.github.com/jbenet/5c191d698fe9ec58c49d -// * https://github.com/golang/go/issues/9661#issuecomment-71043147 -func wrapConnWithRemoteAddr(c net.Conn, raddr net.Addr) net.Conn { - if c.RemoteAddr() == nil { - return &conn{Conn: c, raddr: raddr} - } - return c // it's fine, no need to wrap. -} - func listen(netw, addr string) (fd int, err error) { var ( family int @@ -256,6 +244,7 @@ func listenStream(netw, addr string) (l net.Listener, err error) { if err = file.Close(); err != nil { syscall.Close(fd) + l.Close() return nil, err } @@ -280,6 +269,7 @@ func listenPacket(netw, addr string) (p net.PacketConn, err error) { if err = file.Close(); err != nil { syscall.Close(fd) + p.Close() return nil, err } @@ -327,6 +317,7 @@ func connect(fd int, ra syscall.Sockaddr, deadline time.Time) error { if err != nil { return err } + defer poller.Close() for { if err = poller.WaitWrite(deadline); err != nil { diff --git a/reuse_test.go b/reuse_test.go index 1607077..cd61343 100644 --- a/reuse_test.go +++ b/reuse_test.go @@ -7,6 +7,7 @@ import ( "io" "net" "os" + "os/exec" "strings" "sync" "testing" @@ -19,6 +20,7 @@ func echo(c net.Conn) { } func packetEcho(c net.PacketConn) { + defer c.Close() buf := make([]byte, 65536) for { n, addr, err := c.ReadFrom(buf) @@ -501,8 +503,8 @@ func TestDialRespectsTimeout(t *testing.T) { go func() { c, err := d.Dial(network, raddr) if err == nil { - errs <- errors.New("should've not connected") c.Close() + errs <- errors.New("should've not connected") return } close(errs) // success! @@ -534,14 +536,37 @@ func TestUnixNotSupported(t *testing.T) { addr := tcase[1] t.Log("testing", network, addr) - _, err := Listen(network, addr) + l, err := Listen(network, addr) if err == nil { + l.Close() t.Fatal("unix supported") continue } } } +func TestOpenFDs(t *testing.T) { + // this is a totally ad-hoc limit. test harnesses may add fds. + // but if this is really much higher than 20, there's obviously leaks. + limit := 20 + start := time.Now() + for countOpenFiles(t) > limit { + <-time.After(time.Second) + t.Log("open fds:", countOpenFiles(t)) + if time.Now().Sub(start) > (time.Second * 15) { + t.Error("fd leak!") + } + } +} + +func countOpenFiles(t *testing.T) int { + out, err := exec.Command("/bin/sh", "-c", fmt.Sprintf("lsof -p %v", os.Getpid())).Output() + if err != nil { + t.Fatal(err) + } + return bytes.Count(out, []byte("\n")) +} + func getPort(a net.Addr) string { if a == nil { return ""