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  }