PageRenderTime 82ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/megaco/src/engine/megaco_messenger.erl

https://github.com/bsmr-erlang/otp
Erlang | 5371 lines | 3901 code | 623 blank | 847 comment | 25 complexity | a23706a7dfa9de93cd45527b8c44eb56 MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.1, MPL-2.0-no-copyleft-exception, Apache-2.0
  1. %%
  2. %% %CopyrightBegin%
  3. %%
  4. %% Copyright Ericsson AB 1999-2016. 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. %% Purpose: Send and process a (sequence of) Megaco/H.248 transactions
  23. %%----------------------------------------------------------------------
  24. -module(megaco_messenger).
  25. %% Application internal export
  26. -export([
  27. process_received_message/4, process_received_message/5,
  28. receive_message/4, receive_message/5,
  29. connect/4, connect/5,
  30. disconnect/2,
  31. encode_actions/3,
  32. call/3,
  33. cast/3,
  34. cancel/2,
  35. request_timeout/2,
  36. request_keep_alive_timeout/2,
  37. pending_timeout/3,
  38. reply_timeout/3,
  39. segment_timeout/3,
  40. %% segment_reply_timeout/4,
  41. test_request/5,
  42. test_reply/5
  43. ]).
  44. %% MIB stat functions
  45. -export([
  46. get_stats/0, get_stats/1, get_stats/2,
  47. reset_stats/0, reset_stats/1
  48. ]).
  49. %% Misc functions
  50. -export([
  51. cleanup/2,
  52. which_requests/1, which_replies/1
  53. ]).
  54. %% Module internal export
  55. -export([
  56. process_received_message/6,
  57. handle_request/2,
  58. handle_long_request/2,
  59. connect_remote/3,
  60. disconnect_local/2,
  61. disconnect_remote/3,
  62. send_request_remote/4,
  63. receive_reply_remote/2, receive_reply_remote/3
  64. ]).
  65. -include_lib("megaco/include/megaco.hrl").
  66. -include("megaco_message_internal.hrl").
  67. -include_lib("megaco/src/app/megaco_internal.hrl").
  68. %% N.B. Update cancel/1 with '_' when a new field is added
  69. -record(request,
  70. {trans_id,
  71. remote_mid,
  72. timer_ref, % {short, Ref} | {long, Ref}
  73. init_timer,
  74. init_long_timer,
  75. curr_timer,
  76. version,
  77. bytes, % {send, Data} | {no_send, Data}, Data = binary() | tuple()
  78. send_handle,
  79. user_mod,
  80. user_args,
  81. reply_action, % call | cast
  82. reply_data,
  83. seg_recv = [], % [integer()] (received segments)
  84. init_seg_timer,
  85. seg_timer_ref,
  86. keep_alive_timer, % plain | integer() >= 0
  87. keep_alive_ref % undefined | ref()
  88. }).
  89. %% N.B. Update cancel/1 with '_' when a new field is added
  90. -record(reply,
  91. {
  92. trans_id,
  93. local_mid,
  94. state = prepare, % prepare | eval_request | waiting_for_ack | aborted
  95. pending_timer_ref,
  96. handler = undefined, % pid of the proc executing the callback func
  97. timer_ref,
  98. version,
  99. %% bytes: Sent reply data: not acknowledged
  100. bytes, % binary() | [{integer(), binary(), timer_ref()}]
  101. ack_action, % discard_ack | {handle_ack, Data}
  102. send_handle,
  103. %% segments: Not sent reply data (segments)
  104. segments = [], % [{integer(), binary()}]
  105. user_mod,
  106. user_args
  107. }).
  108. -record(trans_id,
  109. {
  110. mid,
  111. serial
  112. }).
  113. -ifdef(MEGACO_TEST_CODE).
  114. -define(SIM(Other,Where),
  115. fun(Afun,Bfun) ->
  116. Kfun = {?MODULE,Bfun},
  117. case (catch ets:lookup(megaco_test_data, Kfun)) of
  118. [{Kfun,Cfun}] ->
  119. Cfun(Afun);
  120. _ ->
  121. Afun
  122. end
  123. end(Other,Where)).
  124. -define(TC_AWAIT_CANCEL_EVENT(),
  125. case megaco_tc_controller:lookup(block_on_cancel) of
  126. {value, {Tag, Pid}} when is_pid(Pid) ->
  127. Pid ! {Tag, self()},
  128. receive
  129. {Tag, Pid} ->
  130. ok
  131. end;
  132. {value, {sleep, To}} when is_integer(To) andalso (To > 0) ->
  133. receive after To -> ok end;
  134. _ ->
  135. ok
  136. end).
  137. -define(TC_AWAIT_REPLY_EVENT(Info),
  138. case megaco_tc_controller:lookup(block_on_reply) of
  139. {value, {Tag, Pid}} when is_pid(Pid) ->
  140. Pid ! {Tag, self(), Info},
  141. receive
  142. {Tag, Pid} ->
  143. ok
  144. end;
  145. _Whatever ->
  146. %% io:format("Whatever: ~p~n", [Whatever]),
  147. ok
  148. end).
  149. -else.
  150. -define(SIM(Other,Where),Other).
  151. -define(TC_AWAIT_CANCEL_EVENT(),ok).
  152. -define(TC_AWAIT_REPLY_EVENT(_),ok).
  153. -endif.
  154. -define(report_pending_limit_exceeded(ConnData),
  155. ?report_important(ConnData, "<ERROR> pending limit exceeded", [])).
  156. -ifdef(megaco_extended_trace).
  157. -define(rt1(T,F,A),?report_trace(T,F,A)).
  158. -define(rt2(F,A), ?rt1(ignore,F,A)).
  159. -define(rt3(F), ?rt2(F,[])).
  160. -else.
  161. -define(rt1(T,F,A),ok).
  162. -define(rt2(F,A), ok).
  163. -define(rt3(F), ok).
  164. -endif.
  165. %%----------------------------------------------------------------------
  166. %% SNMP statistics handling functions
  167. %%----------------------------------------------------------------------
  168. %%-----------------------------------------------------------------
  169. %% Func: get_stats/0, get_stats/1, get_stats/2
  170. %% Description: Retreive statistics (counters) for TCP
  171. %%-----------------------------------------------------------------
  172. get_stats() ->
  173. megaco_stats:get_stats(megaco_stats).
  174. get_stats(ConnHandleOrCounter) ->
  175. megaco_stats:get_stats(megaco_stats, ConnHandleOrCounter).
  176. get_stats(ConnHandle, Counter) ->
  177. megaco_stats:get_stats(megaco_stats, ConnHandle, Counter).
  178. %%-----------------------------------------------------------------
  179. %% Func: reset_stats/0, reaet_stats/1
  180. %% Description: Reset statistics (counters)
  181. %%-----------------------------------------------------------------
  182. reset_stats() ->
  183. megaco_stats:reset_stats(megaco_stats).
  184. reset_stats(ConnHandleOrCounter) ->
  185. megaco_stats:reset_stats(megaco_stats, ConnHandleOrCounter).
  186. %%----------------------------------------------------------------------
  187. %% cleanup utility functions
  188. %%----------------------------------------------------------------------
  189. cleanup(#megaco_conn_handle{local_mid = LocalMid}, Force)
  190. when (Force =:= true) orelse (Force =:= false) ->
  191. Pat = #reply{trans_id = '$1',
  192. local_mid = LocalMid,
  193. state = '$2',
  194. _ = '_'},
  195. do_cleanup(Pat, Force);
  196. cleanup(LocalMid, Force)
  197. when (Force =:= true) orelse (Force =:= false) ->
  198. Pat = #reply{trans_id = '$1',
  199. local_mid = LocalMid,
  200. state = '$2',
  201. _ = '_'},
  202. do_cleanup(Pat, Force).
  203. do_cleanup(Pat, Force) ->
  204. Match = megaco_monitor:which_replies(Pat),
  205. Reps = [{V1, V2} || [V1, V2] <- Match],
  206. do_cleanup2(Reps, Force).
  207. do_cleanup2([], _) ->
  208. ok;
  209. do_cleanup2([{TransId, aborted}|T], Force = false) ->
  210. megaco_monitor:delete_reply(TransId),
  211. do_cleanup2(T, Force);
  212. do_cleanup2([_|T], Force = false) ->
  213. do_cleanup2(T, Force);
  214. do_cleanup2([{TransId, _State}|T], Force = true) ->
  215. megaco_monitor:delete_reply(TransId),
  216. do_cleanup2(T, Force).
  217. %%----------------------------------------------------------------------
  218. %% which_requests and which_replies utility functions
  219. %%----------------------------------------------------------------------
  220. which_requests(#megaco_conn_handle{local_mid = LocalMid,
  221. remote_mid = RemoteMid}) ->
  222. Pat1 = #trans_id{mid = LocalMid,
  223. serial = '$1', _ = '_'},
  224. Pat2 = #request{trans_id = Pat1,
  225. remote_mid = RemoteMid,
  226. _ = '_'},
  227. Match = megaco_monitor:which_requests(Pat2),
  228. [S || [S] <- Match];
  229. which_requests(LocalMid) ->
  230. Pat1 = #trans_id{mid = LocalMid,
  231. serial = '$1', _ = '_'},
  232. Pat2 = #request{trans_id = Pat1,
  233. remote_mid = '$2', _ = '_'},
  234. Match0 = megaco_monitor:which_requests(Pat2),
  235. Match1 = [{mk_ch(LocalMid, V2), V1} || [V1, V2] <- Match0],
  236. which_requests1(lists:sort(Match1)).
  237. which_requests1([]) ->
  238. [];
  239. which_requests1([{CH, S}|T]) ->
  240. which_requests2(T, CH, [S], []).
  241. which_requests2([], CH, Serials, Reqs) ->
  242. lists:reverse([{CH, Serials}|Reqs]);
  243. which_requests2([{CH, S}|T], CH, Serials, Reqs) ->
  244. which_requests2(T, CH, [S|Serials], Reqs);
  245. which_requests2([{CH1, S}|T], CH2, Serials, Reqs) ->
  246. which_requests2(T, CH1, [S], [{CH2, lists:reverse(Serials)}| Reqs]).
  247. which_replies(#megaco_conn_handle{local_mid = LocalMid,
  248. remote_mid = RemoteMid}) ->
  249. Pat1 = #trans_id{mid = RemoteMid,
  250. serial = '$1', _ = '_'},
  251. Pat2 = #reply{trans_id = Pat1,
  252. local_mid = LocalMid,
  253. state = '$2',
  254. handler = '$3', _ = '_'},
  255. Match = megaco_monitor:which_replies(Pat2),
  256. [{V1, V2, V3} || [V1, V2, V3] <- Match];
  257. which_replies(LocalMid) ->
  258. Pat1 = #trans_id{mid = '$1',
  259. serial = '$2', _ = '_'},
  260. Pat2 = #reply{trans_id = Pat1,
  261. local_mid = LocalMid,
  262. state = '$3',
  263. handler = '$4', _ = '_'},
  264. Match0 = megaco_monitor:which_replies(Pat2),
  265. Match1 = [{mk_ch(LocalMid,V1),{V2,V3,V4}} || [V1, V2, V3, V4] <- Match0],
  266. which_replies1(lists:sort(Match1)).
  267. which_replies1([]) ->
  268. [];
  269. which_replies1([{CH, Data}|T]) ->
  270. which_replies2(T, CH, [Data], []).
  271. which_replies2([], CH, Data, Reps) ->
  272. lists:reverse([{CH, Data}|Reps]);
  273. which_replies2([{CH, Data}|T], CH, Datas, Reps) ->
  274. which_replies2(T, CH, [Data|Datas], Reps);
  275. which_replies2([{CH1, Data}|T], CH2, Datas, Reps) ->
  276. which_replies2(T, CH1, [Data], [{CH2, lists:reverse(Datas)}| Reps]).
  277. mk_ch(LM, RM) ->
  278. #megaco_conn_handle{local_mid = LM, remote_mid = RM}.
  279. %%----------------------------------------------------------------------
  280. %% Register/unreister connections
  281. %%----------------------------------------------------------------------
  282. %% Returns {ok, ConnHandle} | {error, Reason}
  283. autoconnect(RH, RemoteMid, SendHandle, ControlPid, Extra)
  284. when is_record(RH, megaco_receive_handle) ->
  285. ?rt2("autoconnect", [RH, RemoteMid, SendHandle, ControlPid]),
  286. case megaco_config:autoconnect(RH, RemoteMid, SendHandle, ControlPid) of
  287. {ok, ConnData} ->
  288. do_connect(ConnData, Extra);
  289. {error, Reason} ->
  290. {error, Reason}
  291. end;
  292. autoconnect(BadHandle, _CH, _SendHandle, _ControlPid, _Extra) ->
  293. {error, {bad_receive_handle, BadHandle}}.
  294. connect(RH, RemoteMid, SendHandle, ControlPid) ->
  295. Extra = ?default_user_callback_extra,
  296. connect(RH, RemoteMid, SendHandle, ControlPid, Extra).
  297. connect(RH, RemoteMid, SendHandle, ControlPid, Extra)
  298. when is_record(RH, megaco_receive_handle) ->
  299. ?rt2("connect", [RH, RemoteMid, SendHandle, ControlPid, Extra]),
  300. %% The purpose of this is to have a temoporary process, to
  301. %% which one can set up a monitor or link and get a
  302. %% notification when process exits. The entire connect is
  303. %% done in the temporary worker process.
  304. %% When it exits, the connect is either successfully done
  305. %% or it failed.
  306. ConnectorFun =
  307. fun() ->
  308. ConnectResult =
  309. case megaco_config:connect(RH, RemoteMid,
  310. SendHandle, ControlPid) of
  311. {ok, ConnData} ->
  312. do_connect(ConnData, Extra);
  313. {error, Reason} ->
  314. {error, Reason}
  315. end,
  316. ?rt2("connector: connected", [self(), ConnectResult]),
  317. exit({result, ConnectResult})
  318. end,
  319. Flag = process_flag(trap_exit, true),
  320. Connector = erlang:spawn_link(ConnectorFun),
  321. receive
  322. {'EXIT', Connector, {result, ConnectResult}} ->
  323. ?rt2("connect result: received expected connector exit signal",
  324. [Connector, ConnectResult]),
  325. process_flag(trap_exit, Flag),
  326. ConnectResult;
  327. {'EXIT', Connector, OtherReason} ->
  328. ?rt2("connect exit: received unexpected connector exit signal",
  329. [Connector, OtherReason]),
  330. process_flag(trap_exit, Flag),
  331. {error, OtherReason}
  332. end;
  333. connect(BadHandle, _CH, _SendHandle, _ControlPid, _Extra) ->
  334. {error, {bad_receive_handle, BadHandle}}.
  335. do_connect(CD, Extra) ->
  336. CH = CD#conn_data.conn_handle,
  337. Version = CD#conn_data.protocol_version,
  338. UserMod = CD#conn_data.user_mod,
  339. UserArgs = CD#conn_data.user_args,
  340. Args =
  341. case Extra of
  342. ?default_user_callback_extra ->
  343. [CH, Version | UserArgs];
  344. _ ->
  345. [CH, Version, Extra | UserArgs]
  346. end,
  347. ?report_trace(CD, "callback: connect", [Args]),
  348. Res = (catch apply(UserMod, handle_connect, Args)),
  349. ?report_debug(CD, "return: connect", [{return, Res}]),
  350. case Res of
  351. ok ->
  352. ?SIM(ok, do_connect), % do_encode),
  353. monitor_process(CH, CD#conn_data.control_pid);
  354. error ->
  355. megaco_config:disconnect(CH),
  356. {error, {connection_refused, CD, error}};
  357. {error, ED} when is_record(ED,'ErrorDescriptor') ->
  358. megaco_config:disconnect(CH),
  359. {error, {connection_refused, CD, ED}};
  360. _Error ->
  361. warning_msg("connect callback failed: ~w", [Res]),
  362. megaco_config:disconnect(CH),
  363. {error, {connection_refused, CD, Res}}
  364. end.
  365. finish_connect(#conn_data{control_pid = ControlPid} = CD)
  366. when is_pid(ControlPid) andalso (node(ControlPid) =:= node()) ->
  367. ?rt1(CD, "finish local connect", [ControlPid]),
  368. do_finish_connect(CD);
  369. finish_connect(#conn_data{conn_handle = CH,
  370. control_pid = ControlPid} = CD)
  371. when is_pid(ControlPid) andalso (node(ControlPid) =/= node()) ->
  372. ?rt1(CD, "finish remote connect", [ControlPid]),
  373. RemoteNode = node(ControlPid),
  374. UserMonitorPid = whereis(megaco_monitor),
  375. Args = [CH, ControlPid, UserMonitorPid],
  376. case rpc:call(RemoteNode, ?MODULE, connect_remote, Args) of
  377. {ok, ControlMonitorPid} ->
  378. do_finish_connect(CD#conn_data{control_pid = ControlMonitorPid});
  379. {error, Reason} ->
  380. disconnect(CH, {connect_remote, Reason}),
  381. {error, Reason};
  382. {badrpc, Reason} ->
  383. Reason2 = {'EXIT', Reason},
  384. disconnect(CH, {connect_remote, Reason2}),
  385. {error, Reason2}
  386. end.
  387. do_finish_connect(#conn_data{conn_handle = CH,
  388. send_handle = SendHandle,
  389. control_pid = ControlPid} = CD) ->
  390. M = ?MODULE,
  391. F = disconnect_local,
  392. A = [CH],
  393. MFA = {M, F, A},
  394. case megaco_config:finish_connect(CH, SendHandle, ControlPid, MFA) of
  395. {ok, Ref} ->
  396. {ok, CD#conn_data{monitor_ref = Ref}};
  397. {error, Reason} ->
  398. {error, {config_update, Reason}}
  399. end.
  400. monitor_process(CH, ControlPid)
  401. when is_pid(ControlPid) andalso (node(ControlPid) =:= node()) ->
  402. M = ?MODULE,
  403. F = disconnect_local,
  404. A = [CH],
  405. Ref = megaco_monitor:apply_at_exit(M, F, A, ControlPid),
  406. case megaco_config:update_conn_info(CH, monitor_ref, Ref) of
  407. ok ->
  408. ?SIM({ok, CH}, monitor_process_local);
  409. {error, Reason} ->
  410. disconnect(CH, {config_update, Reason}),
  411. {error, Reason}
  412. end;
  413. monitor_process(CH, ControlPid)
  414. when is_pid(ControlPid) andalso (node(ControlPid) =/= node()) ->
  415. RemoteNode = node(ControlPid),
  416. UserMonitorPid = whereis(megaco_monitor),
  417. Args = [CH, ControlPid, UserMonitorPid],
  418. case rpc:call(RemoteNode, ?MODULE, connect_remote, Args) of
  419. {ok, ControlMonitorPid} ->
  420. M = ?MODULE,
  421. F = disconnect_local,
  422. A = [CH],
  423. Ref = megaco_monitor:apply_at_exit(M, F, A, ControlMonitorPid),
  424. case megaco_config:update_conn_info(CH, monitor_ref, Ref) of
  425. ok ->
  426. ?SIM({ok, CH}, monitor_process_remote);
  427. {error, Reason} ->
  428. disconnect(CH, {config_update, Reason}),
  429. {error, Reason}
  430. end;
  431. {error, Reason} ->
  432. disconnect(CH, {connect_remote, Reason}),
  433. {error, Reason};
  434. {badrpc, Reason} ->
  435. Reason2 = {'EXIT', Reason},
  436. disconnect(CH, {connect_remote, Reason2}),
  437. {error, Reason2}
  438. end;
  439. monitor_process(CH, undefined = _ControlPid) ->
  440. %% We have to do this later (setting up the monitor),
  441. %% when the first message arrives. The 'connected' atom is
  442. %% the indication for the first arriving message to finish
  443. %% the connect.
  444. %% This may be the case when an MGC performs a pre-connect
  445. %% in order to speed up the handling of an (expected) connecting
  446. %% MG.
  447. case megaco_config:update_conn_info(CH, monitor_ref, connected) of
  448. ok ->
  449. ?SIM({ok, CH}, monitor_process_local);
  450. {error, Reason} ->
  451. disconnect(CH, {config_update, Reason}),
  452. {error, Reason}
  453. end.
  454. connect_remote(CH, ControlPid, UserMonitorPid)
  455. when node(ControlPid) =:= node() andalso node(UserMonitorPid) =/= node() ->
  456. case megaco_config:lookup_local_conn(CH) of
  457. [_ConnData] ->
  458. UserNode = node(UserMonitorPid),
  459. M = ?MODULE,
  460. F = disconnect_remote,
  461. A = [CH, UserNode],
  462. Ref = megaco_monitor:apply_at_exit(M, F, A, UserMonitorPid),
  463. case megaco_config:connect_remote(CH, UserNode, Ref) of
  464. ok ->
  465. ControlMonitorPid = whereis(megaco_monitor),
  466. ?SIM({ok, ControlMonitorPid}, connect_remote);
  467. {error, Reason} ->
  468. {error, Reason}
  469. end;
  470. [] ->
  471. {error, {no_connection, CH}}
  472. end.
  473. cancel_apply_at_exit({connecting, _ConnectorPid}) ->
  474. ok;
  475. cancel_apply_at_exit(connected) ->
  476. ok;
  477. cancel_apply_at_exit(ControlRef) ->
  478. megaco_monitor:cancel_apply_at_exit(ControlRef).
  479. node_of_control_pid(Pid) when is_pid(Pid) ->
  480. node(Pid);
  481. node_of_control_pid(_) ->
  482. node().
  483. disconnect(ConnHandle, DiscoReason)
  484. when is_record(ConnHandle, megaco_conn_handle) ->
  485. case megaco_config:disconnect(ConnHandle) of
  486. {ok, ConnData, RemoteConnData} ->
  487. ControlRef = ConnData#conn_data.monitor_ref,
  488. cancel_apply_at_exit(ControlRef),
  489. handle_disconnect_callback(ConnData, DiscoReason),
  490. ControlNode = node_of_control_pid(ConnData#conn_data.control_pid),
  491. case ControlNode =:= node() of
  492. true ->
  493. %% Propagate to remote users
  494. CancelFun =
  495. fun(RCD) ->
  496. UserRef = RCD#remote_conn_data.monitor_ref,
  497. cancel_apply_at_exit(UserRef),
  498. RCD#remote_conn_data.user_node
  499. end,
  500. Nodes = lists:map(CancelFun, RemoteConnData),
  501. %% io:format("NODES: ~p~n", [Nodes]),
  502. M = ?MODULE,
  503. F = disconnect,
  504. A = [ConnHandle, DiscoReason],
  505. case rpc:multicall(Nodes, M, F, A) of
  506. {Res, []} ->
  507. Check = fun(ok) -> false;
  508. ({error, {no_connection, _CH}}) -> false;
  509. (_) -> true
  510. end,
  511. case lists:filter(Check, Res) of
  512. [] ->
  513. ok;
  514. Bad ->
  515. {error, {remote_disconnect_error, ConnHandle, Bad}}
  516. end;
  517. {_Res, Bad} ->
  518. {error, {remote_disconnect_crash, ConnHandle, Bad}}
  519. end;
  520. false when (RemoteConnData =:= []) ->
  521. %% Propagate to remote control node
  522. M = ?MODULE,
  523. F = disconnect_remote,
  524. A = [DiscoReason, ConnHandle, node()],
  525. case rpc:call(ControlNode, M, F, A) of
  526. {badrpc, Reason} ->
  527. {error, {'EXIT', Reason}};
  528. Other ->
  529. Other
  530. end
  531. end;
  532. {error, Reason} ->
  533. {error, Reason}
  534. end;
  535. disconnect(BadHandle, Reason) ->
  536. {error, {bad_conn_handle, BadHandle, Reason}}.
  537. disconnect_local(Reason, ConnHandle) ->
  538. disconnect(ConnHandle, {no_controlling_process, Reason}).
  539. disconnect_remote(_Reason, ConnHandle, UserNode) ->
  540. case megaco_config:disconnect_remote(ConnHandle, UserNode) of
  541. [RCD] ->
  542. Ref = RCD#remote_conn_data.monitor_ref,
  543. cancel_apply_at_exit(Ref),
  544. ok;
  545. [] ->
  546. {error, {no_connection, ConnHandle}}
  547. end.
  548. %%----------------------------------------------------------------------
  549. %% Handle incoming message
  550. %%----------------------------------------------------------------------
  551. receive_message(ReceiveHandle, ControlPid, SendHandle, Bin) ->
  552. Extra = ?default_user_callback_extra,
  553. receive_message(ReceiveHandle, ControlPid, SendHandle, Bin, Extra).
  554. receive_message(ReceiveHandle, ControlPid, SendHandle, Bin, Extra) ->
  555. Opts = [link , {min_heap_size, 5000}],
  556. spawn_opt(?MODULE,
  557. process_received_message,
  558. [ReceiveHandle, ControlPid, SendHandle, Bin, self(), Extra], Opts),
  559. ok.
  560. %% This function is called via the spawn_opt function with the link
  561. %% option, therefor the unlink before the exit.
  562. process_received_message(ReceiveHandle, ControlPid, SendHandle, Bin, Receiver,
  563. Extra) ->
  564. process_received_message(ReceiveHandle, ControlPid, SendHandle, Bin, Extra),
  565. unlink(Receiver),
  566. exit(normal).
  567. process_received_message(ReceiveHandle, ControlPid, SendHandle, Bin) ->
  568. Extra = ?default_user_callback_extra,
  569. process_received_message(ReceiveHandle, ControlPid, SendHandle, Bin, Extra).
  570. process_received_message(ReceiveHandle, ControlPid, SendHandle, Bin, Extra) ->
  571. Flag = process_flag(trap_exit, true),
  572. case prepare_message(ReceiveHandle, SendHandle, Bin, ControlPid, Extra) of
  573. {ok, ConnData, MegaMsg} when is_record(MegaMsg, 'MegacoMessage') ->
  574. ?rt1(ConnData, "message prepared", [MegaMsg]),
  575. Mess = MegaMsg#'MegacoMessage'.mess,
  576. case Mess#'Message'.messageBody of
  577. {transactions, Transactions} ->
  578. {AckList, ReqList} =
  579. prepare_trans(ConnData, Transactions, [], [], Extra),
  580. handle_acks(AckList, Extra),
  581. case ReqList of
  582. [] ->
  583. ?rt3("no transaction requests"),
  584. ignore;
  585. [Req|Reqs] when (ConnData#conn_data.threaded =:= true) ->
  586. ?rt3("handle requests (spawned)"),
  587. lists:foreach(
  588. fun(R) ->
  589. spawn(?MODULE, handle_request, [R, Extra])
  590. end,
  591. Reqs),
  592. handle_request(Req, Extra);
  593. _ ->
  594. ?rt3("handle requests"),
  595. case handle_requests(ReqList, [], Extra) of
  596. [] ->
  597. ignore;
  598. [LongRequest | More] ->
  599. lists:foreach(
  600. fun(LR) ->
  601. spawn(?MODULE, handle_long_request, [LR, Extra])
  602. end,
  603. More),
  604. handle_long_request(LongRequest, Extra)
  605. end
  606. end;
  607. {messageError, Error} ->
  608. handle_message_error(ConnData, Error, Extra)
  609. end;
  610. {silent_fail, ConnData, {_Code, Reason, Error}} ->
  611. ?report_debug(ConnData, Reason, [no_reply, Error]),
  612. ignore;
  613. {verbose_fail, ConnData, {Code, Reason, Error}} ->
  614. ?report_debug(ConnData, Reason, [Error]),
  615. send_message_error(ConnData, Code, Reason)
  616. end,
  617. process_flag(trap_exit, Flag),
  618. ok.
  619. prepare_message(RH, SH, Bin, Pid, Extra)
  620. when is_record(RH, megaco_receive_handle) andalso is_pid(Pid) ->
  621. ?report_trace(RH, "receive bytes", [{bytes, Bin}]),
  622. EncodingMod = RH#megaco_receive_handle.encoding_mod,
  623. EncodingConfig = RH#megaco_receive_handle.encoding_config,
  624. ProtVersion = RH#megaco_receive_handle.protocol_version,
  625. case (catch EncodingMod:decode_message(EncodingConfig, ProtVersion, Bin)) of
  626. {ok, MegaMsg} when is_record(MegaMsg, 'MegacoMessage') ->
  627. ?report_trace(RH, "receive message", [{message, MegaMsg}]),
  628. Mess = MegaMsg#'MegacoMessage'.mess,
  629. RemoteMid = Mess#'Message'.mId,
  630. Version = Mess#'Message'.version,
  631. LocalMid = RH#megaco_receive_handle.local_mid,
  632. CH = #megaco_conn_handle{local_mid = LocalMid,
  633. remote_mid = RemoteMid},
  634. case megaco_config:lookup_local_conn(CH) of
  635. %%
  636. %% Message is not of the negotiated version
  637. %%
  638. [#conn_data{protocol_version = NegVersion,
  639. strict_version = true} = ConnData]
  640. when NegVersion =/= Version ->
  641. %% Use already established connection,
  642. %% but incorrect version
  643. ?rt1(ConnData, "not negotiated version", [Version]),
  644. Error = {error, {not_negotiated_version,
  645. NegVersion, Version}},
  646. handle_syntax_error_callback(RH, ConnData,
  647. prepare_error(Error),
  648. Extra);
  649. [ConnData] ->
  650. %%
  651. %% Use an already established connection
  652. %%
  653. %% This *may* have been set up in the
  654. %% "non-official" way, so we may need to
  655. %% create the monitor to the control process
  656. %% and store the SendHandle (which is normally
  657. %% done when creating the "temporary" connection).
  658. %%
  659. ?rt1(ConnData, "use already established connection", []),
  660. ConnData2 = ConnData#conn_data{send_handle = SH,
  661. control_pid = Pid,
  662. protocol_version = Version},
  663. check_message_auth(CH, ConnData2, MegaMsg, Bin);
  664. [] ->
  665. %% Setup a temporary connection
  666. ?rt3("setup a temporary connection"),
  667. case autoconnect(RH, RemoteMid, SH, Pid, Extra) of
  668. {ok, _} ->
  669. do_prepare_message(RH, CH, SH, MegaMsg, Pid, Bin);
  670. {error, {already_connected, _ConnHandle}} ->
  671. do_prepare_message(RH, CH, SH, MegaMsg, Pid, Bin);
  672. {error, {connection_refused, ConnData, Reason}} ->
  673. Error = prepare_error({error, {connection_refused, Reason}}),
  674. {verbose_fail, ConnData, Error};
  675. {error, Reason} ->
  676. ConnData = fake_conn_data(RH, RemoteMid, SH, Pid),
  677. ConnData2 = ConnData#conn_data{protocol_version = Version},
  678. Error = prepare_error({error, Reason}),
  679. {verbose_fail, ConnData2, Error}
  680. end
  681. end;
  682. Error ->
  683. ?rt2("decode error", [Error]),
  684. ConnData = handle_decode_error(Error,
  685. RH, SH, Bin, Pid,
  686. EncodingMod,
  687. EncodingConfig,
  688. ProtVersion),
  689. handle_syntax_error_callback(RH, ConnData, prepare_error(Error), Extra)
  690. end;
  691. prepare_message(RH, SendHandle, _Bin, ControlPid, _Extra) ->
  692. ConnData = fake_conn_data(RH, SendHandle, ControlPid),
  693. Error = prepare_error({'EXIT', {bad_receive_handle, RH}}),
  694. {verbose_fail, ConnData, Error}.
  695. handle_decode_error({error, {unsupported_version, _}},
  696. #megaco_receive_handle{local_mid = LocalMid} = RH, SH,
  697. Bin, Pid,
  698. EM, EC, V) ->
  699. case (catch EM:decode_mini_message(EC, V, Bin)) of
  700. {ok, #'MegacoMessage'{mess = #'Message'{version = _Ver,
  701. mId = RemoteMid}}} ->
  702. ?rt2("erroneous message received", [SH, RemoteMid, _Ver]),
  703. CH = #megaco_conn_handle{local_mid = LocalMid,
  704. remote_mid = RemoteMid},
  705. incNumErrors(CH),
  706. %% We cannot put the version into conn-data, that will
  707. %% make the resulting error message impossible to sent
  708. %% (unsupported version)
  709. case megaco_config:lookup_local_conn(CH) of
  710. [ConnData] ->
  711. ?rt3("known to us"),
  712. ConnData#conn_data{send_handle = SH};
  713. [] ->
  714. ?rt3("unknown to us"),
  715. ConnData = fake_conn_data(RH, SH, Pid),
  716. ConnData#conn_data{conn_handle = CH}
  717. end;
  718. _ ->
  719. ?rt2("erroneous message received", [SH]),
  720. incNumErrors(),
  721. fake_conn_data(RH, SH, Pid)
  722. end;
  723. handle_decode_error(_,
  724. #megaco_receive_handle{local_mid = LocalMid} = RH, SH,
  725. Bin, Pid,
  726. EM, EC, V) ->
  727. case (catch EM:decode_mini_message(EC, V, Bin)) of
  728. {ok, #'MegacoMessage'{mess = #'Message'{version = Ver,
  729. mId = RemoteMid}}} ->
  730. ?rt2("erroneous message received", [SH, Ver, RemoteMid]),
  731. CH = #megaco_conn_handle{local_mid = LocalMid,
  732. remote_mid = RemoteMid},
  733. incNumErrors(CH),
  734. case megaco_config:lookup_local_conn(CH) of
  735. [ConnData] ->
  736. ?rt3("known to us"),
  737. ConnData#conn_data{send_handle = SH,
  738. protocol_version = Ver};
  739. [] ->
  740. ?rt3("unknown to us"),
  741. ConnData = fake_conn_data(RH, SH, Pid),
  742. ConnData#conn_data{conn_handle = CH,
  743. protocol_version = Ver}
  744. end;
  745. _ ->
  746. ?rt2("erroneous message received", [SH]),
  747. incNumErrors(),
  748. fake_conn_data(RH, SH, Pid)
  749. end.
  750. do_prepare_message(RH, CH, SendHandle, MegaMsg, ControlPid, Bin) ->
  751. case megaco_config:lookup_local_conn(CH) of
  752. [ConnData] ->
  753. case check_message_auth(CH, ConnData, MegaMsg, Bin) of
  754. {ok, ConnData2, MegaMsg} ->
  755. %% Let the connection be permanent
  756. {ok, ConnData2, MegaMsg};
  757. {ReplyTag, ConnData, Reason} ->
  758. %% Remove the temporary connection
  759. disconnect(CH, {bad_auth, Reason}),
  760. {ReplyTag, ConnData, Reason}
  761. end;
  762. [] ->
  763. Reason = no_connection,
  764. disconnect(CH, Reason),
  765. RemoteMid = CH#megaco_conn_handle.remote_mid,
  766. ConnData = fake_conn_data(RH, RemoteMid, SendHandle, ControlPid),
  767. Error = prepare_error({error, Reason}),
  768. {silent_fail, ConnData, Error}
  769. end.
  770. check_message_auth(_ConnHandle, ConnData, MegaMsg, Bin) ->
  771. MsgAuth = MegaMsg#'MegacoMessage'.authHeader,
  772. Mess = MegaMsg#'MegacoMessage'.mess,
  773. Version = Mess#'Message'.version,
  774. ConnData2 = ConnData#conn_data{protocol_version = Version},
  775. ConnAuth = ConnData2#conn_data.auth_data,
  776. ?report_trace(ConnData2, "check message auth", [{bytes, Bin}]),
  777. if
  778. (MsgAuth =:= asn1_NOVALUE) andalso (ConnAuth =:= asn1_NOVALUE) ->
  779. ?SIM({ok, ConnData2, MegaMsg}, check_message_auth);
  780. true ->
  781. ED = #'ErrorDescriptor'{errorCode = ?megaco_unauthorized,
  782. errorText = "Autentication is not supported"},
  783. {verbose_fail, ConnData2, prepare_error({error, ED})}
  784. end.
  785. handle_syntax_error_callback(ReceiveHandle, ConnData, PrepError, Extra) ->
  786. {Code, Reason, Error} = PrepError,
  787. ErrorDesc = #'ErrorDescriptor'{errorCode = Code, errorText = Reason},
  788. Version =
  789. case Error of
  790. {error, {unsupported_version, UV}} ->
  791. UV;
  792. _ ->
  793. ConnData#conn_data.protocol_version
  794. end,
  795. UserMod = ConnData#conn_data.user_mod,
  796. UserArgs = ConnData#conn_data.user_args,
  797. ?report_trace(ReceiveHandle, "callback: syntax error", [ErrorDesc, Error]),
  798. Args =
  799. case Extra of
  800. ?default_user_callback_extra ->
  801. [ReceiveHandle, Version, ErrorDesc | UserArgs];
  802. _ ->
  803. [ReceiveHandle, Version, ErrorDesc, Extra | UserArgs]
  804. end,
  805. Res = (catch apply(UserMod, handle_syntax_error, Args)),
  806. ?report_debug(ReceiveHandle, "return: syntax error",
  807. [{return, Res}, ErrorDesc, Error]),
  808. case Res of
  809. reply ->
  810. {verbose_fail, ConnData, PrepError};
  811. {reply,#'ErrorDescriptor'{errorCode = Code1, errorText = Reason1}} ->
  812. {verbose_fail, ConnData, {Code1,Reason1,Error}};
  813. no_reply ->
  814. {silent_fail, ConnData, PrepError};
  815. {no_reply,#'ErrorDescriptor'{errorCode=Code2,errorText=Reason2}} ->
  816. {silent_fail, ConnData, {Code2,Reason2,Error}}; %%% OTP-????
  817. _ ->
  818. warning_msg("syntax error callback failed: ~w", [Res]),
  819. {verbose_fail, ConnData, PrepError}
  820. end.
  821. fake_conn_data(CH) when is_record(CH, megaco_conn_handle) ->
  822. case (catch megaco_config:conn_info(CH, receive_handle)) of
  823. RH when is_record(RH, megaco_receive_handle) ->
  824. RemoteMid = CH#megaco_conn_handle.remote_mid,
  825. ConnData =
  826. fake_conn_data(RH, RemoteMid, no_send_handle, no_control_pid),
  827. ConnData#conn_data{conn_handle = CH};
  828. {'EXIT', _} ->
  829. UserMid = CH#megaco_conn_handle.local_mid,
  830. case catch megaco_config:user_info(UserMid, receive_handle) of
  831. {'EXIT', _} -> % No such user
  832. #conn_data{conn_handle = CH,
  833. serial = undefined_serial,
  834. control_pid = no_control_pid,
  835. monitor_ref = undefined_monitor_ref,
  836. send_mod = no_send_mod,
  837. send_handle = no_send_handle,
  838. encoding_mod = no_encoding_mod,
  839. encoding_config = no_encoding_config,
  840. reply_action = undefined,
  841. sent_pending_limit = infinity,
  842. recv_pending_limit = infinity};
  843. RH ->
  844. ConnData =
  845. fake_conn_data(RH, no_send_handle, no_control_pid),
  846. ConnData#conn_data{conn_handle = CH}
  847. end
  848. end.
  849. fake_conn_data(RH, SendHandle, ControlPid) ->
  850. fake_conn_data(RH, unknown_remote_mid, SendHandle, ControlPid).
  851. fake_conn_data(RH, RemoteMid, SendHandle, ControlPid) ->
  852. case catch megaco_config:init_conn_data(RH, RemoteMid, SendHandle, ControlPid) of
  853. {'EXIT', _} -> % No such user
  854. fake_user_data(RH, RemoteMid, SendHandle, ControlPid);
  855. ConnData ->
  856. ConnData
  857. end.
  858. fake_user_data(RH, RemoteMid, SendHandle, ControlPid) ->
  859. LocalMid = RH#megaco_receive_handle.local_mid,
  860. RH2 = RH#megaco_receive_handle{local_mid = default},
  861. case catch megaco_config:init_conn_data(RH2, RemoteMid, SendHandle, ControlPid) of
  862. {'EXIT', _} -> % Application stopped?
  863. ConnHandle = #megaco_conn_handle{local_mid = LocalMid,
  864. remote_mid = RemoteMid},
  865. EncodingMod = RH#megaco_receive_handle.encoding_mod,
  866. EncodingConfig = RH#megaco_receive_handle.encoding_config,
  867. SendMod = RH#megaco_receive_handle.send_mod,
  868. #conn_data{conn_handle = ConnHandle,
  869. serial = undefined_serial,
  870. control_pid = ControlPid,
  871. monitor_ref = undefined_monitor_ref,
  872. send_mod = SendMod,
  873. send_handle = SendHandle,
  874. encoding_mod = EncodingMod,
  875. encoding_config = EncodingConfig,
  876. reply_action = undefined,
  877. sent_pending_limit = infinity,
  878. recv_pending_limit = infinity};
  879. ConnData ->
  880. ConnData
  881. end.
  882. prepare_error(Error) ->
  883. case Error of
  884. {error, ED} when is_record(ED, 'ErrorDescriptor') ->
  885. Code = ED#'ErrorDescriptor'.errorCode,
  886. Reason = ED#'ErrorDescriptor'.errorText,
  887. {Code, Reason, Error};
  888. {error, [{reason, {bad_token, [BadToken, _Acc]}, Line}]} when is_integer(Line) ->
  889. Reason =
  890. lists:flatten(
  891. io_lib:format("Illegal token (~p) on line ~w", [BadToken, Line])),
  892. Code = ?megaco_bad_request,
  893. {Code, Reason, Error};
  894. {error, [{reason, {bad_token, _}, Line}]} when is_integer(Line) ->
  895. Reason = lists:concat(["Illegal token on line ", Line]),
  896. Code = ?megaco_bad_request,
  897. {Code, Reason, Error};
  898. {error, [{reason, {Line, _ParserMod, RawReasonString}} | _]} when is_integer(Line) andalso is_list(RawReasonString) ->
  899. Reason =
  900. case RawReasonString of
  901. [[$s, $y, $n, $t, $a, $x | _], TokenString] ->
  902. lists:flatten(
  903. io_lib:format("Syntax error on line ~w before token ~s", [Line, TokenString]));
  904. _ ->
  905. lists:flatten(io_lib:format("Syntax error on line ~w", [Line]))
  906. end,
  907. Code = ?megaco_bad_request,
  908. {Code, Reason, Error};
  909. {error, [{reason, {Line, _, _}} | _]} when is_integer(Line) ->
  910. Reason = lists:concat(["Syntax error on line ", Line]),
  911. Code = ?megaco_bad_request,
  912. {Code, Reason, Error};
  913. {error, {connection_refused, ED}} when is_record(ED,'ErrorDescriptor') ->
  914. Code = ED#'ErrorDescriptor'.errorCode,
  915. Reason = ED#'ErrorDescriptor'.errorText,
  916. {Code, Reason, Error};
  917. {error, {connection_refused, _}} ->
  918. Reason = "Connection refused by user",
  919. Code = ?megaco_unauthorized,
  920. {Code, Reason, Error};
  921. {error, {unsupported_version, V}} ->
  922. Reason =
  923. lists:flatten(io_lib:format("Unsupported version: ~w",[V])),
  924. Code = ?megaco_version_not_supported,
  925. {Code, Reason, Error};
  926. {error, {not_negotiated_version, NegV, MsgV}} ->
  927. Reason =
  928. lists:flatten(
  929. io_lib:format("Not negotiated version: ~w [negotiated ~w]",
  930. [MsgV, NegV])),
  931. Code = ?megaco_version_not_supported,
  932. {Code, Reason, Error};
  933. {error, _} ->
  934. Reason = "Syntax error",
  935. Code = ?megaco_bad_request,
  936. {Code, Reason, Error};
  937. {ok, MegaMsg} when is_record(MegaMsg, 'MegacoMessage') ->
  938. Reason = "MID does not match config",
  939. Code = ?megaco_incorrect_identifier,
  940. {Code, Reason, Error};
  941. _ ->
  942. Reason = "Fatal syntax error",
  943. Code = ?megaco_internal_gateway_error,
  944. {Code, Reason, Error}
  945. end.
  946. prepare_trans(_ConnData, [], AckList, ReqList, _Extra) ->
  947. ?SIM({AckList, ReqList}, prepare_trans_done);
  948. prepare_trans(ConnData, Trans, AckList, ReqList, Extra)
  949. when ConnData#conn_data.monitor_ref =:= undefined_auto_monitor_ref ->
  950. ?rt3("prepare_trans - autoconnect"),
  951. %% <BUGBUG>
  952. %% Do we need something here, if we send more then one
  953. %% trans per message?
  954. %% </BUGBUG>
  955. %% May occur if another process has already setup a
  956. %% temporary connection, but the handle_connect callback
  957. %% function has not yet returned before the eager MG
  958. %% re-sends its initial service change message.
  959. prepare_autoconnecting_trans(ConnData, Trans, AckList, ReqList, Extra);
  960. prepare_trans(#conn_data{monitor_ref = connected} = ConnData,
  961. Trans, AckList, ReqList, Extra) ->
  962. ?rt3("prepare_trans - connected"),
  963. %%
  964. %% This will happen when the "MGC" user performs a "pre" connect,
  965. %% instead of waiting for the auto-connect (which normally
  966. %% happen when the MGC receives the first message from the
  967. %% MG).
  968. %%
  969. %%
  970. %% The monitor_ref will have this value when the pre-connect
  971. %% is complete, so we finish it here and then continue with the
  972. %% normal transaction prepare.
  973. %%
  974. case finish_connect(ConnData) of
  975. {ok, CD} ->
  976. prepare_normal_trans(CD, Trans, AckList, ReqList, Extra);
  977. {error, Reason} ->
  978. disconnect(ConnData#conn_data.conn_handle, Reason),
  979. {[], []}
  980. end;
  981. prepare_trans(#conn_data{monitor_ref = {connecting, _}} = _ConnData,
  982. _Trans, _AckList, _ReqList, _Extra) ->
  983. ?rt3("prepare_trans - connecting"),
  984. %%
  985. %% This will happen when the "MGC" user performs a "pre" connect,
  986. %% instead of waiting for the auto-connect (which normally
  987. %% happen when the MGC receives the first message from the
  988. %% MG).
  989. %%
  990. %%
  991. %% The monitor_ref will have this value when the pre-connect
  992. %% is in progress. We drop (ignore) this message and hope the
  993. %% other side (MG) will resend.
  994. %%
  995. %% prepare_connecting_trans(ConnData, Trans, AckList, ReqList, Extra);
  996. {[], []};
  997. prepare_trans(ConnData, Trans, AckList, ReqList, Extra) ->
  998. ?rt3("prepare_trans - normal"),
  999. %% Handle transaction in the normal case
  1000. prepare_normal_trans(ConnData, Trans, AckList, ReqList, Extra).
  1001. prepare_autoconnecting_trans(_ConnData, [], AckList, ReqList, _Extra) ->
  1002. ?SIM({AckList, ReqList}, prepare_autoconnecting_trans_done);
  1003. prepare_autoconnecting_trans(ConnData, [Trans | Rest], AckList, ReqList, Extra) ->
  1004. ?rt1(ConnData, "[autoconnecting] prepare trans", [Trans]),
  1005. case Trans of
  1006. {transactionRequest, T} when is_record(T, 'TransactionRequest') ->
  1007. Serial = T#'TransactionRequest'.transactionId,
  1008. ConnData2 = ConnData#conn_data{serial = Serial},
  1009. ?report_trace(ConnData2, "Pending handle_connect", [T]),
  1010. %% ------------------------------------------
  1011. %%
  1012. %% Check pending limit
  1013. %%
  1014. %% ------------------------------------------
  1015. Limit = ConnData#conn_data.sent_pending_limit,
  1016. TransId = to_remote_trans_id(ConnData2),
  1017. case check_and_maybe_incr_pending_limit(Limit, sent, TransId) of
  1018. ok ->
  1019. send_pending(ConnData2);
  1020. error ->
  1021. %% Pending limit:
  1022. %% In this (granted, highly hypothetical case)
  1023. %% we would make the user very confused if we
  1024. %% called the abort callback function, since
  1025. %% the request callback function has not yet
  1026. %% been called. Alas, we skip this call here.
  1027. send_pending_limit_error(ConnData);
  1028. aborted ->
  1029. ignore
  1030. end,
  1031. prepare_autoconnecting_trans(ConnData2, Rest, AckList, ReqList,
  1032. Extra);
  1033. _ ->
  1034. prepare_autoconnecting_trans(ConnData, Rest, AckList, ReqList,
  1035. Extra)
  1036. end.
  1037. %% =================================================================
  1038. %%
  1039. %% Note that the TransactionReply record was changed i v3 (two
  1040. %% new fields where added), and since we don't know which version,
  1041. %% we cannot use the record definition of TransactionReply.
  1042. %% Instead we transform the record into our own internal format
  1043. %% #megaco_transaction_reply{}
  1044. %%
  1045. %% =================================================================
  1046. prepare_normal_trans(_ConnData, [], AckList, ReqList, _Extra) ->
  1047. ?SIM({AckList, ReqList}, prepare_normal_trans_done);
  1048. prepare_normal_trans(ConnData, [Trans | Rest], AckList, ReqList, Extra) ->
  1049. ?rt1(ConnData, "prepare [normal] trans", [Trans]),
  1050. case Trans of
  1051. {transactionRequest, #'TransactionRequest'{transactionId = asn1_NOVALUE}} ->
  1052. ConnData2 = ConnData#conn_data{serial = 0},
  1053. Code = ?megaco_bad_request,
  1054. Reason = "Syntax error in message: transaction id missing",
  1055. send_trans_error(ConnData2, Code, Reason),
  1056. prepare_normal_trans(ConnData2, Rest, AckList, ReqList, Extra);
  1057. {transactionRequest, T} when is_record(T, 'TransactionRequest') ->
  1058. Serial = T#'TransactionRequest'.transactionId,
  1059. ConnData2 = ConnData#conn_data{serial = Serial},
  1060. prepare_request(ConnData2, T, Rest, AckList, ReqList, Extra);
  1061. {transactionPending, T} when is_record(T, 'TransactionPending') ->
  1062. Serial = T#'TransactionPending'.transactionId,
  1063. ConnData2 = ConnData#conn_data{serial = Serial},
  1064. handle_pending(ConnData2, T, Extra),
  1065. prepare_normal_trans(ConnData2, Rest, AckList, ReqList, Extra);
  1066. {transactionReply, T} when is_tuple(T) andalso
  1067. (element(1, T) == 'TransactionReply') ->
  1068. T2 = transform_transaction_reply_dec(T),
  1069. Serial = T2#megaco_transaction_reply.transactionId,
  1070. ConnData2 = ConnData#conn_data{serial = Serial},
  1071. handle_reply(ConnData2, T2, Extra),
  1072. prepare_normal_trans(ConnData2, Rest, AckList, ReqList, Extra);
  1073. {transactionResponseAck, List} when is_list(List) ->
  1074. prepare_ack(ConnData, List, Rest, AckList, ReqList, Extra);
  1075. {segmentReply, SR} when is_record(SR, 'SegmentReply') ->
  1076. handle_segment_reply(ConnData, SR, Extra),
  1077. prepare_normal_trans(ConnData, Rest, AckList, ReqList, Extra)
  1078. end.
  1079. prepare_request(ConnData, T, Rest, AckList, ReqList, Extra) ->
  1080. ?rt2("prepare request", [T]),
  1081. ConnHandle = ConnData#conn_data.conn_handle,
  1082. LocalMid = ConnHandle#megaco_conn_handle.local_mid,
  1083. TransId = to_remote_trans_id(ConnData),
  1084. ?rt2("prepare request", [LocalMid, TransId]),
  1085. case lookup_reply(ConnData, TransId) of
  1086. [] ->
  1087. ?rt3("brand new request"),
  1088. %% Brand new request
  1089. %% Check pending limit:
  1090. %%
  1091. %% We should actually check the pending limit here
  1092. %% but since we have to do it later in the
  1093. %% handle_request function (just before we call
  1094. %% the handle_trans_request callback function) we
  1095. %% can just as well wait (this is after all a very
  1096. %% unlikely case: see function prepare_trans when
  1097. %% monitor_ref == undefined_auto_monitor_ref).
  1098. %%
  1099. #conn_data{send_handle = SendHandle,
  1100. pending_timer = InitTimer,
  1101. protocol_version = Version,
  1102. user_mod = UserMod,
  1103. user_args = UserArgs} = ConnData,
  1104. {WaitFor, CurrTimer} = megaco_timer:init(InitTimer),
  1105. M = ?MODULE,
  1106. F = pending_timeout,
  1107. A = [ConnHandle, TransId, CurrTimer],
  1108. PendingRef = megaco_monitor:apply_after(M, F, A, WaitFor),
  1109. Rep = #reply{send_handle = SendHandle,
  1110. trans_id = TransId,
  1111. local_mid = LocalMid,
  1112. pending_timer_ref = PendingRef,
  1113. handler = self(),
  1114. version = Version,
  1115. user_mod = UserMod,
  1116. user_args = UserArgs},
  1117. case megaco_monitor:insert_reply_new(Rep) of
  1118. true ->
  1119. prepare_normal_trans(ConnData, Rest, AckList,
  1120. [{ConnData, TransId, T} | ReqList],
  1121. Extra);
  1122. false ->
  1123. %% Oups - someone got there before we did...
  1124. ?report_debug(ConnData,
  1125. "prepare request: conflicting requests",
  1126. [TransId]),
  1127. send_pending(ConnData),
  1128. megaco_monitor:cancel_apply_after(PendingRef),
  1129. prepare_normal_trans(ConnData, Rest, AckList, ReqList,
  1130. Extra)
  1131. end;
  1132. %% We can ignore the Converted value here as we *know*
  1133. %% conn-data to be correct (not faked), so even if
  1134. %% the record was converted, it will now have correct
  1135. %% values for user_mod and user_args.
  1136. {_Converted,
  1137. #reply{state = State,
  1138. handler = Pid,
  1139. pending_timer_ref = Ref} = Rep}
  1140. when (State =:= prepare) orelse (State =:= eval_request) ->
  1141. ?rt2("request resend", [State, Pid, Ref]),
  1142. %% Pending limit:
  1143. %% We are still preparing/evaluating the request
  1144. %% Check if the pending limit has been exceeded...
  1145. %% If the pending limit is _not_ exceeded then
  1146. %% we shall send a pending (and actually restart
  1147. %% the pending timer, but that we cannot do).
  1148. %% Don't care about Msg and Rep version diff
  1149. #conn_data{sent_pending_limit = Limit} = ConnData,
  1150. case check_and_maybe_incr_pending_limit(Limit, sent, TransId) of
  1151. ok ->
  1152. %% ------------------------------------------
  1153. %%
  1154. %% Pending limit not exceeded
  1155. %%
  1156. %% 1) Increment number of pendings sent
  1157. %% (done in the check function above)
  1158. %% 2) Send pending message
  1159. %% (We should really restart the pending
  1160. %% timer, but we have no way of doing that).
  1161. %%
  1162. %% ------------------------------------------
  1163. send_pending(ConnData),
  1164. prepare_normal_trans(ConnData, Rest, AckList, ReqList,
  1165. Extra);
  1166. error ->
  1167. %% -------------------------------------------
  1168. %%
  1169. %% Pending limit exceeded
  1170. %%
  1171. %% 1) Cancel pending timer
  1172. %% 2) Send 506 error message to other side
  1173. %% 3) Inform user (depends on state)
  1174. %% 4) Set reply in aborted state
  1175. %%
  1176. %% -------------------------------------------
  1177. %%
  1178. %% State == eval_request:
  1179. %% This means that the request is currently beeing
  1180. %% evaluated by the user, and the reply timer has
  1181. %% not yet been started.
  1182. %% Either:
  1183. %% a) The "other side" will resend (which will
  1184. %% trigger a pending message send) until we pass the
  1185. %% pending limit
  1186. %% b) We will send pending messages (when the pending
  1187. %% timer expire) until we pass the pending limit.
  1188. %% In any event, we cannot delete the reply record
  1189. %% or the pending counter in this case. Is there
  1190. %% a risk we accumulate aborted reply records?
  1191. %%
  1192. %% State == prepare:
  1193. %% The user does not know about this request
  1194. %% so we can safely perform cleanup.
  1195. %%
  1196. megaco_monitor:cancel_apply_after(Ref),
  1197. send_pending_limit_error(ConnData),
  1198. if
  1199. State == eval_request ->
  1200. %%
  1201. %% What if the user never replies?
  1202. %% In that case we will have a record
  1203. %% (and counters) that is never cleaned up...
  1204. NewFields =
  1205. [{#reply.state, aborted},
  1206. {#reply.pending_timer_ref, undefined}],
  1207. megaco_monitor:update_reply_fields(TransId,
  1208. NewFields),
  1209. handle_request_abort_callback(ConnData,
  1210. TransId, Pid, Extra);
  1211. true ->
  1212. %% Since the user does not know about
  1213. %% this call yet, it is safe to cleanup.
  1214. %% Should we inform?
  1215. Rep2 = Rep#reply{state = aborted},
  1216. cancel_reply(ConnData, Rep2, aborted),
  1217. ok
  1218. end,
  1219. prepare_normal_trans(ConnData, Rest, AckList, ReqList,
  1220. Extra);
  1221. aborted ->
  1222. %% -------------------------------------------
  1223. %%
  1224. %% Pending limit already exceeded
  1225. %%
  1226. %% Cleanup, just to make sure:
  1227. %% reply record & pending counter
  1228. %%
  1229. %% -------------------------------------------
  1230. Rep2 = Rep#reply{state = aborted},
  1231. cancel_reply(ConnData, Rep2, aborted),
  1232. prepare_normal_trans(ConnData, Rest, AckList, ReqList,
  1233. Extra)
  1234. end;
  1235. %% We can ignore the Converted value here as we *know*
  1236. %% conn-data to be correct (not faked), so even if
  1237. %% the record was converted, it will now have correct
  1238. %% values for user_mod and user_args.
  1239. {_Converted,
  1240. #reply{state = waiting_for_ack,
  1241. bytes = Bin,
  1242. version = Version} = Rep} ->
  1243. ?rt3("request resend when waiting for ack"),
  1244. %% We have already sent a reply, but the receiver
  1245. %% has obviously not got it. Resend the reply but
  1246. %% don't restart the reply_timer.
  1247. ConnData2 = ConnData#conn_data{protocol_version = Version},
  1248. ?report_trace(ConnData2,
  1249. "re-send trans reply", [T | {bytes, Bin}]),
  1250. case megaco_messenger_misc:send_message(ConnData2, true, Bin) of
  1251. {ok, _} ->
  1252. ok;
  1253. {error, Reason} ->
  1254. %% Pass it on to the user (via handle_ack)
  1255. cancel_reply(ConnData2, Rep, Reason)
  1256. end,
  1257. prepare_normal_trans(ConnData2, Rest, AckList, ReqList, Extra);
  1258. %% We can ignore the Converted value here as we *know*
  1259. %% conn-data to be correct (not faked), so even if
  1260. %% the record was converted, it will now have correct
  1261. %% values for user_mod and user_args.
  1262. {_Converted,
  1263. #reply{state = aborted} = Rep} ->
  1264. ?rt3("request resend when already in aborted state"),
  1265. %% OTP-4956:
  1266. %% Already aborted so ignore.
  1267. %% This furthermore means that the abnoxious user at the
  1268. %% other end has already been informed (pending-limit
  1269. %% passed => error descriptor sent), but keeps sending...
  1270. %%
  1271. %% Shall we perform a cleanup?
  1272. cancel_reply(ConnData, Rep, aborted),
  1273. prepare_normal_trans(ConnData, Rest, AckList, ReqList, Extra)
  1274. end.
  1275. prepare_ack(ConnData, [TA | T], Rest, AckList, ReqList, Extra)
  1276. when is_record(TA, 'TransactionAck') ->
  1277. First = TA#'TransactionAck'.firstAck,
  1278. Last = TA#'TransactionAck'.lastAck,
  1279. TA2 = TA#'TransactionAck'{lastAck = asn1_NOVALUE},
  1280. ConnData2 = ConnData#conn_data{serial = First},
  1281. AckList2 = do_prepare_ack(ConnData2, TA2, AckList),
  1282. if
  1283. Last =:= asn1_NOVALUE ->
  1284. prepare_ack(ConnData, T, Rest, AckList2, ReqList, Extra);
  1285. First < Last ->
  1286. TA3 = TA#'TransactionAck'{firstAck = First + 1},
  1287. prepare_ack(ConnData, [TA3 | T], Rest, AckList2, ReqList, Extra);
  1288. First =:= Last ->
  1289. prepare_ack(ConnData, T, Rest, AckList2, ReqList, Extra);
  1290. First > Last ->
  1291. %% Protocol violation from the sender of this ack
  1292. ?report_important(ConnData, "<ERROR> discard trans",
  1293. [TA, {error, "firstAck > lastAck"}]),
  1294. prepare_ack(ConnData, T, Rest, AckList2, ReqList, Extra)
  1295. end;
  1296. prepare_ack(ConnData, [], Rest, AckList, ReqList, Extra) ->
  1297. prepare_normal_trans(ConnData, Rest, AckList, ReqList, Extra).
  1298. do_prepare_ack(ConnData, T, AckList) ->
  1299. TransId = to_remote_trans_id(ConnData),
  1300. case lookup_reply(ConnData, TransId) of
  1301. [] ->
  1302. %% The reply has already been garbage collected. Ignore.
  1303. ?report_trace(ConnData, "discard ack (no receiver)", [T]),
  1304. AckList;
  1305. {_Converted, Rep} when Rep#reply.state =:= waiting_for_ack ->
  1306. %% Don't care about Msg and Rep version diff
  1307. [{ConnData, Rep, T} | AckList];
  1308. {_Converted, _Rep} ->
  1309. %% Protocol violation from the sender of this ack
  1310. ?report_important(ConnData, "<ERROR> discard trans",
  1311. [T, {error, "got ack before reply was sent"}]),
  1312. AckList
  1313. end.
  1314. increment_request_keep_alive_counter(#conn_data{conn_handle = CH}, TransId) ->
  1315. ?rt1(CH, "increment request keep alive counter", [TransId]),
  1316. megaco_config:incr_reply_counter(CH, TransId).
  1317. create_or_maybe_increment_request_keep_alive_counter(
  1318. #conn_data{conn_handle = CH}, TransId) ->
  1319. ?rt1(CH, "create or maybe increment request keep alive counter",
  1320. [TransId]),
  1321. try
  1322. begin
  1323. megaco_config:cre_reply_counter(CH, TransId)
  1324. end
  1325. catch
  1326. _:_ ->
  1327. megaco_config:incr_reply_counter(CH, TransId)
  1328. end.
  1329. check_and_maybe_create_pending_limit(infinity, _, _) ->
  1330. ok;
  1331. check_and_maybe_create_pending_limit(Limit, Direction, TransId) ->
  1332. ?rt2("check and maybe create pending limit counter",
  1333. [Limit, Direction, TransId]),
  1334. try megaco_config:get_pending_counter(Direction, TransId) of
  1335. Val when Val =< Limit ->
  1336. %% Since we have no intention to increment here, it
  1337. %% is ok to be _at_ the limit
  1338. ok;
  1339. _ ->
  1340. aborted
  1341. catch
  1342. _:_ ->
  1343. %% Has not been created yet (connect).
  1344. megaco_config:cre_pending_counter(Direction, TransId, 0),
  1345. ok
  1346. end.
  1347. %% check_and_maybe_create_pending_limit(infinity, _, _) ->
  1348. %% ok;
  1349. %% check_and_maybe_create_pending_limit(Limit, Direction, TransId) ->
  1350. %% case (catch megaco_config:get_pending_counter(Direction, TransId)) of
  1351. %% {'EXIT', _} ->
  1352. %% %% Has not been created yet (connect).
  1353. %% megaco_config:cre_pending_counter(Direction, TransId, 0),
  1354. %% ok;
  1355. %% Val when Val =< Limit ->
  1356. %% %% Since we have no intention to increment here, it
  1357. %% %% is ok to be _at_ the limit
  1358. %% ok;
  1359. %% _ ->
  1360. %% aborted
  1361. %% end.
  1362. check_pending_limit(infinity, _, _) ->
  1363. {ok, 0};
  1364. check_pending_limit(Limit, Direction, TransId) ->
  1365. ?rt2("check pending limit", [Direction, Limit, TransId]),
  1366. try megaco_config:get_pending_counter(Direction, TransId) of
  1367. Val when Val =< Limit ->
  1368. %% Since we have no intention to increment here, it
  1369. %% is ok to be _at_ the limit
  1370. ?rt2("check pending limit - ok", [Val]),
  1371. {ok, Val};
  1372. _Val ->
  1373. ?rt2("check pending limit - aborted", [_Val]),
  1374. aborted
  1375. catch
  1376. _:_ ->
  1377. %% This function is only called when we "know" the
  1378. %% counter to exist. So, the only reason that this
  1379. %% would happen is of the counter has been removed.
  1380. %% This only happen if the pending limit has been
  1381. %% reached. In any case, this is basically the same
  1382. %% as aborted!
  1383. ?rt2("check pending limit - exit", []),
  1384. aborted
  1385. end.
  1386. check_and_maybe_incr_pending_limit(infinity, _, _) ->
  1387. ok;
  1388. check_and_maybe_incr_pending_limit(Limit, Direction, TransId) ->
  1389. %%
  1390. %% We need this kind of test to detect when we _pass_ the limit
  1391. %%
  1392. ?rt2("check and maybe incr pending limit", [{direction, Direction},
  1393. {transaction_id, TransId},
  1394. {counter_limit, Limit}]),
  1395. try megaco_config:get_pending_counter(Direction, TransId) of
  1396. Val when Val > Limit ->
  1397. ?rt2("check and maybe incr - aborted", [{counter_value, Val}]),
  1398. aborted; % Already passed the limit
  1399. Val ->
  1400. ?rt2("check and maybe incr - incr", [{counter_value, Val}]),
  1401. megaco_config:incr_pending_counter(Direction, TransId),
  1402. if
  1403. Val < Limit ->
  1404. ok; % Still within the limit
  1405. true ->
  1406. ?rt2("check and maybe incr - error",
  1407. [{counter_value, Val}]),
  1408. error % Passed the limit
  1409. end
  1410. catch
  1411. _:_ ->
  1412. %% Has not been created yet (connect).
  1413. %% Try create it, but bevare of possible raise condition
  1414. try
  1415. begin
  1416. megaco_config:cre_pending_counter(Direction, TransId, 1),
  1417. ok
  1418. end
  1419. catch
  1420. _:_ ->
  1421. %% Ouch, raise condition, increment instead...
  1422. megaco_config:incr_pending_counter(Direction, TransId),
  1423. ok
  1424. end
  1425. end.
  1426. %% BUGBUG BUGBUG BUGBUG
  1427. %%
  1428. %% Do we know that the Rep is still valid? A previous transaction
  1429. %% could have taken a lot of time.
  1430. %%
  1431. handle_request({ConnData, TransId, T}, Extra) ->
  1432. case handle_request(ConnData, TransId, T, Extra) of
  1433. {pending, _RequestData} ->
  1434. handle_long_request(ConnData, TransId, T, Extra);
  1435. Else ->
  1436. Else
  1437. end.
  1438. handle_request(ConnData, TransId, T, Extra) ->
  1439. ?report_trace(ConnData, "handle request", [TransId, T]),
  1440. %% Pending limit:
  1441. %% Ok, before we begin, lets check that this request
  1442. %% has not been aborted. I.e. exceeded the pending
  1443. %% limit, so go check it...
  1444. #conn_data{sent_pending_limit = Limit} = ConnData,
  1445. case check_and_maybe_create_pending_limit(Limit, sent, TransId) of
  1446. ok ->
  1447. %% Ok so far, now update state
  1448. case megaco_monitor:update_reply_field(TransId,
  1449. #reply.state,
  1450. eval_request) of
  1451. true ->
  1452. Actions = T#'TransactionRequest'.actions,
  1453. {AckAction, SendReply} =
  1454. handle_request_callback(ConnData, TransId, Actions,
  1455. T, Extra),
  1456. %% Next step, while we where in the callback function,
  1457. %% the pending limit could have been exceeded, so check
  1458. %% it again...
  1459. do_handle_request(AckAction, SendReply,
  1460. ConnData, TransId);
  1461. false ->
  1462. %% Ugh?
  1463. ignore
  1464. end;
  1465. aborted ->
  1466. %% Pending limit
  1467. %% Already exceeded the limit
  1468. %% The user does not yet know about this request, so
  1469. %% don't bother telling that it has been aborted...
  1470. %% Furthermore, the reply timer has not been started,
  1471. %% so do the cleanup now
  1472. ?rt1(ConnData, "pending limit already passed", [TransId]),
  1473. case lookup_reply(ConnData, TransId) of
  1474. {_Converted, Rep} ->
  1475. cancel_reply(ConnData, Rep, aborted);
  1476. _ ->
  1477. ok
  1478. end,
  1479. ignore
  1480. end.
  1481. do_handle_request(_, ignore, _ConnData, _TransId) ->
  1482. ?rt1(_ConnData, "ignore: don't reply", [_TransId]),
  1483. ignore;
  1484. do_handle_request(_, ignore_trans_request, ConnData, TransId) ->
  1485. ?rt1(ConnData, "ignore trans request: don't reply", [TransId]),
  1486. case lookup_reply(ConnData, TransId) of
  1487. {_Converted, #reply{} = Rep} ->
  1488. cancel_reply(ConnData, Rep, ignore);
  1489. _ ->
  1490. ignore
  1491. end;
  1492. do_handle_request({pending, _RequestData}, {aborted, ignore}, _, _) ->
  1493. ?rt2("handle request: pending - aborted - ignore => don't reply", []),
  1494. ignore;
  1495. do_handle_request({pending, _RequestData}, {aborted, _SendReply}, _, _) ->
  1496. ?rt2("handle request: pending - aborted => don't reply", []),
  1497. ignore;
  1498. do_handle_request({pending, RequestData}, _SendReply, _ConnData, _) ->
  1499. ?rt2("handle request: pending", [RequestData]),
  1500. {pending, RequestData};
  1501. do_handle_request(AckAction, {ok, Bin}, ConnData, TransId)
  1502. when is_binary(Bin) ->
  1503. ?rt1(ConnData, "handle request - ok", [AckAction, TransId]),
  1504. case lookup_reply(ConnData, TransId) of
  1505. {_Converted, #reply{pending_timer_ref = PendingRef} = Rep} ->
  1506. #conn_data{reply_timer = InitTimer,
  1507. conn_handle = ConnHandle} = ConnData,
  1508. %% Pending limit update:
  1509. %% - Cancel the pending timer, if running
  1510. %% - Delete the pending counter
  1511. %%
  1512. megaco_monitor:cancel_apply_after(PendingRef),
  1513. megaco_config:del_pending_counter(sent, TransId),
  1514. Method = timer_method(AckAction),
  1515. {WaitFor, CurrTimer} = megaco_timer:init(InitTimer),
  1516. OptBin = opt_garb_binary(CurrTimer, Bin),
  1517. M = ?MODULE,
  1518. F = reply_timeout,
  1519. A = [ConnHandle, TransId, CurrTimer],
  1520. Ref = megaco_monitor:apply_after(Method, M, F, A,
  1521. WaitFor),
  1522. Rep2 = Rep#reply{pending_timer_ref = undefined,
  1523. handler = undefined,
  1524. bytes = OptBin,
  1525. state = waiting_for_ack,
  1526. timer_ref = Ref,
  1527. ack_action = AckAction},
  1528. megaco_monitor:insert_reply(Rep2), % Timing problem?
  1529. ignore;
  1530. _ ->
  1531. %% Been removed already?
  1532. ignore
  1533. end;
  1534. do_handle_request(AckAction, {ok, {Sent, NotSent}}, ConnData, TransId)
  1535. when is_list(Sent) andalso is_list(NotSent) ->
  1536. ?rt1(ConnData, "handle request - ok [segmented reply]",
  1537. [AckAction, TransId]),
  1538. case lookup_reply(ConnData, TransId) of
  1539. {_Converted, #reply{pending_timer_ref = PendingRef} = Rep} ->
  1540. %% d("do_handle_request -> found reply record:"
  1541. %% "~n Rep: ~p", [Rep]),
  1542. #conn_data{reply_timer = InitTimer,
  1543. conn_handle = ConnHandle} = ConnData,
  1544. %% Pending limit update:
  1545. %% - Cancel the pending timer, if running
  1546. %% - Delete the pending counter
  1547. %%
  1548. megaco_monitor:cancel_apply_after(PendingRef),
  1549. megaco_config:del_pending_counter(sent, TransId),
  1550. Method = timer_method(AckAction),
  1551. {WaitFor, CurrTimer} = megaco_timer:init(InitTimer),
  1552. Garb = fun(Bin) -> opt_garb_binary(CurrTimer, Bin) end,
  1553. OptBins = [{SN, Garb(Bin), undefined} || {SN, Bin} <- Sent],
  1554. M = ?MODULE,
  1555. F = reply_timeout,
  1556. A = [ConnHandle, TransId, CurrTimer],
  1557. Ref = megaco_monitor:apply_after(Method, M, F, A, WaitFor),
  1558. Rep2 = Rep#reply{pending_timer_ref = undefined,
  1559. handler = undefined,
  1560. bytes = OptBins,
  1561. state = waiting_for_ack,
  1562. timer_ref = Ref,
  1563. ack_action = AckAction,
  1564. segments = NotSent},
  1565. megaco_monitor:insert_reply(Rep2), % Timing problem?
  1566. ignore;
  1567. _ ->
  1568. %% Been removed already?
  1569. ignore
  1570. end;
  1571. do_handle_request(_, {error, aborted}, ConnData, TransId) ->
  1572. ?report_trace(ConnData, "aborted during our absence", [TransId]),
  1573. case lookup_reply(ConnData, TransId) of
  1574. {_Converted, Rep} ->
  1575. cancel_reply(ConnData, Rep, aborted);
  1576. _ ->
  1577. ok
  1578. end,
  1579. ignore;
  1580. do_handle_request(AckAction, {error, Reason}, ConnData, TransId) ->
  1581. ?report_trace(ConnData, "error", [TransId, Reason]),
  1582. case lookup_reply(ConnData, TransId) of
  1583. {_Converted, Rep} ->
  1584. Rep2 = Rep#reply{state = waiting_for_ack,
  1585. ack_action = AckAction},
  1586. cancel_reply(ConnData, Rep2, Reason);
  1587. _ ->
  1588. ok
  1589. end,
  1590. ignore;
  1591. do_handle_request(AckAction, SendReply, ConnData, TransId) ->
  1592. ?report_trace(ConnData, "unknown send trans reply result",
  1593. [TransId, AckAction, SendReply]),
  1594. ignore.
  1595. handle_requests([{ConnData, TransId, T} | Rest], Pending, Extra) ->
  1596. ?rt2("handle requests", [TransId]),
  1597. case handle_request(ConnData, TransId, T, Extra) of
  1598. {pending, RequestData} ->
  1599. handle_requests(Rest, [{ConnData,TransId,RequestData} | Pending], Extra);
  1600. _ ->
  1601. handle_requests(Rest, Pending, Extra)
  1602. end;
  1603. handle_requests([], Pending, _Extra) ->
  1604. ?rt2("handle requests - done", [Pending]),
  1605. Pending.
  1606. %% opt_garb_binary(timeout, _Bin) -> garb_binary; % Need msg at restart of timer
  1607. opt_garb_binary(_Timer, Bin) -> Bin.
  1608. timer_method(discard_ack) ->
  1609. apply_method;
  1610. timer_method(_) ->
  1611. spawn_method.
  1612. handle_long_request({ConnData, TransId, RequestData}, Extra) ->
  1613. ?rt2("handle long request", [TransId, RequestData]),
  1614. %% Pending limit:
  1615. %% We need to check the pending limit, in case it was
  1616. %% exceeded before we got this far...
  1617. %% We dont need to be able to create the counter here,
  1618. %% since that was done in the handle_request function.
  1619. #conn_data{sent_pending_limit = Limit} = ConnData,
  1620. case check_pending_limit(Limit, sent, TransId) of
  1621. {ok, _} ->
  1622. handle_long_request(ConnData, TransId, RequestData, Extra);
  1623. _ ->
  1624. %% Already exceeded the limit
  1625. ignore
  1626. end.
  1627. handle_long_request(ConnData, TransId, RequestData, Extra) ->
  1628. ?report_trace(ConnData, "callback: trans long request",
  1629. [TransId, {request_data, RequestData}]),
  1630. %% Attempt to update the handler field for this reply record
  1631. %% (if there is one).
  1632. case megaco_monitor:update_reply_field(TransId, #reply.handler, self()) of
  1633. true ->
  1634. {AckAction, Res} =
  1635. handle_long_request_callback(ConnData, TransId,
  1636. RequestData, Extra),
  1637. do_handle_long_request(AckAction, Res, ConnData, TransId);
  1638. false ->
  1639. %% Been removed already?
  1640. ignore
  1641. end.
  1642. do_handle_long_request(AckAction, {ok, Bin}, ConnData, TransId) ->
  1643. case megaco_monitor:lookup_reply_field(TransId, #reply.trans_id) of
  1644. {ok, _} ->
  1645. Method = timer_method(AckAction),
  1646. InitTimer = ConnData#conn_data.reply_timer,
  1647. {WaitFor, CurrTimer} = megaco_timer:init(InitTimer),
  1648. OptBin = opt_garb_binary(CurrTimer, Bin),
  1649. ConnHandle = ConnData#conn_data.conn_handle,
  1650. M = ?MODULE,
  1651. F = reply_timeout,
  1652. A = [ConnHandle, TransId, CurrTimer],
  1653. Ref = megaco_monitor:apply_after(Method, M, F, A, WaitFor),
  1654. NewFields =
  1655. [{#reply.bytes, OptBin},
  1656. {#reply.state, waiting_for_ack},
  1657. {#reply.timer_ref, Ref},
  1658. {#reply.ack_action, AckAction}],
  1659. megaco_monitor:update_reply_fields(TransId, NewFields); % Timing problem?
  1660. _ ->
  1661. %% Been removed already?
  1662. ignore
  1663. end;
  1664. do_handle_long_request(_, {error, Reason}, ConnData, TransId) ->
  1665. ?report_trace(ConnData, "send trans reply", [TransId, {error, Reason}]),
  1666. ignore.
  1667. handle_request_abort_callback(ConnData, TransId, Pid) ->
  1668. Extra = ?default_user_callback_extra,
  1669. handle_request_abort_callback(ConnData, TransId, Pid, Extra).
  1670. handle_request_abort_callback(ConnData, TransId, Pid, Extra) ->
  1671. ?report_trace(ConnData, "callback: trans request aborted", [TransId, Pid]),
  1672. ConnHandle = ConnData#conn_data.conn_handle,
  1673. Version = ConnData#conn_data.protocol_version,
  1674. UserMod = ConnData#conn_data.user_mod,
  1675. UserArgs = ConnData#conn_data.user_args,
  1676. Serial = TransId#trans_id.serial,
  1677. Args =
  1678. case Extra of
  1679. ?default_user_callback_extra ->
  1680. [ConnHandle, Version, Serial, Pid | UserArgs];
  1681. _ ->
  1682. [ConnHandle, Version, Serial, Pid, Extra | UserArgs]
  1683. end,
  1684. Res = (catch apply(UserMod, handle_trans_request_abort, Args)),
  1685. ?report_debug(ConnData, "return: trans request aborted",
  1686. [TransId, {return, Res}]),
  1687. case Res of
  1688. ok ->
  1689. ok;
  1690. _ ->
  1691. warning_msg("transaction request abort callback failed: ~w",
  1692. [Res]),
  1693. ok
  1694. end.
  1695. handle_request_callback(ConnData, TransId, Actions, T, Extra) ->
  1696. ?report_trace(ConnData, "callback: trans request", [T]),
  1697. ConnHandle = ConnData#conn_data.conn_handle,
  1698. Version = ConnData#conn_data.protocol_version,
  1699. UserMod = ConnData#conn_data.user_mod,
  1700. UserArgs = ConnData#conn_data.user_args,
  1701. Args =
  1702. case Extra of
  1703. ?default_user_callback_extra ->
  1704. [ConnHandle, Version, Actions | UserArgs];
  1705. _ ->
  1706. [ConnHandle, Version, Actions, Extra | UserArgs]
  1707. end,
  1708. Res = (catch apply(UserMod, handle_trans_request, Args)),
  1709. ?report_debug(ConnData, "return: trans request", [T, {return, Res}]),
  1710. case Res of
  1711. ignore -> %% NOTE: Only used for testing!!
  1712. {discard_ack, ignore};
  1713. ignore_trans_request ->
  1714. {discard_ack, ignore_trans_request};
  1715. {discard_ack, Replies} when is_list(Replies) ->
  1716. Reply = {actionReplies, Replies},
  1717. SendReply = maybe_send_reply(ConnData, TransId, Reply,
  1718. [], asn1_NOVALUE),
  1719. {discard_ack, SendReply};
  1720. {discard_ack, Error} when is_record(Error, 'ErrorDescriptor') ->
  1721. Reply = {transactionError, Error},
  1722. SendReply = maybe_send_reply(ConnData, TransId, Reply,
  1723. [], asn1_NOVALUE),
  1724. {discard_ack, SendReply};
  1725. {discard_ack, Replies, SendOpts} when is_list(Replies) andalso
  1726. is_list(SendOpts) ->
  1727. Reply = {actionReplies, Replies},
  1728. SendReply = maybe_send_reply(ConnData, TransId, Reply,
  1729. SendOpts, asn1_NOVALUE),
  1730. {discard_ack, SendReply};
  1731. {discard_ack, Error, SendOpts}
  1732. when is_record(Error, 'ErrorDescriptor') andalso
  1733. is_list(SendOpts) ->
  1734. Reply = {transactionError, Error},
  1735. SendReply = maybe_send_reply(ConnData, TransId, Reply,
  1736. SendOpts, asn1_NOVALUE),
  1737. {discard_ack, SendReply};
  1738. {{handle_pending_ack, AckData}, Replies} when is_list(Replies) ->
  1739. Reply = {actionReplies, Replies},
  1740. SendReply = maybe_send_reply(ConnData, TransId, Reply,
  1741. [], when_pending_sent),
  1742. {{handle_ack, AckData}, SendReply};
  1743. {{handle_pending_ack, AckData}, Error}
  1744. when is_record(Error, 'ErrorDescriptor') ->
  1745. Reply = {transactionError, Error},
  1746. SendReply = maybe_send_reply(ConnData, TransId, Reply,
  1747. [], when_pending_sent),
  1748. {{handle_ack, AckData}, SendReply};
  1749. {{handle_pending_ack, AckData}, Replies, SendOpts}
  1750. when is_list(Replies) andalso
  1751. is_list(SendOpts) ->
  1752. Reply = {actionReplies, Replies},
  1753. SendReply = maybe_send_reply(ConnData, TransId, Reply,
  1754. SendOpts, when_pending_sent),
  1755. {{handle_ack, AckData}, SendReply};
  1756. {{handle_pending_ack, AckData}, Error, SendOpts}
  1757. when is_record(Error, 'ErrorDescriptor') andalso
  1758. is_list(SendOpts) ->
  1759. Reply = {transactionError, Error},
  1760. SendReply = maybe_send_reply(ConnData, TransId, Reply,
  1761. SendOpts, when_pending_sent),
  1762. {{handle_ack, AckData}, SendReply};
  1763. {{handle_ack, AckData}, Replies} when is_list(Replies) ->
  1764. Reply = {actionReplies, Replies},
  1765. SendReply = maybe_send_reply(ConnData, TransId, Reply,
  1766. [], 'NULL'),
  1767. {{handle_ack, AckData}, SendReply};
  1768. {{handle_ack, AckData}, Error}
  1769. when is_record(Error, 'ErrorDescriptor') ->
  1770. Reply = {transactionError, Error},
  1771. SendReply = maybe_send_reply(ConnData, TransId, Reply,
  1772. [], 'NULL'),
  1773. {{handle_ack, AckData}, SendReply};
  1774. {{handle_ack, AckData}, Replies, SendOpts}
  1775. when is_list(Replies) andalso
  1776. is_list(SendOpts) ->
  1777. Reply = {actionReplies, Replies},
  1778. SendReply = maybe_send_reply(ConnData, TransId, Reply,
  1779. SendOpts, 'NULL'),
  1780. {{handle_ack, AckData}, SendReply};
  1781. {{handle_ack, AckData}, Error, SendOpts}
  1782. when is_record(Error, 'ErrorDescriptor') andalso
  1783. is_list(SendOpts) ->
  1784. Reply = {transactionError, Error},
  1785. SendReply = maybe_send_reply(ConnData, TransId, Reply,
  1786. SendOpts, 'NULL'),
  1787. {{handle_ack, AckData}, SendReply};
  1788. {{handle_sloppy_ack, AckData}, Replies} when is_list(Replies) ->
  1789. Reply = {actionReplies, Replies},
  1790. SendReply = maybe_send_reply(ConnData, TransId, Reply,
  1791. [], asn1_NOVALUE),
  1792. {{handle_ack, AckData}, SendReply};
  1793. {{handle_sloppy_ack, AckData}, Error}
  1794. when is_record(Error, 'ErrorDescriptor') ->
  1795. Reply = {transactionError, Error},
  1796. SendReply = maybe_send_reply(ConnData, TransId, Reply,
  1797. [], asn1_NOVALUE),
  1798. {{handle_ack, AckData}, SendReply};
  1799. {{handle_sloppy_ack, AckData}, Replies, SendOpts}
  1800. when is_list(Replies) andalso
  1801. is_list(SendOpts) ->
  1802. Reply = {actionReplies, Replies},
  1803. SendReply = maybe_send_reply(ConnData, TransId, Reply,
  1804. SendOpts, asn1_NOVALUE),
  1805. {{handle_ack, AckData}, SendReply};
  1806. {{handle_sloppy_ack, AckData}, Error, SendOpts}
  1807. when is_record(Error, 'ErrorDescriptor') andalso
  1808. is_list(SendOpts) ->
  1809. Reply = {transactionError, Error},
  1810. SendReply = maybe_send_reply(ConnData, TransId, Reply,
  1811. SendOpts, asn1_NOVALUE),
  1812. {{handle_ack, AckData}, SendReply};
  1813. {pending, RequestData} ->
  1814. %% The user thinks that this request will take
  1815. %% quite a while to evaluate. Maybe respond with
  1816. %% a pending trans (depends on the pending limit)
  1817. SendReply = maybe_send_pending(ConnData, TransId),
  1818. {{pending, RequestData}, SendReply};
  1819. Error ->
  1820. ErrorText = atom_to_list(UserMod),
  1821. ED = #'ErrorDescriptor'{
  1822. errorCode = ?megaco_internal_gateway_error,
  1823. errorText = ErrorText},
  1824. ?report_important(ConnData,
  1825. "callback: <ERROR> trans request",
  1826. [ED, {error, Error}]),
  1827. error_msg("transaction request callback failed: ~w", [Error]),
  1828. Reply = {transactionError, ED},
  1829. SendReply = maybe_send_reply(ConnData, TransId, Reply,
  1830. [], asn1_NOVALUE),
  1831. {discard_ack, SendReply}
  1832. end.
  1833. handle_long_request_callback(ConnData, TransId, RequestData, Extra) ->
  1834. ?report_trace(ConnData, "callback: trans long request", [RequestData]),
  1835. ConnHandle = ConnData#conn_data.conn_handle,
  1836. Version = ConnData#conn_data.protocol_version,
  1837. UserMod = ConnData#conn_data.user_mod,
  1838. UserArgs = ConnData#conn_data.user_args,
  1839. Args =
  1840. case Extra of
  1841. ?default_user_callback_extra ->
  1842. [ConnHandle, Version, RequestData | UserArgs];
  1843. _ ->
  1844. [ConnHandle, Version, RequestData, Extra | UserArgs]
  1845. end,
  1846. Res = (catch apply(UserMod, handle_trans_long_request, Args)),
  1847. ?report_debug(ConnData, "return: trans long request",
  1848. [{request_data, RequestData}, {return, Res}]),
  1849. case Res of
  1850. ignore ->
  1851. {discard_ack, ignore};
  1852. {discard_ack, Replies} when is_list(Replies) ->
  1853. Reply = {actionReplies, Replies},
  1854. SendReply = maybe_send_reply(ConnData, TransId, Reply,
  1855. [], asn1_NOVALUE),
  1856. {discard_ack, SendReply};
  1857. {discard_ack, Replies, SendOpts} when is_list(Replies) andalso
  1858. is_list(SendOpts) ->
  1859. Reply = {actionReplies, Replies},
  1860. SendReply = maybe_send_reply(ConnData, TransId, Reply,
  1861. SendOpts, asn1_NOVALUE),
  1862. {discard_ack, SendReply};
  1863. {{handle_ack, AckData}, Replies} when is_list(Replies) ->
  1864. Reply = {actionReplies, Replies},
  1865. SendReply = maybe_send_reply(ConnData, TransId, Reply,
  1866. [], 'NULL'),
  1867. {{handle_ack, AckData}, SendReply};
  1868. {{handle_ack, AckData}, Replies, SendOpts} when is_list(Replies)
  1869. andalso
  1870. is_list(SendOpts) ->
  1871. Reply = {actionReplies, Replies},
  1872. SendReply = maybe_send_reply(ConnData, TransId, Reply,
  1873. SendOpts, 'NULL'),
  1874. {{handle_ack, AckData}, SendReply};
  1875. Error ->
  1876. ErrorText = atom_to_list(UserMod),
  1877. ED = #'ErrorDescriptor'{errorCode = ?megaco_internal_gateway_error,
  1878. errorText = ErrorText},
  1879. ?report_important(ConnData, "callback: <ERROR> trans long request",
  1880. [ED, {error, Error}]),
  1881. error_msg("long transaction request callback failed: ~w", [Error]),
  1882. Reply = {transactionError, ED},
  1883. SendReply = maybe_send_reply(ConnData, TransId, Reply,
  1884. [], asn1_NOVALUE),
  1885. {discard_ack, SendReply}
  1886. end.
  1887. handle_pending(ConnData, T, Extra) ->
  1888. TransId = to_local_trans_id(ConnData),
  1889. ?rt2("handle pending", [T, TransId]),
  1890. case megaco_monitor:lookup_request(TransId) of
  1891. [Req] ->
  1892. %% ------------------------------------------
  1893. %%
  1894. %% Check received pending limit
  1895. %%
  1896. %% ------------------------------------------
  1897. Limit = ConnData#conn_data.recv_pending_limit,
  1898. case check_and_maybe_incr_pending_limit(Limit,
  1899. recv, TransId) of
  1900. ok ->
  1901. %% ----------------------------------------------------
  1902. %%
  1903. %% Received pending limit not exceeded
  1904. %%
  1905. %% ----------------------------------------------------
  1906. handle_recv_pending(ConnData, TransId, Req, T);
  1907. error ->
  1908. %% ----------------------------------------------------
  1909. %%
  1910. %% Received pending limit exceeded
  1911. %%
  1912. %% Time to give up on this transaction
  1913. %% 1) Delete request record
  1914. %% 2) Cancel timers
  1915. %% 3) Delete the (receive) pending counter
  1916. %% 4) Inform the user (handle_trans_reply)
  1917. %%
  1918. %% ----------------------------------------------------
  1919. handle_recv_pending_error(ConnData, TransId, Req, T, Extra);
  1920. aborted ->
  1921. %% ----------------------------------------------------
  1922. %%
  1923. %% Received pending limit already exceeded
  1924. %%
  1925. %% BMK BMK BMK -- can this really happen?
  1926. %%
  1927. %% The user has already been notified about this
  1928. %% (see error above)
  1929. %%
  1930. %% ----------------------------------------------------
  1931. ok
  1932. end;
  1933. [] ->
  1934. ?report_trace(ConnData, "remote pending (no receiver)", [T]),
  1935. return_unexpected_trans(ConnData, T, Extra)
  1936. end.
  1937. handle_recv_pending(#conn_data{long_request_resend = LRR,
  1938. conn_handle = ConnHandle} = ConnData,
  1939. TransId,
  1940. #request{timer_ref = {short, Ref},
  1941. init_long_timer = InitTimer}, T) ->
  1942. ?rt2("handle pending - long request", [LRR, InitTimer]),
  1943. %% The request seems to take a while,
  1944. %% let's reset our transmission timer.
  1945. %% We now know the other side has got
  1946. %% the request and is working on it,
  1947. %% so there is no need to keep the binary
  1948. %% message for re-transmission.
  1949. %% Start using the long timer.
  1950. %% We can now drop the "bytes", since we will
  1951. %% not resend from now on.
  1952. megaco_monitor:cancel_apply_after(Ref),
  1953. {WaitFor, CurrTimer} = megaco_timer:init(InitTimer),
  1954. ConnHandle = ConnData#conn_data.conn_handle,
  1955. M = ?MODULE,
  1956. F = request_timeout,
  1957. A = [ConnHandle, TransId],
  1958. Ref2 = megaco_monitor:apply_after(M, F, A, WaitFor),
  1959. NewFields =
  1960. case LRR of
  1961. true ->
  1962. [{#request.timer_ref, {long, Ref2}},
  1963. {#request.curr_timer, CurrTimer}];
  1964. false ->
  1965. [{#request.bytes, {no_send, garb_binary}},
  1966. {#request.timer_ref, {long, Ref2}},
  1967. {#request.curr_timer, CurrTimer}]
  1968. end,
  1969. ?report_trace(ConnData, "trans pending (timer restarted)", [T]),
  1970. megaco_monitor:update_request_fields(TransId, NewFields); % Timing problem?
  1971. handle_recv_pending(_ConnData, _TransId,
  1972. #request{timer_ref = {long, _Ref},
  1973. curr_timer = timeout}, _T) ->
  1974. ?rt3("handle pending - timeout"),
  1975. %% The request seems to take a while,
  1976. %% let's reset our transmission timer.
  1977. %% We now know the other side has got
  1978. %% the request and is working on it,
  1979. %% so there is no need to keep the binary
  1980. %% message for re-transmission.
  1981. %% This can happen if the timer is running for the last
  1982. %% time. I.e. next time it expires, will be the last.
  1983. %% Therefor we really do not need to do anything here.
  1984. %% The cleanup will be done in request_timeout.
  1985. ok;
  1986. handle_recv_pending(#conn_data{conn_handle = ConnHandle} = ConnData, TransId,
  1987. #request{timer_ref = {long, Ref},
  1988. curr_timer = CurrTimer}, T) ->
  1989. ?rt2("handle pending - still waiting", [CurrTimer]),
  1990. %% The request seems to take a while,
  1991. %% let's reset our transmission timer.
  1992. %% We now know the other side has got
  1993. %% the request and is working on it,
  1994. %% so there is no need to keep the binary
  1995. %% message for re-transmission.
  1996. %% We just need to recalculate the timer, i.e.
  1997. %% increment the timer (one "slot" has been consumed).
  1998. megaco_monitor:cancel_apply_after(Ref),
  1999. {WaitFor, Timer2} = megaco_timer:restart(CurrTimer),
  2000. ConnHandle = ConnData#conn_data.conn_handle,
  2001. M = ?MODULE,
  2002. F = request_timeout,
  2003. A = [ConnHandle, TransId],
  2004. Ref2 = megaco_monitor:apply_after(M, F, A, WaitFor),
  2005. NewFields =
  2006. [{#request.timer_ref, {long, Ref2}},
  2007. {#request.curr_timer, Timer2}],
  2008. ?report_trace(ConnData,
  2009. "long trans pending"
  2010. " (timer restarted)", [T]),
  2011. %% Timing problem?
  2012. megaco_monitor:update_request_fields(TransId, NewFields).
  2013. handle_recv_pending_error(ConnData, TransId, Req, T, Extra) ->
  2014. %% 1) Delete the request record
  2015. megaco_monitor:delete_request(TransId),
  2016. %% 2) Possibly cancel the timer
  2017. case Req#request.timer_ref of
  2018. {_, Ref} ->
  2019. megaco_monitor:cancel_apply_after(Ref);
  2020. _ ->
  2021. ok
  2022. end,
  2023. %% 3) Delete the (receive) pending counter
  2024. megaco_config:del_pending_counter(recv, TransId),
  2025. %% 4) Inform the user that his/her request reached
  2026. %% the receive pending limit
  2027. UserMod = Req#request.user_mod,
  2028. UserArgs = Req#request.user_args,
  2029. Action = Req#request.reply_action,
  2030. UserData = Req#request.reply_data,
  2031. UserReply = {error, exceeded_recv_pending_limit},
  2032. ConnData2 = ConnData#conn_data{user_mod = UserMod,
  2033. user_args = UserArgs,
  2034. reply_action = Action,
  2035. reply_data = UserData},
  2036. ?report_trace(ConnData, "receive pending limit reached", [T]),
  2037. return_reply(ConnData2, TransId, UserReply, Extra).
  2038. %%
  2039. %% This _is_ a segmented message.
  2040. %%
  2041. %% Since this is not the last segment, we shall not send any ack.
  2042. %% (even if three-way-handshake has been configured).
  2043. %%
  2044. handle_reply(
  2045. ConnData,
  2046. #megaco_transaction_reply{segmentNumber = SN,
  2047. segmentationComplete = asn1_NOVALUE} = T, Extra)
  2048. when is_integer(SN) ->
  2049. TransId = to_local_trans_id(ConnData),
  2050. ?rt2("handle segmented reply", [T, TransId, SN]),
  2051. case megaco_monitor:lookup_request(TransId) of
  2052. %% ---------------------------------------------------------
  2053. %% The first segment, so stop the request timer. No longer
  2054. %% needed when the segment(s) start to arrive.
  2055. [#request{timer_ref = {_Type, Ref},
  2056. seg_recv = [],
  2057. seg_timer_ref = undefined} = Req] ->
  2058. %% Don't care about Req and Rep version diff
  2059. ?report_trace(ConnData, "[segmented] trans reply - first seg",
  2060. [T]),
  2061. %% Stop the request timer
  2062. megaco_monitor:cancel_apply_after(Ref), %% OTP-4843
  2063. %% Acknowledge the segment
  2064. send_segment_reply(ConnData, SN),
  2065. %% First segment for this reply
  2066. NewFields =
  2067. [{#request.timer_ref, undefined},
  2068. {#request.seg_recv, [SN]}],
  2069. megaco_monitor:update_request_fields(TransId, NewFields),
  2070. %% Handle the reply
  2071. UserMod = Req#request.user_mod,
  2072. UserArgs = Req#request.user_args,
  2073. Action = Req#request.reply_action,
  2074. UserData = Req#request.reply_data,
  2075. UserReply =
  2076. case T#megaco_transaction_reply.transactionResult of
  2077. {transactionError, Reason} ->
  2078. {error, {SN, false, Reason}};
  2079. {actionReplies, Replies} ->
  2080. {ok, {SN, false, Replies}}
  2081. end,
  2082. ConnData2 = ConnData#conn_data{user_mod = UserMod,
  2083. user_args = UserArgs,
  2084. reply_action = Action,
  2085. reply_data = UserData},
  2086. return_reply(ConnData2, TransId, UserReply, Extra);
  2087. %% ---------------------------------------------------------
  2088. %% This is not the first segment.
  2089. %% The segment timer has not been started, so the last
  2090. %% segment have been received.
  2091. %% We must check that this is not a re-transmission!
  2092. [#request{seg_recv = Segs,
  2093. seg_timer_ref = undefined} = Req] ->
  2094. %% Don't care about Req and Rep version diff
  2095. ?report_trace(ConnData, "[segmented] trans reply - no seg acc",
  2096. [T]),
  2097. %% Acknowledge the segment
  2098. send_segment_reply(ConnData, SN),
  2099. %% Updated/handle received segment
  2100. case lists:member(SN, Segs) of
  2101. true ->
  2102. %% This is a re-transmission, so we shall not pass
  2103. %% it on to the user (or update the request record).
  2104. ok;
  2105. false ->
  2106. %% First time for this segment
  2107. megaco_monitor:update_request_field(TransId,
  2108. #request.seg_recv,
  2109. [ SN | Segs ]),
  2110. %% Handle the reply
  2111. UserMod = Req#request.user_mod,
  2112. UserArgs = Req#request.user_args,
  2113. Action = Req#request.reply_action,
  2114. UserData = Req#request.reply_data,
  2115. UserReply =
  2116. case T#megaco_transaction_reply.transactionResult of
  2117. {transactionError, Reason} ->
  2118. {error, {SN, false, Reason}};
  2119. {actionReplies, Replies} ->
  2120. {ok, {SN, false, Replies}}
  2121. end,
  2122. ConnData2 = ConnData#conn_data{user_mod = UserMod,
  2123. user_args = UserArgs,
  2124. reply_action = Action,
  2125. reply_data = UserData},
  2126. return_reply(ConnData2, TransId, UserReply, Extra)
  2127. end;
  2128. %% ---------------------------------------------------------
  2129. %% The segment timer is running!
  2130. %% This could be the last (out-of-order) segment!
  2131. %% We must check that this is not a re-transmission!
  2132. [#request{seg_recv = Segs,
  2133. seg_timer_ref = SegRef} = Req] ->
  2134. %% Don't care about Req and Rep version diff
  2135. ?report_trace(ConnData, "[segmented] trans reply - no seg acc",
  2136. [T]),
  2137. %% Acknowledge the segment
  2138. send_segment_reply(ConnData, SN),
  2139. %% Updated received segments
  2140. case lists:member(SN, Segs) of
  2141. true ->
  2142. %% This is a re-transmission
  2143. ok;
  2144. false ->
  2145. %% First time for this segment,
  2146. %% we may now have a complete set
  2147. Last =
  2148. case is_all_segments([SN | Segs]) of
  2149. {true, _Sorted} ->
  2150. megaco_monitor:cancel_apply_after(SegRef),
  2151. megaco_monitor:delete_request(TransId),
  2152. send_ack(ConnData),
  2153. true;
  2154. {false, Sorted} ->
  2155. megaco_monitor:update_request_field(TransId,
  2156. #request.seg_recv,
  2157. Sorted),
  2158. false
  2159. end,
  2160. %% Handle the reply
  2161. UserMod = Req#request.user_mod,
  2162. UserArgs = Req#request.user_args,
  2163. Action = Req#request.reply_action,
  2164. UserData = Req#request.reply_data,
  2165. UserReply =
  2166. case T#megaco_transaction_reply.transactionResult of
  2167. {transactionError, Reason} ->
  2168. {error, {SN, Last, Reason}};
  2169. {actionReplies, Replies} ->
  2170. {ok, {SN, Last, Replies}}
  2171. end,
  2172. ConnData2 = ConnData#conn_data{user_mod = UserMod,
  2173. user_args = UserArgs,
  2174. reply_action = Action,
  2175. reply_data = UserData},
  2176. return_reply(ConnData2, TransId, UserReply, Extra)
  2177. end;
  2178. [] ->
  2179. ?report_trace(ConnData, "trans reply (no receiver)", [T]),
  2180. return_unexpected_trans(ConnData, T, Extra)
  2181. end;
  2182. %%
  2183. %% This _is_ a segmented message and it's the last segment of the
  2184. %% message.
  2185. %%
  2186. handle_reply(
  2187. ConnData,
  2188. #megaco_transaction_reply{segmentNumber = SN,
  2189. segmentationComplete = 'NULL'} = T, Extra)
  2190. when is_integer(SN) ->
  2191. TransId = to_local_trans_id(ConnData),
  2192. ?rt2("handle (last) segmented reply", [T, TransId, SN]),
  2193. case megaco_monitor:lookup_request(TransId) of
  2194. %% ---------------------------------------------------------
  2195. %% The first segment, so stop the request timer. No longer
  2196. %% needed when the segment(s) start to arrive.
  2197. [#request{timer_ref = {_Type, Ref},
  2198. seg_recv = [],
  2199. seg_timer_ref = undefined} = Req] ->
  2200. %% Don't care about Req and Rep version diff
  2201. ?report_trace(ConnData, "[segmented] trans reply - "
  2202. "first/complete seg", [T]),
  2203. %% Stop the request timer
  2204. megaco_monitor:cancel_apply_after(Ref), %% OTP-4843
  2205. %% Acknowledge the ("last") segment
  2206. send_segment_reply_complete(ConnData, SN),
  2207. %% It is ofcourse pointless to split
  2208. %% a transaction into just one segment,
  2209. %% but just to be sure, we handle that
  2210. %% case also
  2211. Last =
  2212. if
  2213. SN > 1 ->
  2214. %% More then one segment
  2215. %% First time for this segment
  2216. ConnHandle = ConnData#conn_data.conn_handle,
  2217. InitSegTmr = Req#request.init_seg_timer,
  2218. {WaitFor, CurrTimer} = megaco_timer:init(InitSegTmr),
  2219. M = ?MODULE,
  2220. F = segment_timeout,
  2221. A = [ConnHandle, TransId, CurrTimer],
  2222. SegRef =
  2223. megaco_monitor:apply_after(M, F, A, WaitFor),
  2224. NewFields =
  2225. [{#request.timer_ref, undefined},
  2226. {#request.seg_recv, [SN]},
  2227. {#request.seg_timer_ref, SegRef}],
  2228. megaco_monitor:update_request_fields(TransId, NewFields),
  2229. false;
  2230. true ->
  2231. %% Just one segment!
  2232. megaco_monitor:delete_request(TransId),
  2233. send_ack(ConnData),
  2234. true
  2235. end,
  2236. %% Handle the reply
  2237. UserMod = Req#request.user_mod,
  2238. UserArgs = Req#request.user_args,
  2239. Action = Req#request.reply_action,
  2240. UserData = Req#request.reply_data,
  2241. UserReply =
  2242. case T#megaco_transaction_reply.transactionResult of
  2243. {transactionError, Reason} ->
  2244. {error, {SN, Last, Reason}};
  2245. {actionReplies, Replies} ->
  2246. {ok, {SN, Last, Replies}}
  2247. end,
  2248. ConnData2 = ConnData#conn_data{user_mod = UserMod,
  2249. user_args = UserArgs,
  2250. reply_action = Action,
  2251. reply_data = UserData},
  2252. return_reply(ConnData2, TransId, UserReply, Extra);
  2253. [#request{seg_recv = Segs} = Req] ->
  2254. %% Don't care about Req and Rep version diff
  2255. ?report_trace(ConnData, "[segmented] trans reply - no seg acc",
  2256. [T]),
  2257. %% Acknowledge the ("last") segment
  2258. send_segment_reply_complete(ConnData, SN),
  2259. %% Updated received segments
  2260. %% This is _probably_ the last segment, but some of
  2261. %% the previous segments may have been lost, so we
  2262. %% may not have a complete set!
  2263. case lists:member(SN, Segs) of
  2264. true ->
  2265. %% This is a re-transmission
  2266. ok;
  2267. false ->
  2268. Last =
  2269. case is_all_segments([SN | Segs]) of
  2270. {true, _Sorted} ->
  2271. ?report_trace(ConnData,
  2272. "[segmented] trans reply - "
  2273. "complete set", [T]),
  2274. megaco_monitor:delete_request(TransId),
  2275. send_ack(ConnData),
  2276. true;
  2277. {false, Sorted} ->
  2278. ConnHandle = ConnData#conn_data.conn_handle,
  2279. InitSegTmr = Req#request.init_seg_timer,
  2280. {WaitFor, CurrTimer} =
  2281. megaco_timer:init(InitSegTmr),
  2282. M = ?MODULE,
  2283. F = segment_timeout,
  2284. A = [ConnHandle, TransId, CurrTimer],
  2285. SegRef =
  2286. megaco_monitor:apply_after(M, F, A,
  2287. WaitFor),
  2288. NewFields =
  2289. [{#request.seg_recv, Sorted},
  2290. {#request.seg_timer_ref, SegRef}],
  2291. megaco_monitor:update_request_fields(TransId, NewFields),
  2292. false
  2293. end,
  2294. %% Handle the reply
  2295. UserMod = Req#request.user_mod,
  2296. UserArgs = Req#request.user_args,
  2297. Action = Req#request.reply_action,
  2298. UserData = Req#request.reply_data,
  2299. UserReply =
  2300. case T#megaco_transaction_reply.transactionResult of
  2301. {transactionError, Reason} ->
  2302. {error, {SN, Last, Reason}};
  2303. {actionReplies, Replies} ->
  2304. {ok, {SN, Last, Replies}}
  2305. end,
  2306. ConnData2 = ConnData#conn_data{user_mod = UserMod,
  2307. user_args = UserArgs,
  2308. reply_action = Action,
  2309. reply_data = UserData},
  2310. return_reply(ConnData2, TransId, UserReply, Extra)
  2311. end;
  2312. [] ->
  2313. ?report_trace(ConnData, "trans reply (no receiver)", [T]),
  2314. return_unexpected_trans(ConnData, T, Extra)
  2315. end;
  2316. %%
  2317. %% This is _not_ a segmented message,
  2318. %% i.e. it's an ordinary transaction reply
  2319. %%
  2320. handle_reply(#conn_data{conn_handle = CH} = CD, T, Extra) ->
  2321. TransId = to_local_trans_id(CD),
  2322. ?rt2("handle reply", [T, TransId]),
  2323. case {megaco_monitor:request_lockcnt_inc(TransId),
  2324. megaco_monitor:lookup_request(TransId)} of
  2325. {_Cnt, [Req]} when (is_record(Req, request) andalso
  2326. (CD#conn_data.cancel =:= true)) ->
  2327. ?TC_AWAIT_REPLY_EVENT(true),
  2328. ?report_trace(CD, "trans reply - cancel(1)", [T]),
  2329. do_handle_reply_cancel(CD, Req, T);
  2330. {Cnt, [#request{remote_mid = RMid} = Req]} when
  2331. ((Cnt =:= 1) andalso
  2332. ((RMid =:= preliminary_mid) orelse
  2333. (RMid =:= CH#megaco_conn_handle.remote_mid))) ->
  2334. ?TC_AWAIT_REPLY_EVENT(false),
  2335. %% Just in case conn_data got update after our lookup
  2336. %% but before we looked up the request record, we
  2337. %% check the cancel field again.
  2338. case megaco_config:conn_info(CD, cancel) of
  2339. true ->
  2340. ?report_trace(CD, "trans reply - cancel(2)", [T]),
  2341. megaco_monitor:request_lockcnt_del(TransId),
  2342. do_handle_reply_cancel(CD, Req, T);
  2343. false ->
  2344. ?report_trace(CD, "trans reply", [T]),
  2345. do_handle_reply(CD, Req, TransId, T, Extra)
  2346. end;
  2347. {Cnt, [#request{remote_mid = RMid} = _Req]} when
  2348. (is_integer(Cnt) andalso
  2349. ((RMid =:= preliminary_mid) orelse
  2350. (RMid =:= CH#megaco_conn_handle.remote_mid))) ->
  2351. ?TC_AWAIT_REPLY_EVENT(false),
  2352. %% Ok, someone got there before me, now what?
  2353. %% This is a plain old raise condition
  2354. ?report_important(CD, "trans reply - raise condition",
  2355. [T, {request_lockcnt, Cnt}]),
  2356. megaco_monitor:request_lockcnt_dec(TransId);
  2357. %% no counter
  2358. {_Cnt, [#request{remote_mid = RMid} = Req]} when
  2359. ((RMid =:= preliminary_mid) orelse
  2360. (RMid =:= CH#megaco_conn_handle.remote_mid)) ->
  2361. ?TC_AWAIT_REPLY_EVENT(false),
  2362. %% The counter does not exist.
  2363. %% This can only mean a code upgrade raise condition.
  2364. %% That is, this request record was created before
  2365. %% this feature (the counters) was instroduced.
  2366. %% The simples solution is this is to behave exactly as
  2367. %% before, that is pass it along, and leave it to the
  2368. %% user to figure out.
  2369. %% Just in case conn_data got update after our lookup
  2370. %% but before we looked up the request record, we
  2371. %% check the cancel field again.
  2372. ?report_verbose(CD, "trans reply - old style", [T]),
  2373. case megaco_config:conn_info(CD, cancel) of
  2374. true ->
  2375. megaco_monitor:request_lockcnt_del(TransId),
  2376. do_handle_reply_cancel(CD, Req, T);
  2377. false ->
  2378. do_handle_reply(CD, Req, TransId, T, Extra)
  2379. end;
  2380. {Cnt, [#request{user_mod = UserMod,
  2381. user_args = UserArgs,
  2382. reply_action = Action,
  2383. reply_data = UserData,
  2384. remote_mid = RMid}]} ->
  2385. ?report_trace(CD,
  2386. "received trans reply with invalid remote mid",
  2387. [{transaction, T},
  2388. {remote_mid, RMid},
  2389. {request_lockcnt, Cnt}]),
  2390. if
  2391. is_integer(Cnt) ->
  2392. megaco_monitor:request_lockcnt_dec(TransId);
  2393. true ->
  2394. ok
  2395. end,
  2396. WrongMid = CH#megaco_conn_handle.remote_mid,
  2397. T2 = transform_transaction_reply_enc(CD#conn_data.protocol_version,
  2398. T),
  2399. UserReply = {error, {wrong_mid, WrongMid, RMid, T2}},
  2400. CD2 = CD#conn_data{user_mod = UserMod,
  2401. user_args = UserArgs,
  2402. reply_action = Action,
  2403. reply_data = UserData},
  2404. return_reply(CD2, TransId, UserReply, Extra);
  2405. {Cnt, []} when is_integer(Cnt) ->
  2406. ?TC_AWAIT_REPLY_EVENT(undefined),
  2407. ?report_trace(CD, "trans reply (no receiver)",
  2408. [T, {request_lockcnt, Cnt}]),
  2409. megaco_monitor:request_lockcnt_dec(TransId),
  2410. return_unexpected_trans(CD, T, Extra);
  2411. %% No counter
  2412. {_Cnt, []} ->
  2413. ?TC_AWAIT_REPLY_EVENT(undefined),
  2414. ?report_trace(CD, "trans reply (no receiver)", [T]),
  2415. return_unexpected_trans(CD, T, Extra)
  2416. end.
  2417. do_handle_reply_cancel(CD, #request{user_mod = UserMod,
  2418. user_args = UserArgs,
  2419. reply_action = Action,
  2420. reply_data = UserData}, T) ->
  2421. CD2 = CD#conn_data{user_mod = UserMod,
  2422. user_args = UserArgs,
  2423. reply_action = Action,
  2424. reply_data = UserData},
  2425. return_unexpected_trans(CD2, T).
  2426. %% Plain old handling of incomming replies
  2427. do_handle_reply(CD,
  2428. #request{timer_ref = {_Type, Ref}, % OTP-4843
  2429. user_mod = UserMod,
  2430. user_args = UserArgs,
  2431. reply_action = Action,
  2432. reply_data = UserData,
  2433. keep_alive_timer = RKAT},
  2434. TransId, T, Extra)
  2435. when ((RKAT =:= plain) orelse (Action =:= call)) ->
  2436. %% Don't care about Req and Rep version diff
  2437. ?report_trace(CD, "trans reply", [T]),
  2438. %% This is the first reply (maybe of many)
  2439. megaco_monitor:delete_request(TransId),
  2440. megaco_monitor:request_lockcnt_del(TransId),
  2441. megaco_monitor:cancel_apply_after(Ref), % OTP-4843
  2442. megaco_config:del_pending_counter(recv, TransId), % OTP-7189
  2443. %% Send acknowledgement
  2444. maybe_send_ack(T#megaco_transaction_reply.immAckRequired, CD),
  2445. UserReply =
  2446. case T#megaco_transaction_reply.transactionResult of
  2447. {transactionError, Reason} ->
  2448. {error, Reason};
  2449. {actionReplies, Replies} ->
  2450. {ok, Replies}
  2451. end,
  2452. CD2 = CD#conn_data{user_mod = UserMod,
  2453. user_args = UserArgs,
  2454. reply_action = Action,
  2455. reply_data = UserData},
  2456. return_reply(CD2, TransId, UserReply, Extra);
  2457. %% This may be the first reply (of maybe many)
  2458. do_handle_reply(CD,
  2459. #request{user_mod = UserMod,
  2460. user_args = UserArgs,
  2461. reply_action = Action,
  2462. reply_data = UserData,
  2463. keep_alive_ref = undefined} = Req,
  2464. TransId, T, Extra) ->
  2465. %% Don't care about Req and Rep version diff
  2466. ?report_trace(CD, "trans reply", [T]),
  2467. %% Could be the first reply, in which case we shall start the
  2468. %% Request Keep Alive timer...
  2469. %% This could happen for more than one (1) reply though, so
  2470. %% we need to check if the counter value actually equals one (1)!
  2471. ReplyNo =
  2472. create_or_maybe_increment_request_keep_alive_counter(CD, TransId),
  2473. if
  2474. (ReplyNo =:= 1) ->
  2475. %% This *is* the first reply!!
  2476. %% 1) Stop resend timer
  2477. {_Type, Ref} = Req#request.timer_ref, % OTP-4843
  2478. megaco_monitor:cancel_apply_after(Ref), % OTP-4843
  2479. %% 2) Delete pending counter
  2480. megaco_config:del_pending_counter(recv, TransId), % OTP-7189
  2481. %% 3) Start request keep alive timer
  2482. ConnHandle = CD#conn_data.conn_handle,
  2483. RKATimer = Req#request.keep_alive_timer,
  2484. {RKAWaitFor, _} = megaco_timer:init(RKATimer),
  2485. RKARef = megaco_monitor:apply_after(?MODULE,
  2486. request_keep_alive_timeout,
  2487. [ConnHandle, TransId],
  2488. RKAWaitFor),
  2489. %% 4) Maybe send acknowledgement (three-way-handshake)
  2490. maybe_send_ack(T#megaco_transaction_reply.immAckRequired, CD),
  2491. %% 5) And finally store the updated request record
  2492. Req2 = Req#request{keep_alive_ref = RKARef},
  2493. megaco_monitor:insert_request(Req2);
  2494. true ->
  2495. ok
  2496. end,
  2497. UserReply =
  2498. case T#megaco_transaction_reply.transactionResult of
  2499. {transactionError, Reason} ->
  2500. {error, ReplyNo, Reason};
  2501. {actionReplies, Replies} ->
  2502. {ok, ReplyNo, Replies}
  2503. end,
  2504. CD2 = CD#conn_data{user_mod = UserMod,
  2505. user_args = UserArgs,
  2506. reply_action = Action,
  2507. reply_data = UserData},
  2508. return_reply(CD2, TransId, UserReply, Extra);
  2509. %% This is *not* the first reply (of many)
  2510. do_handle_reply(CD, #request{user_mod = UserMod,
  2511. user_args = UserArgs,
  2512. reply_action = Action,
  2513. reply_data = UserData}, TransId, T, Extra) ->
  2514. %% Don't care about Req and Rep version diff
  2515. ?report_trace(CD, "trans reply (first reply already delivered)", [T]),
  2516. ReplyNo = increment_request_keep_alive_counter(CD, TransId),
  2517. UserReply =
  2518. case T#megaco_transaction_reply.transactionResult of
  2519. {transactionError, Reason} ->
  2520. {error, ReplyNo, Reason};
  2521. {actionReplies, Replies} ->
  2522. {ok, ReplyNo, Replies}
  2523. end,
  2524. CD2 = CD#conn_data{user_mod = UserMod,
  2525. user_args = UserArgs,
  2526. reply_action = Action,
  2527. reply_data = UserData},
  2528. return_reply(CD2, TransId, UserReply, Extra).
  2529. is_all_segments(Segs) ->
  2530. Sorted = lists:sort(Segs),
  2531. {is_all_segments(Sorted, 1, lists:last(Sorted)), Sorted}.
  2532. is_all_segments([Last], Last, Last) ->
  2533. true;
  2534. is_all_segments([_], _, _) ->
  2535. false;
  2536. is_all_segments([SN|Segs], SN, Last) when (SN < Last) ->
  2537. is_all_segments(Segs, SN+1, Last);
  2538. is_all_segments([SN1|_], SN2, _Last) when SN1 =/= SN2 ->
  2539. false.
  2540. handle_segment_reply(CD,
  2541. #'SegmentReply'{transactionId = TransId,
  2542. segmentNumber = SN,
  2543. segmentationComplete = SC}, Extra) ->
  2544. ?rt2("handle segment reply", [{trans_id, TransId},
  2545. {segment_no, SN},
  2546. {segmentation_complete, SC}]),
  2547. TransId2 = to_remote_trans_id(CD#conn_data{serial = TransId}),
  2548. case lookup_reply(CD, TransId2) of
  2549. {_Converted,
  2550. #reply{bytes = Sent,
  2551. segments = []} = Rep} when is_list(Sent) ->
  2552. ?rt2("no unsent segments", [Sent]),
  2553. handle_segment_reply_callback(CD, TransId, SN, SC, Extra),
  2554. case lists:keysearch(SN, 1, Sent) of
  2555. {value, {SN, _Bin, SegTmr}} ->
  2556. megaco_monitor:cancel_apply_after(SegTmr), %% BMK BMK
  2557. case lists:keydelete(SN, 1, Sent) of
  2558. [] -> %% We are done
  2559. Ref = Rep#reply.timer_ref,
  2560. megaco_monitor:cancel_apply_after(Ref),
  2561. megaco_monitor:update_reply_field(TransId2,
  2562. #reply.bytes,
  2563. []),
  2564. ok;
  2565. NewSent ->
  2566. megaco_monitor:update_reply_field(TransId2,
  2567. #reply.bytes,
  2568. NewSent),
  2569. ok
  2570. end;
  2571. _ ->
  2572. ok
  2573. end;
  2574. {_Converted,
  2575. #reply{bytes = Sent,
  2576. segments = NotSent}} when is_list(Sent) andalso
  2577. is_list(NotSent) ->
  2578. ?rt2("unsent segments", [Sent, NotSent]),
  2579. handle_segment_reply_callback(CD, TransId, SN, SC, Extra),
  2580. case lists:keysearch(SN, 1, Sent) of
  2581. {value, {SN, _Bin, SegTmr}} ->
  2582. megaco_monitor:cancel_apply_after(SegTmr), %% BMK BMK
  2583. NewSent = lists:keydelete(SN, 1, Sent),
  2584. [{SN2, Bin2}|NewNotSent] = NotSent,
  2585. case send_reply_segment(CD, "send trans reply segment",
  2586. SN2, Bin2) of
  2587. {ok, Bin3} ->
  2588. ?rt2("another segment sent", [Bin3]),
  2589. NewSent2 = [{SN2, Bin3, undefined}|NewSent],
  2590. NewFields =
  2591. [{#reply.bytes, NewSent2},
  2592. {#reply.segments, NewNotSent}],
  2593. megaco_monitor:update_reply_fields(TransId2,
  2594. NewFields),
  2595. ok;
  2596. Error ->
  2597. incNumErrors(CD#conn_data.conn_handle),
  2598. ?report_important(CD, "failed sending segment",
  2599. [{segment_no, SN2},
  2600. {error, Error}]),
  2601. error_msg("failed sending transaction reply [~w] "
  2602. "segment [~w]: ~w",
  2603. [TransId, SN2, Error]),
  2604. megaco_monitor:update_reply_field(TransId2,
  2605. #reply.bytes,
  2606. NewSent),
  2607. ok
  2608. end;
  2609. _ ->
  2610. ok
  2611. end;
  2612. {_Converted,
  2613. #reply{state = State}} ->
  2614. %% We received a segment reply for a segmented reply we have
  2615. %% not yet sent? This is either some sort of race condition
  2616. %% or the "the other side" is really confused.
  2617. %% Ignore the message but issue a warning just in case...
  2618. warning_msg("received unexpected segment reply: "
  2619. "~n Transaction Id: ~p"
  2620. "~n Segment Number: ~p"
  2621. "~n Segmentation Complete: ~p"
  2622. "~n Reply state: ~p",
  2623. [TransId2, SN, SC, State]),
  2624. ignore;
  2625. [] ->
  2626. ignore
  2627. end.
  2628. %%
  2629. %% This should be passed on to the user only if the user wish it
  2630. %% (sri = segment reply indication)
  2631. %%
  2632. handle_segment_reply_callback(#conn_data{segment_reply_ind = true,
  2633. conn_handle = ConnHandle,
  2634. protocol_version = Version,
  2635. user_mod = UserMod,
  2636. user_args = UserArgs},
  2637. TransId, SN, SC, Extra) ->
  2638. Args =
  2639. case Extra of
  2640. ?default_user_callback_extra ->
  2641. [ConnHandle, Version, TransId, SN, SC | UserArgs];
  2642. _ ->
  2643. [ConnHandle, Version, TransId, SN, SC, Extra | UserArgs]
  2644. end,
  2645. (catch apply(UserMod, handle_segment_reply, Args));
  2646. handle_segment_reply_callback(_CD, _TransId, _SN, _SC, _Extra) ->
  2647. ok.
  2648. handle_acks([{ConnData, Rep, T} | Rest], Extra)
  2649. when Rep#reply.state == waiting_for_ack ->
  2650. handle_ack(ConnData, ok, Rep, T, Extra),
  2651. handle_acks(Rest, Extra);
  2652. handle_acks([], _Extra) ->
  2653. ok.
  2654. %% If the reply to which this is the ack was segmented,
  2655. %% then we also need to check that we have received all
  2656. %% the segment-replies. If not, an error callback call
  2657. %% shall be made instead.
  2658. handle_ack(ConnData, AckStatus,
  2659. #reply{trans_id = TransId,
  2660. bytes = Bytes,
  2661. timer_ref = ReplyRef,
  2662. pending_timer_ref = PendingRef, %% BMK Still running?
  2663. ack_action = AckAction}, T, Extra)
  2664. when is_binary(Bytes) orelse (Bytes =:= undefined) ->
  2665. handle_ack_cleanup(TransId, ReplyRef, PendingRef),
  2666. handle_ack_callback(ConnData, AckStatus, AckAction, T, Extra);
  2667. handle_ack(ConnData, AckStatus,
  2668. #reply{trans_id = TransId,
  2669. bytes = [],
  2670. segments = [],
  2671. timer_ref = ReplyRef,
  2672. pending_timer_ref = PendingRef, %% BMK Still running?
  2673. ack_action = AckAction}, T, Extra) ->
  2674. handle_ack_cleanup(TransId, ReplyRef, PendingRef),
  2675. handle_ack_callback(ConnData, AckStatus, AckAction, T, Extra);
  2676. handle_ack(ConnData, OrigAckStatus,
  2677. #reply{trans_id = TransId,
  2678. bytes = SegSent,
  2679. segments = NotSent,
  2680. timer_ref = ReplyRef,
  2681. pending_timer_ref = PendingRef, %% BMK Still running?
  2682. ack_action = OrigAckAction}, T, Extra)
  2683. when is_list(SegSent) andalso is_list(NotSent) ->
  2684. SN_NotAcked = [SN || {SN, _, _} <- SegSent],
  2685. SN_NotSent = [SN || {SN, _} <- NotSent],
  2686. AckStatus = {error, {segment_failure,
  2687. [{original_ack_status, OrigAckStatus},
  2688. {segments_not_acked, SN_NotAcked},
  2689. {segments_not_sent, SN_NotSent}]}},
  2690. AckAction =
  2691. case OrigAckAction of
  2692. {handle_ack, _} ->
  2693. OrigAckAction;
  2694. _ ->
  2695. {handle_ack, segmented_reply}
  2696. end,
  2697. cancel_segment_timers(SegSent),
  2698. handle_ack_cleanup(TransId, ReplyRef, PendingRef),
  2699. handle_ack_callback(ConnData, AckStatus, AckAction, T, Extra).
  2700. handle_ack_cleanup(TransId, ReplyRef, PendingRef) ->
  2701. megaco_monitor:cancel_apply_after(ReplyRef),
  2702. megaco_monitor:cancel_apply_after(PendingRef),
  2703. megaco_monitor:delete_reply(TransId),
  2704. megaco_config:del_pending_counter(sent, TransId). %% BMK: Still existing?
  2705. cancel_segment_timers(SegSent) when is_list(SegSent) ->
  2706. Cancel = fun({_, _, Ref}) ->
  2707. megaco_monitor:cancel_apply_after(Ref)
  2708. end,
  2709. lists:foreach(Cancel, SegSent);
  2710. cancel_segment_timers(_) ->
  2711. ok.
  2712. handle_ack_callback(_CD, ok = _AS, discard_ack = _AA, _T, _Extra) ->
  2713. ok;
  2714. handle_ack_callback(ConnData, {error, Reason}, discard_ack = AckAction, T, Extra) ->
  2715. ?report_trace(ConnData, "handle ack (no callback)",
  2716. [T, AckAction, {error, Reason}, Extra]);
  2717. handle_ack_callback(ConnData, AckStatus, {handle_ack, AckData}, T, Extra) ->
  2718. ?report_trace(ConnData, "callback: trans ack", [{ack_data, AckData}]),
  2719. ConnHandle = ConnData#conn_data.conn_handle,
  2720. Version = ConnData#conn_data.protocol_version,
  2721. UserMod = ConnData#conn_data.user_mod,
  2722. UserArgs = ConnData#conn_data.user_args,
  2723. Args =
  2724. case Extra of
  2725. ?default_user_callback_extra ->
  2726. [ConnHandle, Version, AckStatus, AckData | UserArgs];
  2727. _ ->
  2728. [ConnHandle, Version, AckStatus, AckData, Extra | UserArgs]
  2729. end,
  2730. Res = (catch handle_callback(ConnData, UserMod, handle_trans_ack, Args)),
  2731. ?report_debug(ConnData, "return: trans ack", [T, AckData, {return, Res}]),
  2732. case Res of
  2733. ok ->
  2734. ok;
  2735. _ ->
  2736. warning_msg("transaction ack callback failed: ~w", [Res]),
  2737. ok
  2738. end,
  2739. Res.
  2740. handle_callback(ConnData, undefined = _UserMod, Func, Args) ->
  2741. ?report_important(ConnData, "callback: unknown callback module",
  2742. [{func, Func}, {args, Args}]),
  2743. ok;
  2744. handle_callback(_ConnData, UserMod, Func, Args) ->
  2745. (catch apply(UserMod, Func, Args)).
  2746. handle_message_error(ConnData, _Error, _Extra)
  2747. when ConnData#conn_data.monitor_ref == undefined_monitor_ref ->
  2748. %% May occur if another process already has setup a
  2749. %% temporary connection, but the handle_connect callback
  2750. %% function has not yet returned before the eager MG
  2751. %% re-sends its initial service change message.
  2752. ignore;
  2753. handle_message_error(ConnData, Error, Extra) ->
  2754. ?report_trace(ConnData, "callback: message error", [Error]),
  2755. ConnHandle = ConnData#conn_data.conn_handle,
  2756. Version = ConnData#conn_data.protocol_version,
  2757. UserMod = ConnData#conn_data.user_mod,
  2758. UserArgs = ConnData#conn_data.user_args,
  2759. Args =
  2760. case Extra of
  2761. ?default_user_callback_extra ->
  2762. [ConnHandle, Version, Error | UserArgs];
  2763. _ ->
  2764. [ConnHandle, Version, Error, Extra | UserArgs]
  2765. end,
  2766. Res = (catch apply(UserMod, handle_message_error, Args)),
  2767. ?report_debug(ConnData, "return: message error", [Error, {return, Res}]),
  2768. case Res of
  2769. ok ->
  2770. ok;
  2771. _ ->
  2772. warning_msg("message error callback failed: ~w", [Res]),
  2773. ok
  2774. end,
  2775. Res.
  2776. handle_disconnect_callback(ConnData, UserReason)
  2777. when is_record(ConnData, conn_data) ->
  2778. ?report_trace(ConnData, "callback: disconnect", [{reason, UserReason}]),
  2779. ConnHandle = ConnData#conn_data.conn_handle,
  2780. Version = ConnData#conn_data.protocol_version,
  2781. UserMod = ConnData#conn_data.user_mod,
  2782. UserArgs = ConnData#conn_data.user_args,
  2783. Args = [ConnHandle, Version, UserReason | UserArgs],
  2784. Res = (catch apply(UserMod, handle_disconnect, Args)),
  2785. ?report_debug(ConnData, "return: disconnect", [{reason, UserReason}, {return, Res}]),
  2786. case Res of
  2787. ok ->
  2788. ok;
  2789. _ ->
  2790. warning_msg("disconnect callback failed: ~w", [Res]),
  2791. ok
  2792. end,
  2793. Res.
  2794. %%----------------------------------------------------------------------
  2795. %% Test "outgoing" messages
  2796. %%----------------------------------------------------------------------
  2797. %% test_request/5 -> {MegacoMessage, EncodingRes}
  2798. %%
  2799. %% This function is only intended for testing
  2800. %% (e.g. answer the question: have I constructed a valid action request?)
  2801. %%
  2802. %% It's not exactly the same code as a call to 'call'
  2803. %% or 'cast' but close enough.
  2804. %%
  2805. test_request(ConnHandle, Actions,
  2806. Version, EncodingMod, EncodingConfig)
  2807. when is_record(ConnHandle, megaco_conn_handle) and
  2808. is_integer(Version) andalso is_atom(EncodingMod) ->
  2809. %% Create a fake conn_data structure
  2810. ConnData = #conn_data{serial = 1,
  2811. protocol_version = Version,
  2812. conn_handle = ConnHandle,
  2813. auth_data = asn1_NOVALUE,
  2814. encoding_mod = EncodingMod,
  2815. encoding_config = EncodingConfig},
  2816. TRs = test_req_compose_transactions(ConnData, Actions),
  2817. Body = {transactions, TRs},
  2818. MegaMsg = megaco_messenger_misc:compose_message(ConnData, Version, Body),
  2819. EncodeRes = megaco_messenger_misc:encode_message(ConnData, MegaMsg),
  2820. {MegaMsg, EncodeRes}.
  2821. test_req_compose_transactions(ConnData, [A|_] = ActionsList) when is_list(A) ->
  2822. LastSerial = ConnData#conn_data.serial,
  2823. test_req_compose_transactions(LastSerial, lists:reverse(ActionsList), []);
  2824. test_req_compose_transactions(#conn_data{serial = Serial}, Actions) ->
  2825. TR = #'TransactionRequest'{transactionId = Serial,
  2826. actions = Actions},
  2827. [{transactionRequest, TR}].
  2828. test_req_compose_transactions(_Serial, [], Acc) ->
  2829. lists:reverse(Acc);
  2830. test_req_compose_transactions(Serial, [A|As], Acc) ->
  2831. TR = #'TransactionRequest'{transactionId = Serial,
  2832. actions = A},
  2833. test_req_compose_transactions(Serial, As, [{transactionRequest, TR}|Acc]).
  2834. test_reply(ConnHandle, Version, EncodingMod, EncodingConfig, Error)
  2835. when is_record(Error, 'ErrorDescriptor') ->
  2836. Reply = {transactionError, Error},
  2837. test_reply_encode(ConnHandle, Version, EncodingMod, EncodingConfig, Reply);
  2838. test_reply(ConnHandle, Version, EncodingMod, EncodingConfig, Replies)
  2839. when is_list(Replies) ->
  2840. Reply = {actionReplies, Replies},
  2841. test_reply_encode(ConnHandle, Version, EncodingMod, EncodingConfig, Reply).
  2842. test_reply_encode(ConnHandle, Version, EncodingMod, EncodingConfig, Reply) ->
  2843. ImmAck = asn1_NOVALUE,
  2844. Serial = 1,
  2845. %% Create a fake conn_data structure
  2846. CD = #conn_data{serial = Serial,
  2847. protocol_version = Version,
  2848. conn_handle = ConnHandle,
  2849. auth_data = asn1_NOVALUE,
  2850. encoding_mod = EncodingMod,
  2851. encoding_config = EncodingConfig},
  2852. TR0 = #megaco_transaction_reply{transactionId = Serial,
  2853. immAckRequired = ImmAck,
  2854. transactionResult = Reply},
  2855. TR = megaco_messenger_misc:transform_transaction_reply(CD, TR0),
  2856. Body = {transactions, [{transactionReply, TR}]},
  2857. MegaMsg = megaco_messenger_misc:compose_message(CD, Version, Body),
  2858. EncodeRes = megaco_messenger_misc:encode_message(CD, MegaMsg),
  2859. {MegaMsg, EncodeRes}.
  2860. %%----------------------------------------------------------------------
  2861. %% Send (or prepare) outgoing messages
  2862. %%----------------------------------------------------------------------
  2863. %% Description:
  2864. %% Encode a list of actions or a list of list of actions for
  2865. %% later sending (using call or cast).
  2866. %%
  2867. %% encode_actions(CH, Acts, Opts) -> {ok, encoded_actions()} | {error, Reason}
  2868. %% CH -> connection_handle()
  2869. %% Acts -> action_reqs() | [action_reqs()]
  2870. %% action_reqs() -> [action_req()]
  2871. %% action_req() -> #'ActionRequest'{}
  2872. %% Opts -> [option()]
  2873. %% option() -> {Tab, Val}
  2874. %% Tag -> atom()
  2875. %% Val -> term()
  2876. %% encoded_actions() -> binary() | [binary()]
  2877. %% Reason -> term()
  2878. encode_actions(CH, [A|_] = ActionsList, Opts)
  2879. when is_record(CH, megaco_conn_handle) andalso is_list(A) ->
  2880. (catch encode_multi_actions(CH, ActionsList, Opts));
  2881. encode_actions(CH, [A|_] = Actions, Opts)
  2882. when is_record(CH, megaco_conn_handle) andalso is_tuple(A) ->
  2883. do_encode_actions(CH, Actions, Opts).
  2884. encode_multi_actions(CH, ActionsList, Opts) ->
  2885. case prepare_req_send_options(CH, Opts) of
  2886. {ok, CD} ->
  2887. ActsList = [encode_multi_actions(CD, Acts) || Acts <- ActionsList],
  2888. {ok, ActsList};
  2889. Error ->
  2890. Error
  2891. end.
  2892. encode_multi_actions(CD, Actions) ->
  2893. case megaco_messenger_misc:encode_actions(CD,
  2894. "encode multi actions",
  2895. Actions) of
  2896. {ok, Bin} ->
  2897. Bin;
  2898. Error ->
  2899. throw(Error)
  2900. end.
  2901. do_encode_actions(CH, Actions, Opts)
  2902. when is_record(CH, megaco_conn_handle) ->
  2903. case prepare_req_send_options(CH, Opts) of
  2904. {ok, CD} ->
  2905. megaco_messenger_misc:encode_actions(CD,
  2906. "encode actions",
  2907. Actions);
  2908. Error ->
  2909. Error
  2910. end.
  2911. prepare_req_send_options(CH, Opts) ->
  2912. case megaco_config:lookup_local_conn(CH) of
  2913. [CD] ->
  2914. override_req_send_options(any, CD, Opts);
  2915. [] ->
  2916. {error, {not_found, conn_data}}
  2917. end.
  2918. call(ConnHandle, Actions, Options) ->
  2919. case lists:keymember(reply_data, 1, Options) of
  2920. true ->
  2921. {error, {bad_option, reply_data}};
  2922. false ->
  2923. Self = self(),
  2924. ProxyFun = fun() -> call_proxy(Self) end,
  2925. {Proxy, MRef} = erlang:spawn_monitor(ProxyFun),
  2926. Options2 = [{reply_data, Proxy} | Options],
  2927. call_or_cast(call, ConnHandle, Actions, Options2, MRef)
  2928. end.
  2929. cast(ConnHandle, Actions, Options) ->
  2930. call_or_cast(cast, ConnHandle, Actions, Options, undefined).
  2931. %% In a transaction there can be several actions, so if the
  2932. %% First element of the Actions list is an ''ActionRequest''
  2933. %% record this a list of ActionRequest's for one Transaction
  2934. %% request. If on the other hand this is not the case, then
  2935. %% the Actions list is assumed to be a list of list of
  2936. %% ActionRequest. That is, action requests for several transactions.
  2937. %% It could also be a binary or a list of binaries (if
  2938. %% the actions has already been encoded).
  2939. call_or_cast(CallOrCast, ConnHandle, [A|_] = Actions, Options, ProxyMon)
  2940. when is_tuple(A) ->
  2941. %% Just one transaction
  2942. case call_or_cast(CallOrCast, ConnHandle, [Actions], Options, ProxyMon) of
  2943. ok ->
  2944. ok;
  2945. {error, Reason} ->
  2946. {error, Reason};
  2947. {Version, [Reply]} when is_integer(Version) ->
  2948. {Version, Reply};
  2949. {Version, Error} when is_integer(Version) ->
  2950. {Version, Error}
  2951. end;
  2952. call_or_cast(CallOrCast, ConnHandle, Actions, Options, ProxyMon)
  2953. when is_binary(Actions) ->
  2954. %% Just one transaction (although the actions has already been encoded)
  2955. case call_or_cast(CallOrCast, ConnHandle, [Actions], Options, ProxyMon) of
  2956. ok ->
  2957. ok;
  2958. {error, Reason} ->
  2959. {error, Reason};
  2960. {Version, [Reply]} when is_integer(Version) ->
  2961. {Version, Reply};
  2962. {Version, Error} when is_integer(Version) ->
  2963. {Version, Error}
  2964. end;
  2965. call_or_cast(CallOrCast, ConnHandle, ActionsList, Options, ProxyMon)
  2966. when is_record(ConnHandle, megaco_conn_handle) ->
  2967. case prepare_req_send_options(CallOrCast,
  2968. ConnHandle, Options, ActionsList) of
  2969. {ok, ConnData} ->
  2970. ?report_trace(ConnData, "call_or_cast - options prepared", []),
  2971. case encode_requests(ConnData, ActionsList) of
  2972. {ok, TRs, BinOrBins} ->
  2973. ?report_trace(ConnData,
  2974. "call_or_cast - request encoded", []),
  2975. send_request(ConnData, ConnHandle,
  2976. TRs, CallOrCast, BinOrBins),
  2977. case CallOrCast of
  2978. call ->
  2979. TransIds = to_local_trans_id(ConnData, TRs),
  2980. wait_for_reply(ConnData, TransIds, ProxyMon);
  2981. cast ->
  2982. ok
  2983. end;
  2984. {error, Reason} ->
  2985. call_proxy_cleanup(ConnData, ProxyMon),
  2986. Version = ConnData#conn_data.protocol_version,
  2987. return_error(CallOrCast, Version, {error, Reason})
  2988. end;
  2989. {error, Reason} ->
  2990. call_proxy_cleanup(Options, ProxyMon),
  2991. return_error(CallOrCast, 1, {error, Reason})
  2992. end;
  2993. call_or_cast(CallOrCast, ConnHandle, _Actions, Options, ProxyMon) ->
  2994. call_proxy_cleanup(Options, ProxyMon),
  2995. return_error(CallOrCast, 1, {error, {bad_megaco_conn_handle, ConnHandle}}).
  2996. return_error(Action, Version, Error) ->
  2997. case Action of
  2998. call -> {Version, Error};
  2999. cast -> Error
  3000. end.
  3001. wait_for_reply(CD, TransIds, ProxyMon) ->
  3002. ProxyPid = CD#conn_data.reply_data,
  3003. ProxyPid ! {go, self(), CD, TransIds},
  3004. receive
  3005. {reply, ProxyPid, Reply} ->
  3006. erlang:demonitor(ProxyMon, [flush]),
  3007. Reply;
  3008. {'DOWN', ProxyMon, process, ProxyPid, Info} ->
  3009. UserReply = {error, {call_proxy_crash, Info}},
  3010. {CD#conn_data.protocol_version, UserReply}
  3011. end.
  3012. call_proxy_cleanup(#conn_data{reply_data = ProxyPid}, ProxyMon) ->
  3013. do_call_proxy_cleanup(ProxyPid, ProxyMon);
  3014. call_proxy_cleanup(Options, ProxyMon) when is_list(Options) ->
  3015. ProxyPid =
  3016. case lists:keysearch(reply_data, 1, Options) of
  3017. {value, {reply_data, Data}} ->
  3018. Data;
  3019. _ ->
  3020. undefined
  3021. end,
  3022. do_call_proxy_cleanup(ProxyPid, ProxyMon);
  3023. call_proxy_cleanup(ProxyPid, ProxyMon) ->
  3024. do_call_proxy_cleanup(ProxyPid, ProxyMon).
  3025. do_call_proxy_cleanup(ProxyPid, ProxyMon) ->
  3026. maybe_demonitor(ProxyMon),
  3027. maybe_stop_proxy(ProxyPid),
  3028. ok.
  3029. maybe_demonitor(undefined) ->
  3030. ok;
  3031. maybe_demonitor(Mon) ->
  3032. (catch erlang:demonitor(Mon, [flush])),
  3033. ok.
  3034. maybe_stop_proxy(Pid) when is_pid(Pid) ->
  3035. Pid ! {stop, self()},
  3036. ok;
  3037. maybe_stop_proxy(_) ->
  3038. ok.
  3039. call_proxy(Parent) ->
  3040. receive
  3041. {go, Parent, CD, TransIds} ->
  3042. call_proxy(Parent, CD, TransIds);
  3043. {stop, Parent} ->
  3044. exit(normal)
  3045. end.
  3046. call_proxy(Parent, CD, TransIds) ->
  3047. Reply = proxy_wait_for_reply(CD, TransIds, []),
  3048. Parent ! {reply, self(), Reply},
  3049. call_proxy_gc(CD, CD#conn_data.call_proxy_gc_timeout).
  3050. call_proxy_gc(CD, Timeout) when (Timeout > 0) ->
  3051. T = t(),
  3052. receive
  3053. {?MODULE, TransId, Version, Result} -> % Old format
  3054. CD2 = CD#conn_data{protocol_version = Version},
  3055. Extra = ?default_user_callback_extra,
  3056. return_unexpected_trans_reply(CD2, TransId, Result, Extra),
  3057. call_proxy_gc(CD, Timeout - (t() - T));
  3058. {?MODULE, TransId, Version, Result, Extra} ->
  3059. CD2 = CD#conn_data{protocol_version = Version},
  3060. return_unexpected_trans_reply(CD2, TransId, Result, Extra),
  3061. call_proxy_gc(CD, Timeout - (t() - T))
  3062. after Timeout ->
  3063. exit(normal)
  3064. end;
  3065. call_proxy_gc(_CD, _Timeout) ->
  3066. exit(normal).
  3067. proxy_wait_for_reply(_CD, [], Replies0) ->
  3068. % Make sure they come in the same order as the requests where sent
  3069. Replies1 = lists:keysort(2, Replies0),
  3070. %% Must all be the same version
  3071. [{Version, _, _}|_] = Replies1,
  3072. Replies2 = [Result || {_Version, _TransId, Result} <- Replies1],
  3073. {Version, Replies2};
  3074. proxy_wait_for_reply(CD, TransIds, Replies) ->
  3075. receive
  3076. {?MODULE, TransId, Version, Reply} -> % Old format
  3077. {TransIds2, Replies2} =
  3078. wfr_handle_reply(CD,
  3079. TransIds, TransId,
  3080. Version, Replies, Reply),
  3081. proxy_wait_for_reply(CD, TransIds2, Replies2);
  3082. {?MODULE, TransId, Version, Reply, Extra} ->
  3083. {TransIds2, Replies2} =
  3084. wfr_handle_reply(CD,
  3085. TransIds, TransId,
  3086. Version, Replies, Reply, Extra),
  3087. proxy_wait_for_reply(CD, TransIds2, Replies2)
  3088. end.
  3089. wfr_handle_reply(CD, TransIds, TransId, Version, Replies, Reply) ->
  3090. Extra = ?default_user_callback_extra,
  3091. wfr_handle_reply(CD, TransIds, TransId, Version, Replies, Reply, Extra).
  3092. wfr_handle_reply(CD, TransIds, TransId, Version, Replies, Reply, Extra) ->
  3093. %% Is this meant for us?
  3094. case lists:member(TransId, TransIds) of
  3095. true -> % Yep
  3096. wfr_update(TransIds, TransId, Version, Replies, Reply, Extra);
  3097. false -> % Nop
  3098. CD2 = CD#conn_data{protocol_version = Version},
  3099. return_unexpected_trans_reply(CD2, TransId, Reply, Extra),
  3100. {TransIds, Replies}
  3101. end.
  3102. wfr_mk_reply(Version, TransId, Result, ?default_user_callback_extra = _Extra) ->
  3103. {Version, TransId, Result};
  3104. wfr_mk_reply(Version, TransId, Result0, Extra) ->
  3105. Result = list_to_tuple(lists:append(tuple_to_list(Result0), [Extra])),
  3106. {Version, TransId, Result}.
  3107. %% Last segment of a reply
  3108. %% transactionResult "=" actionReplies
  3109. wfr_update(TransIds, TransId, Version, Results, {ok, {SegNo, Last, ARs}}, Extra)
  3110. when is_integer(SegNo) andalso (Last == true) ->
  3111. TransIds2 = lists:delete(TransId, TransIds),
  3112. case lists:keysearch(TransId, 2, Results) of
  3113. %% All segments ok (actionReplies)
  3114. {value, {V, TransId, {ok, SegReps}}} ->
  3115. SegReps2 = lists:keysort(1, [{SegNo, ARs}|SegReps]),
  3116. Rep = wfr_mk_reply(V, TransId, {ok, SegReps2}, Extra),
  3117. Results2 = lists:keyreplace(TransId, 2, Results, Rep),
  3118. {TransIds2, Results2};
  3119. %% Atleast one segment error (transactionError)
  3120. {value, {V, TransId, {error, {segment, OkSegs, ErrSegs}}}} ->
  3121. OkSegs2 = lists:keysort(1, [{SegNo, ARs}|OkSegs]),
  3122. ErrSegs2 = lists:keysort(1, ErrSegs),
  3123. Error = {error, {segment, OkSegs2, ErrSegs2}},
  3124. Rep = wfr_mk_reply(V, TransId, Error, Extra),
  3125. Results2 = lists:keyreplace(TransId, 2, Results, Rep),
  3126. {TransIds2, Results2};
  3127. false ->
  3128. %% First and only segment
  3129. Rep = wfr_mk_reply(Version, TransId, {ok, [{SegNo, ARs}]}, Extra),
  3130. {TransIds2, [Rep | Results]}
  3131. end;
  3132. %% Last segment of a reply
  3133. %% transactionResult "=" transactionError
  3134. wfr_update(TransIds, TransId, Version, Results, {error, {SegNo, Last, ED}}, Extra)
  3135. when is_integer(SegNo) andalso (Last == true) ->
  3136. TransIds2 = lists:delete(TransId, TransIds),
  3137. case lists:keysearch(TransId, 2, Results) of
  3138. %% First segment with error (transactionError)
  3139. {value, {V, TransId, {ok, SegReps}}} ->
  3140. OkSegs = lists:keysort(1, [{SegNo, ED}|SegReps]),
  3141. ErrSegs = [{SegNo, ED}],
  3142. Error = {error, {segment, OkSegs, ErrSegs}},
  3143. Rep = wfr_mk_reply(V, TransId, Error, Extra),
  3144. Results2 = lists:keyreplace(TransId, 2, Results, Rep),
  3145. {TransIds2, Results2};
  3146. %% Another segment with error (transactionError)
  3147. {value, {V, TransId, {error, {segment, OkSegs, ErrSegs}}}} ->
  3148. OkSegs2 = lists:keysort(1, OkSegs),
  3149. ErrSegs2 = lists:keysort(1, [{SegNo, ED}|ErrSegs]),
  3150. Error = {error, {segment, OkSegs2, ErrSegs2}},
  3151. Rep = wfr_mk_reply(V, TransId, Error, Extra),
  3152. Results2 = lists:keyreplace(TransId, 2, Results, Rep),
  3153. {TransIds2, Results2};
  3154. false ->
  3155. %% First and only segment
  3156. OkSegs = [],
  3157. ErrSegs = [{SegNo, ED}],
  3158. Error = {error, {segment, OkSegs, ErrSegs}},
  3159. Rep = wfr_mk_reply(Version, TransId, Error, Extra),
  3160. {TransIds2, [Rep]}
  3161. end;
  3162. %% One segment of a reply
  3163. %% transactionResult "=" actionReplies
  3164. wfr_update(TransIds, TransId, Version, Results, {ok, {SegNo, _Last, ARs}}, Extra)
  3165. when is_integer(SegNo) ->
  3166. case lists:keysearch(TransId, 2, Results) of
  3167. %% All segments ok (actionReplies)
  3168. {value, {V, TransId, {ok, SegReps}}} ->
  3169. SegReps2 = [{SegNo, ARs}|SegReps],
  3170. Rep = wfr_mk_reply(V, TransId, {ok, SegReps2}, Extra),
  3171. Results2 = lists:keyreplace(TransId, 2, Results, Rep),
  3172. {TransIds, Results2};
  3173. %% Atleast one segment error (transactionError)
  3174. {value, {V, TransId, {error, {segment, OkSegs, ErrSegs}}}} ->
  3175. OkSegs2 = [{SegNo, ARs}|OkSegs],
  3176. Error = {error, {segment, OkSegs2, ErrSegs}},
  3177. Rep = wfr_mk_reply(V, TransId, Error, Extra),
  3178. Results2 = lists:keyreplace(TransId, 2, Results, Rep),
  3179. {TransIds, Results2};
  3180. false ->
  3181. %% First and only segment
  3182. Rep = wfr_mk_reply(Version, TransId, {ok, [{SegNo, ARs}]}, Extra),
  3183. {TransIds, [Rep | Results]}
  3184. end;
  3185. %% One segment of a reply
  3186. %% transactionResult "=" transactionError
  3187. wfr_update(TransIds, TransId, Version, Results, {error, {SegNo, _Last, ED}}, Extra)
  3188. when is_integer(SegNo) ->
  3189. case lists:keysearch(TransId, 2, Results) of
  3190. %% First segment with error (transactionError)
  3191. {value, {V, TransId, {ok, OkSegs}}} ->
  3192. ErrSegs = [{SegNo, ED}],
  3193. Error = {error, {segment, OkSegs, ErrSegs}},
  3194. Rep = wfr_mk_reply(V, TransId, Error, Extra),
  3195. Results2 = lists:keyreplace(TransId, 2, Results, Rep),
  3196. {TransIds, Results2};
  3197. %% Another segment with error (transactionError)
  3198. {value, {V, TransId, {error, {OkSegs, ErrSegs}}}} ->
  3199. ErrSegs2 = [{SegNo, ED}|ErrSegs],
  3200. Error = {error, {segment, OkSegs, ErrSegs2}},
  3201. Rep = wfr_mk_reply(V, TransId, Error, Extra),
  3202. Results2 = lists:keyreplace(TransId, 2, Results, Rep),
  3203. {TransIds, Results2};
  3204. false ->
  3205. %% First segment
  3206. OkSegs = [],
  3207. ErrSegs = [{SegNo, ED}],
  3208. Error = {error, {segment, OkSegs, ErrSegs}},
  3209. Rep = wfr_mk_reply(Version, TransId, Error, Extra),
  3210. {TransIds, [Rep]}
  3211. end;
  3212. %% This means that some segments did not make it in time
  3213. wfr_update(TransIds, TransId, Version, Results,
  3214. {error, {segment_timeout, Missing}}, Extra) ->
  3215. TransIds2 = lists:delete(TransId, TransIds),
  3216. case lists:keysearch(TransId, 2, Results) of
  3217. %% First segment with error (transactionError)
  3218. {value, {V, TransId, {ok, OkSegs}}} ->
  3219. Error = {error, {segment_timeout, Missing, OkSegs, []}},
  3220. Rep = wfr_mk_reply(V, TransId, Error, Extra),
  3221. Results2 = lists:keyreplace(TransId, 2, Results, Rep),
  3222. {TransIds2, Results2};
  3223. %% Another segment with error (transactionError)
  3224. {value, {V, TransId, {error, {segment, OkSegs, ErrSegs}}}} ->
  3225. Error = {error, {segment_timeout, Missing, OkSegs, ErrSegs}},
  3226. Rep = wfr_mk_reply(V, TransId, Error, Extra),
  3227. Results2 = lists:keyreplace(TransId, 2, Results, Rep),
  3228. {TransIds2, Results2};
  3229. false ->
  3230. %% First segment
  3231. Error = {error, {segment_timeout, Missing, [], []}},
  3232. Rep = wfr_mk_reply(Version, TransId, Error, Extra),
  3233. {TransIds2, [Rep]}
  3234. end;
  3235. %% And all other results (presumably results without segments).
  3236. wfr_update(TransIds, TransId, Version, Results, Result, Extra) ->
  3237. TransIds2 = lists:delete(TransId, TransIds),
  3238. Results2 = [wfr_mk_reply(Version, TransId, Result, Extra)|Results],
  3239. {TransIds2, Results2}.
  3240. %% TransInfo is either [trans_id()] or a [trans_req()]
  3241. %% This is the normal case where we have just one
  3242. %% transaction to be sent (using call or cast) using
  3243. %% the transaction sender process.
  3244. send_request(#conn_data{control_pid = CP,
  3245. trans_req = true,
  3246. trans_sender = Pid} = CD,
  3247. CH, [Serial], Action, [Bin])
  3248. when is_pid(Pid) andalso
  3249. is_integer(Serial) andalso
  3250. (node(CP) =:= node()) ->
  3251. ?report_trace(CD,
  3252. "send_request - one transaction via trans-sender",
  3253. [Serial]),
  3254. #conn_data{request_timer = InitTimer,
  3255. long_request_timer = LongTimer} = CD,
  3256. TransId = to_local_trans_id(CH, Serial),
  3257. insert_request(CD, CH, TransId, Action, {Serial, Bin},
  3258. InitTimer, LongTimer),
  3259. megaco_trans_sender:send_req(Pid, Serial, Bin);
  3260. %% This is the general case where we have several transactions
  3261. %% beeing sent (using call or cast) at once using
  3262. %% the transaction sender process.
  3263. send_request(#conn_data{control_pid = CP,
  3264. trans_req = true,
  3265. trans_sender = Pid} = CD,
  3266. CH, TransInfo, Action, Bins)
  3267. when is_pid(Pid) andalso
  3268. is_list(Bins) andalso
  3269. (node(CP) =:= node()) ->
  3270. ?report_trace(CD,
  3271. "send_request - multi transactions via trans_sender",
  3272. [TransInfo, Pid]),
  3273. #conn_data{request_timer = InitTimer,
  3274. long_request_timer = LongTimer} = CD,
  3275. insert_requests(CD, CH, TransInfo, Action, Bins,
  3276. InitTimer, LongTimer),
  3277. megaco_trans_sender:send_reqs(Pid, TransInfo, Bins);
  3278. %% This is the case when one or more transactions is
  3279. %% beeing sent in one message immediatelly (not using
  3280. %% the transaction sender process. E.g. the binary is
  3281. %% this encoded message.
  3282. send_request(#conn_data{control_pid = CP} = CD,
  3283. CH, TRs, Action, Bin)
  3284. when is_list(TRs) andalso
  3285. is_binary(Bin) andalso
  3286. (node(CP) =:= node()) ->
  3287. %% d("send_request -> entry with"
  3288. %% "~n TRs: ~p", [TRs]),
  3289. ?report_trace(CD, "send_request - multi transaction", [TRs]),
  3290. #conn_data{request_timer = InitTimer,
  3291. long_request_timer = LongTimer} = CD,
  3292. insert_requests(CD, CH, TRs, Action, Bin,
  3293. InitTimer, LongTimer),
  3294. case megaco_messenger_misc:send_message(CD, false, Bin) of
  3295. {error, Reason} ->
  3296. cancel_requests(CD, TRs, Reason);
  3297. {ok, _} ->
  3298. ignore
  3299. end;
  3300. %% This is the case where we are not on the node where the
  3301. %% transport process run.
  3302. send_request(#conn_data{control_pid = CP} = CD,
  3303. CH, TransInfo, Action, Bin)
  3304. when node(CP) =/= node() ->
  3305. ?report_trace(CD, "send_request - remote", [TransInfo]),
  3306. InitTimer = infinity,
  3307. LongTimer = infinity,
  3308. insert_requests(CD, CH, TransInfo, Action, Bin,
  3309. InitTimer, LongTimer),
  3310. Node = node(CP),
  3311. Args = [node(), CD, TransInfo, Bin],
  3312. rpc:cast(Node, ?MODULE, send_request_remote, Args).
  3313. insert_requests(_, _, [], _, _, _, _) ->
  3314. ok;
  3315. insert_requests(ConnData, ConnHandle, [Serial|Serials],
  3316. Action, [Bin|Bins], InitTimer, LongTimer)
  3317. when is_integer(Serial) andalso is_binary(Bin) ->
  3318. TransId = to_local_trans_id(ConnHandle, Serial),
  3319. insert_request(ConnData, ConnHandle,
  3320. TransId, Action, Bin, InitTimer, LongTimer),
  3321. insert_requests(ConnData, ConnHandle, Serials, Action, Bins,
  3322. InitTimer, LongTimer);
  3323. insert_requests(ConnData, ConnHandle,
  3324. [{transactionRequest, TR}|TRs],
  3325. Action, Bin, InitTimer, LongTimer)
  3326. when is_record(TR, 'TransactionRequest') andalso is_binary(Bin) ->
  3327. #'TransactionRequest'{transactionId = Serial} = TR,
  3328. TransId = to_local_trans_id(ConnHandle, Serial),
  3329. insert_request(ConnData, ConnHandle,
  3330. TransId, Action, TR, InitTimer, LongTimer),
  3331. insert_requests(ConnData, ConnHandle, TRs, Action, Bin,
  3332. InitTimer, LongTimer).
  3333. insert_request(ConnData, ConnHandle, TransId,
  3334. Action, Data, InitTimer, LongTimer) ->
  3335. %% We dont check the result of the lock-counter creation because
  3336. %% the only way it could already exist is if the transaction-id
  3337. %% range has wrapped and an old counter was not deleted.
  3338. megaco_monitor:request_lockcnt_cre(TransId),
  3339. #megaco_conn_handle{remote_mid = RemoteMid} = ConnHandle,
  3340. #conn_data{protocol_version = Version,
  3341. user_mod = UserMod,
  3342. user_args = UserArgs,
  3343. send_handle = SendHandle,
  3344. reply_data = ReplyData,
  3345. segment_recv_timer = InitSegTimer,
  3346. request_keep_alive_timeout = RKATimer} = ConnData,
  3347. {WaitFor, CurrTimer} = megaco_timer:init(InitTimer),
  3348. M = ?MODULE,
  3349. F = request_timeout,
  3350. A = [ConnHandle, TransId],
  3351. Ref = megaco_monitor:apply_after(M, F, A, WaitFor),
  3352. Req = #request{trans_id = TransId,
  3353. remote_mid = RemoteMid,
  3354. timer_ref = ?SIM({short, Ref}, init_request_timer),
  3355. init_timer = InitTimer,
  3356. init_long_timer = LongTimer,
  3357. curr_timer = CurrTimer,
  3358. version = Version,
  3359. bytes = {send, Data},
  3360. send_handle = SendHandle,
  3361. user_mod = UserMod,
  3362. user_args = UserArgs,
  3363. reply_action = Action,
  3364. reply_data = ReplyData,
  3365. init_seg_timer = InitSegTimer,
  3366. keep_alive_timer = RKATimer},
  3367. megaco_monitor:insert_request(Req). % Timing problem?
  3368. send_request_remote(ReplyNode, ConnData, TransInfo, Bin) ->
  3369. Action = remote,
  3370. ConnHandle = ConnData#conn_data.conn_handle,
  3371. ConnData2 = ConnData#conn_data{reply_data = ReplyNode},
  3372. send_request(ConnData2, ConnHandle, TransInfo, Action, Bin).
  3373. prepare_req_send_options(CallOrCast, ConnHandle, Options, Actions) ->
  3374. %% Ensures that two processes cannot get same transaction id.
  3375. %% Bad send options may cause spurious transaction id to be consumed.
  3376. Incr = number_of_transactions(Actions),
  3377. case megaco_config:incr_trans_id_counter(ConnHandle, Incr) of
  3378. {ok, ConnData} ->
  3379. override_req_send_options(CallOrCast, ConnData, Options);
  3380. {error, Reason} ->
  3381. {error, Reason}
  3382. end.
  3383. number_of_transactions([Action|_]) when is_tuple(Action) ->
  3384. 1;
  3385. number_of_transactions(ActionsList) ->
  3386. length(ActionsList).
  3387. override_req_send_options(ReplyAction, ConnData, [{Key, Val} | Tail]) ->
  3388. case Key of
  3389. protocol_version ->
  3390. ConnData2 = ConnData#conn_data{protocol_version = Val},
  3391. override_req_send_options(ReplyAction, ConnData2, Tail);
  3392. send_handle ->
  3393. ConnData2 = ConnData#conn_data{send_handle = Val},
  3394. override_req_send_options(ReplyAction, ConnData2, Tail);
  3395. request_timer ->
  3396. case megaco_config:verify_val(Key, Val) of
  3397. true ->
  3398. ConnData2 = ConnData#conn_data{request_timer = Val},
  3399. override_req_send_options(ReplyAction, ConnData2, Tail);
  3400. false ->
  3401. {error, {bad_send_option, {Key, Val}}}
  3402. end;
  3403. long_request_timer ->
  3404. case megaco_config:verify_val(Key, Val) of
  3405. true ->
  3406. ConnData2 = ConnData#conn_data{long_request_timer = Val},
  3407. override_req_send_options(ReplyAction, ConnData2, Tail);
  3408. false ->
  3409. {error, {bad_send_option, {Key, Val}}}
  3410. end;
  3411. call_proxy_gc_timeout when (ReplyAction =:= call) orelse
  3412. (ReplyAction =:= any) ->
  3413. case megaco_config:verify_val(Key, Val) of
  3414. true ->
  3415. ConnData2 =
  3416. ConnData#conn_data{call_proxy_gc_timeout = Val},
  3417. override_req_send_options(ReplyAction, ConnData2, Tail);
  3418. false ->
  3419. {error, {bad_send_option, {Key, Val}}}
  3420. end;
  3421. request_keep_alive_timeout when (ReplyAction =:= cast) orelse
  3422. (ReplyAction =:= any) ->
  3423. case megaco_config:verify_val(Key, Val) of
  3424. true ->
  3425. ConnData2 =
  3426. ConnData#conn_data{request_keep_alive_timeout = Val},
  3427. override_req_send_options(ReplyAction, ConnData2, Tail);
  3428. false ->
  3429. {error, {bad_send_option, {Key, Val}}}
  3430. end;
  3431. reply_data ->
  3432. ConnData2 = ConnData#conn_data{reply_data = Val},
  3433. override_req_send_options(ReplyAction, ConnData2, Tail);
  3434. user_mod when is_atom(Val) ->
  3435. ConnData2 = ConnData#conn_data{user_mod = Val},
  3436. override_req_send_options(ReplyAction, ConnData2, Tail);
  3437. user_args when is_list(Val) ->
  3438. ConnData2 = ConnData#conn_data{user_args = Val},
  3439. override_req_send_options(ReplyAction, ConnData2, Tail);
  3440. trans_req when Val =:= false ->
  3441. %% We only allow turning the transaction-sender off, since
  3442. %% the opposite (turning it on) would causing to much headake...
  3443. %% This will allow not using the transaction sender for
  3444. %% occasional messages
  3445. ConnData2 = ConnData#conn_data{trans_req = Val,
  3446. trans_sender = undefined},
  3447. override_req_send_options(ReplyAction, ConnData2, Tail);
  3448. _Bad ->
  3449. {error, {bad_send_option, {Key, Val}}}
  3450. end;
  3451. override_req_send_options(_ReplyAction, ConnData, []) ->
  3452. {ok, ConnData}.
  3453. override_rep_send_options(ConnData, [{Key, Val} | Tail]) ->
  3454. case Key of
  3455. protocol_version ->
  3456. ConnData2 = ConnData#conn_data{protocol_version = Val},
  3457. override_rep_send_options(ConnData2, Tail);
  3458. send_handle ->
  3459. ConnData2 = ConnData#conn_data{send_handle = Val},
  3460. override_rep_send_options(ConnData2, Tail);
  3461. reply_timer ->
  3462. case megaco_config:verify_val(Key, Val) of
  3463. true ->
  3464. ConnData2 = ConnData#conn_data{reply_timer = Val},
  3465. override_rep_send_options(ConnData2, Tail);
  3466. false ->
  3467. {error, {bad_send_option, {Key, Val}}}
  3468. end;
  3469. trans_req when Val =:= false ->
  3470. %% We only allow turning the transaction-sender off, since
  3471. %% the opposite (turning it on) would causing to much headake...
  3472. %% This will allow not using the transaction sender for
  3473. %% occasional messages
  3474. ConnData2 = ConnData#conn_data{trans_req = Val,
  3475. trans_sender = undefined},
  3476. override_rep_send_options(ConnData2, Tail);
  3477. _Bad ->
  3478. {error, {bad_send_option, {Key, Val}}}
  3479. end;
  3480. override_rep_send_options(ConnData, []) ->
  3481. {ok, ConnData}.
  3482. %% ----
  3483. %% This list is allways atleast one (list of actions) long.
  3484. %% ----
  3485. %% The proper number of transaction id numbers has already
  3486. %% been "allocated", and the connection data record is
  3487. %% updated accordingly.
  3488. encode_requests(#conn_data{trans_req = true,
  3489. trans_sender = Pid,
  3490. serial = LastSerial} = CD, ActionsList)
  3491. when is_pid(Pid) ->
  3492. (catch encode_requests(CD, LastSerial,
  3493. lists:reverse(ActionsList), [], []));
  3494. encode_requests(#conn_data{serial = LastSerial} = CD, ActionsList) ->
  3495. %% We shall not accumulate transactions.
  3496. %% This means that we shall not encode
  3497. %% the transactions individually (and send
  3498. %% them to the sender process, which
  3499. %% accumulate transactions for later sending),
  3500. %% Instead we encode the entire message directly.
  3501. %% => We shall return one binary, containing,
  3502. %% possibly, many transactions
  3503. encode_requests_in_msg(CD, LastSerial, lists:reverse(ActionsList)).
  3504. %% This means that we shall compose and encode one complete
  3505. %% megaco message, containing one or more transactions.
  3506. encode_requests_in_msg(CD, LastSerial, ActionsList) ->
  3507. TRs = compose_requests_in_msg(LastSerial, ActionsList, []),
  3508. Body = {transactions, TRs},
  3509. Res = megaco_messenger_misc:encode_body(CD,
  3510. "encode trans request(s) msg",
  3511. Body),
  3512. case Res of
  3513. {ok, Bin} ->
  3514. {ok, TRs, Bin};
  3515. Error ->
  3516. Error
  3517. end.
  3518. compose_requests_in_msg(_S, [], TRs) ->
  3519. TRs;
  3520. compose_requests_in_msg(Serial, [A|As], Acc) ->
  3521. TR = #'TransactionRequest'{transactionId = Serial,
  3522. actions = A},
  3523. compose_requests_in_msg(Serial - 1, As, [{transactionRequest, TR}|Acc]).
  3524. %% We have done the encoding in reverse order, so there
  3525. %% is no need to reverse now.
  3526. encode_requests(_, _, [], Serials, EncodedTRs) ->
  3527. {ok, Serials, EncodedTRs};
  3528. encode_requests(CD, Serial, [Actions|ActionsList], Serials, EncodedTRs) ->
  3529. case do_encode_request(CD, Serial, Actions) of
  3530. {ok, Bin} ->
  3531. encode_requests(CD, Serial - 1, ActionsList,
  3532. [Serial|Serials], [Bin|EncodedTRs]);
  3533. Error ->
  3534. throw(Error)
  3535. end.
  3536. do_encode_request(CD, Serial, Actions) ->
  3537. TR = #'TransactionRequest'{transactionId = Serial,
  3538. actions = Actions},
  3539. megaco_messenger_misc:encode_trans_request(CD, TR).
  3540. imm_ack_req(Counter, when_pending_sent) when (Counter > 0) -> 'NULL';
  3541. imm_ack_req(_Counter, when_pending_sent) -> asn1_NOVALUE;
  3542. imm_ack_req(_Counter, ImmAck) -> ImmAck.
  3543. maybe_send_reply(#conn_data{sent_pending_limit = Limit} = ConnData,
  3544. TransId, Result, SendOpts, ImmAck) ->
  3545. %% d("maybe_send_reply -> entry with"
  3546. %% "~n Limit: ~p"
  3547. %% "~n TransId: ~p"
  3548. %% "~n Result: ~p"
  3549. %% "~n SendOpts: ~p"
  3550. %% "~n ImmAck: ~p", [Limit, TransId, Result, SendOpts, ImmAck]),
  3551. %% Pending limit
  3552. %% Before we can send the reply we must check that we have
  3553. %% not passed the pending limit (and sent an error message).
  3554. case check_pending_limit(Limit, sent, TransId) of
  3555. {ok, Counter} ->
  3556. case override_rep_send_options(ConnData, SendOpts) of
  3557. {ok, ConnData2} ->
  3558. send_reply(ConnData2, Result,
  3559. imm_ack_req(Counter, ImmAck));
  3560. Error ->
  3561. Error
  3562. end;
  3563. aborted ->
  3564. {error, aborted}
  3565. end.
  3566. encode_reply(CD, TR) ->
  3567. megaco_messenger_misc:encode_trans_reply(CD, TR).
  3568. send_reply(#conn_data{serial = Serial,
  3569. trans_req = TransReq,
  3570. trans_sender = TransSnd} = CD, TransRes, ImmAck) ->
  3571. %% Encapsule the transaction result into a reply message
  3572. %% d("send_reply -> entry with"
  3573. %% "~n Serial: ~p"
  3574. %% "~n TransRes: ~p"
  3575. %% "~n ImmAck: ~p", [Serial, TransRes, ImmAck]),
  3576. TR = #megaco_transaction_reply{transactionId = Serial,
  3577. immAckRequired = ImmAck,
  3578. transactionResult = TransRes},
  3579. case encode_reply(CD, TR) of
  3580. {ok, Bin} when is_binary(Bin) andalso (TransReq =:= true) ->
  3581. ?rt2("send_reply - pass it on to the transaction sender",
  3582. [size(Bin)]),
  3583. megaco_trans_sender:send_reply(TransSnd, Bin),
  3584. {ok, Bin};
  3585. {ok, Bin} when is_binary(Bin) ->
  3586. ?rt2("send_reply - encoded", [size(Bin)]),
  3587. TraceLabel = "send trans reply",
  3588. Body = {transactions, [Bin]},
  3589. megaco_messenger_misc:send_body(CD, TraceLabel, Body);
  3590. {ok, Bins} when is_list(Bins) ->
  3591. ?rt2("send_reply - encoded (segmented)", [length(Bins)]),
  3592. Res = send_reply_segments(CD, Bins),
  3593. {ok, Res};
  3594. {error, not_implemented} ->
  3595. %% Oups, we cannot segment regardless the config,
  3596. %% so pack it all into one message and hope for
  3597. %% the best...
  3598. ?rt2("send_reply - cannot encode separate transactions", []),
  3599. TR2 = megaco_messenger_misc:transform_transaction_reply(CD, TR),
  3600. Body = {transactions, [{transactionReply, TR2}]},
  3601. megaco_messenger_misc:send_body(CD, "encode trans reply", Body);
  3602. {error, Reason} = Error ->
  3603. Code = ?megaco_internal_gateway_error,
  3604. Text = "encode transaction reply",
  3605. ED = #'ErrorDescriptor'{errorCode = Code,
  3606. errorText = Text},
  3607. Res = {transactionError, ED},
  3608. TR2 = #megaco_transaction_reply{transactionId = Serial,
  3609. transactionResult = Res},
  3610. TR3 = megaco_messenger_misc:transform_transaction_reply(CD, TR2),
  3611. TraceLabel = "<ERROR> encode trans reply body failed",
  3612. ?report_important(CD, TraceLabel, [TR, TR3, ED, Error]),
  3613. error_msg("failed encoding transaction reply body: ~s",
  3614. [format_encode_error_reason(Reason)]),
  3615. Body = {transactions, [{transactionReply, TR3}]},
  3616. megaco_messenger_misc:send_body(CD, TraceLabel, Body),
  3617. Error
  3618. end.
  3619. send_reply_segments(CD, Bins) ->
  3620. TraceLabelPre = "send segmented trans reply",
  3621. (catch send_reply_segments(CD, TraceLabelPre, Bins)).
  3622. send_reply_segments(#conn_data{segment_send = infinity} = CD, Label, Bins) ->
  3623. send_reply_segments(CD, Label, length(Bins), Bins);
  3624. send_reply_segments(#conn_data{segment_send = K} = CD, Label, Bins)
  3625. when is_integer(K) andalso (K =< length(Bins)) ->
  3626. send_reply_segments(CD, Label, K, Bins);
  3627. send_reply_segments(#conn_data{segment_send = K} = CD, Label, Bins)
  3628. when is_integer(K) ->
  3629. send_reply_segments(CD, Label, length(Bins), Bins).
  3630. send_reply_segments(CD, Label, K, Bins) ->
  3631. send_reply_segments(CD, Label, K, Bins, []).
  3632. send_reply_segments(_CD, _Label, 0, Bins, Sent) ->
  3633. ?rt2("send_reply_segments - done", [Sent, Bins]),
  3634. {Sent, Bins};
  3635. send_reply_segments(CD, TraceLabelPre, K, [{SN, Bin}|Bins], Sent) ->
  3636. case send_reply_segment(CD, TraceLabelPre, SN, Bin) of
  3637. {ok, Bin2} ->
  3638. ?rt2("send_reply_segments - send", [K, SN]),
  3639. send_reply_segments(CD, TraceLabelPre, K-1,
  3640. Bins, [{SN, Bin2}|Sent]);
  3641. Error ->
  3642. throw(Error)
  3643. end.
  3644. send_reply_segment(CD, TraceLabelPre, SN, Bin) ->
  3645. Label = lists:flatten(io_lib:format("~s[~w]", [TraceLabelPre, SN])),
  3646. Body = {transactions, [Bin]},
  3647. megaco_messenger_misc:send_body(CD, Label, Body).
  3648. format_encode_error_reason(Reason) ->
  3649. FS =
  3650. case Reason of
  3651. {Mod, Func, [EC, Msg], {AE, CS}} when is_atom(Mod) andalso
  3652. is_atom(Func) andalso
  3653. is_list(EC) and
  3654. is_tuple(Msg) and
  3655. is_list(CS) ->
  3656. io_lib:format("~n Encode module: ~w"
  3657. "~n Func: ~w"
  3658. "~n Encode config: ~w"
  3659. "~n Message part: ~p"
  3660. "~n Actual error: ~p"
  3661. "~n Call stack: ~w",
  3662. [Mod, Func, EC, Msg, AE, CS]);
  3663. {Mod, Func, [EC, Msg], AE} when is_atom(Mod) andalso
  3664. is_atom(Func) andalso
  3665. is_list(EC) andalso
  3666. is_tuple(Msg) ->
  3667. io_lib:format("~n Encode module: ~w"
  3668. "~n Func: ~w"
  3669. "~n Encode config: ~w"
  3670. "~n Message part: ~p"
  3671. "~n Actual error: ~p",
  3672. [Mod, Func, EC, Msg, AE]);
  3673. {Mod, [EC, Msg], {AE, CS}} when is_atom(Mod) andalso
  3674. is_list(EC) andalso
  3675. is_tuple(Msg) andalso
  3676. is_list(CS) ->
  3677. io_lib:format("~n Encode module: ~w"
  3678. "~n Encode config: ~w"
  3679. "~n Message part: ~p"
  3680. "~n Actual error: ~p"
  3681. "~n Call stack: ~w",
  3682. [Mod, EC, Msg, AE, CS]);
  3683. {Mod, [EC, Msg], AE} when is_atom(Mod) andalso
  3684. is_list(EC) andalso
  3685. is_tuple(Msg) ->
  3686. io_lib:format("~n Encode module: ~w"
  3687. "~n Encode config: ~w"
  3688. "~n Message part: ~p"
  3689. "~n Actual error: ~p",
  3690. [Mod, EC, Msg, AE]);
  3691. Error ->
  3692. io_lib:format("~n ~w", [Error])
  3693. end,
  3694. lists:flatten(FS).
  3695. %% Presumably the user would return immediately (with {pending, Data}) if it
  3696. %% knows or suspects a request to take a long time to process.
  3697. %% For this reason we assume that handling a resent request
  3698. %% could not have caused an update of the pending limit counter.
  3699. maybe_send_pending(#conn_data{sent_pending_limit = Limit} = ConnData,
  3700. TransId) ->
  3701. case check_and_maybe_incr_pending_limit(Limit, sent, TransId) of
  3702. ok ->
  3703. send_pending(ConnData);
  3704. error ->
  3705. SendReply = send_pending_limit_error(ConnData),
  3706. {aborted, SendReply};
  3707. aborted ->
  3708. {aborted, ignore}
  3709. end.
  3710. send_pending(#conn_data{serial = Serial,
  3711. trans_req = true,
  3712. trans_sender = Pid}) ->
  3713. megaco_trans_sender:send_pending(Pid, Serial);
  3714. send_pending(#conn_data{serial = Serial} = CD) ->
  3715. %% Encapsule the transaction result into a pending message
  3716. TP = #'TransactionPending'{transactionId = Serial},
  3717. Body = {transactions, [{transactionPending, TP}]},
  3718. megaco_messenger_misc:send_body(CD, "send trans pending", Body).
  3719. maybe_send_ack('NULL', #conn_data{serial = Serial,
  3720. trans_ack = true,
  3721. trans_sender = Pid}) ->
  3722. megaco_trans_sender:send_ack_now(Pid, Serial);
  3723. maybe_send_ack('NULL', CD) ->
  3724. send_ack(CD);
  3725. maybe_send_ack(_, #conn_data{auto_ack = false}) ->
  3726. ignore;
  3727. maybe_send_ack(_, #conn_data{serial = Serial,
  3728. trans_ack = true,
  3729. trans_sender = Pid})
  3730. when is_pid(Pid) ->
  3731. %% Send (later) via the transaction sender
  3732. megaco_trans_sender:send_ack(Pid, Serial),
  3733. ok;
  3734. maybe_send_ack(_, CD) ->
  3735. %% Send now
  3736. send_ack(CD).
  3737. send_ack(#conn_data{serial = Serial} = CD) ->
  3738. %% Encapsule the transaction result into a ack message
  3739. TRA = #'TransactionAck'{firstAck = Serial},
  3740. Body = {transactions, [{transactionResponseAck, [TRA]}]},
  3741. megaco_messenger_misc:send_body(CD, "send trans ack", Body).
  3742. send_segment_reply(#conn_data{serial = Serial} = CD, SegNo) ->
  3743. SR = #'SegmentReply'{transactionId = Serial,
  3744. segmentNumber = SegNo},
  3745. Body = {transactions, [{segmentReply, SR}]},
  3746. megaco_messenger_misc:send_body(CD, "send segment reply", Body).
  3747. send_segment_reply(#conn_data{serial = Serial} = CD, SegNo, Complete) ->
  3748. SR = #'SegmentReply'{transactionId = Serial,
  3749. segmentNumber = SegNo,
  3750. segmentationComplete = Complete},
  3751. Body = {transactions, [{segmentReply, SR}]},
  3752. megaco_messenger_misc:send_body(CD, "send segment reply", Body).
  3753. send_segment_reply_complete(CD, SegNo) ->
  3754. send_segment_reply(CD, SegNo, 'NULL').
  3755. send_pending_limit_error(ConnData) ->
  3756. ?report_pending_limit_exceeded(ConnData),
  3757. Code = ?megaco_number_of_transactionpending_exceeded,
  3758. Reason = "Pending limit exceeded",
  3759. send_trans_error(ConnData, Code, Reason).
  3760. send_trans_error(ConnData, Code, Reason) ->
  3761. %% Encapsulate the transaction error into a reply message
  3762. ED = #'ErrorDescriptor'{errorCode = Code, errorText = Reason},
  3763. Serial = ConnData#conn_data.serial,
  3764. %% Version = ConnData#conn_data.protocol_version,
  3765. TransRes = {transactionError, ED},
  3766. TR = #megaco_transaction_reply{transactionId = Serial,
  3767. transactionResult = TransRes},
  3768. TR2 = megaco_messenger_misc:transform_transaction_reply(ConnData, TR),
  3769. Body = {transactions, [{transactionReply, TR2}]},
  3770. case megaco_messenger_misc:send_body(ConnData, "send trans error", Body) of
  3771. {error, Reason2} ->
  3772. ?report_important(ConnData,
  3773. "<ERROR> failed sending transaction error",
  3774. [Body, {error, Reason2}]),
  3775. error;
  3776. _ ->
  3777. ok
  3778. end.
  3779. send_message_error(ConnData, Code, Reason) ->
  3780. ED = #'ErrorDescriptor'{errorCode = Code, errorText = Reason},
  3781. Body = {messageError, ED},
  3782. case megaco_messenger_misc:send_body(ConnData, "send trans error", Body) of
  3783. {error, Reason2} ->
  3784. ?report_important(ConnData,
  3785. "<ERROR> failed sending message error",
  3786. [Body, {error, Reason2}]),
  3787. error;
  3788. _ ->
  3789. ok
  3790. end.
  3791. cancel(ConnHandle, Reason) when is_record(ConnHandle, megaco_conn_handle) ->
  3792. case megaco_config:lookup_local_conn(ConnHandle) of
  3793. [CD] ->
  3794. megaco_config:update_conn_info(CD, cancel, true),
  3795. do_cancel(ConnHandle, Reason, CD#conn_data{cancel = true}),
  3796. megaco_config:update_conn_info(CD, cancel, false),
  3797. ok;
  3798. [] ->
  3799. ConnData = fake_conn_data(ConnHandle),
  3800. do_cancel(ConnHandle, Reason, ConnData)
  3801. end.
  3802. do_cancel(ConnHandle, Reason, ConnData) ->
  3803. ?report_trace(ConnData, "cancel", [ConnHandle, Reason]),
  3804. LocalMid = ConnHandle#megaco_conn_handle.local_mid,
  3805. RemoteMid = ConnHandle#megaco_conn_handle.remote_mid,
  3806. ReqTransIdPat = #trans_id{mid = LocalMid, _ = '_'},
  3807. ReqPat = #request{trans_id = ReqTransIdPat,
  3808. remote_mid = RemoteMid,
  3809. _ = '_'},
  3810. CancelReq = fun(Req) ->
  3811. cancel_request(ConnData, Req, Reason),
  3812. {_Type, Ref} = Req#request.timer_ref, %% OTP-4843
  3813. megaco_monitor:cancel_apply_after(Ref)
  3814. end,
  3815. Requests = megaco_monitor:match_requests(ReqPat),
  3816. lists:foreach(CancelReq, Requests),
  3817. RemoteMid = ConnHandle#megaco_conn_handle.remote_mid,
  3818. RepTransIdPat = #trans_id{mid = RemoteMid, _ = '_'}, % BUGBUG List here?
  3819. RepPat = #reply{trans_id = RepTransIdPat,
  3820. local_mid = LocalMid,
  3821. _ = '_'},
  3822. CancelRep = fun(Rep) ->
  3823. cancel_reply(ConnData, Rep, Reason)
  3824. end,
  3825. Replies = megaco_monitor:match_replies(RepPat),
  3826. lists:foreach(CancelRep, Replies),
  3827. ok.
  3828. cancel_requests(_ConnData, [], _Reason) ->
  3829. ok;
  3830. cancel_requests(ConnData, [{transactionRequest,TR}|TRs], Reason) ->
  3831. #'TransactionRequest'{transactionId = TransId0} = TR,
  3832. TransId = to_local_trans_id(ConnData#conn_data.conn_handle, TransId0),
  3833. case megaco_monitor:lookup_request(TransId) of
  3834. [] ->
  3835. ignore;
  3836. [Req] when is_record(Req, request) ->
  3837. cancel_request(ConnData, Req, Reason)
  3838. end,
  3839. cancel_requests(ConnData, TRs, Reason).
  3840. cancel_request(ConnData, Req, Reason) ->
  3841. ?report_trace(ignore, "cancel request", [Req]),
  3842. ?TC_AWAIT_CANCEL_EVENT(),
  3843. TransId = Req#request.trans_id,
  3844. Version = Req#request.version,
  3845. UserMod = Req#request.user_mod,
  3846. UserArgs = Req#request.user_args,
  3847. Action = Req#request.reply_action,
  3848. UserData = Req#request.reply_data,
  3849. UserReply = {error, Reason},
  3850. ConnData2 = ConnData#conn_data{protocol_version = Version,
  3851. user_mod = UserMod,
  3852. user_args = UserArgs,
  3853. reply_action = Action,
  3854. reply_data = UserData},
  3855. cancel_request2(ConnData2, TransId, UserReply).
  3856. cancel_request2(ConnData, TransId, UserReply) ->
  3857. megaco_monitor:delete_request(TransId),
  3858. megaco_monitor:request_lockcnt_del(TransId),
  3859. megaco_config:del_pending_counter(recv, TransId), % OTP-7189
  3860. Serial = TransId#trans_id.serial,
  3861. ConnData2 = ConnData#conn_data{serial = Serial},
  3862. return_reply(ConnData2, TransId, UserReply).
  3863. return_reply(ConnData, TransId, UserReply) ->
  3864. Extra = ?default_user_callback_extra,
  3865. return_reply(ConnData, TransId, UserReply, Extra).
  3866. return_reply(ConnData, TransId, UserReply, Extra) ->
  3867. ?report_trace(ConnData, "callback: trans reply", [UserReply]),
  3868. Version = ConnData#conn_data.protocol_version,
  3869. UserData = ConnData#conn_data.reply_data,
  3870. case ConnData#conn_data.reply_action of
  3871. call when is_pid(UserData) ->
  3872. ?report_trace(ConnData, "callback: (call) trans reply",
  3873. [UserReply]),
  3874. Pid = UserData,
  3875. Pid ! {?MODULE, TransId, Version, UserReply, Extra};
  3876. cast ->
  3877. ?report_trace(ConnData, "callback: (cast) trans reply", [UserReply]),
  3878. UserMod = ConnData#conn_data.user_mod,
  3879. UserArgs = ConnData#conn_data.user_args,
  3880. ConnHandle = ConnData#conn_data.conn_handle,
  3881. Args =
  3882. case Extra of
  3883. ?default_user_callback_extra ->
  3884. [ConnHandle, Version, UserReply, UserData | UserArgs];
  3885. _ ->
  3886. [ConnHandle, Version, UserReply, UserData, Extra | UserArgs]
  3887. end,
  3888. Res = (catch apply(UserMod, handle_trans_reply, Args)),
  3889. ?report_debug(ConnData, "return: (cast) trans reply",
  3890. [UserReply, {return, Res}]),
  3891. case Res of
  3892. ok ->
  3893. ok;
  3894. _ ->
  3895. warning_msg("transaction reply callback failed: ~w",
  3896. [Res]),
  3897. ok
  3898. end,
  3899. Res;
  3900. remote ->
  3901. ?report_trace(ConnData, "callback: (remote) trans reply", [UserReply]),
  3902. Node = UserData,
  3903. Args = [ConnData, UserReply, Extra],
  3904. rpc:cast(Node, ?MODULE, receive_reply_remote, Args)
  3905. end.
  3906. receive_reply_remote(ConnData, UserReply) ->
  3907. Extra = ?default_user_callback_extra,
  3908. receive_reply_remote(ConnData, UserReply, Extra).
  3909. receive_reply_remote(ConnData, UserReply, Extra) ->
  3910. TransId = to_local_trans_id(ConnData),
  3911. case {megaco_monitor:request_lockcnt_inc(TransId),
  3912. (catch megaco_monitor:lookup_request(TransId))} of
  3913. {Cnt, [Req]} when (Cnt =:= 1) andalso is_record(Req, request) ->
  3914. %% Don't care about Req and Rep version diff
  3915. do_receive_reply_remote(ConnData, TransId, Req, UserReply, Extra);
  3916. {Cnt, [Req]} when is_integer(Cnt) andalso is_record(Req, request) ->
  3917. %% Another process is accessing, handle as unexpected
  3918. %% (so it has a possibillity to get logged).
  3919. ?report_important(ConnData, "trans reply (no receiver)",
  3920. [{user_reply, UserReply},
  3921. {request_lockcnt, Cnt}]),
  3922. megaco_monitor:request_lockcnt_dec(TransId),
  3923. return_unexpected_trans_reply(ConnData, TransId, UserReply, Extra);
  3924. %% no counter
  3925. {_Cnt, [Req]} when is_record(Req, request) ->
  3926. %% The counter does not exist.
  3927. %% This can only mean a code upgrade raise condition.
  3928. %% That is, this request record was created before
  3929. %% this feature (the counters) was instroduced.
  3930. %% The simples solution to this is to behave exactly as
  3931. %% before, that is, pass it along, and leave it to the
  3932. %% user to figure out.
  3933. ?report_trace(ConnData,
  3934. "remote reply - "
  3935. "code upgrade raise condition",
  3936. [{user_reply, UserReply}]),
  3937. do_receive_reply_remote(ConnData, TransId, Req, UserReply, Extra);
  3938. {Cnt, _} when is_integer(Cnt) ->
  3939. ?report_trace(ConnData, "trans reply (no receiver)",
  3940. [{user_reply, UserReply}, {request_lockcnt, Cnt}]),
  3941. megaco_monitor:request_lockcnt_dec(TransId),
  3942. return_unexpected_trans_reply(ConnData, TransId, UserReply, Extra);
  3943. _ ->
  3944. ?report_trace(ConnData, "remote reply (no receiver)",
  3945. [{user_reply, UserReply}]),
  3946. return_unexpected_trans_reply(ConnData, TransId, UserReply, Extra)
  3947. end.
  3948. do_receive_reply_remote(ConnData, TransId,
  3949. #request{timer_ref = {_Type, Ref},
  3950. user_mod = UserMod,
  3951. user_args = UserArgs,
  3952. reply_action = Action,
  3953. reply_data = UserData} = _Req,
  3954. UserReply, Extra) ->
  3955. megaco_monitor:delete_request(TransId),
  3956. megaco_monitor:request_lockcnt_del(TransId),
  3957. megaco_monitor:cancel_apply_after(Ref), % OTP-4843
  3958. megaco_config:del_pending_counter(recv, TransId), % OTP-7189
  3959. ConnData2 = ConnData#conn_data{user_mod = UserMod,
  3960. user_args = UserArgs,
  3961. reply_action = Action,
  3962. reply_data = UserData},
  3963. return_reply(ConnData2, TransId, UserReply, Extra).
  3964. cancel_reply(ConnData, #reply{state = waiting_for_ack,
  3965. user_mod = UserMod,
  3966. user_args = UserArgs} = Rep, Reason) ->
  3967. ?report_trace(ignore, "cancel reply [waiting_for_ack]", [Rep]),
  3968. megaco_monitor:cancel_apply_after(Rep#reply.pending_timer_ref),
  3969. Serial = (Rep#reply.trans_id)#trans_id.serial,
  3970. ConnData2 = ConnData#conn_data{serial = Serial,
  3971. user_mod = UserMod,
  3972. user_args = UserArgs},
  3973. T = #'TransactionAck'{firstAck = Serial},
  3974. Extra = ?default_user_callback_extra,
  3975. handle_ack(ConnData2, {error, Reason}, Rep, T, Extra);
  3976. cancel_reply(_ConnData, #reply{state = aborted} = Rep, _Reason) ->
  3977. ?report_trace(ignore, "cancel reply [aborted]", [Rep]),
  3978. #reply{trans_id = TransId,
  3979. timer_ref = ReplyRef,
  3980. pending_timer_ref = PendingRef} = Rep,
  3981. megaco_monitor:delete_reply(TransId),
  3982. megaco_monitor:cancel_apply_after(ReplyRef),
  3983. megaco_monitor:cancel_apply_after(PendingRef), % Still running?
  3984. megaco_config:del_pending_counter(sent, TransId), % Still existing?
  3985. ok;
  3986. cancel_reply(_ConnData, Rep, ignore) ->
  3987. ?report_trace(ignore, "cancel reply [ignore]", [Rep]),
  3988. #reply{trans_id = TransId,
  3989. timer_ref = ReplyRef,
  3990. pending_timer_ref = PendingRef} = Rep,
  3991. megaco_monitor:delete_reply(TransId),
  3992. megaco_monitor:cancel_apply_after(ReplyRef),
  3993. megaco_monitor:cancel_apply_after(PendingRef), % Still running?
  3994. megaco_config:del_pending_counter(sent, TransId), % Still existing?
  3995. ok;
  3996. cancel_reply(_CD, _Rep, _Reason) ->
  3997. ok.
  3998. request_keep_alive_timeout(ConnHandle, TransId) ->
  3999. megaco_config:del_pending_counter(ConnHandle, TransId),
  4000. megaco_monitor:lookup_request(TransId),
  4001. ok.
  4002. request_timeout(ConnHandle, TransId) ->
  4003. ?rt1(ConnHandle, "request timeout", [TransId]),
  4004. case megaco_monitor:lookup_request(TransId) of
  4005. [] ->
  4006. request_not_found_ignore;
  4007. [Req] when is_record(Req, request) ->
  4008. case megaco_config:lookup_local_conn(ConnHandle) of
  4009. [CD] when (CD#conn_data.cancel =:= true) ->
  4010. cancel_in_progress_ignore;
  4011. [CD] ->
  4012. incNumTimerRecovery(ConnHandle),
  4013. do_request_timeout(ConnHandle, TransId, CD, Req);
  4014. [] when ConnHandle#megaco_conn_handle.remote_mid =:= preliminary_mid ->
  4015. %% There are two possibillities:
  4016. %% 1) The connection has just been upgraded from a
  4017. %% preliminary to a real connection. So this timeout
  4018. %% is just a glitch. E.g. between the removel of this
  4019. %% ConnHandle and the timer.
  4020. %% 2) The first message sent, the service-change, got no
  4021. %% reply (UDP without three-way-handshake).
  4022. %% And then the other side (MGC) sends a request,
  4023. %% which causes an auto-upgrade
  4024. request_timeout_upgraded(ConnHandle, Req);
  4025. [] ->
  4026. incNumTimerRecovery(ConnHandle),
  4027. ConnData = fake_conn_data(ConnHandle),
  4028. do_request_timeout(ConnHandle, TransId, ConnData, Req)
  4029. end
  4030. end.
  4031. request_timeout_upgraded(ConnHandle, Req) ->
  4032. CD = fake_conn_data(ConnHandle),
  4033. cancel_request(CD, Req, timeout).
  4034. do_request_timeout(ConnHandle, TransId, ConnData,
  4035. #request{curr_timer = CurrTimer} = Req) ->
  4036. ?rt1(ConnHandle, "process request timeout", [TransId, CurrTimer]),
  4037. SendHandle = Req#request.send_handle,
  4038. Version = Req#request.version,
  4039. ConnData2 = ConnData#conn_data{send_handle = SendHandle,
  4040. protocol_version = Version},
  4041. case CurrTimer of
  4042. timeout -> %%%%%%%
  4043. cancel_request(ConnData2, Req, timeout),
  4044. timeout1;
  4045. %% Restartable timer
  4046. %% (max_retries = infinity_restartable)
  4047. {_, timeout} ->
  4048. cancel_request(ConnData2, Req, timeout),
  4049. timeout2;
  4050. Timer ->
  4051. {SendOrNoSend, Data} = Req#request.bytes,
  4052. case SendOrNoSend of
  4053. send ->
  4054. case maybe_encode(ConnData2, Data) of
  4055. {ok, Bin} ->
  4056. ?report_trace(ConnData2, "re-send trans request",
  4057. [{bytes, Bin}]),
  4058. case maybe_send_message(ConnData2, true, Bin) of
  4059. ok ->
  4060. sent1_ignore;
  4061. {ok, _} ->
  4062. sent2_ignore;
  4063. {error, Reason} ->
  4064. ?report_important(ConnData2,
  4065. "<ERROR> "
  4066. "re-send trans "
  4067. "request failed",
  4068. [{bytes, Bin},
  4069. {error, Reason}])
  4070. end;
  4071. {error, Reason} ->
  4072. %% Since it was possible to encode the original
  4073. %% message this should really never happen...
  4074. ?report_important(ConnData2,
  4075. "<ERROR> "
  4076. "re-send trans request failed",
  4077. [{transaction,
  4078. Req#request.bytes},
  4079. {error, Reason}])
  4080. end;
  4081. no_send ->
  4082. not_sent_ok
  4083. end,
  4084. {WaitFor, Timer2} = megaco_timer:restart(Timer),
  4085. OptBin = opt_garb_binary(Timer2, Data),
  4086. {Type, _} = Req#request.timer_ref,
  4087. M = ?MODULE,
  4088. F = request_timeout,
  4089. A = [ConnHandle, TransId],
  4090. Ref2 = megaco_monitor:apply_after(M, F, A, WaitFor),
  4091. NewFields =
  4092. [{#request.bytes, {SendOrNoSend, OptBin}},
  4093. {#request.timer_ref, {Type, Ref2}},
  4094. {#request.curr_timer, Timer2}],
  4095. megaco_monitor:update_request_fields(TransId, NewFields), % Timing problem
  4096. {restarted, WaitFor, Timer2}
  4097. end.
  4098. maybe_encode(#conn_data{trans_req = false} = CD, {_Serial, Bin})
  4099. when is_binary(Bin) ->
  4100. Body = {transactions, [{transactionRequest, Bin}]},
  4101. megaco_messenger_misc:encode_body(CD, "encode trans request msg", Body);
  4102. maybe_encode(_CD, {_Serial, Bin} = D) when is_binary(Bin) ->
  4103. {ok, D};
  4104. maybe_encode(#conn_data{trans_req = true,
  4105. trans_sender = Pid} = CD,
  4106. #'TransactionRequest'{transactionId = Serial} = TR)
  4107. when is_pid(Pid) ->
  4108. case megaco_messenger_misc:encode_trans_request(CD, TR) of
  4109. {ok, Bin} ->
  4110. {ok, {Serial, Bin}};
  4111. Error ->
  4112. Error
  4113. end;
  4114. maybe_encode(CD, TR)
  4115. when is_record(TR, 'TransactionRequest') ->
  4116. Body = {transactions, [{transactionRequest, TR}]},
  4117. megaco_messenger_misc:encode_body(CD, "encode trans request msg", Body);
  4118. maybe_encode(_CD, Trash) ->
  4119. {error, {invalid_bin, Trash}}.
  4120. maybe_send_message(CD, Resend, Bin) when is_binary(Bin) ->
  4121. megaco_messenger_misc:send_message(CD, Resend, Bin);
  4122. maybe_send_message(#conn_data{trans_sender = Pid}, _Resend, {Serial, Bin})
  4123. when is_pid(Pid) andalso is_integer(Serial) andalso is_binary(Bin) ->
  4124. megaco_trans_sender:send_req(Pid, Serial, Bin).
  4125. reply_timeout(ConnHandle, TransId, timeout) ->
  4126. handle_reply_timer_timeout(ConnHandle, TransId);
  4127. %% This means that infinity_restartable was used for max_retries.
  4128. %% There is currently no reason to use this for the reply_timeout,
  4129. %% since there is no external event to restart the timer!
  4130. reply_timeout(ConnHandle, TransId, {_, timeout}) ->
  4131. handle_reply_timer_timeout(ConnHandle, TransId);
  4132. reply_timeout(ConnHandle, TransId, Timer) ->
  4133. ?report_trace(ConnHandle, "reply timeout", [Timer, TransId]),
  4134. case lookup_reply(undefined, TransId) of
  4135. [] ->
  4136. reply_not_found_ignore;
  4137. {Converted,
  4138. #reply{state = waiting_for_ack,
  4139. ack_action = {handle_ack, _}} = Rep} ->
  4140. case megaco_config:lookup_local_conn(ConnHandle) of
  4141. [CD] when (CD#conn_data.cancel =:= true) ->
  4142. cancel_in_progress_ignore;
  4143. [CD] when (Converted =:= true) ->
  4144. incNumTimerRecovery(ConnHandle),
  4145. %% When we did the reply record lookup, we had no
  4146. %% conn_data record, and the reply record was
  4147. %% converted. This means that the reply record
  4148. %% has no valid info about user_mod or user_args.
  4149. %% Therefor, the user_mod and user_args of the
  4150. %% conn_data record is better then nothing.
  4151. #conn_data{user_mod = UserMod,
  4152. user_args = UserArgs} = CD,
  4153. Rep2 = Rep#reply{user_mod = UserMod,
  4154. user_args = UserArgs},
  4155. do_reply_timeout(ConnHandle, TransId, CD, Timer, Rep2);
  4156. [CD] when (Converted =:= false) ->
  4157. incNumTimerRecovery(ConnHandle),
  4158. do_reply_timeout(ConnHandle, TransId, CD, Timer, Rep);
  4159. [] ->
  4160. incNumTimerRecovery(ConnHandle),
  4161. CD = fake_conn_data(ConnHandle),
  4162. do_reply_timeout(ConnHandle, TransId, CD, Timer, Rep)
  4163. end;
  4164. {Converted,
  4165. #reply{state = waiting_for_ack,
  4166. bytes = Sent} = Rep} when is_list(Sent) ->
  4167. case megaco_config:lookup_local_conn(ConnHandle) of
  4168. [ConnData] when (Converted =:= true) ->
  4169. incNumTimerRecovery(ConnHandle),
  4170. %% When we did the reply record lookup, we had no
  4171. %% conn_data record, and the reply record was
  4172. %% converted. This means that the reply record
  4173. %% has no valid info about user_mod or user_args.
  4174. %% Therefor, the user_mod and user_args of the
  4175. %% conn_data record is better then nothing.
  4176. #conn_data{user_mod = UserMod,
  4177. user_args = UserArgs} = ConnData,
  4178. Rep2 = Rep#reply{user_mod = UserMod,
  4179. user_args = UserArgs},
  4180. do_reply_timeout(ConnHandle, TransId, ConnData,
  4181. Timer, Rep2);
  4182. [ConnData] when (Converted =:= false) ->
  4183. incNumTimerRecovery(ConnHandle),
  4184. do_reply_timeout(ConnHandle, TransId, ConnData,
  4185. Timer, Rep);
  4186. [] ->
  4187. incNumTimerRecovery(ConnHandle),
  4188. ConnData = fake_conn_data(ConnHandle),
  4189. do_reply_timeout(ConnHandle, TransId, ConnData,
  4190. Timer, Rep)
  4191. end;
  4192. {_Converted,
  4193. #reply{state = waiting_for_ack} = Rep} ->
  4194. do_reply_timeout(ConnHandle, TransId, Timer, Rep);
  4195. {_Converted,
  4196. #reply{state = aborted} = Rep} ->
  4197. do_reply_timeout(ConnHandle, TransId, Timer, Rep);
  4198. _ ->
  4199. ignore
  4200. end.
  4201. do_reply_timeout(ConnHandle, TransId, ConnData, Timer,
  4202. #reply{send_handle = SH,
  4203. version = V,
  4204. bytes = Bytes} = Rep) when is_binary(Bytes) ->
  4205. %% d("do_reply_timeout -> entry with"
  4206. %% "~n ConnHandle: ~p"
  4207. %% "~n TransId: ~p"
  4208. %% "~n Timer: ~p"
  4209. %% "~n Rep: ~p"
  4210. %% "~n", [ConnHandle, TransId, Timer, Rep]),
  4211. CD = ConnData#conn_data{send_handle = SH,
  4212. protocol_version = V},
  4213. ?rt1(CD, "re-send trans reply", [{bytes, Bytes}]),
  4214. case megaco_messenger_misc:send_message(CD, true, Bytes) of
  4215. {ok, _} ->
  4216. ignore;
  4217. {error, Reason} ->
  4218. ?report_important(CD, "<ERROR> re-send trans reply failed",
  4219. [{bytes, Bytes}, {error, Reason}])
  4220. end,
  4221. do_reply_timeout(ConnHandle, TransId, Timer, Rep);
  4222. do_reply_timeout(ConnHandle, TransId, ConnData, Timer,
  4223. #reply{send_handle = SH,
  4224. version = V,
  4225. bytes = Sent} = Rep) when is_list(Sent) ->
  4226. %% d("do_reply_timeout -> entry with"
  4227. %% "~n ConnHandle: ~p"
  4228. %% "~n TransId: ~p"
  4229. %% "~n Timer: ~p"
  4230. %% "~n Rep: ~p"
  4231. %% "~n", [ConnHandle, TransId, Timer, Rep]),
  4232. CD = ConnData#conn_data{send_handle = SH,
  4233. protocol_version = V},
  4234. ReSend =
  4235. fun({SN, Bytes}) ->
  4236. ?rt1(CD, "re-send segmented trans reply",
  4237. [{segment_no, SN}, {bytes, Bytes}]),
  4238. case megaco_messenger_misc:send_message(CD, true, Bytes) of
  4239. %% ok ->
  4240. %% ignore;
  4241. {ok, _} ->
  4242. ignore;
  4243. {error, Reason} ->
  4244. ?report_important(CD,
  4245. "<ERROR> re-send segmented "
  4246. "trans reply failed",
  4247. [{segment_no, SN},
  4248. {bytes, Bytes},
  4249. {error, Reason}])
  4250. end
  4251. end,
  4252. lists:foreach(ReSend, Sent),
  4253. do_reply_timeout(ConnHandle, TransId, Timer, Rep).
  4254. do_reply_timeout(ConnHandle, TransId, Timer, #reply{bytes = Bytes}) ->
  4255. {WaitFor, Timer2} = megaco_timer:restart(Timer),
  4256. OptBin = case Bytes of
  4257. Bin when is_binary(Bin) ->
  4258. opt_garb_binary(Timer2, Bin);
  4259. Sent when is_list(Sent) ->
  4260. Garb = fun(Bin) -> opt_garb_binary(Timer2, Bin) end,
  4261. [{SN, Garb(Bin)} || {SN, Bin} <- Sent]
  4262. end,
  4263. M = ?MODULE,
  4264. F = reply_timeout,
  4265. A = [ConnHandle, TransId, Timer2],
  4266. Ref2 = megaco_monitor:apply_after(M, F, A, WaitFor),
  4267. NewFields =
  4268. [{#reply.bytes, OptBin},
  4269. {#reply.timer_ref, Ref2}],
  4270. megaco_monitor:update_reply_fields(TransId, NewFields), % Timing problem?
  4271. {restarted, WaitFor, Timer2}.
  4272. handle_reply_timer_timeout(ConnHandle, TransId) ->
  4273. ?report_trace(ConnHandle, "handle reply timeout", [timeout, TransId]),
  4274. incNumTimerRecovery(ConnHandle),
  4275. %% OTP-4378
  4276. case lookup_reply(undefined, TransId) of
  4277. {Converted,
  4278. #reply{state = waiting_for_ack} = Rep} ->
  4279. Serial = (Rep#reply.trans_id)#trans_id.serial,
  4280. {Rep2, ConnData} =
  4281. case megaco_config:lookup_local_conn(ConnHandle) of
  4282. [ConnData0] when (Converted =:= false) ->
  4283. #reply{user_mod = UserMod,
  4284. user_args = UserArgs} = Rep,
  4285. {Rep,
  4286. ConnData0#conn_data{user_mod = UserMod,
  4287. user_args = UserArgs}};
  4288. [ConnData0] when (Converted =:= true) ->
  4289. {Rep#reply{user_mod = ConnData0#conn_data.user_mod,
  4290. user_args = ConnData0#conn_data.user_args},
  4291. ConnData0};
  4292. [] when (Converted =:= false) ->
  4293. ConnData0 = fake_conn_data(ConnHandle),
  4294. #reply{user_mod = UserMod,
  4295. user_args = UserArgs} = Rep,
  4296. {Rep,
  4297. ConnData0#conn_data{user_mod = UserMod,
  4298. user_args = UserArgs}};
  4299. [] when (Converted =:= true) ->
  4300. %% We have no valid info about user_mod and user_args
  4301. {Rep, fake_conn_data(ConnHandle)}
  4302. end,
  4303. ConnData2 = ConnData#conn_data{serial = Serial},
  4304. T = #'TransactionAck'{firstAck = Serial},
  4305. Extra = ?default_user_callback_extra,
  4306. handle_ack(ConnData2, {error, timeout}, Rep2, T, Extra);
  4307. {_Converted,
  4308. #reply{pending_timer_ref = Ref, % aborted?
  4309. bytes = SegSent}} -> % may be a binary
  4310. megaco_monitor:cancel_apply_after(Ref),
  4311. cancel_segment_timers(SegSent),
  4312. megaco_monitor:delete_reply(TransId),
  4313. megaco_config:del_pending_counter(sent, TransId);
  4314. [] ->
  4315. ignore_reply_removed
  4316. end.
  4317. %% segment_reply_timeout(ConnHandle, TransId, SN, timeout) ->
  4318. %% ?report_trace(ConnHandle, "segment reply timeout", [timeout, SN, TransId]),
  4319. %% D = fun({_, _, SegRef}) ->
  4320. %% megaco_monitor:cancel_apply_after(SegRef)
  4321. %% end,
  4322. %% incNumTimerRecovery(ConnHandle),
  4323. %% %% OTP-4378
  4324. %% case megaco_monitor:lookup_reply(TransId) of
  4325. %% [#reply{state = waiting_for_ack,
  4326. %% bytes = Sent} = Rep] ->
  4327. %% Serial = (Rep#reply.trans_id)#trans_id.serial,
  4328. %% ConnData =
  4329. %% case megaco_config:lookup_local_conn(ConnHandle) of
  4330. %% [ConnData0] ->
  4331. %% ConnData0;
  4332. %% [] ->
  4333. %% fake_conn_data(ConnHandle)
  4334. %% end,
  4335. %% ConnData2 = ConnData#conn_data{serial = Serial},
  4336. %% T = #'TransactionAck'{firstAck = Serial},
  4337. %% lists:foreach(D, Sent),
  4338. %% Extra = ?default_user_callback_extra,
  4339. %% handle_ack(ConnData2, {error, timeout}, Rep, T, Extra);
  4340. %% [#reply{pending_timer_ref = Ref,
  4341. %% bytes = Sent}] -> % aborted?
  4342. %% lists:foreach(D, Sent),
  4343. %% megaco_monitor:cancel_apply_after(Ref),
  4344. %% megaco_monitor:delete_reply(TransId),
  4345. %% megaco_config:del_pending_counter(sent, TransId);
  4346. %% [] ->
  4347. %% ignore
  4348. %% end.
  4349. %% segment_reply_timeout(ConnHandle, TransId, SN, Timer) ->
  4350. %% ?report_trace(ConnHandle, "reply timeout", [Timer, SN, TransId]),
  4351. %% %% d("reply_timeout -> entry with"
  4352. %% %% "~n ConnHandle: ~p"
  4353. %% %% "~n TransId: ~p"
  4354. %% %% "~n Timer: ~p", [ConnHandle, TransId, Timer]),
  4355. %% case megaco_monitor:lookup_reply(TransId) of
  4356. %% [] ->
  4357. %% ignore; % Trace ??
  4358. %% [#reply{state = waiting_for_ack,
  4359. %% bytes = ack_action = {handle_ack, _}} = Rep] ->
  4360. %% case megaco_config:lookup_local_conn(ConnHandle) of
  4361. %% [ConnData] ->
  4362. %% incNumTimerRecovery(ConnHandle),
  4363. %% do_reply_timeout(ConnHandle, TransId, ConnData,
  4364. %% Timer, Rep);
  4365. %% [] ->
  4366. %% incNumTimerRecovery(ConnHandle),
  4367. %% ConnData = fake_conn_data(ConnHandle),
  4368. %% do_reply_timeout(ConnHandle, TransId, ConnData,
  4369. %% Timer, Rep)
  4370. %% end;
  4371. %% [#reply{state = waiting_for_ack} = Rep] ->
  4372. %% do_reply_timeout(ConnHandle, TransId, Timer, Rep);
  4373. %% [#reply{state = aborted} = Rep] ->
  4374. %% do_reply_timeout(ConnHandle, TransId, Timer, Rep);
  4375. %% _ ->
  4376. %% ignore
  4377. %% end.
  4378. %% This clause is to catch the timers started prior to the code-upgrade
  4379. pending_timeout(#conn_data{conn_handle = CH}, TransId, Timer) ->
  4380. ?report_trace(CH, "pending timeout(1)", [Timer, TransId]),
  4381. pending_timeout(CH, TransId, Timer);
  4382. pending_timeout(ConnHandle, TransId, Timer) ->
  4383. ?report_trace(ConnHandle, "pending timeout(2)", [Timer, TransId]),
  4384. case megaco_config:lookup_local_conn(ConnHandle) of
  4385. [CD] when (CD#conn_data.cancel == true) ->
  4386. cancel_in_progress_ignore;
  4387. [CD] ->
  4388. Serial = TransId#trans_id.serial,
  4389. handle_pending_timeout(CD#conn_data{serial = Serial},
  4390. TransId, Timer);
  4391. [] ->
  4392. no_such_connection_ignore
  4393. end.
  4394. handle_pending_timeout(CD, TransId, Timer) ->
  4395. ?report_trace(CD, "handle pending timeout", []),
  4396. case lookup_reply(CD, TransId) of
  4397. {_Converted,
  4398. #reply{state = State,
  4399. handler = Pid} = Rep} when (State =:= prepare) orelse
  4400. (State =:= eval_request) ->
  4401. #conn_data{sent_pending_limit = Limit,
  4402. conn_handle = ConnHandle} = CD,
  4403. %% ------------------------------------------
  4404. %%
  4405. %% Check pending limit
  4406. %%
  4407. %% ------------------------------------------
  4408. case check_and_maybe_incr_pending_limit(Limit, sent, TransId) of
  4409. ok ->
  4410. %% ---------------------------------------------
  4411. %%
  4412. %% 1) Send pending message
  4413. %% 2) Possibly restart the pending timer
  4414. %%
  4415. %% ---------------------------------------------
  4416. send_pending(CD),
  4417. case Timer of
  4418. timeout ->
  4419. %% We are done
  4420. incNumTimerRecovery(ConnHandle),
  4421. timeout1;
  4422. {_, timeout} ->
  4423. %% We are done
  4424. incNumTimerRecovery(ConnHandle),
  4425. timeout2;
  4426. _ ->
  4427. {WaitFor, Timer2} = megaco_timer:restart(Timer),
  4428. M = ?MODULE,
  4429. F = pending_timeout,
  4430. A = [ConnHandle, TransId, Timer2],
  4431. PendingRef =
  4432. megaco_monitor:apply_after(M, F, A, WaitFor),
  4433. %% Timing problem?
  4434. megaco_monitor:update_reply_field(TransId,
  4435. #reply.pending_timer_ref,
  4436. PendingRef),
  4437. {restarted, WaitFor, Timer2}
  4438. end;
  4439. error ->
  4440. %% ------------------------------------------
  4441. %%
  4442. %% 1) Send 506 error message to other side
  4443. %% 2) Notify user
  4444. %% 3) Set reply data in aborted state
  4445. %%
  4446. %% -------------------------------------------
  4447. send_pending_limit_error(CD),
  4448. handle_request_abort_callback(CD, TransId, Pid),
  4449. %% Timing problem?
  4450. Rep2 = Rep#reply{state = aborted},
  4451. cancel_reply(CD, Rep2, aborted),
  4452. pending_limit_error;
  4453. aborted ->
  4454. %% ------------------------------------------
  4455. %%
  4456. %% Pending limit already passed
  4457. %%
  4458. %% -------------------------------------------
  4459. Rep2 = Rep#reply{state = aborted},
  4460. cancel_reply(CD, Rep2, aborted),
  4461. pending_limit_aborted
  4462. end;
  4463. [] ->
  4464. reply_not_found; % Trace ??
  4465. {_Converted,
  4466. #reply{state = waiting_for_ack}} ->
  4467. %% The reply has already been sent
  4468. %% No need for any pending trans reply
  4469. reply_has_been_sent;
  4470. {_Converted,
  4471. #reply{state = aborted} = Rep} ->
  4472. %% glitch, but cleanup just the same
  4473. cancel_reply(CD, Rep, aborted),
  4474. reply_aborted_state
  4475. end.
  4476. segment_timeout(ConnHandle, TransId, timeout = Timer) ->
  4477. ?report_trace(ConnHandle, "segment timeout", [TransId, Timer]),
  4478. incNumTimerRecovery(ConnHandle),
  4479. case megaco_monitor:lookup_request(TransId) of
  4480. [] ->
  4481. timeout_not_found_ignore;
  4482. [#request{seg_recv = Segs} = Req] ->
  4483. ConnData =
  4484. case megaco_config:lookup_local_conn(ConnHandle) of
  4485. [ConnData0] ->
  4486. ConnData0;
  4487. [] ->
  4488. fake_conn_data(ConnHandle)
  4489. end,
  4490. Last = lists:last(lists:sort(Segs)),
  4491. All = lists:seq(1,Last),
  4492. case All -- Segs of
  4493. [] ->
  4494. %% The last segment has just arrived, ignore
  4495. ok;
  4496. Missing ->
  4497. %% Send the error message
  4498. Code = ?megaco_segments_not_received,
  4499. Reason = missing_to_str(Missing),
  4500. send_message_error(ConnData, Code, Reason),
  4501. %% Report to the user
  4502. UserMod = Req#request.user_mod,
  4503. UserArgs = Req#request.user_args,
  4504. Action = Req#request.reply_action,
  4505. UserData = Req#request.reply_data,
  4506. UserReply = {error, {segment_timeout, Missing}},
  4507. ConnData2 = ConnData#conn_data{user_mod = UserMod,
  4508. user_args = UserArgs,
  4509. reply_action = Action,
  4510. reply_data = UserData},
  4511. return_reply(ConnData2, TransId, UserReply)
  4512. end
  4513. end;
  4514. segment_timeout(ConnHandle, TransId, Timer) ->
  4515. ?report_trace(ConnHandle, "segment timeout", [TransId, Timer]),
  4516. case megaco_monitor:lookup_request_field(TransId, #request.trans_id) of
  4517. {ok, _} ->
  4518. {WaitFor, Timer2} = megaco_timer:restart(Timer),
  4519. M = ?MODULE,
  4520. F = segment_timeout,
  4521. A = [ConnHandle, TransId, Timer2],
  4522. Ref = megaco_monitor:apply_after(M, F, A, WaitFor),
  4523. %% Timing problem?
  4524. megaco_monitor:update_request_field(TransId,
  4525. #request.seg_timer_ref,
  4526. Ref),
  4527. {restarted, WaitFor, Timer2};
  4528. _ ->
  4529. not_found_ignore
  4530. end.
  4531. %% segment_reply_timeout() ->
  4532. %% ok.
  4533. missing_to_str(Missing) ->
  4534. lists:flatten(missing_to_str2(Missing)).
  4535. missing_to_str2([X]) ->
  4536. [integer_to_list(X)];
  4537. missing_to_str2([H|T]) ->
  4538. [integer_to_list(H) , "," | missing_to_str2(T)].
  4539. return_unexpected_trans_reply(ConnData, TransId,
  4540. {actionReplies, _} = UserReply, Extra) ->
  4541. Trans = make_transaction_reply(ConnData, TransId, UserReply),
  4542. return_unexpected_trans(ConnData, Trans, Extra);
  4543. return_unexpected_trans_reply(ConnData, TransId,
  4544. {transactionError, _} = UserReply, Extra) ->
  4545. Trans = make_transaction_reply(ConnData, TransId, UserReply),
  4546. return_unexpected_trans(ConnData, Trans, Extra);
  4547. return_unexpected_trans_reply(CD, TransId, {error, Reason}, Extra) ->
  4548. ?report_important(CD, "unexpected trans reply with error",
  4549. [TransId, Reason, Extra]),
  4550. ok;
  4551. return_unexpected_trans_reply(CD, TransId, Crap, Extra) ->
  4552. ?report_important(CD, "unexpected trans reply with crap",
  4553. [TransId, Crap, Extra]),
  4554. ok.
  4555. return_unexpected_trans(ConnData, Trans) ->
  4556. Extra = ?default_user_callback_extra,
  4557. return_unexpected_trans(ConnData, Trans, Extra).
  4558. return_unexpected_trans(ConnData, Trans0, Extra) ->
  4559. UserMod = ConnData#conn_data.user_mod,
  4560. UserArgs = ConnData#conn_data.user_args,
  4561. ConnHandle = ConnData#conn_data.conn_handle,
  4562. Version = ConnData#conn_data.protocol_version,
  4563. Trans = transform_transaction_reply_enc(Version, Trans0),
  4564. Args =
  4565. case Extra of
  4566. ?default_user_callback_extra ->
  4567. [ConnHandle, Version, Trans | UserArgs];
  4568. _ ->
  4569. [ConnHandle, Version, Trans, Extra | UserArgs]
  4570. end,
  4571. Res = (catch apply(UserMod, handle_unexpected_trans, Args)),
  4572. ?report_debug(ConnData, "return: unexpected trans",
  4573. [Trans, {return, Res}]),
  4574. case Res of
  4575. ok ->
  4576. ok;
  4577. _ ->
  4578. warning_msg("unexpected transaction callback failed: ~w", [Res]),
  4579. ok
  4580. end,
  4581. Res.
  4582. %%-----------------------------------------------------------------
  4583. to_remote_trans_id(#conn_data{conn_handle = CH, serial = Serial}) ->
  4584. Mid = CH#megaco_conn_handle.remote_mid,
  4585. #trans_id{mid = Mid, serial = Serial}.
  4586. to_local_trans_id(#conn_data{conn_handle = CH, serial = Serial}) ->
  4587. Mid = CH#megaco_conn_handle.local_mid,
  4588. #trans_id{mid = Mid, serial = Serial}.
  4589. to_local_trans_id(#conn_data{conn_handle = CH}, [S|_] = Serials)
  4590. when is_integer(S) ->
  4591. Mid = CH#megaco_conn_handle.local_mid,
  4592. [#trans_id{mid = Mid, serial = Serial} || Serial <- Serials];
  4593. to_local_trans_id(#conn_data{conn_handle = CH},
  4594. [{transactionRequest, TR}|_] = TRs)
  4595. when is_record(TR, 'TransactionRequest') ->
  4596. Mid = CH#megaco_conn_handle.local_mid,
  4597. [#trans_id{mid = Mid, serial = Serial} ||
  4598. {transactionRequest,
  4599. #'TransactionRequest'{transactionId = Serial}} <- TRs];
  4600. to_local_trans_id(#megaco_conn_handle{local_mid = Mid}, Serial)
  4601. when is_integer(Serial) ->
  4602. #trans_id{mid = Mid, serial = Serial};
  4603. to_local_trans_id(#conn_data{conn_handle = CH}, Serial)
  4604. when is_integer(Serial) ->
  4605. Mid = CH#megaco_conn_handle.local_mid,
  4606. #trans_id{mid = Mid, serial = Serial}.
  4607. %%-----------------------------------------------------------------
  4608. transform_transaction_reply_dec({'TransactionReply',
  4609. TransId, IAR, TransRes}) ->
  4610. #megaco_transaction_reply{transactionId = TransId,
  4611. immAckRequired = IAR,
  4612. transactionResult = TransRes};
  4613. transform_transaction_reply_dec({'TransactionReply',
  4614. TransId, IAR, TransRes,
  4615. SegNo, SegComplete}) ->
  4616. #megaco_transaction_reply{transactionId = TransId,
  4617. immAckRequired = IAR,
  4618. transactionResult = TransRes,
  4619. segmentNumber = SegNo,
  4620. segmentationComplete = SegComplete}.
  4621. transform_transaction_reply_enc(
  4622. 3,
  4623. #megaco_transaction_reply{transactionId = TransId,
  4624. immAckRequired = IAR,
  4625. transactionResult = TransRes,
  4626. segmentNumber = SegNo,
  4627. segmentationComplete = SegComplete}) ->
  4628. {'TransactionReply', TransId, IAR, TransRes, SegNo, SegComplete};
  4629. transform_transaction_reply_enc(
  4630. Version,
  4631. #megaco_transaction_reply{transactionId = TransId,
  4632. immAckRequired = IAR,
  4633. transactionResult = TransRes})
  4634. when (Version < 3) ->
  4635. {'TransactionReply', TransId, IAR, TransRes};
  4636. transform_transaction_reply_enc(_, TR) ->
  4637. TR.
  4638. make_transaction_reply(#conn_data{protocol_version = Version},
  4639. TransId, TransRes) ->
  4640. make_transaction_reply(Version, TransId, asn1_NOVALUE, TransRes).
  4641. %% make_transaction_reply(#conn_data{protocol_version = Version},
  4642. %% TransId, IAR, TransRes) ->
  4643. %% make_transaction_reply(Version, TransId, IAR, TransRes);
  4644. make_transaction_reply(3, TransId, IAR, TransRes) ->
  4645. {'TransactionReply', TransId, IAR, TransRes, asn1_NOVALUE, asn1_NOVALUE};
  4646. make_transaction_reply(_, TransId, IAR, TransRes) ->
  4647. {'TransactionReply', TransId, IAR, TransRes}.
  4648. %%-----------------------------------------------------------------
  4649. %% This function is used as a wrapper for reply-record lookups.
  4650. %% The intention is that during upgrade, this function
  4651. %% can perform on-the-fly conversions of reply-records.
  4652. lookup_reply(CD, TransId) ->
  4653. case megaco_monitor:lookup_reply(TransId) of
  4654. [#reply{} = Rep] ->
  4655. {false, Rep};
  4656. %% Old (pre-3.13.1) version of the record => Convert to new version
  4657. [{reply, TransId,
  4658. LocalMid, State, PendingTmrRef, Handler, TimerRef,
  4659. Version, Bytes, AckAction, SendHandle, Segments}]
  4660. when is_record(CD, conn_data) ->
  4661. #conn_data{user_mod = UserMod,
  4662. user_args = UserArgs} = CD,
  4663. Rep = #reply{trans_id = TransId,
  4664. local_mid = LocalMid,
  4665. state = State,
  4666. pending_timer_ref = PendingTmrRef,
  4667. handler = Handler,
  4668. timer_ref = TimerRef,
  4669. version = Version,
  4670. bytes = Bytes,
  4671. ack_action = AckAction,
  4672. send_handle = SendHandle,
  4673. segments = Segments,
  4674. user_mod = UserMod,
  4675. user_args = UserArgs},
  4676. {true, Rep};
  4677. %% Old (pre-3.13.1) version of the record => Convert to new version
  4678. [{reply, TransId,
  4679. LocalMid, State, PendingTmrRef, Handler, TimerRef,
  4680. Version, Bytes, AckAction, SendHandle, Segments}] ->
  4681. %% ConnData is not known here, so ignore for now
  4682. Rep = #reply{trans_id = TransId,
  4683. local_mid = LocalMid,
  4684. state = State,
  4685. pending_timer_ref = PendingTmrRef,
  4686. handler = Handler,
  4687. timer_ref = TimerRef,
  4688. version = Version,
  4689. bytes = Bytes,
  4690. ack_action = AckAction,
  4691. send_handle = SendHandle,
  4692. segments = Segments},
  4693. {true, Rep};
  4694. Else ->
  4695. Else
  4696. end.
  4697. %%-----------------------------------------------------------------
  4698. %%-----------------------------------------------------------------
  4699. %% info_msg(F, A) ->
  4700. %% ?megaco_info(F, A).
  4701. warning_msg(F, A) ->
  4702. ?megaco_warning(F, A).
  4703. error_msg(F, A) ->
  4704. ?megaco_error(F, A).
  4705. %%-----------------------------------------------------------------
  4706. %% d(F) ->
  4707. %% d(F,[]).
  4708. %%
  4709. %% d(F,A) ->
  4710. %% d(true,F,A).
  4711. %% %% d(get(dbg),F,A).
  4712. %%
  4713. %% d(true,F,A) ->
  4714. %% io:format("*** [~s] ~p:~p ***"
  4715. %% "~n " ++ F ++ "~n",
  4716. %% [format_timestamp(now()), self(),?MODULE|A]);
  4717. %% d(_, _, _) ->
  4718. %% ok.
  4719. %%
  4720. %% format_timestamp({_N1, _N2, N3} = Now) ->
  4721. %% {Date, Time} = calendar:now_to_datetime(Now),
  4722. %% {YYYY,MM,DD} = Date,
  4723. %% {Hour,Min,Sec} = Time,
  4724. %% FormatDate =
  4725. %% io_lib:format("~.4w:~.2.0w:~.2.0w ~.2.0w:~.2.0w:~.2.0w 4~w",
  4726. %% [YYYY,MM,DD,Hour,Min,Sec,round(N3/1000)]),
  4727. %% lists:flatten(FormatDate).
  4728. %% Time in milli seconds
  4729. t() ->
  4730. {A,B,C} = os:timestamp(),
  4731. A*1000000000+B*1000+(C div 1000).
  4732. %%-----------------------------------------------------------------
  4733. %% Func: incNumErrors/0, incNumErrors/1, incNumTimerRecovery/1
  4734. %% Description: SNMP counter increment functions
  4735. %%-----------------------------------------------------------------
  4736. incNumErrors() ->
  4737. incNum(medGwyGatewayNumErrors).
  4738. incNumErrors(CH) ->
  4739. incNum({CH, medGwyGatewayNumErrors}).
  4740. incNumTimerRecovery(CH) ->
  4741. incNum({CH, medGwyGatewayNumTimerRecovery}).
  4742. incNum(Cnt) ->
  4743. case (catch ets:update_counter(megaco_stats, Cnt, 1)) of
  4744. {'EXIT', {badarg, _Reason}} ->
  4745. ets:insert(megaco_stats, {Cnt, 1});
  4746. Old ->
  4747. Old
  4748. end.