%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%%
%% %CopyrightEnd%
%%
-module(diameter_peer).
-behaviour(gen_server).
%% Interface towards transport modules ...
-export([recv/2,
up/1,
up/2]).
%% ... and the stack.
-export([start/3,
send/2,
close/1,
abort/1,
notify/2]).
%% Server start.
-export([start_link/0]).
%% gen_server callbacks
-export([init/1,
terminate/2,
handle_call/3,
handle_cast/2,
handle_info/2,
code_change/3]).
%% debug
-export([state/0,
uptime/0]).
-include_lib("diameter/include/diameter.hrl").
-include("diameter_internal.hrl").
%% Registered name of the server.
-define(SERVER, ?MODULE).
%% Server state.
-record(state, {id = now()}).
%%% ---------------------------------------------------------------------------
%%% # notify/2
%%% ---------------------------------------------------------------------------
notify(SvcName, T) ->
rpc:abcast(nodes(), ?SERVER, {notify, SvcName, T}).
%%% ---------------------------------------------------------------------------
%%% # start/3
%%% ---------------------------------------------------------------------------
start(T, Opts, #diameter_service{} = Svc) ->
{Mod, Cfg} = split_transport(Opts),
apply(Mod, start, [T, Svc, Cfg]).
%%% ---------------------------------------------------------------------------
%%% # up/[12]
%%% ---------------------------------------------------------------------------
up(Pid) -> %% accepting transport
ifc_send(Pid, {self(), connected}).
up(Pid, Remote) -> %% connecting transport
ifc_send(Pid, {self(), connected, Remote}).
%%% ---------------------------------------------------------------------------
%%% # recv/2
%%% ---------------------------------------------------------------------------
recv(Pid, Pkt) ->
ifc_send(Pid, {recv, Pkt}).
%%% ---------------------------------------------------------------------------
%%% # send/2
%%% ---------------------------------------------------------------------------
send(Pid, #diameter_packet{transport_data = undefined,
bin = Bin}) ->
send(Pid, Bin);
send(Pid, Pkt) ->
ifc_send(Pid, {send, Pkt}).
%%% ---------------------------------------------------------------------------
%%% # close/1
%%% ---------------------------------------------------------------------------
close(Pid) ->
ifc_send(Pid, {close, self()}).
%%% ---------------------------------------------------------------------------
%%% # abort/1
%%% ---------------------------------------------------------------------------
abort(Pid) ->
exit(Pid, shutdown).
%% ---------------------------------------------------------------------------
%% ---------------------------------------------------------------------------
start_link() ->
ServerName = {local, ?SERVER},
Module = ?MODULE,
Args = [],
Options = [{spawn_opt, diameter_lib:spawn_opts(server, [])}],
gen_server:start_link(ServerName, Module, Args, Options).
state() ->
call(state).
uptime() ->
call(uptime).
%%% ----------------------------------------------------------
%%% # init(Role)
%%% ----------------------------------------------------------
init([]) ->
{ok, #state{}}.
%%% ----------------------------------------------------------
%%% # handle_call(Request, From, State)
%%% ----------------------------------------------------------
handle_call(state, _, State) ->
{reply, State, State};
handle_call(uptime, _, #state{id = Time} = State) ->
{reply, diameter_lib:now_diff(Time), State};
handle_call(Req, From, State) ->
?UNEXPECTED([Req, From]),
{reply, nok, State}.
%%% ----------------------------------------------------------
%%% # handle_cast(Request, State)
%%% ----------------------------------------------------------
handle_cast(Msg, State) ->
?UNEXPECTED([Msg]),
{noreply, State}.
%%% ----------------------------------------------------------
%%% # handle_info(Request, State)
%%% ----------------------------------------------------------
%% Remote service is distributing a message.
handle_info({notify, SvcName, T}, S) ->
bang(diameter_service:whois(SvcName), T),
{noreply, S};
handle_info(Info, State) ->
?UNEXPECTED([Info]),
{noreply, State}.
%% ----------------------------------------------------------
%% terminate(Reason, State)
%% ----------------------------------------------------------
terminate(_Reason, _State) ->
ok.
%% ----------------------------------------------------------
%% code_change(OldVsn, State, Extra)
%% ----------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%% ---------------------------------------------------------
%% INTERNAL FUNCTIONS
%% ---------------------------------------------------------
%% ifc_send/2
%%
%% Send something over the transport interface.
ifc_send(Pid, T) ->
Pid ! {diameter, T}.
%% bang/2
bang(undefined = No, _) ->
No;
bang(Pid, T) ->
Pid ! T.
%% split_transport/1
%%
%% Split options into transport module, transport config and
%% remaining options.
split_transport(Opts) ->
{[M,C], _} = proplists:split(Opts, [transport_module,
transport_config]),
{value(M, diameter_tcp), value(C, [])}.
value([{_,V}], _) ->
V;
value([], V) ->
V.
%% call/1
call(Request) ->
gen_server:call(?SERVER, Request, infinity).