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()],
79                                      GS_ARGS),
80    unlink(Pid),
81    {ok, Pid}.
82
83connect(Sup, Opts) ->
84    call({connect, Sup, Opts}).
85
86listen(Sup, Opts) ->
87    call({listen, Sup, Opts}).
88
89stop() ->
90    call(stop).
91
92
93%%----------------------------------------------------------------------
94%% Megaco transport callback
95%%----------------------------------------------------------------------
96
97send_message(SendHandle, Bin) ->
98    d("send_message -> entry with"
99      "~n      SendHandle: ~p", [SendHandle]),
100    call({transport, {send_message, SendHandle, Bin}}).
101
102send_message(SendHandle, Bin, Resend) ->
103    d("send_message -> entry with"
104      "~n      SendHandle: ~p"
105      "~n      Resend:     ~p", [SendHandle, Resend]),
106    call({transport, {send_message, SendHandle, Bin, Resend}}).
107
108resend_message(SendHandle, Bin) ->
109    d("resend_message -> entry with"
110      "~n      SendHandle: ~p", [SendHandle]),
111    call({transport, {resend_message, SendHandle, Bin}}).
112
113incomming_message(Pid, Msg) ->
114    d("incomming_message -> entry with"
115      "~n      Pid: ~p"
116      "~n      Msg: ~p", [Pid, Msg]),
117    cast(Pid, {incomming_message, Msg}).
118
119
120%%%-------------------------------------------------------------------
121%%% Callback functions from gen_server
122%%%-------------------------------------------------------------------
123
124%%--------------------------------------------------------------------
125%% Func: init/1
126%% Returns: {ok, State}          |
127%%          {ok, State, Timeout} |
128%%          ignore               |
129%%          {stop, Reason}
130%%--------------------------------------------------------------------
131init([Parent]) ->
132    {ok, #state{parent = Parent}}.
133
134
135%%--------------------------------------------------------------------
136%% Func: handle_call/3
137%% Returns: {reply, Reply, State}          |
138%%          {reply, Reply, State, Timeout} |
139%%          {noreply, State}               |
140%%          {noreply, State, Timeout}      |
141%%          {stop, Reason, Reply, State}   | (terminate/2 is called)
142%%          {stop, Reason, State}            (terminate/2 is called)
143%%--------------------------------------------------------------------
144handle_call({connect, _Sup, Opts}, _From, State) ->
145    d("handle_call(connect) -> 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    d("handle_call(connect) -> done when"
153      "~n      Reply: ~p", [Reply]),
154    {reply, Reply, State#state{controller     = Controller,
155			       receive_handle = ReceiveHandle}};
156
157handle_call({listen, _Sup, Opts}, _From, State) ->
158    d("handle_call(listen) -> entry with"
159      "~n   Opts: ~p", [Opts]),
160    {value, {_, ReceiveHandle}} = lists:keysearch(receive_handle, 1, Opts),
161    {value, {_, Controller}}    = lists:keysearch(port, 1, Opts),
162    SendHandle = self(),
163    ControlPid = self(),
164    Reply  = {ok, SendHandle, ControlPid},
165    d("handle_call(listen) -> inform controller"),
166    Controller ! {listen, ReceiveHandle, SendHandle, ControlPid},
167    d("handle_call(listen) -> done when"
168      "~n      Reply: ~p", [Reply]),
169    {reply, Reply, State#state{controller     = Controller,
170			       receive_handle = ReceiveHandle}};
171
172handle_call(stop, _From, State) ->
173    d("handle_call(stop) -> entry"),
174    Reply  = ok,
175    Reason = normal,
176    {stop, Reason, Reply, State};
177
178handle_call({transport, Event}, _From,
179	    #state{controller = Pid, receive_handle = RH} = State) ->
180    d("handle_call(transport) -> entry with"
181      "~n   Event: ~p", [Event]),
182    Reply = handle_transport(Pid, RH, Event),
183    d("handle_call(transport) -> done when"
184      "~n      Reply: ~p", [Reply]),
185    {reply, Reply, State};
186
187handle_call(Req, From, State) ->
188    d("handle_call -> entry with"
189      "~n   Req: ~p", [Req]),
190    Reply  = {error, {unknown_request, Req}},
191    Reason = {received_unexpected_request, Req, From},
192    {stop, Reason, Reply, State}.
193
194
195%%--------------------------------------------------------------------
196%% Func: handle_cast/2
197%% Returns: {noreply, State}          |
198%%          {noreply, State, Timeout} |
199%%          {stop, Reason, State}            (terminate/2 is called)
200%%--------------------------------------------------------------------
201handle_cast({incomming_message, Msg},
202	    #state{receive_handle = RH} = State) ->
203    d("handle_cast(incomming_message) -> entry with"
204      "~n   Msg: ~p", [Msg]),
205    handle_incomming_message(Msg, RH),
206    {noreply, State};
207
208handle_cast(Msg, State) ->
209    d("handle_cast -> entry with"
210      "~n   Msg: ~p", [Msg]),
211    Reason = {received_unexpected_message, Msg},
212    {stop, Reason, State}.
213
214
215%%--------------------------------------------------------------------
216%% Func: handle_info/2
217%% Returns: {noreply, State}          |
218%%          {noreply, State, Timeout} |
219%%          {stop, Reason, State}            (terminate/2 is called)
220%%--------------------------------------------------------------------
221handle_info(Info, State) ->
222    d("handle_info -> entry with"
223      "~n   Info: ~p", [Info]),
224    Reason = {received_unexpected_info, Info},
225    {stop, Reason, State}.
226
227
228%%--------------------------------------------------------------------
229%% Func: terminate/2
230%% Purpose: Shutdown the server
231%% Returns: any (ignored by gen_server)
232%%--------------------------------------------------------------------
233terminate(_Reason, _State) ->
234    ok.
235
236
237%%----------------------------------------------------------------------
238%% Func: code_change/3
239%% Purpose: Convert process state when code is changed
240%% Returns: {ok, NewState}
241%%----------------------------------------------------------------------
242
243code_change(_Vsn, State, _Extra) ->
244    {ok, State}.
245
246
247%%%-------------------------------------------------------------------
248%%% Internal functions
249%%%-------------------------------------------------------------------
250
251handle_transport(Pid,
252		 #megaco_receive_handle{encoding_mod    = EM,
253					encoding_config = EC},
254		 {Event, SendHandle, Bin, Resend}) ->
255    Info =
256	case (catch EM:decode_message(EC, Bin)) of
257	    {ok, MegMsg} ->
258		{message, MegMsg, Resend};
259	    Error ->
260		d("handle_transport -> decode failed"
261		  "~n   Error: ~p", [Error]),
262		{bad_message, Error, Bin}
263	end,
264    handle_transport(Pid, Event, SendHandle, Info);
265handle_transport(Pid,
266		 #megaco_receive_handle{encoding_mod    = EM,
267					encoding_config = EC},
268		 {Event, SendHandle, Bin}) ->
269    Info =
270	case (catch EM:decode_message(EC, Bin)) of
271	    {ok, MegMsg} ->
272		{message, MegMsg};
273	    Error ->
274		d("handle_transport -> decode failed"
275		  "~n   Error: ~p", [Error]),
276		{bad_message, Error, Bin}
277	end,
278    handle_transport(Pid, Event, SendHandle, Info).
279
280handle_transport(Pid, Event, SendHandle, Info) ->
281    Pid ! {transport_event, {Event, SendHandle, Info}, self()},
282    receive
283	{transport_reply, Reply, Pid} ->
284	    d("handle_transport -> received reply"
285	      "~n   Reply: ~p", [Reply]),
286	    Reply
287    after 10000 ->
288	    receive
289		Any ->
290		    d("handle_transport -> received crap after timeout"
291		      "~n   Any: ~p", [Any]),
292		    exit({timeout, Any})
293	    after 0 ->
294		    d("handle_transport -> timeout"),
295		    exit(timeout)
296	    end
297    end.
298
299
300%% This function is used to simulate incomming messages
301handle_incomming_message(Msg,
302			 #megaco_receive_handle{encoding_mod    = EM,
303						encoding_config = EC} = RH) ->
304    Self = self(),
305    case EM:encode_message(EC, Msg) of
306	{ok, Bin} ->
307	    ProcessMessage =
308		fun() ->
309			megaco:process_received_message(RH, Self, Self, Bin)
310		end,
311	    spawn(ProcessMessage),
312	    ok;
313	Error ->
314	    d("handle_incomming_message -> encode failed"
315	      "~n   Error: ~p", [Error]),
316	    exit(Error)
317    end.
318
319
320%%-------------------------------------------------------------------
321
322call(Req) ->
323    call(Req, infinity).
324
325call(Req, Timeout) ->
326    case (catch gen_server:call(?SERVER, Req, Timeout)) of
327        {'EXIT', _} ->
328            {error, not_started};
329        Res ->
330            Res
331    end.
332
333%% cast(Msg) ->
334%%     cast(whereis(?SERVER), Msg).
335
336cast(Pid, Msg) ->
337    d("cast -> entry with"
338      "~n   Pid: ~p"
339      "~n   Msg: ~p", [Pid, Msg]),
340    case (catch gen_server:cast(Pid, Msg)) of
341        {'EXIT', Reason} ->
342	    d("cast -> failed casting"
343	      "~n   Reason: ~p", [Reason]),
344            {error, not_started};
345        Res ->
346            Res
347    end.
348
349
350%%-------------------------------------------------------------------
351
352d(F) ->
353    d(F, []).
354
355d(F, A) ->
356    print(F, A).
357
358
359print(F, A) ->
360    io:format("*** [~s] GENERIC TRANSPORT [~p] ***"
361	      "~n   " ++ F ++ "~n",
362	      [?FTS(), self() | A]).
363
364