Set UDP conn to non-blocking on Linux to fix Shutdown()

The call to conn.File() causes Go to call dup() and then set the resulting FD
to be blocking. This sets the FD back to non-blocking, allowing Shutdown() to
work properly.

Fixes #279.
This commit is contained in:
Ryan Leavengood 2015-12-01 17:15:42 -05:00
parent f520760857
commit 7b51cba64a
2 changed files with 27 additions and 3 deletions

View File

@ -39,9 +39,15 @@ func AnotherHelloServer(w ResponseWriter, req *Msg) {
} }
func RunLocalUDPServer(laddr string) (*Server, string, error) { func RunLocalUDPServer(laddr string) (*Server, string, error) {
server, l, _, err := RunLocalUDPServerWithFinChan(laddr)
return server, l, err
}
func RunLocalUDPServerWithFinChan(laddr string) (*Server, string, chan struct{}, error) {
pc, err := net.ListenPacket("udp", laddr) pc, err := net.ListenPacket("udp", laddr)
if err != nil { if err != nil {
return nil, "", err return nil, "", nil, err
} }
server := &Server{PacketConn: pc, ReadTimeout: time.Hour, WriteTimeout: time.Hour} server := &Server{PacketConn: pc, ReadTimeout: time.Hour, WriteTimeout: time.Hour}
@ -49,13 +55,16 @@ func RunLocalUDPServer(laddr string) (*Server, string, error) {
waitLock.Lock() waitLock.Lock()
server.NotifyStartedFunc = waitLock.Unlock server.NotifyStartedFunc = waitLock.Unlock
fin := make(chan struct{}, 0)
go func() { go func() {
server.ActivateAndServe() server.ActivateAndServe()
close(fin)
pc.Close() pc.Close()
}() }()
waitLock.Lock() waitLock.Lock()
return server, pc.LocalAddr().String(), nil return server, pc.LocalAddr().String(), fin, nil
} }
func RunLocalUDPServerUnsafe(laddr string) (*Server, string, error) { func RunLocalUDPServerUnsafe(laddr string) (*Server, string, error) {
@ -448,7 +457,7 @@ func TestHandlerCloseTCP(t *testing.T) {
} }
func TestShutdownUDP(t *testing.T) { func TestShutdownUDP(t *testing.T) {
s, _, err := RunLocalUDPServer("127.0.0.1:0") s, _, fin, err := RunLocalUDPServerWithFinChan("127.0.0.1:0")
if err != nil { if err != nil {
t.Fatalf("unable to run test server: %v", err) t.Fatalf("unable to run test server: %v", err)
} }
@ -456,6 +465,11 @@ func TestShutdownUDP(t *testing.T) {
if err != nil { if err != nil {
t.Errorf("could not shutdown test UDP server, %v", err) t.Errorf("could not shutdown test UDP server, %v", err)
} }
select {
case <-fin:
case <-time.After(2 * time.Second):
t.Error("Could not shutdown test UDP server. Gave up waiting")
}
} }
type ExampleFrameLengthWriter struct { type ExampleFrameLengthWriter struct {

View File

@ -24,6 +24,12 @@ func setUDPSocketOptions4(conn *net.UDPConn) error {
if err := syscall.SetsockoptInt(int(file.Fd()), syscall.IPPROTO_IP, syscall.IP_PKTINFO, 1); err != nil { if err := syscall.SetsockoptInt(int(file.Fd()), syscall.IPPROTO_IP, syscall.IP_PKTINFO, 1); err != nil {
return err return err
} }
// Calling File() above results in the connection becoming blocking, we must fix that.
// See https://github.com/miekg/dns/issues/279
err = syscall.SetNonblock(int(file.Fd()), true)
if err != nil {
return err
}
return nil return nil
} }
@ -36,6 +42,10 @@ func setUDPSocketOptions6(conn *net.UDPConn) error {
if err := syscall.SetsockoptInt(int(file.Fd()), syscall.IPPROTO_IPV6, syscall.IPV6_RECVPKTINFO, 1); err != nil { if err := syscall.SetsockoptInt(int(file.Fd()), syscall.IPPROTO_IPV6, syscall.IPV6_RECVPKTINFO, 1); err != nil {
return err return err
} }
err = syscall.SetNonblock(int(file.Fd()), true)
if err != nil {
return err
}
return nil return nil
} }