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