📄 megaco_messenger.erl
字号:
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) and 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.%% BUGBUG%% Do we need something here, if we send more then one trans per message?prepare_trans(ConnData, [Trans | Rest], AckList, ReqList) when ConnData#conn_data.monitor_ref == undefined_monitor_ref -> %% May occur if another process already has 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. 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_trans(ConnData2, Rest, AckList, ReqList); _ -> prepare_trans(ConnData, Rest, AckList, ReqList) end;prepare_trans(ConnData, [Trans | Rest], AckList, ReqList) -> ?rt1(ConnData, "prepare 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_trans(ConnData2, Rest, AckList, ReqList); {transactionRequest, T} when is_record(T, 'TransactionRequest') -> Serial = T#'TransactionRequest'.transactionId, ConnData2 = ConnData#conn_data{serial = Serial}, prepare_request(ConnData2, T, Rest, AckList, ReqList); {transactionPending, T} when is_record(T, 'TransactionPending') -> Serial = T#'TransactionPending'.transactionId, ConnData2 = ConnData#conn_data{serial = Serial}, handle_pending(ConnData2, T), prepare_trans(ConnData2, Rest, AckList, ReqList); {transactionReply, T} when is_record(T, 'TransactionReply') -> Serial = T#'TransactionReply'.transactionId, ConnData2 = ConnData#conn_data{serial = Serial}, handle_reply(ConnData2, T), prepare_trans(ConnData2, Rest, AckList, ReqList); {transactionResponseAck, List} when is_list(List) -> prepare_ack(ConnData, List, Rest, AckList, ReqList) end;prepare_trans(_ConnData, [], AckList, ReqList) -> ?SIM({AckList, ReqList}, prepare_trans_done).prepare_request(ConnData, T, Rest, AckList, ReqList) -> ?rt2("prepare request", [T]), LocalMid = (ConnData#conn_data.conn_handle)#megaco_conn_handle.local_mid, TransId = to_remote_trans_id(ConnData), ?rt2("prepare request", [LocalMid, TransId]), case megaco_monitor:lookup_reply(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_monitor_ref). %% #conn_data{send_handle = SendHandle, pending_timer = InitTimer, protocol_version = Version} = ConnData, {WaitFor, CurrTimer} = megaco_timer:init(InitTimer), M = ?MODULE, F = pending_timeout, A = [ConnData, 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}, megaco_monitor:insert_reply(Rep), prepare_trans(ConnData, Rest, AckList, [{ConnData, TransId, T} | ReqList]); [#reply{state = State, handler = Pid, pending_timer_ref = Ref} = Rep] when (State == prepare) or (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 %% ?report_trace(ignore, "still preparing/evaluating request", []), #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_trans(ConnData, Rest, AckList, ReqList); 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... Rep2 = Rep#reply{state = aborted, pending_timer_ref = undefined}, megaco_monitor:insert_reply(Rep2), handle_request_abort_callback(ConnData, TransId, Pid); 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_trans(ConnData, Rest, AckList, ReqList); 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_trans(ConnData, Rest, AckList, ReqList) end; [#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}]),
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -