github.com/TBD54566975/ftl@v0.219.0/internal/bind/bind_allocator.go (about)

     1  package bind
     2  
     3  import (
     4  	"net"
     5  	"net/url"
     6  	"strconv"
     7  
     8  	"github.com/alecthomas/atomic"
     9  )
    10  
    11  type BindAllocator struct {
    12  	baseURL *url.URL
    13  	port    atomic.Int32
    14  }
    15  
    16  func NewBindAllocator(url *url.URL) (*BindAllocator, error) {
    17  	_, portStr, err := net.SplitHostPort(url.Host)
    18  	if err != nil {
    19  		return nil, err
    20  	}
    21  
    22  	port, err := strconv.Atoi(portStr)
    23  	if err != nil {
    24  		return nil, err
    25  	}
    26  
    27  	return &BindAllocator{
    28  		baseURL: url,
    29  		port:    atomic.NewInt32(int32(port) - 1), //nolint:gosec
    30  	}, nil
    31  }
    32  
    33  func (b *BindAllocator) Next() *url.URL {
    34  	var l *net.TCPListener
    35  	var err error
    36  	for {
    37  		b.port.Add(1)
    38  		l, err = net.ListenTCP("tcp", &net.TCPAddr{IP: net.ParseIP(b.baseURL.Hostname()), Port: int(b.port.Load())})
    39  
    40  		if err != nil {
    41  			continue
    42  		}
    43  		_ = l.Close()
    44  
    45  		newURL := *b.baseURL
    46  		newURL.Host = net.JoinHostPort(b.baseURL.Hostname(), strconv.Itoa(int(b.port.Load())))
    47  		return &newURL
    48  	}
    49  }