github.com/cristalhq/netx@v0.0.0-20221116164110-442313ef3309/utils.go (about) 1 package netx 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "math/rand" 8 "net" 9 "os" 10 "time" 11 ) 12 13 // EmptyPort looks for an empty port to listen on local interface. 14 func EmptyPort() (int, error) { 15 for p := 30000 + rand.Intn(1000); p < 60000; p++ { 16 l, err := net.Listen("tcp", fmt.Sprintf(":%d", p)) 17 if err == nil { 18 l.Close() 19 return p, nil 20 } 21 } 22 return 0, errors.New("cannot find available port") 23 } 24 25 // WaitPort until you port is free. 26 func WaitPort(ctx context.Context, port int) error { 27 return WaitAddr(ctx, fmt.Sprintf(":%d", port)) 28 } 29 30 // WaitAddr until you addr is free. 31 func WaitAddr(ctx context.Context, addr string) error { 32 errCh := make(chan error, 1) 33 go func() { 34 for { 35 select { 36 case <-ctx.Done(): 37 errCh <- fmt.Errorf("cannot connect to %s", addr) 38 return 39 default: 40 c, err := net.Dial("tcp", addr) 41 if err == nil { 42 c.Close() 43 errCh <- nil 44 return 45 } 46 time.Sleep(100 * time.Millisecond) 47 } 48 } 49 }() 50 51 select { 52 case <-ctx.Done(): 53 return ctx.Err() 54 case err := <-errCh: 55 return err 56 } 57 } 58 59 // newError same as os.NewSyscallError but shorter. 60 func newError(syscall string, err error) error { 61 return os.NewSyscallError(syscall, err) 62 }