wayfarer/test/support/port_tracker.ex
James Harton a381ca4b34
All checks were successful
continuous-integration/drone/push Build is passing
feat: add proxying. (#7)
Reviewed-on: https://code.harton.nz/james/wayfarer/pulls/7
Co-authored-by: James Harton <james@harton.nz>
Co-committed-by: James Harton <james@harton.nz>
2023-11-19 17:56:07 +13:00

61 lines
1.3 KiB
Elixir

defmodule Support.PortTracker do
@moduledoc """
Generates port numbers which aren't currently being used by any processes.
"""
use GenServer
require Logger
defmacro __using__(_) do
quote do
defp random_port(opts \\ []) do
unquote(__MODULE__).allocate(opts)
end
end
end
@doc false
@spec start_link(any) :: GenServer.on_start()
def start_link(arg), do: GenServer.start_link(__MODULE__, arg, name: __MODULE__)
@doc false
@impl true
def init(_) do
table = :ets.new(__MODULE__, [:public, :named_table, :set])
{:ok, table}
end
@doc false
@impl true
def handle_cast({:monitor, pid}, table) do
Process.monitor(pid)
{:noreply, table}
end
@doc false
@impl true
def handle_info({:DOWN, _, :process, pid, _}, table) do
:ets.match_delete(table, {:_, pid})
{:noreply, table}
end
@doc """
Allocate an unused random port between `min_port` and `max_port`.
"""
def allocate(opts) do
port = random_port(opts)
if :ets.insert_new(__MODULE__, {port, self()}) do
GenServer.cast(__MODULE__, {:monitor, self()})
port
else
allocate(opts)
end
end
defp random_port(opts) do
max_port = Keyword.get(opts, :max_port, 0xFFFF)
min_port = Keyword.get(opts, :min_port, 20_000)
:rand.uniform(max_port - min_port) + min_port
end
end