Current section
Files
Jump to
Current section
Files
src/grpcbox_socket.erl
-module(grpcbox_socket).
-behaviour(gen_server).
-export([start_link/3]).
-export([init/1,
handle_call/3,
handle_cast/2,
handle_info/2,
code_change/3,
terminate/2]).
%% public api
start_link(Pool, ListenOpts, AcceptorOpts) ->
gen_server:start_link(?MODULE, [Pool, ListenOpts, AcceptorOpts], []).
%% gen_server api
init([Pool, ListenOpts, PoolOpts]) ->
Port = maps:get(port, ListenOpts, 8080),
IPAddress = maps:get(ip, ListenOpts, {0,0,0,0}),
AcceptorPoolSize = maps:get(size, PoolOpts, 10),
SocketOpts = maps:get(socket_options, ListenOpts, [{reuseaddr, true},
{nodelay, true},
{reuseaddr, true},
{backlog, 32768},
{keepalive, true}]),
%% Trapping exit so can close socket in terminate/2
_ = process_flag(trap_exit, true),
Opts = [{active, false}, {mode, binary}, {packet, raw}, {ip, IPAddress} | SocketOpts],
case gen_tcp:listen(Port, Opts) of
{ok, Socket} ->
%% acceptor could close the socket if there is a problem
MRef = monitor(port, Socket),
grpcbox_pool:accept_socket(Pool, Socket, AcceptorPoolSize),
{ok, {Socket, MRef}};
{error, Reason} ->
{stop, Reason}
end.
handle_call(Req, _, State) ->
{stop, {bad_call, Req}, State}.
handle_cast(Req, State) ->
{stop, {bad_cast, Req}, State}.
handle_info({'DOWN', MRef, port, Socket, Reason}, {Socket, MRef} = State) ->
{stop, Reason, State};
handle_info(_, State) ->
{noreply, State}.
code_change(_, State, _) ->
{ok, State}.
terminate(_, {Socket, MRef}) ->
%% Socket may already be down but need to ensure it is closed to avoid
%% eaddrinuse error on restart
case demonitor(MRef, [flush, info]) of
true -> gen_tcp:close(Socket);
false -> ok
end.