src/erlmld_tcp_acceptor.erl

%%%-------------------------------------------------------------------
%%% @copyright (C) 2016, AdRoll
%%% @doc
%%%
%%%    TCP connection acceptor.  Accepts connections on Socket.  Calls AcceptCallback from
%%%    the acceptor process with successfully-accepted sockets (result ignored).  Does not
%%%    trap exits.  Exits if an accept call fails.  Sets some options before calling
%%%    AcceptCallback.
%%%
%%% @end
%%% Created : 18 Nov 2016 by Mike Watters <mike.watters@adroll.com>
%%%-------------------------------------------------------------------
-module(erlmld_tcp_acceptor).

-export([start_link/3, run/3, listen/2]).

start_link(Regname, Socket, AcceptCallback) ->
    {ok, spawn_link(?MODULE, run, [Regname, Socket, AcceptCallback])}.

run(Regname, ListenSocket, AcceptCallback) ->
    register(Regname, self()),
    loop(ListenSocket, AcceptCallback).

loop(ListenSocket, AcceptCallback) ->
    case gen_tcp:accept(ListenSocket) of
        {ok, AcceptedSocket} ->
            ok =
                inet:setopts(AcceptedSocket,
                             [{delay_send, true},
                              {send_timeout, 10000},
                              {send_timeout_close, true}]),
            AcceptCallback(AcceptedSocket),
            loop(ListenSocket, AcceptCallback);
        {error, Error} ->
            exit({accept_failed, Error})
    end.

%% given an IP specification and listen port, listen on that IP and port, returning the
%% socket and actual port we're listening on.  ip may be 'loopback' (listen on loopback
%% address) and port may be 0 (listen on a random port).
listen(ListenIP, ListenPort) ->
    {ok, Socket} =
        gen_tcp:listen(ListenPort,
                       [binary,
                        {ip, ListenIP},
                        {packet, raw},
                        {reuseaddr, true},
                        {active, false},
                        {backlog, 128}]),
    {ok, ActualPort} = inet:port(Socket),
    {ok, Socket, ActualPort}.