/lib/megaco/src/engine/megaco_messenger.erl
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
Large files files are truncated, but you can click here to view the full file
- %%
- %% %CopyrightBegin%
- %%
- %% Copyright Ericsson AB 1999-2016. All Rights Reserved.
- %%
- %% Licensed under the Apache License, Version 2.0 (the "License");
- %% you may not use this file except in compliance with the License.
- %% You may obtain a copy of the License at
- %%
- %% http://www.apache.org/licenses/LICENSE-2.0
- %%
- %% Unless required by applicable law or agreed to in writing, software
- %% distributed under the License is distributed on an "AS IS" BASIS,
- %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- %% See the License for the specific language governing permissions and
- %% limitations under the License.
- %%
- %% %CopyrightEnd%
- %%
- %%
- %%----------------------------------------------------------------------
- %% Purpose: Send and process a (sequence of) Megaco/H.248 transactions
- %%----------------------------------------------------------------------
- -module(megaco_messenger).
- %% Application internal export
- -export([
- process_received_message/4, process_received_message/5,
- receive_message/4, receive_message/5,
- connect/4, connect/5,
- disconnect/2,
- encode_actions/3,
- call/3,
- cast/3,
- cancel/2,
- request_timeout/2,
- request_keep_alive_timeout/2,
- pending_timeout/3,
- reply_timeout/3,
- segment_timeout/3,
- %% segment_reply_timeout/4,
- test_request/5,
- test_reply/5
- ]).
- %% MIB stat functions
- -export([
- get_stats/0, get_stats/1, get_stats/2,
- reset_stats/0, reset_stats/1
- ]).
- %% Misc functions
- -export([
- cleanup/2,
- which_requests/1, which_replies/1
- ]).
- %% Module internal export
- -export([
- process_received_message/6,
- handle_request/2,
- handle_long_request/2,
- connect_remote/3,
- disconnect_local/2,
- disconnect_remote/3,
- send_request_remote/4,
- receive_reply_remote/2, receive_reply_remote/3
- ]).
- -include_lib("megaco/include/megaco.hrl").
- -include("megaco_message_internal.hrl").
- -include_lib("megaco/src/app/megaco_internal.hrl").
- %% N.B. Update cancel/1 with '_' when a new field is added
- -record(request,
- {trans_id,
- remote_mid,
- timer_ref, % {short, Ref} | {long, Ref}
- init_timer,
- init_long_timer,
- curr_timer,
- version,
- bytes, % {send, Data} | {no_send, Data}, Data = binary() | tuple()
- send_handle,
- user_mod,
- user_args,
- reply_action, % call | cast
- reply_data,
- seg_recv = [], % [integer()] (received segments)
- init_seg_timer,
- seg_timer_ref,
- keep_alive_timer, % plain | integer() >= 0
- keep_alive_ref % undefined | ref()
- }).
- %% N.B. Update cancel/1 with '_' when a new field is added
- -record(reply,
- {
- trans_id,
- local_mid,
- state = prepare, % prepare | eval_request | waiting_for_ack | aborted
- pending_timer_ref,
- handler = undefined, % pid of the proc executing the callback func
- timer_ref,
- version,
- %% bytes: Sent reply data: not acknowledged
- bytes, % binary() | [{integer(), binary(), timer_ref()}]
- ack_action, % discard_ack | {handle_ack, Data}
- send_handle,
- %% segments: Not sent reply data (segments)
- segments = [], % [{integer(), binary()}]
- user_mod,
- user_args
- }).
- -record(trans_id,
- {
- mid,
- serial
- }).
- -ifdef(MEGACO_TEST_CODE).
- -define(SIM(Other,Where),
- fun(Afun,Bfun) ->
- Kfun = {?MODULE,Bfun},
- case (catch ets:lookup(megaco_test_data, Kfun)) of
- [{Kfun,Cfun}] ->
- Cfun(Afun);
- _ ->
- Afun
- end
- end(Other,Where)).
- -define(TC_AWAIT_CANCEL_EVENT(),
- case megaco_tc_controller:lookup(block_on_cancel) of
- {value, {Tag, Pid}} when is_pid(Pid) ->
- Pid ! {Tag, self()},
- receive
- {Tag, Pid} ->
- ok
- end;
- {value, {sleep, To}} when is_integer(To) andalso (To > 0) ->
- receive after To -> ok end;
- _ ->
- ok
- end).
- -define(TC_AWAIT_REPLY_EVENT(Info),
- case megaco_tc_controller:lookup(block_on_reply) of
- {value, {Tag, Pid}} when is_pid(Pid) ->
- Pid ! {Tag, self(), Info},
- receive
- {Tag, Pid} ->
- ok
- end;
- _Whatever ->
- %% io:format("Whatever: ~p~n", [Whatever]),
- ok
- end).
- -else.
- -define(SIM(Other,Where),Other).
- -define(TC_AWAIT_CANCEL_EVENT(),ok).
- -define(TC_AWAIT_REPLY_EVENT(_),ok).
- -endif.
- -define(report_pending_limit_exceeded(ConnData),
- ?report_important(ConnData, "<ERROR> pending limit exceeded", [])).
- -ifdef(megaco_extended_trace).
- -define(rt1(T,F,A),?report_trace(T,F,A)).
- -define(rt2(F,A), ?rt1(ignore,F,A)).
- -define(rt3(F), ?rt2(F,[])).
- -else.
- -define(rt1(T,F,A),ok).
- -define(rt2(F,A), ok).
- -define(rt3(F), ok).
- -endif.
- %%----------------------------------------------------------------------
- %% SNMP statistics handling functions
- %%----------------------------------------------------------------------
- %%-----------------------------------------------------------------
- %% Func: get_stats/0, get_stats/1, get_stats/2
- %% Description: Retreive statistics (counters) for TCP
- %%-----------------------------------------------------------------
- get_stats() ->
- megaco_stats:get_stats(megaco_stats).
- get_stats(ConnHandleOrCounter) ->
- megaco_stats:get_stats(megaco_stats, ConnHandleOrCounter).
- get_stats(ConnHandle, Counter) ->
- megaco_stats:get_stats(megaco_stats, ConnHandle, Counter).
- %%-----------------------------------------------------------------
- %% Func: reset_stats/0, reaet_stats/1
- %% Description: Reset statistics (counters)
- %%-----------------------------------------------------------------
- reset_stats() ->
- megaco_stats:reset_stats(megaco_stats).
- reset_stats(ConnHandleOrCounter) ->
- megaco_stats:reset_stats(megaco_stats, ConnHandleOrCounter).
- %%----------------------------------------------------------------------
- %% cleanup utility functions
- %%----------------------------------------------------------------------
- cleanup(#megaco_conn_handle{local_mid = LocalMid}, Force)
- when (Force =:= true) orelse (Force =:= false) ->
- Pat = #reply{trans_id = '$1',
- local_mid = LocalMid,
- state = '$2',
- _ = '_'},
- do_cleanup(Pat, Force);
- cleanup(LocalMid, Force)
- when (Force =:= true) orelse (Force =:= false) ->
- Pat = #reply{trans_id = '$1',
- local_mid = LocalMid,
- state = '$2',
- _ = '_'},
- do_cleanup(Pat, Force).
-
- do_cleanup(Pat, Force) ->
- Match = megaco_monitor:which_replies(Pat),
- Reps = [{V1, V2} || [V1, V2] <- Match],
- do_cleanup2(Reps, Force).
- do_cleanup2([], _) ->
- ok;
- do_cleanup2([{TransId, aborted}|T], Force = false) ->
- megaco_monitor:delete_reply(TransId),
- do_cleanup2(T, Force);
- do_cleanup2([_|T], Force = false) ->
- do_cleanup2(T, Force);
- do_cleanup2([{TransId, _State}|T], Force = true) ->
- megaco_monitor:delete_reply(TransId),
- do_cleanup2(T, Force).
-
- %%----------------------------------------------------------------------
- %% which_requests and which_replies utility functions
- %%----------------------------------------------------------------------
- which_requests(#megaco_conn_handle{local_mid = LocalMid,
- remote_mid = RemoteMid}) ->
- Pat1 = #trans_id{mid = LocalMid,
- serial = '$1', _ = '_'},
- Pat2 = #request{trans_id = Pat1,
- remote_mid = RemoteMid,
- _ = '_'},
- Match = megaco_monitor:which_requests(Pat2),
- [S || [S] <- Match];
- which_requests(LocalMid) ->
- Pat1 = #trans_id{mid = LocalMid,
- serial = '$1', _ = '_'},
- Pat2 = #request{trans_id = Pat1,
- remote_mid = '$2', _ = '_'},
- Match0 = megaco_monitor:which_requests(Pat2),
- Match1 = [{mk_ch(LocalMid, V2), V1} || [V1, V2] <- Match0],
- which_requests1(lists:sort(Match1)).
- which_requests1([]) ->
- [];
- which_requests1([{CH, S}|T]) ->
- which_requests2(T, CH, [S], []).
- which_requests2([], CH, Serials, Reqs) ->
- lists:reverse([{CH, Serials}|Reqs]);
- which_requests2([{CH, S}|T], CH, Serials, Reqs) ->
- which_requests2(T, CH, [S|Serials], Reqs);
- which_requests2([{CH1, S}|T], CH2, Serials, Reqs) ->
- which_requests2(T, CH1, [S], [{CH2, lists:reverse(Serials)}| Reqs]).
-
- which_replies(#megaco_conn_handle{local_mid = LocalMid,
- remote_mid = RemoteMid}) ->
- Pat1 = #trans_id{mid = RemoteMid,
- serial = '$1', _ = '_'},
- Pat2 = #reply{trans_id = Pat1,
- local_mid = LocalMid,
- state = '$2',
- handler = '$3', _ = '_'},
- Match = megaco_monitor:which_replies(Pat2),
- [{V1, V2, V3} || [V1, V2, V3] <- Match];
- which_replies(LocalMid) ->
- Pat1 = #trans_id{mid = '$1',
- serial = '$2', _ = '_'},
- Pat2 = #reply{trans_id = Pat1,
- local_mid = LocalMid,
- state = '$3',
- handler = '$4', _ = '_'},
- Match0 = megaco_monitor:which_replies(Pat2),
- Match1 = [{mk_ch(LocalMid,V1),{V2,V3,V4}} || [V1, V2, V3, V4] <- Match0],
- which_replies1(lists:sort(Match1)).
- which_replies1([]) ->
- [];
- which_replies1([{CH, Data}|T]) ->
- which_replies2(T, CH, [Data], []).
- which_replies2([], CH, Data, Reps) ->
- lists:reverse([{CH, Data}|Reps]);
- which_replies2([{CH, Data}|T], CH, Datas, Reps) ->
- which_replies2(T, CH, [Data|Datas], Reps);
- which_replies2([{CH1, Data}|T], CH2, Datas, Reps) ->
- which_replies2(T, CH1, [Data], [{CH2, lists:reverse(Datas)}| Reps]).
-
-
- mk_ch(LM, RM) ->
- #megaco_conn_handle{local_mid = LM, remote_mid = RM}.
-
- %%----------------------------------------------------------------------
- %% Register/unreister connections
- %%----------------------------------------------------------------------
- %% Returns {ok, ConnHandle} | {error, Reason}
- autoconnect(RH, RemoteMid, SendHandle, ControlPid, Extra)
- when is_record(RH, megaco_receive_handle) ->
- ?rt2("autoconnect", [RH, RemoteMid, SendHandle, ControlPid]),
- case megaco_config:autoconnect(RH, RemoteMid, SendHandle, ControlPid) of
- {ok, ConnData} ->
- do_connect(ConnData, Extra);
- {error, Reason} ->
- {error, Reason}
- end;
- autoconnect(BadHandle, _CH, _SendHandle, _ControlPid, _Extra) ->
- {error, {bad_receive_handle, BadHandle}}.
- connect(RH, RemoteMid, SendHandle, ControlPid) ->
- Extra = ?default_user_callback_extra,
- connect(RH, RemoteMid, SendHandle, ControlPid, Extra).
- connect(RH, RemoteMid, SendHandle, ControlPid, Extra)
- when is_record(RH, megaco_receive_handle) ->
- ?rt2("connect", [RH, RemoteMid, SendHandle, ControlPid, Extra]),
- %% The purpose of this is to have a temoporary process, to
- %% which one can set up a monitor or link and get a
- %% notification when process exits. The entire connect is
- %% done in the temporary worker process.
- %% When it exits, the connect is either successfully done
- %% or it failed.
- ConnectorFun =
- fun() ->
- ConnectResult =
- case megaco_config:connect(RH, RemoteMid,
- SendHandle, ControlPid) of
- {ok, ConnData} ->
- do_connect(ConnData, Extra);
- {error, Reason} ->
- {error, Reason}
- end,
- ?rt2("connector: connected", [self(), ConnectResult]),
- exit({result, ConnectResult})
- end,
- Flag = process_flag(trap_exit, true),
- Connector = erlang:spawn_link(ConnectorFun),
- receive
- {'EXIT', Connector, {result, ConnectResult}} ->
- ?rt2("connect result: received expected connector exit signal",
- [Connector, ConnectResult]),
- process_flag(trap_exit, Flag),
- ConnectResult;
- {'EXIT', Connector, OtherReason} ->
- ?rt2("connect exit: received unexpected connector exit signal",
- [Connector, OtherReason]),
- process_flag(trap_exit, Flag),
- {error, OtherReason}
- end;
- connect(BadHandle, _CH, _SendHandle, _ControlPid, _Extra) ->
- {error, {bad_receive_handle, BadHandle}}.
- do_connect(CD, Extra) ->
- CH = CD#conn_data.conn_handle,
- Version = CD#conn_data.protocol_version,
- UserMod = CD#conn_data.user_mod,
- UserArgs = CD#conn_data.user_args,
- Args =
- case Extra of
- ?default_user_callback_extra ->
- [CH, Version | UserArgs];
- _ ->
- [CH, Version, Extra | UserArgs]
- end,
- ?report_trace(CD, "callback: connect", [Args]),
- Res = (catch apply(UserMod, handle_connect, Args)),
- ?report_debug(CD, "return: connect", [{return, Res}]),
- case Res of
- ok ->
- ?SIM(ok, do_connect), % do_encode),
- monitor_process(CH, CD#conn_data.control_pid);
- error ->
- megaco_config:disconnect(CH),
- {error, {connection_refused, CD, error}};
- {error, ED} when is_record(ED,'ErrorDescriptor') ->
- megaco_config:disconnect(CH),
- {error, {connection_refused, CD, ED}};
- _Error ->
- warning_msg("connect callback failed: ~w", [Res]),
- megaco_config:disconnect(CH),
- {error, {connection_refused, CD, Res}}
- end.
- finish_connect(#conn_data{control_pid = ControlPid} = CD)
- when is_pid(ControlPid) andalso (node(ControlPid) =:= node()) ->
- ?rt1(CD, "finish local connect", [ControlPid]),
- do_finish_connect(CD);
- finish_connect(#conn_data{conn_handle = CH,
- control_pid = ControlPid} = CD)
- when is_pid(ControlPid) andalso (node(ControlPid) =/= node()) ->
- ?rt1(CD, "finish remote connect", [ControlPid]),
- RemoteNode = node(ControlPid),
- UserMonitorPid = whereis(megaco_monitor),
- Args = [CH, ControlPid, UserMonitorPid],
- case rpc:call(RemoteNode, ?MODULE, connect_remote, Args) of
- {ok, ControlMonitorPid} ->
- do_finish_connect(CD#conn_data{control_pid = ControlMonitorPid});
- {error, Reason} ->
- disconnect(CH, {connect_remote, Reason}),
- {error, Reason};
- {badrpc, Reason} ->
- Reason2 = {'EXIT', Reason},
- disconnect(CH, {connect_remote, Reason2}),
- {error, Reason2}
- end.
- do_finish_connect(#conn_data{conn_handle = CH,
- send_handle = SendHandle,
- control_pid = ControlPid} = CD) ->
- M = ?MODULE,
- F = disconnect_local,
- A = [CH],
- MFA = {M, F, A},
- case megaco_config:finish_connect(CH, SendHandle, ControlPid, MFA) of
- {ok, Ref} ->
- {ok, CD#conn_data{monitor_ref = Ref}};
- {error, Reason} ->
- {error, {config_update, Reason}}
- end.
- monitor_process(CH, ControlPid)
- when is_pid(ControlPid) andalso (node(ControlPid) =:= node()) ->
- M = ?MODULE,
- F = disconnect_local,
- A = [CH],
- Ref = megaco_monitor:apply_at_exit(M, F, A, ControlPid),
- case megaco_config:update_conn_info(CH, monitor_ref, Ref) of
- ok ->
- ?SIM({ok, CH}, monitor_process_local);
- {error, Reason} ->
- disconnect(CH, {config_update, Reason}),
- {error, Reason}
- end;
- monitor_process(CH, ControlPid)
- when is_pid(ControlPid) andalso (node(ControlPid) =/= node()) ->
- RemoteNode = node(ControlPid),
- UserMonitorPid = whereis(megaco_monitor),
- Args = [CH, ControlPid, UserMonitorPid],
- case rpc:call(RemoteNode, ?MODULE, connect_remote, Args) of
- {ok, ControlMonitorPid} ->
- M = ?MODULE,
- F = disconnect_local,
- A = [CH],
- Ref = megaco_monitor:apply_at_exit(M, F, A, ControlMonitorPid),
- case megaco_config:update_conn_info(CH, monitor_ref, Ref) of
- ok ->
- ?SIM({ok, CH}, monitor_process_remote);
- {error, Reason} ->
- disconnect(CH, {config_update, Reason}),
- {error, Reason}
- end;
- {error, Reason} ->
- disconnect(CH, {connect_remote, Reason}),
- {error, Reason};
- {badrpc, Reason} ->
- Reason2 = {'EXIT', Reason},
- disconnect(CH, {connect_remote, Reason2}),
- {error, Reason2}
- end;
- monitor_process(CH, undefined = _ControlPid) ->
- %% We have to do this later (setting up the monitor),
- %% when the first message arrives. The 'connected' atom is
- %% the indication for the first arriving message to finish
- %% the connect.
- %% This may be the case when an MGC performs a pre-connect
- %% in order to speed up the handling of an (expected) connecting
- %% MG.
- case megaco_config:update_conn_info(CH, monitor_ref, connected) of
- ok ->
- ?SIM({ok, CH}, monitor_process_local);
- {error, Reason} ->
- disconnect(CH, {config_update, Reason}),
- {error, Reason}
- end.
- connect_remote(CH, ControlPid, UserMonitorPid)
- when node(ControlPid) =:= node() andalso node(UserMonitorPid) =/= node() ->
- case megaco_config:lookup_local_conn(CH) of
- [_ConnData] ->
- UserNode = node(UserMonitorPid),
- M = ?MODULE,
- F = disconnect_remote,
- A = [CH, UserNode],
- Ref = megaco_monitor:apply_at_exit(M, F, A, UserMonitorPid),
- case megaco_config:connect_remote(CH, UserNode, Ref) of
- ok ->
- ControlMonitorPid = whereis(megaco_monitor),
- ?SIM({ok, ControlMonitorPid}, connect_remote);
- {error, Reason} ->
- {error, Reason}
- end;
- [] ->
- {error, {no_connection, CH}}
- end.
- cancel_apply_at_exit({connecting, _ConnectorPid}) ->
- ok;
- cancel_apply_at_exit(connected) ->
- ok;
- cancel_apply_at_exit(ControlRef) ->
- megaco_monitor:cancel_apply_at_exit(ControlRef).
- node_of_control_pid(Pid) when is_pid(Pid) ->
- node(Pid);
- node_of_control_pid(_) ->
- node().
- disconnect(ConnHandle, DiscoReason)
- when is_record(ConnHandle, megaco_conn_handle) ->
- case megaco_config:disconnect(ConnHandle) of
- {ok, ConnData, RemoteConnData} ->
- ControlRef = ConnData#conn_data.monitor_ref,
- cancel_apply_at_exit(ControlRef),
- handle_disconnect_callback(ConnData, DiscoReason),
- ControlNode = node_of_control_pid(ConnData#conn_data.control_pid),
- case ControlNode =:= node() of
- true ->
- %% Propagate to remote users
- CancelFun =
- fun(RCD) ->
- UserRef = RCD#remote_conn_data.monitor_ref,
- cancel_apply_at_exit(UserRef),
- RCD#remote_conn_data.user_node
- end,
- Nodes = lists:map(CancelFun, RemoteConnData),
- %% io:format("NODES: ~p~n", [Nodes]),
- M = ?MODULE,
- F = disconnect,
- A = [ConnHandle, DiscoReason],
- case rpc:multicall(Nodes, M, F, A) of
- {Res, []} ->
- Check = fun(ok) -> false;
- ({error, {no_connection, _CH}}) -> false;
- (_) -> true
- end,
- case lists:filter(Check, Res) of
- [] ->
- ok;
- Bad ->
- {error, {remote_disconnect_error, ConnHandle, Bad}}
- end;
- {_Res, Bad} ->
- {error, {remote_disconnect_crash, ConnHandle, Bad}}
- end;
- false when (RemoteConnData =:= []) ->
- %% Propagate to remote control node
- M = ?MODULE,
- F = disconnect_remote,
- A = [DiscoReason, ConnHandle, node()],
- case rpc:call(ControlNode, M, F, A) of
- {badrpc, Reason} ->
- {error, {'EXIT', Reason}};
- Other ->
- Other
- end
- end;
- {error, Reason} ->
- {error, Reason}
- end;
- disconnect(BadHandle, Reason) ->
- {error, {bad_conn_handle, BadHandle, Reason}}.
- disconnect_local(Reason, ConnHandle) ->
- disconnect(ConnHandle, {no_controlling_process, Reason}).
- disconnect_remote(_Reason, ConnHandle, UserNode) ->
- case megaco_config:disconnect_remote(ConnHandle, UserNode) of
- [RCD] ->
- Ref = RCD#remote_conn_data.monitor_ref,
- cancel_apply_at_exit(Ref),
- ok;
- [] ->
- {error, {no_connection, ConnHandle}}
- end.
- %%----------------------------------------------------------------------
- %% Handle incoming message
- %%----------------------------------------------------------------------
- receive_message(ReceiveHandle, ControlPid, SendHandle, Bin) ->
- Extra = ?default_user_callback_extra,
- receive_message(ReceiveHandle, ControlPid, SendHandle, Bin, Extra).
- receive_message(ReceiveHandle, ControlPid, SendHandle, Bin, Extra) ->
- Opts = [link , {min_heap_size, 5000}],
- spawn_opt(?MODULE,
- process_received_message,
- [ReceiveHandle, ControlPid, SendHandle, Bin, self(), Extra], Opts),
- ok.
- %% This function is called via the spawn_opt function with the link
- %% option, therefor the unlink before the exit.
- process_received_message(ReceiveHandle, ControlPid, SendHandle, Bin, Receiver,
- Extra) ->
- process_received_message(ReceiveHandle, ControlPid, SendHandle, Bin, Extra),
- unlink(Receiver),
- exit(normal).
- process_received_message(ReceiveHandle, ControlPid, SendHandle, Bin) ->
- Extra = ?default_user_callback_extra,
- process_received_message(ReceiveHandle, ControlPid, SendHandle, Bin, Extra).
- process_received_message(ReceiveHandle, ControlPid, SendHandle, Bin, Extra) ->
- Flag = process_flag(trap_exit, true),
- case prepare_message(ReceiveHandle, SendHandle, Bin, ControlPid, Extra) of
- {ok, ConnData, MegaMsg} when is_record(MegaMsg, 'MegacoMessage') ->
- ?rt1(ConnData, "message prepared", [MegaMsg]),
- Mess = MegaMsg#'MegacoMessage'.mess,
- case Mess#'Message'.messageBody of
- {transactions, Transactions} ->
- {AckList, ReqList} =
- prepare_trans(ConnData, Transactions, [], [], Extra),
- handle_acks(AckList, Extra),
- case ReqList of
- [] ->
- ?rt3("no transaction requests"),
- ignore;
- [Req|Reqs] when (ConnData#conn_data.threaded =:= true) ->
- ?rt3("handle requests (spawned)"),
- lists:foreach(
- fun(R) ->
- spawn(?MODULE, handle_request, [R, Extra])
- end,
- Reqs),
- handle_request(Req, Extra);
- _ ->
- ?rt3("handle requests"),
- case handle_requests(ReqList, [], Extra) of
- [] ->
- ignore;
- [LongRequest | More] ->
- lists:foreach(
- fun(LR) ->
- spawn(?MODULE, handle_long_request, [LR, Extra])
- end,
- More),
- handle_long_request(LongRequest, Extra)
- end
- end;
- {messageError, Error} ->
- handle_message_error(ConnData, Error, Extra)
- end;
- {silent_fail, ConnData, {_Code, Reason, Error}} ->
- ?report_debug(ConnData, Reason, [no_reply, Error]),
- ignore;
- {verbose_fail, ConnData, {Code, Reason, Error}} ->
- ?report_debug(ConnData, Reason, [Error]),
- send_message_error(ConnData, Code, Reason)
- end,
- process_flag(trap_exit, Flag),
- ok.
- prepare_message(RH, SH, Bin, Pid, Extra)
- when is_record(RH, megaco_receive_handle) andalso is_pid(Pid) ->
- ?report_trace(RH, "receive bytes", [{bytes, Bin}]),
- EncodingMod = RH#megaco_receive_handle.encoding_mod,
- EncodingConfig = RH#megaco_receive_handle.encoding_config,
- ProtVersion = RH#megaco_receive_handle.protocol_version,
- case (catch EncodingMod:decode_message(EncodingConfig, ProtVersion, Bin)) of
- {ok, MegaMsg} when is_record(MegaMsg, 'MegacoMessage') ->
- ?report_trace(RH, "receive message", [{message, MegaMsg}]),
- Mess = MegaMsg#'MegacoMessage'.mess,
- RemoteMid = Mess#'Message'.mId,
- Version = Mess#'Message'.version,
- LocalMid = RH#megaco_receive_handle.local_mid,
- CH = #megaco_conn_handle{local_mid = LocalMid,
- remote_mid = RemoteMid},
- case megaco_config:lookup_local_conn(CH) of
- %%
- %% Message is not of the negotiated version
- %%
- [#conn_data{protocol_version = NegVersion,
- strict_version = true} = ConnData]
- when NegVersion =/= Version ->
- %% Use already established connection,
- %% but incorrect version
- ?rt1(ConnData, "not negotiated version", [Version]),
- Error = {error, {not_negotiated_version,
- NegVersion, Version}},
- handle_syntax_error_callback(RH, ConnData,
- prepare_error(Error),
- Extra);
- [ConnData] ->
- %%
- %% Use an already established connection
- %%
- %% This *may* have been set up in the
- %% "non-official" way, so we may need to
- %% create the monitor to the control process
- %% and store the SendHandle (which is normally
- %% done when creating the "temporary" connection).
- %%
- ?rt1(ConnData, "use already established connection", []),
- ConnData2 = ConnData#conn_data{send_handle = SH,
- control_pid = Pid,
- protocol_version = Version},
- check_message_auth(CH, ConnData2, MegaMsg, Bin);
- [] ->
- %% Setup a temporary connection
- ?rt3("setup a temporary connection"),
- case autoconnect(RH, RemoteMid, SH, Pid, Extra) of
- {ok, _} ->
- do_prepare_message(RH, CH, SH, MegaMsg, Pid, Bin);
- {error, {already_connected, _ConnHandle}} ->
- do_prepare_message(RH, CH, SH, MegaMsg, Pid, Bin);
- {error, {connection_refused, ConnData, Reason}} ->
- Error = prepare_error({error, {connection_refused, Reason}}),
- {verbose_fail, ConnData, Error};
- {error, Reason} ->
- ConnData = fake_conn_data(RH, RemoteMid, SH, Pid),
- ConnData2 = ConnData#conn_data{protocol_version = Version},
- Error = prepare_error({error, Reason}),
- {verbose_fail, ConnData2, Error}
- end
- end;
- Error ->
- ?rt2("decode error", [Error]),
- ConnData = handle_decode_error(Error,
- RH, SH, Bin, Pid,
- EncodingMod,
- EncodingConfig,
- ProtVersion),
- handle_syntax_error_callback(RH, ConnData, prepare_error(Error), Extra)
- end;
- prepare_message(RH, SendHandle, _Bin, ControlPid, _Extra) ->
- ConnData = fake_conn_data(RH, SendHandle, ControlPid),
- Error = prepare_error({'EXIT', {bad_receive_handle, RH}}),
- {verbose_fail, ConnData, Error}.
- handle_decode_error({error, {unsupported_version, _}},
- #megaco_receive_handle{local_mid = LocalMid} = RH, SH,
- Bin, Pid,
- EM, EC, V) ->
- case (catch EM:decode_mini_message(EC, V, Bin)) of
- {ok, #'MegacoMessage'{mess = #'Message'{version = _Ver,
- mId = RemoteMid}}} ->
- ?rt2("erroneous message received", [SH, RemoteMid, _Ver]),
- CH = #megaco_conn_handle{local_mid = LocalMid,
- remote_mid = RemoteMid},
- incNumErrors(CH),
- %% We cannot put the version into conn-data, that will
- %% make the resulting error message impossible to sent
- %% (unsupported version)
- case megaco_config:lookup_local_conn(CH) of
- [ConnData] ->
- ?rt3("known to us"),
- ConnData#conn_data{send_handle = SH};
- [] ->
- ?rt3("unknown to us"),
- ConnData = fake_conn_data(RH, SH, Pid),
- ConnData#conn_data{conn_handle = CH}
- end;
- _ ->
- ?rt2("erroneous message received", [SH]),
- incNumErrors(),
- fake_conn_data(RH, SH, Pid)
- end;
- handle_decode_error(_,
- #megaco_receive_handle{local_mid = LocalMid} = RH, SH,
- Bin, Pid,
- EM, EC, V) ->
- case (catch EM:decode_mini_message(EC, V, Bin)) of
- {ok, #'MegacoMessage'{mess = #'Message'{version = Ver,
- mId = RemoteMid}}} ->
- ?rt2("erroneous message received", [SH, Ver, RemoteMid]),
- CH = #megaco_conn_handle{local_mid = LocalMid,
- remote_mid = RemoteMid},
- incNumErrors(CH),
- case megaco_config:lookup_local_conn(CH) of
- [ConnData] ->
- ?rt3("known to us"),
- ConnData#conn_data{send_handle = SH,
- protocol_version = Ver};
- [] ->
- ?rt3("unknown to us"),
- ConnData = fake_conn_data(RH, SH, Pid),
- ConnData#conn_data{conn_handle = CH,
- protocol_version = Ver}
- end;
- _ ->
- ?rt2("erroneous message received", [SH]),
- incNumErrors(),
- fake_conn_data(RH, SH, Pid)
- end.
- do_prepare_message(RH, CH, SendHandle, MegaMsg, ControlPid, Bin) ->
- case megaco_config:lookup_local_conn(CH) of
- [ConnData] ->
- case check_message_auth(CH, ConnData, MegaMsg, Bin) of
- {ok, ConnData2, MegaMsg} ->
- %% Let the connection be permanent
- {ok, ConnData2, MegaMsg};
- {ReplyTag, ConnData, Reason} ->
- %% Remove the temporary connection
- disconnect(CH, {bad_auth, Reason}),
- {ReplyTag, ConnData, Reason}
- end;
- [] ->
- Reason = no_connection,
- disconnect(CH, Reason),
- RemoteMid = CH#megaco_conn_handle.remote_mid,
- ConnData = fake_conn_data(RH, RemoteMid, SendHandle, ControlPid),
- Error = prepare_error({error, Reason}),
- {silent_fail, ConnData, Error}
- end.
- check_message_auth(_ConnHandle, ConnData, MegaMsg, Bin) ->
- MsgAuth = MegaMsg#'MegacoMessage'.authHeader,
- Mess = MegaMsg#'MegacoMessage'.mess,
- Version = Mess#'Message'.version,
- ConnData2 = ConnData#conn_data{protocol_version = Version},
- ConnAuth = ConnData2#conn_data.auth_data,
- ?report_trace(ConnData2, "check message auth", [{bytes, Bin}]),
- if
- (MsgAuth =:= asn1_NOVALUE) andalso (ConnAuth =:= asn1_NOVALUE) ->
- ?SIM({ok, ConnData2, MegaMsg}, check_message_auth);
- true ->
- ED = #'ErrorDescriptor'{errorCode = ?megaco_unauthorized,
- errorText = "Autentication is not supported"},
- {verbose_fail, ConnData2, prepare_error({error, ED})}
- end.
- handle_syntax_error_callback(ReceiveHandle, ConnData, PrepError, Extra) ->
- {Code, Reason, Error} = PrepError,
- ErrorDesc = #'ErrorDescriptor'{errorCode = Code, errorText = Reason},
- Version =
- case Error of
- {error, {unsupported_version, UV}} ->
- UV;
- _ ->
- ConnData#conn_data.protocol_version
- end,
- UserMod = ConnData#conn_data.user_mod,
- UserArgs = ConnData#conn_data.user_args,
- ?report_trace(ReceiveHandle, "callback: syntax error", [ErrorDesc, Error]),
- Args =
- case Extra of
- ?default_user_callback_extra ->
- [ReceiveHandle, Version, ErrorDesc | UserArgs];
- _ ->
- [ReceiveHandle, Version, ErrorDesc, Extra | UserArgs]
- end,
- Res = (catch apply(UserMod, handle_syntax_error, Args)),
- ?report_debug(ReceiveHandle, "return: syntax error",
- [{return, Res}, ErrorDesc, Error]),
- case Res of
- reply ->
- {verbose_fail, ConnData, PrepError};
- {reply,#'ErrorDescriptor'{errorCode = Code1, errorText = Reason1}} ->
- {verbose_fail, ConnData, {Code1,Reason1,Error}};
- no_reply ->
- {silent_fail, ConnData, PrepError};
- {no_reply,#'ErrorDescriptor'{errorCode=Code2,errorText=Reason2}} ->
- {silent_fail, ConnData, {Code2,Reason2,Error}}; %%% OTP-????
- _ ->
- warning_msg("syntax error callback failed: ~w", [Res]),
- {verbose_fail, ConnData, PrepError}
- end.
- fake_conn_data(CH) when is_record(CH, megaco_conn_handle) ->
- case (catch megaco_config:conn_info(CH, receive_handle)) of
- RH when is_record(RH, megaco_receive_handle) ->
- RemoteMid = CH#megaco_conn_handle.remote_mid,
- ConnData =
- fake_conn_data(RH, RemoteMid, no_send_handle, no_control_pid),
- ConnData#conn_data{conn_handle = CH};
- {'EXIT', _} ->
- UserMid = CH#megaco_conn_handle.local_mid,
- case catch megaco_config:user_info(UserMid, receive_handle) of
- {'EXIT', _} -> % No such user
- #conn_data{conn_handle = CH,
- serial = undefined_serial,
- control_pid = no_control_pid,
- monitor_ref = undefined_monitor_ref,
- send_mod = no_send_mod,
- send_handle = no_send_handle,
- encoding_mod = no_encoding_mod,
- encoding_config = no_encoding_config,
- reply_action = undefined,
- sent_pending_limit = infinity,
- recv_pending_limit = infinity};
- RH ->
- ConnData =
- fake_conn_data(RH, no_send_handle, no_control_pid),
- ConnData#conn_data{conn_handle = CH}
- end
- end.
- fake_conn_data(RH, SendHandle, ControlPid) ->
- fake_conn_data(RH, unknown_remote_mid, SendHandle, ControlPid).
- fake_conn_data(RH, RemoteMid, SendHandle, ControlPid) ->
- case catch megaco_config:init_conn_data(RH, RemoteMid, SendHandle, ControlPid) of
- {'EXIT', _} -> % No such user
- fake_user_data(RH, RemoteMid, SendHandle, ControlPid);
- ConnData ->
- ConnData
- end.
- fake_user_data(RH, RemoteMid, SendHandle, ControlPid) ->
- LocalMid = RH#megaco_receive_handle.local_mid,
- RH2 = RH#megaco_receive_handle{local_mid = default},
- case catch megaco_config:init_conn_data(RH2, RemoteMid, SendHandle, ControlPid) of
- {'EXIT', _} -> % Application stopped?
- ConnHandle = #megaco_conn_handle{local_mid = LocalMid,
- remote_mid = RemoteMid},
- EncodingMod = RH#megaco_receive_handle.encoding_mod,
- EncodingConfig = RH#megaco_receive_handle.encoding_config,
- SendMod = RH#megaco_receive_handle.send_mod,
- #conn_data{conn_handle = ConnHandle,
- serial = undefined_serial,
- control_pid = ControlPid,
- monitor_ref = undefined_monitor_ref,
- send_mod = SendMod,
- send_handle = SendHandle,
- encoding_mod = EncodingMod,
- encoding_config = EncodingConfig,
- reply_action = undefined,
- sent_pending_limit = infinity,
- recv_pending_limit = infinity};
- ConnData ->
- ConnData
- end.
- prepare_error(Error) ->
- case Error of
- {error, ED} when is_record(ED, 'ErrorDescriptor') ->
- Code = ED#'ErrorDescriptor'.errorCode,
- Reason = ED#'ErrorDescriptor'.errorText,
- {Code, Reason, Error};
- {error, [{reason, {bad_token, [BadToken, _Acc]}, Line}]} when is_integer(Line) ->
- Reason =
- lists:flatten(
- io_lib:format("Illegal token (~p) on line ~w", [BadToken, Line])),
- Code = ?megaco_bad_request,
- {Code, Reason, Error};
- {error, [{reason, {bad_token, _}, Line}]} when is_integer(Line) ->
- Reason = lists:concat(["Illegal token on line ", Line]),
- Code = ?megaco_bad_request,
- {Code, Reason, Error};
- {error, [{reason, {Line, _ParserMod, RawReasonString}} | _]} when is_integer(Line) andalso is_list(RawReasonString) ->
- Reason =
- case RawReasonString of
- [[$s, $y, $n, $t, $a, $x | _], TokenString] ->
- lists:flatten(
- io_lib:format("Syntax error on line ~w before token ~s", [Line, TokenString]));
- _ ->
- lists:flatten(io_lib:format("Syntax error on line ~w", [Line]))
- end,
- Code = ?megaco_bad_request,
- {Code, Reason, Error};
- {error, [{reason, {Line, _, _}} | _]} when is_integer(Line) ->
- Reason = lists:concat(["Syntax error on line ", Line]),
- Code = ?megaco_bad_request,
- {Code, Reason, Error};
- {error, {connection_refused, ED}} when is_record(ED,'ErrorDescriptor') ->
- Code = ED#'ErrorDescriptor'.errorCode,
- Reason = ED#'ErrorDescriptor'.errorText,
- {Code, Reason, Error};
- {error, {connection_refused, _}} ->
- Reason = "Connection refused by user",
- Code = ?megaco_unauthorized,
- {Code, Reason, Error};
- {error, {unsupported_version, V}} ->
- Reason =
- lists:flatten(io_lib:format("Unsupported version: ~w",[V])),
- Code = ?megaco_version_not_supported,
- {Code, Reason, Error};
- {error, {not_negotiated_version, NegV, MsgV}} ->
- Reason =
- lists:flatten(
- io_lib:format("Not negotiated version: ~w [negotiated ~w]",
- [MsgV, NegV])),
- Code = ?megaco_version_not_supported,
- {Code, Reason, Error};
- {error, _} ->
- Reason = "Syntax error",
- Code = ?megaco_bad_request,
- {Code, Reason, Error};
- {ok, MegaMsg} when is_record(MegaMsg, 'MegacoMessage') ->
- Reason = "MID does not match config",
- Code = ?megaco_incorrect_identifier,
- {Code, Reason, Error};
- _ ->
- Reason = "Fatal syntax error",
- Code = ?megaco_internal_gateway_error,
- {Code, Reason, Error}
- end.
- prepare_trans(_ConnData, [], AckList, ReqList, _Extra) ->
- ?SIM({AckList, ReqList}, prepare_trans_done);
- prepare_trans(ConnData, Trans, AckList, ReqList, Extra)
- when ConnData#conn_data.monitor_ref =:= undefined_auto_monitor_ref ->
- ?rt3("prepare_trans - autoconnect"),
- %% <BUGBUG>
- %% Do we need something here, if we send more then one
- %% trans per message?
- %% </BUGBUG>
-
- %% May occur if another process has already setup a
- %% temporary connection, but the handle_connect callback
- %% function has not yet returned before the eager MG
- %% re-sends its initial service change message.
- prepare_autoconnecting_trans(ConnData, Trans, AckList, ReqList, Extra);
- prepare_trans(#conn_data{monitor_ref = connected} = ConnData,
- Trans, AckList, ReqList, Extra) ->
- ?rt3("prepare_trans - connected"),
- %%
- %% This will happen when the "MGC" user performs a "pre" connect,
- %% instead of waiting for the auto-connect (which normally
- %% happen when the MGC receives the first message from the
- %% MG).
- %%
- %%
- %% The monitor_ref will have this value when the pre-connect
- %% is complete, so we finish it here and then continue with the
- %% normal transaction prepare.
- %%
- case finish_connect(ConnData) of
- {ok, CD} ->
- prepare_normal_trans(CD, Trans, AckList, ReqList, Extra);
- {error, Reason} ->
- disconnect(ConnData#conn_data.conn_handle, Reason),
- {[], []}
- end;
- prepare_trans(#conn_data{monitor_ref = {connecting, _}} = _ConnData,
- _Trans, _AckList, _ReqList, _Extra) ->
-
- ?rt3("prepare_trans - connecting"),
- %%
- %% This will happen when the "MGC" user performs a "pre" connect,
- %% instead of waiting for the auto-connect (which normally
- %% happen when the MGC receives the first message from the
- %% MG).
- %%
- %%
- %% The monitor_ref will have this value when the pre-connect
- %% is in progress. We drop (ignore) this message and hope the
- %% other side (MG) will resend.
- %%
- %% prepare_connecting_trans(ConnData, Trans, AckList, ReqList, Extra);
- {[], []};
- prepare_trans(ConnData, Trans, AckList, ReqList, Extra) ->
- ?rt3("prepare_trans - normal"),
- %% Handle transaction in the normal case
- prepare_normal_trans(ConnData, Trans, AckList, ReqList, Extra).
- prepare_autoconnecting_trans(_ConnData, [], AckList, ReqList, _Extra) ->
- ?SIM({AckList, ReqList}, prepare_autoconnecting_trans_done);
- prepare_autoconnecting_trans(ConnData, [Trans | Rest], AckList, ReqList, Extra) ->
- ?rt1(ConnData, "[autoconnecting] prepare trans", [Trans]),
- case Trans of
- {transactionRequest, T} when is_record(T, 'TransactionRequest') ->
-
- Serial = T#'TransactionRequest'.transactionId,
- ConnData2 = ConnData#conn_data{serial = Serial},
- ?report_trace(ConnData2, "Pending handle_connect", [T]),
- %% ------------------------------------------
- %%
- %% Check pending limit
- %%
- %% ------------------------------------------
- Limit = ConnData#conn_data.sent_pending_limit,
- TransId = to_remote_trans_id(ConnData2),
- case check_and_maybe_incr_pending_limit(Limit, sent, TransId) of
- ok ->
- send_pending(ConnData2);
- error ->
- %% Pending limit:
- %% In this (granted, highly hypothetical case)
- %% we would make the user very confused if we
- %% called the abort callback function, since
- %% the request callback function has not yet
- %% been called. Alas, we skip this call here.
- send_pending_limit_error(ConnData);
- aborted ->
- ignore
- end,
- prepare_autoconnecting_trans(ConnData2, Rest, AckList, ReqList,
- Extra);
- _ ->
- prepare_autoconnecting_trans(ConnData, Rest, AckList, ReqList,
- Extra)
- end.
- %% =================================================================
- %%
- %% Note that the TransactionReply record was changed i v3 (two
- %% new fields where added), and since we don't know which version,
- %% we cannot use the record definition of TransactionReply.
- %% Instead we transform the record into our own internal format
- %% #megaco_transaction_reply{}
- %%
- %% =================================================================
- prepare_normal_trans(_ConnData, [], AckList, ReqList, _Extra) ->
- ?SIM({AckList, ReqList}, prepare_normal_trans_done);
- prepare_normal_trans(ConnData, [Trans | Rest], AckList, ReqList, Extra) ->
- ?rt1(ConnData, "prepare [normal] trans", [Trans]),
- case Trans of
- {transactionRequest, #'TransactionRequest'{transactionId = asn1_NOVALUE}} ->
- ConnData2 = ConnData#conn_data{serial = 0},
- Code = ?megaco_bad_request,
- Reason = "Syntax error in message: transaction id missing",
- send_trans_error(ConnData2, Code, Reason),
- prepare_normal_trans(ConnData2, Rest, AckList, ReqList, Extra);
- {transactionRequest, T} when is_record(T, 'TransactionRequest') ->
- Serial = T#'TransactionRequest'.transactionId,
- ConnData2 = ConnData#conn_data{serial = Serial},
- prepare_request(ConnData2, T, Rest, AckList, ReqList, Extra);
- {transactionPending, T} when is_record(T, 'TransactionPending') ->
- Serial = T#'TransactionPending'.transactionId,
- ConnData2 = ConnData#conn_data{serial = Serial},
- handle_pending(ConnData2, T, Extra),
- prepare_normal_trans(ConnData2, Rest, AckList, ReqList, Extra);
- {transactionReply, T} when is_tuple(T) andalso
- (element(1, T) == 'TransactionReply') ->
- T2 = transform_transaction_reply_dec(T),
- Serial = T2#megaco_transaction_reply.transactionId,
- ConnData2 = ConnData#conn_data{serial = Serial},
- handle_reply(ConnData2, T2, Extra),
- prepare_normal_trans(ConnData2, Rest, AckList, ReqList, Extra);
- {transactionResponseAck, List} when is_list(List) ->
- prepare_ack(ConnData, List, Rest, AckList, ReqList, Extra);
- {segmentReply, SR} when is_record(SR, 'SegmentReply') ->
- handle_segment_reply(ConnData, SR, Extra),
- prepare_normal_trans(ConnData, Rest, AckList, ReqList, Extra)
- end.
- prepare_request(ConnData, T, Rest, AckList, ReqList, Extra) ->
- ?rt2("prepare request", [T]),
- ConnHandle = ConnData#conn_data.conn_handle,
- LocalMid = ConnHandle#megaco_conn_handle.local_mid,
- TransId = to_remote_trans_id(ConnData),
- ?rt2("prepare request", [LocalMid, TransId]),
- case lookup_reply(ConnData, TransId) of
- [] ->
- ?rt3("brand new request"),
- %% Brand new request
- %% Check pending limit:
- %%
- %% We should actually check the pending limit here
- %% but since we have to do it later in the
- %% handle_request function (just before we call
- %% the handle_trans_request callback function) we
- %% can just as well wait (this is after all a very
- %% unlikely case: see function prepare_trans when
- %% monitor_ref == undefined_auto_monitor_ref).
- %%
- #conn_data{send_handle = SendHandle,
- pending_timer = InitTimer,
- protocol_version = Version,
- user_mod = UserMod,
- user_args = UserArgs} = ConnData,
- {WaitFor, CurrTimer} = megaco_timer:init(InitTimer),
- M = ?MODULE,
- F = pending_timeout,
- A = [ConnHandle, TransId, CurrTimer],
- PendingRef = megaco_monitor:apply_after(M, F, A, WaitFor),
- Rep = #reply{send_handle = SendHandle,
- trans_id = TransId,
- local_mid = LocalMid,
- pending_timer_ref = PendingRef,
- handler = self(),
- version = Version,
- user_mod = UserMod,
- user_args = UserArgs},
- case megaco_monitor:insert_reply_new(Rep) of
- true ->
- prepare_normal_trans(ConnData, Rest, AckList,
- [{ConnData, TransId, T} | ReqList],
- Extra);
- false ->
- %% Oups - someone got there before we did...
- ?report_debug(ConnData,
- "prepare request: conflicting requests",
- [TransId]),
- send_pending(ConnData),
- megaco_monitor:cancel_apply_after(PendingRef),
- prepare_normal_trans(ConnData, Rest, AckList, ReqList,
- Extra)
- end;
- %% We can ignore the Converted value here as we *know*
- %% conn-data to be correct (not faked), so even if
- %% the record was converted, it will now have correct
- %% values for user_mod and user_args.
- {_Converted,
- #reply{state = State,
- handler = Pid,
- pending_timer_ref = Ref} = Rep}
- when (State =:= prepare) orelse (State =:= eval_request) ->
- ?rt2("request resend", [State, Pid, Ref]),
- %% Pending limit:
- %% We are still preparing/evaluating the request
- %% Check if the pending limit has been exceeded...
- %% If the pending limit is _not_ exceeded then
- %% we shall send a pending (and actually restart
- %% the pending timer, but that we cannot do).
- %% Don't care about Msg and Rep version diff
- #conn_data{sent_pending_limit = Limit} = ConnData,
-
- case check_and_maybe_incr_pending_limit(Limit, sent, TransId) of
- ok ->
- %% ------------------------------------------
- %%
- %% Pending limit not exceeded
- %%
- %% 1) Increment number of pendings sent
- %% (done in the check function above)
- %% 2) Send pending message
- %% (We should really restart the pending
- %% timer, but we have no way of doing that).
- %%
- %% ------------------------------------------
- send_pending(ConnData),
- prepare_normal_trans(ConnData, Rest, AckList, ReqList,
- Extra);
- error ->
- %% -------------------------------------------
- %%
- %% Pending limit exceeded
- %%
- %% 1) Cancel pending timer
- %% 2) Send 506 error message to other side
- %% 3) Inform user (depends on state)
- %% 4) Set reply in aborted state
- %%
- %% -------------------------------------------
- %%
- %% State == eval_request:
- %% This means that the request is currently beeing
- %% evaluated by the user, and the reply timer has
- %% not yet been started.
- %% Either:
- %% a) The "other side" will resend (which will
- %% trigger a pending message send) until we pass the
- %% pending limit
- %% b) We will send pending messages (when the pending
- %% timer expire) until we pass the pending limit.
- %% In any event, we cannot delete the reply record
- %% or the pending counter in this case. Is there
- %% a risk we accumulate aborted reply records?
- %%
- %% State == prepare:
- %% The user does not know about this request
- %% so we can safely perform cleanup.
- %%
- megaco_monitor:cancel_apply_after(Ref),
- send_pending_limit_error(ConnData),
- if
- State == eval_request ->
- %%
- %% What if the user never replies?
- %% In that case we will have a record
- %% (and counters) that is never cleaned up...
- NewFields =
- [{#reply.state, aborted},
- {#reply.pending_timer_ref, undefined}],
- megaco_monitor:update_reply_fields(TransId,
- NewFields),
- handle_request_abort_callback(ConnData,
- TransId, Pid, Extra);
- true ->
- %% Since the user does not know about
- %% this call yet, it is safe to cleanup.
- %% Should we inform?
- Rep2 = Rep#reply{state = aborted},
- cancel_reply(ConnData, Rep2, aborted),
- ok
- end,
- prepare_normal_trans(ConnData, Rest, AckList, ReqList,
- Extra);
- aborted ->
- %% -------------------------------------------
- %%
- %% Pending limit already exceeded
- %%
- %% Cleanup, just to make sure:
- %% reply record & pending counter
- %%
- %% -------------------------------------------
- Rep2 = Rep#reply{state = aborted},
- cancel_reply(ConnData, Rep2, aborted),
- prepare_normal_trans(ConnData, Rest, AckList, ReqList,
- Extra)
- end;
- %% We can ignore the Converted value here as we *know*
- %% conn-data to be correct (not faked), so even if
- %% the record was converted, it will now have correct
- %% values for user_mod and user_args.
- {_Converted,
- #reply{state = waiting_for_ack,
- bytes = Bin,
- version = Version} = Rep} ->
- ?rt3("request resend when waiting for ack"),
- %% We have already sent a reply, but the receiver
- %% has obviously not got it. Resend the reply but
- %% don't restart the reply_timer.
- ConnData2 = ConnData#conn_data{protocol_version = Version},
- ?report_trace(ConnData2,
- "re-send trans reply", [T | {bytes, Bin}]),…
Large files files are truncated, but you can click here to view the full file