//go:build linux || darwin || dragonfly || freebsd || netbsd || openbsd || solaris || illumos package pool import ( "errors" "io" "net" "syscall" "time" ) var errUnexpectedRead = errors.New("unexpected read from socket") // connCheck checks if the connection is still alive and if there is data in the socket // it will try to peek at the next byte without consuming it since we may want to work with it // later on (e.g. push notifications) func connCheck(conn net.Conn) error { // Reset previous timeout. _ = conn.SetDeadline(time.Time{}) sysConn, ok := conn.(syscall.Conn) if !ok { return nil } rawConn, err := sysConn.SyscallConn() if err != nil { return err } var sysErr error if err := rawConn.Read(func(fd uintptr) bool { var buf [1]byte // Use MSG_PEEK to peek at data without consuming it n, _, err := syscall.Recvfrom(int(fd), buf[:], syscall.MSG_PEEK|syscall.MSG_DONTWAIT) switch { case n == 0 && err == nil: sysErr = io.EOF case n > 0: sysErr = errUnexpectedRead case err == syscall.EAGAIN || err == syscall.EWOULDBLOCK: sysErr = nil default: sysErr = err } return true }); err != nil { return err } return sysErr } // maybeHasData checks if there is data in the socket without consuming it func maybeHasData(conn net.Conn) bool { return connCheck(conn) == errUnexpectedRead }