blob: ec6a41786b237eddb30bfc8b65c633a1fb024418 [file] [log] [blame]
Tim Windelschmidt6d33a432025-02-04 14:34:25 +01001// Copyright The Monogon Project Authors.
Serge Bazanski549b72b2021-01-07 14:54:19 +01002// SPDX-License-Identifier: Apache-2.0
Serge Bazanski549b72b2021-01-07 14:54:19 +01003
4package freeport
5
6import (
7 "io"
8 "net"
9)
10
Serge Bazanski216fe7b2021-05-21 18:36:16 +020011// AllocateTCPPort allocates a TCP port on the looopback address, and starts a
12// temporary listener on it. That listener is returned to the caller alongside with
13// the allocated port number. The listener must be closed right before the port is
14// used by the caller. This naturally still leaves a race condition window where
15// that port number might be snatched up by some other process, but there doesn't
16// seem to be a better way to do this.
Serge Bazanski549b72b2021-01-07 14:54:19 +010017func AllocateTCPPort() (uint16, io.Closer, error) {
18 addr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:0")
19 if err != nil {
20 return 0, nil, err
21 }
22
23 l, err := net.ListenTCP("tcp", addr)
24 if err != nil {
25 return 0, nil, err
26 }
27 return uint16(l.Addr().(*net.TCPAddr).Port), l, nil
28}
29
Serge Bazanski216fe7b2021-05-21 18:36:16 +020030// MustConsume takes the result of AllocateTCPPort, closes the listener and returns
31// the allocated port. If anything goes wrong (port could not be allocated or
32// closed) it will panic.
Serge Bazanski549b72b2021-01-07 14:54:19 +010033func MustConsume(port uint16, lis io.Closer, err error) int {
34 if err != nil {
35 panic(err)
36 }
37 if err := lis.Close(); err != nil {
38 panic(err)
39 }
40 return int(port)
41}