1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2007-2019. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20
21%%
22%%----------------------------------------------------------------------
23%% Purpose: Generic megaco transport simulator module
24%%----------------------------------------------------------------------
25
26-module(megaco_test_generic_transport).
27
28-behaviour(gen_server).
29-behaviour(megaco_transport).
30
31-export([
32	 start_transport/0,
33	 listen/2,
34	 connect/2,
35	 start/1,
36	 stop/0,
37	 incomming_message/2
38	]).
39
40%% gen_server callbacks
41-export([
42	 init/1,
43	 handle_call/3,
44	 handle_cast/2,
45	 handle_info/2,
46	 terminate/2,
47	 code_change/3
48	]).
49
50%% megaco_transport callbacks
51-export([
52	 send_message/2,
53	 send_message/3,
54	 resend_message/2
55	]).
56
57-record(state, {parent,
58		controller,
59		receive_handle}).
60
61-include_lib("megaco/include/megaco.hrl").
62-include("megaco_test_lib.hrl").
63-define(SERVER, ?MODULE).
64
65
66%%-------------------------------------------------------------------
67%% API
68%%-------------------------------------------------------------------
69
70start(RH) ->
71    {ok, Pid} = start_transport(),
72    {ok, SendHandle, _} = connect(Pid, [{receive_handle, RH}]),
73    {ok, SendHandle}.
74
75start_transport() ->
76    %% GS_ARGS = [{debug,[trace]}],
77    GS_ARGS = [],
78    {ok, Pid} = gen_server:start_link({local, ?SERVER}, ?MODULE, [self()], GS_ARGS),
79    unlink(Pid),
80    {ok, Pid}.
81
82connect(Sup, Opts) ->
83    call({connect, Sup, Opts}).
84
85listen(Sup, Opts) ->
86    call({listen, Sup, Opts}).
87
88stop() ->
89    call(stop).
90
91
92%%----------------------------------------------------------------------
93%% Megaco transport callback
94%%----------------------------------------------------------------------
95
96send_message(SendHandle, Bin) ->
97    call({transport, {send_message, SendHandle, Bin}}).
98
99send_message(SendHandle, Bin, Resend) ->
100    call({transport, {send_message, SendHandle, Bin, Resend}}).
101
102resend_message(SendHandle, Bin) ->
103    call({transport, {resend_message, SendHandle, Bin}}).
104
105incomming_message(Pid, Msg) ->
106    cast(Pid, {incomming_message, Msg}).
107
108
109%%%-------------------------------------------------------------------
110%%% Callback functions from gen_server
111%%%-------------------------------------------------------------------
112
113%%--------------------------------------------------------------------
114%% Func: init/1
115%% Returns: {ok, State}          |
116%%          {ok, State, Timeout} |
117%%          ignore               |
118%%          {stop, Reason}
119%%--------------------------------------------------------------------
120init([Parent]) ->
121    {ok, #state{parent = Parent}}.
122
123
124%%--------------------------------------------------------------------
125%% Func: handle_call/3
126%% Returns: {reply, Reply, State}          |
127%%          {reply, Reply, State, Timeout} |
128%%          {noreply, State}               |
129%%          {noreply, State, Timeout}      |
130%%          {stop, Reason, Reply, State}   | (terminate/2 is called)
131%%          {stop, Reason, State}            (terminate/2 is called)
132%%--------------------------------------------------------------------
133handle_call({connect, _Sup, Opts}, _From, State) ->
134    d("handle_call(connect) -> entry with"
135      "~n   Opts: ~p", [Opts]),
136    {value, {_, ReceiveHandle}} = lists:keysearch(receive_handle, 1, Opts),
137    {value, {_, Controller}}    = lists:keysearch(port, 1, Opts),
138    SendHandle = self(),
139    ControlPid = self(),
140    Reply  = {ok, SendHandle, ControlPid},
141    {reply, Reply, State#state{controller     = Controller,
142			       receive_handle = ReceiveHandle}};
143
144handle_call({listen, _Sup, Opts}, _From, State) ->
145    d("handle_call(listen) -> entry with"
146      "~n   Opts: ~p", [Opts]),
147    {value, {_, ReceiveHandle}} = lists:keysearch(receive_handle, 1, Opts),
148    {value, {_, Controller}}    = lists:keysearch(port, 1, Opts),
149    SendHandle = self(),
150    ControlPid = self(),
151    Reply  = {ok, SendHandle, ControlPid},
152    Controller ! {listen, ReceiveHandle, SendHandle, ControlPid},
153    {reply, Reply, State#state{controller     = Controller,
154			       receive_handle = ReceiveHandle}};
155
156handle_call(stop, _From, State) ->
157    d("handle_call(stop) -> entry"),
158    Reply  = ok,
159    Reason = normal,
160    {stop, Reason, Reply, State};
161
162handle_call({transport, Event}, _From,
163	    #state{controller = Pid, receive_handle = RH} = State) ->
164    d("handle_call(transport) -> entry with"
165      "~n   Event: ~p", [Event]),
166    Reply = handle_transport(Pid, RH, Event),
167    {reply, Reply, State};
168
169handle_call(Req, From, State) ->
170    d("handle_call -> entry with"
171      "~n   Req: ~p", [Req]),
172    Reply  = {error, {unknown_request, Req}},
173    Reason = {received_unexpected_request, Req, From},
174    {stop, Reason, Reply, State}.
175
176
177%%--------------------------------------------------------------------
178%% Func: handle_cast/2
179%% Returns: {noreply, State}          |
180%%          {noreply, State, Timeout} |
181%%          {stop, Reason, State}            (terminate/2 is called)
182%%--------------------------------------------------------------------
183handle_cast({incomming_message, Msg},
184	    #state{receive_handle = RH} = State) ->
185    d("handle_cast(incomming_message) -> entry with"
186      "~n   Msg: ~p", [Msg]),
187    handle_incomming_message(Msg, RH),
188    {noreply, State};
189
190handle_cast(Msg, State) ->
191    d("handle_cast -> entry with"
192      "~n   Msg: ~p", [Msg]),
193    Reason = {received_unexpected_message, Msg},
194    {stop, Reason, State}.
195
196
197%%--------------------------------------------------------------------
198%% Func: handle_info/2
199%% Returns: {noreply, State}          |
200%%          {noreply, State, Timeout} |
201%%          {stop, Reason, State}            (terminate/2 is called)
202%%--------------------------------------------------------------------
203handle_info(Info, State) ->
204    d("handle_info -> entry with"
205      "~n   Info: ~p", [Info]),
206    Reason = {received_unexpected_info, Info},
207    {stop, Reason, State}.
208
209
210%%--------------------------------------------------------------------
211%% Func: terminate/2
212%% Purpose: Shutdown the server
213%% Returns: any (ignored by gen_server)
214%%--------------------------------------------------------------------
215terminate(_Reason, _State) ->
216    ok.
217
218
219%%----------------------------------------------------------------------
220%% Func: code_change/3
221%% Purpose: Convert process state when code is changed
222%% Returns: {ok, NewState}
223%%----------------------------------------------------------------------
224
225code_change(_Vsn, State, _Extra) ->
226    {ok, State}.
227
228
229%%%-------------------------------------------------------------------
230%%% Internal functions
231%%%-------------------------------------------------------------------
232
233handle_transport(Pid,
234		 #megaco_receive_handle{encoding_mod    = EM,
235					encoding_config = EC},
236		 {Event, SendHandle, Bin, Resend}) ->
237    Info =
238	case (catch EM:decode_message(EC, Bin)) of
239	    {ok, MegMsg} ->
240		{message, MegMsg, Resend};
241	    Error ->
242		d("handle_transport -> decode failed"
243		  "~n   Error: ~p", [Error]),
244		{bad_message, Error, Bin}
245	end,
246    handle_transport(Pid, Event, SendHandle, Info);
247handle_transport(Pid,
248		 #megaco_receive_handle{encoding_mod    = EM,
249					encoding_config = EC},
250		 {Event, SendHandle, Bin}) ->
251    Info =
252	case (catch EM:decode_message(EC, Bin)) of
253	    {ok, MegMsg} ->
254		{message, MegMsg};
255	    Error ->
256		d("handle_transport -> decode failed"
257		  "~n   Error: ~p", [Error]),
258		{bad_message, Error, Bin}
259	end,
260    handle_transport(Pid, Event, SendHandle, Info).
261
262handle_transport(Pid, Event, SendHandle, Info) ->
263    Pid ! {transport_event, {Event, SendHandle, Info}, self()},
264    receive
265	{transport_reply, Reply, Pid} ->
266	    d("handle_transport -> received reply"
267	      "~n   Reply: ~p", [Reply]),
268	    Reply
269    after 10000 ->
270	    receive
271		Any ->
272		    d("handle_transport -> received crap after timeout"
273		      "~n   Any: ~p", [Any]),
274		    exit({timeout, Any})
275	    after 0 ->
276		    d("handle_transport -> timeout"),
277		    exit(timeout)
278	    end
279    end.
280
281
282%% This function is used to simulate incomming messages
283handle_incomming_message(Msg,
284			 #megaco_receive_handle{encoding_mod    = EM,
285						encoding_config = EC} = RH) ->
286    Self = self(),
287    case EM:encode_message(EC, Msg) of
288	{ok, Bin} ->
289	    ProcessMessage =
290		fun() ->
291			megaco:process_received_message(RH, Self, Self, Bin)
292		end,
293	    spawn(ProcessMessage),
294	    ok;
295	Error ->
296	    d("handle_incomming_message -> encode failed"
297	      "~n   Error: ~p", [Error]),
298	    exit(Error)
299    end.
300
301
302%%-------------------------------------------------------------------
303
304call(Req) ->
305    call(Req, infinity).
306
307call(Req, Timeout) ->
308    case (catch gen_server:call(?SERVER, Req, Timeout)) of
309        {'EXIT', _} ->
310            {error, not_started};
311        Res ->
312            Res
313    end.
314
315%% cast(Msg) ->
316%%     cast(whereis(?SERVER), Msg).
317
318cast(Pid, Msg) ->
319    d("cast -> entry with"
320      "~n   Pid: ~p"
321      "~n   Msg: ~p", [Pid, Msg]),
322    case (catch gen_server:cast(Pid, Msg)) of
323        {'EXIT', Reason} ->
324	    d("cast -> failed casting"
325	      "~n   Reason: ~p", [Reason]),
326            {error, not_started};
327        Res ->
328            Res
329    end.
330
331
332%%-------------------------------------------------------------------
333
334d(F) ->
335    d(F, []).
336
337d(F, A) ->
338    print(F, A).
339
340
341print(F, A) ->
342    io:format("*** [~s] GENERIC TRANSPORT [~p] ***"
343	      "~n   " ++ F ++ "~n",
344	      [?FTS(), self() | A]).
345
346