src/whitecap_acceptor.erl

-module(whitecap_acceptor).
-include("whitecap.hrl").

-compile(inline).
-compile({inline_size, 512}).

%% internal
-export([
    init/3,
    start_link/2
]).

%% public
-spec start_link(atom(), map()) ->
    {ok, pid()}.

start_link(Name, Opts) ->
    proc_lib:start_link(?MODULE, init, [Name, Opts, self()]).

-spec init(atom(), map(), pid()) ->
    no_return() | ok.

init(Name, Opts, Parent) ->
    case safe_register(Name) of
        true ->
            Ip = maps:get(ip, Opts, {0, 0, 0, 0}),
            Port = maps:get(port, Opts, 8080),
            case listen(Ip, Port) of
                {ok, LSocket} ->
                    proc_lib:init_ack(Parent, {ok, self()}),
                    loop(LSocket, Opts);
                {error, _} = Error ->
                    proc_lib:init_ack(Parent, Error)
            end;
        {false, Pid} ->
            proc_lib:init_ack(Parent, {error, {already_started, Pid}})
    end.

%% private
listen(Ip, Port) ->
    Options = [
        binary,
        {active, false},
        {backlog, 4096},
        {nodelay, true},
        {reuseaddr, true},
        {ip, Ip}
    ] ++ so_reuseport(),

    gen_tcp:listen(Port, Options).

loop(LSocket, Opts) ->
    case gen_tcp:accept(LSocket) of
        {ok, Socket} ->
            telemetry:execute([whitecap, connections, accept], #{}),
            whitecap_connection:start_link(Socket, Opts),
            loop(LSocket, Opts);
        {error, closed} ->
            ok;
        {error, Reason} ->
            logger:warning("whitecap accept error: ~p", [Reason]),
            telemetry:execute([whitecap, connections, accept_error],
                #{}, #{reason => Reason}),
            loop(LSocket, Opts)
    end.

safe_register(Name) ->
    try register(Name, self()) of
        true ->
            true
    catch
        _:_ ->
            {false, whereis(Name)}
    end.

so_reuseport() ->
    case os:type() of
        {unix, linux} ->
            [{raw, 1, 15, <<1:32/native>>}];
        {unix, darwin} ->
            [{raw, 16#ffff, 16#0200, <<1:32/native>>}];
        _ ->
            []
    end.