Ejabberd源码解读-ejabberd_router模块
来源:互联网 发布:我只是数据txt书包网 编辑:程序博客网 时间:2024/05/18 02:39
概述
ejabberd_router是ejabberd项目消息传递的重要路由模块,属于路由总线,消息通过socket传递到达ejabberd_receiver模块解析处理后,传递给ejabberd_c2s逻辑处理模块,调用ejabberd_router:route/3进行路由分发,然后再区分不同情况,选择合适的二级路由中转,最后调用ejabberd_sm:route/3分发到目的用户
详述
路由章节其实并不局限于ejabberd_router模块,涉及到多个模块,以下将根据消息路由流程,逐步查看相关模块
ejabberd_c2s逻辑模块
1 Sever=>Client handle_info/4(处理来自ejabberd_sm:route/3的消息)
handle_info/4处理ejabberd_sm模块定义类型broadcast_data()
-type broadcast_data() :: {rebind, pid(), binary()} | %%% ejabberd_c2s {item, ljid(), mod_roster:subscription()} | %%% mod_roster/mod_shared_roster {exit, binary()} | %%% mod_roster/mod_shared_roster {privacy_list, mod_privacy:userlist(), binary()} | %%% mod_privacy {blocking, unblock_all | {block | unblock, [ljid()]}}. %%% mod_blocking
handle_info({route, _From, _To, {broadcast, Data}}, StateName, StateData) -> ?DEBUG("broadcast~n~p~n", [Data]), case Data of %%%更新发送花名册 %%%订阅from_返回最后出席信息(未更新) %%%取消from_返回unavailable出席信息==>(具体查看roster_change/3) {item, IJID, ISubscription} -> fsm_next_state(StateName, roster_change(IJID, ISubscription, StateData)); %%%发送错误信息 {exit, Reason} -> Lang = StateData#state.lang, send_element(StateData, xmpp:serr_conflict(Reason, Lang)), {stop, normal, StateData}; %%%更新发送黑名单 {privacy_list, PrivList, PrivListName} -> case ejabberd_hooks:run_fold(privacy_updated_list, StateData#state.server, false, [StateData#state.privacy_list, PrivList]) of false -> fsm_next_state(StateName, StateData); NewPL -> PrivPushIQ = #iq{type = set, from = jid:remove_resource(StateData#state.jid), to = StateData#state.jid, id = <<"push", (randoms:get_string())/binary>>, sub_els = [#privacy_query{ lists = [#privacy_list{ name = PrivListName}]}]}, NewState = send_stanza(StateData, PrivPushIQ), fsm_next_state(StateName, NewState#state{privacy_list = NewPL}) end; %%%通讯阻止 {blocking, What} -> NewState = route_blocking(What, StateData), fsm_next_state(StateName, NewState); _ -> fsm_next_state(StateName, StateData) end;
handle_info/4处理常规消息
handle_info({route, From, To, Packet}, StateName, StateData) when ?is_stanza(Packet) -> %%%Pass==false,无需做后续处理(消息发送完成或已发送报错信息),若Pass==true,路由信息 {Pass, NewState} = case Packet of %%%出席信息 #presence{type = T} -> State = ejabberd_hooks:run_fold(c2s_presence_in, StateData#state.server, StateData, [{From, To, Packet}]), case T of %%%#State.pres_f 出席from(“某用户订阅了我的出席”) %%%#State.pres_t 出席to(“我订阅了某用户出席”) %%%#State.pres_a 出席(all) %%%probe出席调查 probe -> LFrom = jid:tolower(From), LBFrom = jid:remove_resource(LFrom), NewStateData = case (?SETS):is_element(LFrom, State#state.pres_a) orelse (?SETS):is_element(LBFrom, State#state.pres_a) of true -> State; false -> case (?SETS):is_element(LFrom, State#state.pres_f) of true -> A = (?SETS):add_element(LFrom, State#state.pres_a), State#state{pres_a = A}; false -> case (?SETS):is_element(LBFrom, State#state.pres_f) of true -> A = (?SETS):add_element(LBFrom, State#state.pres_a), State#state{pres_a = A}; false -> State end end end, %%%直接回复probe请求-包含最后出席信息和时间戳(若源节点(from)为自身,则不做处理) process_presence_probe(From, To, NewStateData), {false, NewStateData}; error -> NewA = ?SETS:del_element(jid:tolower(From), State#state.pres_a), {true, State#state{pres_a = NewA}}; subscribe -> SRes = is_privacy_allow(State, From, To, Packet, in), {SRes, State}; subscribed -> SRes = is_privacy_allow(State, From, To, Packet, in), {SRes, State}; unsubscribe -> SRes = is_privacy_allow(State, From, To, Packet, in), {SRes, State}; unsubscribed -> SRes = is_privacy_allow(State, From, To, Packet, in), {SRes, State}; _ -> case privacy_check_packet(State, From, To, Packet, in) of allow -> LFrom = jid:tolower(From), LBFrom = jid:remove_resource(LFrom), case (?SETS):is_element(LFrom, State#state.pres_a) orelse (?SETS):is_element(LBFrom, State#state.pres_a) of true -> {true, State}; false -> case (?SETS):is_element(LFrom, State#state.pres_f) of true -> A = (?SETS):add_element(LFrom, State#state.pres_a), {true, State#state{pres_a = A}}; false -> case (?SETS):is_element(LBFrom, State#state.pres_f) of true -> A = (?SETS):add_element( LBFrom, State#state.pres_a), {true, State#state{pres_a = A}}; false -> {true, State} end end end; deny -> {false, State} end end; %%%iq信息 #iq{type = T} -> case xmpp:has_subtag(Packet, #last{}) of %%%type==get/set true when T == get; T == set -> LFrom = jid:tolower(From), LBFrom = jid:remove_resource(LFrom), HasFromSub = ((?SETS):is_element(LFrom, StateData#state.pres_f) orelse (?SETS):is_element(LBFrom, StateData#state.pres_f)) andalso is_privacy_allow(StateData, To, From, #presence{}, out), case HasFromSub of true -> case privacy_check_packet( StateData, From, To, Packet, in) of allow -> {true, StateData}; deny -> ejabberd_router:route_error( To, From, Packet, xmpp:err_service_unavailable()), {false, StateData} end; _ -> ejabberd_router:route_error( To, From, Packet, xmpp:err_forbidden()), {false, StateData} end; %%%iq信息节type==result _ -> case privacy_check_packet(StateData, From, To, Packet, in) of allow -> {true, StateData}; deny -> ejabberd_router:route_error( To, From, Packet, xmpp:err_service_unavailable()), {false, StateData} end end; %%%消息节 #message{type = T} -> case privacy_check_packet(StateData, From, To, Packet, in) of allow -> {true, StateData}; deny -> case T of groupchat -> ok; headline -> ok; _ -> case xmpp:has_subtag(Packet, #muc_user{}) of true -> ok; false -> ejabberd_router:route_error( To, From, Packet, xmpp:err_service_unavailable()) end end, {false, StateData} end end, %%%Pass==true向客户端传递消息 if Pass -> FixedPacket0 = xmpp:set_from_to(Packet, From, To), FixedPacket = ejabberd_hooks:run_fold( user_receive_packet, NewState#state.server, FixedPacket0, [NewState, NewState#state.jid, From, To]), SentStateData = send_packet(NewState, FixedPacket), ejabberd_hooks:run(c2s_loop_debug, [{route, From, To, Packet}]), fsm_next_state(StateName, SentStateData); true -> ejabberd_hooks:run(c2s_loop_debug, [{route, From, To, Packet}]), fsm_next_state(StateName, NewState) end;
2 Client=>Server session_established2/2
-spec session_established2(xmpp_element(), state()) -> fsm_next().%%% Process packets sent by user (coming from user on c2s XMPP connection)session_established2(Pkt, StateData) -> User = StateData#state.user, Server = StateData#state.server, FromJID = StateData#state.jid, %%%#jid{user,server,resource,luser,lserver,lresource},设置ToJID为标准Jid ToJID = case xmpp:get_to(Pkt) of undefined -> jid:make(User, Server, <<"">>); J -> J end, Lang = case xmpp:get_lang(Pkt) of <<"">> -> StateData#state.lang; L -> L end, NewPkt = xmpp:set_lang(Pkt, Lang), %%%根据消息类型区别处理 NewState = case NewPkt of %%%出席信息节 #presence{} -> Presence0 = ejabberd_hooks:run_fold( c2s_update_presence, Server, NewPkt, [User, Server]), Presence = ejabberd_hooks:run_fold( user_send_packet, Server, Presence0, [StateData, FromJID, ToJID]), case ToJID of %%%ToJid目标是自己,则只需更新出席信息 #jid{user = User, server = Server, resource = <<"">>} -> ?DEBUG("presence_update(~p,~n\t~p,~n\t~p)", [FromJID, Presence, StateData]), %%% User updates his presence (non-directed presence packet) presence_update(FromJID, Presence, StateData); %%%出席信息跟踪 _ -> presence_track(FromJID, ToJID, Presence, StateData) end; %%%info/query信息节 #iq{type = T, sub_els = [El]} when T == set; T == get -> NS = xmpp:get_ns(El), %%%-define(NS_PRIVACY, <<"jabber:iq:privacy">>). %%%-define(NS_BLOCKING, <<"urn:xmpp:blocking">>). %%%-define(NS_SESSION, <<"urn:ietf:params:xml:ns:xmpp-session">>). %%%ns相关定义可自行查看ns.hrl文件(deps/xmpp/include/ns.hrl) if NS == ?NS_BLOCKING; NS == ?NS_PRIVACY -> %%%黑名单相关操作 IQ = xmpp:set_from_to(Pkt, FromJID, ToJID), process_privacy_iq(IQ, StateData); NS == ?NS_SESSION -> %%%疑似是会话建立阶段(调用ejabberd_sm:open_session函数建立会话)返回会话建立成功 %%%例:Res=<iq type='result' id='aabaa'/> Res = xmpp:make_iq_result(Pkt), send_stanza(StateData, Res); true -> NewPkt0 = ejabberd_hooks:run_fold( user_send_packet, Server, NewPkt, [StateData, FromJID, ToJID]), check_privacy_route(FromJID, StateData, FromJID, ToJID, NewPkt0) end; %%%消息节,直接判断权限,传递消息 _ -> NewPkt0 = ejabberd_hooks:run_fold( user_send_packet, Server, NewPkt, [StateData, FromJID, ToJID]), check_privacy_route(FromJID, StateData, FromJID, ToJID, NewPkt0) end, ejabberd_hooks:run(c2s_loop_debug, [{xmlstreamelement, Pkt}]), fsm_next_state(session_established, NewState).
#presence部分处理函数调用注释
%%% User updates his presence (non-directed presence packet)%%%非定向出席-spec presence_update(jid(), presence(), state()) -> state().presence_update(From, Packet, StateData) -> #presence{type = Type} = Packet, case Type of %%% 该用户下线,需向"联络列表"以及"订阅了该用户的用户"广播离线出席信息(unavailable) unavailable -> Status = xmpp:get_text(Packet#presence.status), Info = [{ip, StateData#state.ip}, {conn, StateData#state.conn}, {auth_module, StateData#state.auth_module}], ejabberd_sm:unset_presence(StateData#state.sid, StateData#state.user, StateData#state.server, StateData#state.resource, Status, Info), %%%广播出席信息 presence_broadcast(StateData, From, StateData#state.pres_a, Packet), %%%用户下线重置#state{}出席相关部分信息 StateData#state{pres_last = undefined, pres_timestamp = undefined, pres_a = (?SETS):new()}; %%%以下error-unsubscribed六种情况对用户自身发送是无效的,故不做处理 error -> StateData; probe -> StateData; subscribe -> StateData; subscribed -> StateData; unsubscribe -> StateData; unsubscribed -> StateData; _ -> %%%出席优先级(priority)-负值资源永远无法收到消息 %%%若出席优先级(priority)从负值转变为0或者正值,需发送离线消息(offline message) OldPriority = case StateData#state.pres_last of undefined -> 0; OldPresence -> get_priority_from_presence(OldPresence) end, NewPriority = get_priority_from_presence(Packet), update_priority(NewPriority, Packet, StateData), FromUnavail = (StateData#state.pres_last == undefined), ?DEBUG("from unavail = ~p~n", [FromUnavail]), NewStateData = StateData#state{pres_last = Packet, pres_timestamp = p1_time_compat:timestamp()}, %%%起始状态为离线 NewState = if FromUnavail -> ejabberd_hooks:run(user_available_hook, NewStateData#state.server, [NewStateData#state.jid]), ResentStateData = if NewPriority >= 0 -> %%%发送离线相关信息 resend_offline_messages(NewStateData), %%%发送订阅相关信息 resend_subscription_requests(NewStateData); true -> NewStateData end, %%%连接时发送广播出席信息 presence_broadcast_first(From, ResentStateData, Packet); true -> %%%更新出席信息时广播出席信息 presence_broadcast_to_trusted(NewStateData, From, NewStateData#state.pres_f, NewStateData#state.pres_a, Packet), if OldPriority < 0, NewPriority >= 0 -> resend_offline_messages(NewStateData); true -> ok end, NewStateData end, NewState end.
%%% User sends a directed presence packet%%%定向出席-spec presence_track(jid(), jid(), presence(), state()) -> state().presence_track(From, To, Packet, StateData) -> #presence{type = Type} = Packet, LTo = jid:tolower(To), User = StateData#state.user, Server = StateData#state.server, Lang = StateData#state.lang, %%%判断消息是否可达 case privacy_check_packet(StateData, From, To, Packet, out) of %%%消息不可达,发送错误信息 deny -> ErrText = <<"Your active privacy list has denied " "the routing of this stanza.">>, Err = xmpp:err_not_acceptable(ErrText, Lang), send_error(StateData, xmpp:set_from_to(Packet, From, To), Err); %%%消息可达,区分类型处理 allow when Type == subscribe; Type == subscribed; Type == unsubscribe; Type == unsubscribed -> Access = gen_mod:get_module_opt(Server, mod_roster, access, fun(A) when is_atom(A) -> A end, all), MyBareJID = jid:make(User, Server, <<"">>), case acl:match_rule(Server, Access, MyBareJID) of deny -> ErrText = <<"Denied by ACL">>, Err = xmpp:err_forbidden(ErrText, Lang), send_error(StateData, xmpp:set_from_to(Packet, From, To), Err); allow -> ejabberd_hooks:run(roster_out_subscription, Server, [User, Server, To, Type]), ejabberd_router:route(jid:remove_resource(From), To, Packet), StateData end; allow when Type == error; Type == probe -> ejabberd_router:route(From, To, Packet), StateData; allow -> ejabberd_router:route(From, To, Packet), A = case Type of available -> ?SETS:add_element(LTo, StateData#state.pres_a); unavailable -> ?SETS:del_element(LTo, StateData#state.pres_a) end, StateData#state{pres_a = A} end.
#iq/#message部分处理函数调用注释
-spec check_privacy_route(jid(), state(), jid(), jid(), stanza()) -> state().check_privacy_route(From, StateData, FromRoute, To, Packet) -> %%%判断消息是否可达 case privacy_check_packet(StateData, From, To, Packet, out) of %%%不可达,发送错误信息 deny -> Lang = StateData#state.lang, ErrText = <<"Your active privacy list has denied " "the routing of this stanza.">>, Err = xmpp:err_not_acceptable(ErrText, Lang), send_error(StateData, xmpp:set_from_to(Packet, From, To), Err); %%%可达,路由消息 allow -> ejabberd_router:route(FromRoute, To, Packet), StateData end.
以上ejabberd_c2s模块消息逻辑处理,c2s除发送消息(in)到已连接的客户端(绑定resource)可通过socket发送,其余消息(out)发送都需调用ejabberd_router:route/3进行消息路由,接下来直接进入ejbberd_router模块
-spec route(jid(), jid(), xmlel() | stanza()) -> ok.%%%跳转do_route/3route(From, To, Packet) -> case catch do_route(From, To, Packet) of {'EXIT', Reason} -> ?ERROR_MSG("~p~nwhen processing: ~p", [Reason, {From, To, Packet}]); _ -> ok end.
-spec do_route(jid(), jid(), xmlel() | xmpp_element()) -> any().do_route(OrigFrom, OrigTo, OrigPacket) -> ?DEBUG("route~n\tfrom ~p~n\tto ~p~n\tpacket " "~p~n", [OrigFrom, OrigTo, OrigPacket]), case ejabberd_hooks:run_fold(filter_packet, {OrigFrom, OrigTo, OrigPacket}, []) of {From, To, Packet} -> %%%域名 LDstDomain = To#jid.lserver, %%%读route表,查询是否存在可路由域名信息,用于确定二级路由 case mnesia:dirty_read(route, LDstDomain) of %%%路由表无相关信息,尝试ejabberd_s2s模块路由 [] -> try xmpp:decode(Packet, ?NS_CLIENT, [ignore_els]) of Pkt -> ejabberd_s2s:route(From, To, Pkt) catch _:{xmpp_codec, Why} -> log_decoding_error(From, To, Packet, Why) end; %%%查询到单条相关记录,调用do_route/4函数继续路由 [R] -> do_route(From, To, Packet, R); %%%查询到多条相关记录,根据既定规则路由 Rs -> Value = get_domain_balancing(From, To, LDstDomain), case get_component_number(LDstDomain) of undefined -> case [R || R <- Rs, node(R#route.pid) == node()] of %%%所有Pid不在当前节点 hash_term [] -> R = lists:nth(erlang:phash(Value, length(Rs)), Rs), do_route(From, To, Packet, R); %%%存在Pid在当前节点 hase_term LRs -> R = lists:nth(erlang:phash(Value, length(LRs)), LRs), do_route(From, To, Packet, R) end; _ -> %%%去重 SRs = lists:ukeysort(#route.local_hint, Rs), %%%hash R = lists:nth(erlang:phash(Value, length(SRs)), SRs), do_route(From, To, Packet, R) end end; drop -> ok end.-spec do_route(jid(), jid(), xmlel() | xmpp_element(), #route{}) -> any().%%%通过Message_Send或MFA_Call将数据包路由到二级路由模块do_route(From, To, Packet, #route{local_hint = LocalHint, pid = Pid}) when is_pid(Pid) -> try xmpp:decode(Packet, ?NS_CLIENT, [ignore_els]) of Pkt -> case LocalHint of %%%MFA_Call {apply, Module, Function} when node(Pid) == node() -> Module:Function(From, To, Pkt); %%%Message_Send _ -> Pid ! {route, From, To, Pkt} end catch error:{xmpp_codec, Why} -> log_decoding_error(From, To, Packet, Why) end;do_route(_From, _To, _Packet, _Route) -> drop.
通过ejabberd_router:route/3会进入二级路由模块
首先查看route表存储的数据(默认值)
{route,<<”irc.localhost”>>,<<”localhost”>>,<0.615.0>,undefined}
{route,<<”pubsub.localhost”>>,<<”localhost”>>,<0.580.0>,undefined}
{route,<<”conference.localhost”>>,<<”localhost”>>,<0.418.0>,undefined}
{route,<<”localhost”>>,<<”localhost”>>,<0.364.0>,{apply,ejabberd_local,route}}
整理如下:
以ejabberd_local为例(接收函数直接调用MFA和消息发送触发handle_info处理)
%%%通过Message_Send调用handle_info({route, From, To, Packet}, State) -> case catch do_route(From, To, Packet) of {'EXIT', Reason} -> ?ERROR_MSG("~p~nwhen processing: ~p", [Reason, {From, To, Packet}]); _ -> ok end, {noreply, State};
%%%直接调用MFA%%%跳转do_route/3-spec route(jid(), jid(), stanza()) -> any().route(From, To, Packet) -> case catch do_route(From, To, Packet) of {'EXIT', Reason} -> ?ERROR_MSG("~p~nwhen processing: ~p", [Reason, {From, To, Packet}]); _ -> ok end.
-spec do_route(jid(), jid(), stanza()) -> any().do_route(From, To, Packet) -> ?DEBUG("local route~n\tfrom ~p~n\tto ~p~n\tpacket " "~P~n", [From, To, Packet, 8]), Type = xmpp:get_type(Packet), %%%luser不为空,直接调用ejabberd_sm:route/3继续路由 %%%ejabberd_sm:route/3路由消息将由c2s进程处理,分发给目标用户 if To#jid.luser /= <<"">> -> ejabberd_sm:route(From, To, Packet); %%%luser为空且lresource亦为空,只有server或为空,类型为iq信息节,执行process_iq/3(例:服务发现) is_record(Packet, iq), To#jid.lresource == <<"">> -> process_iq(From, To, Packet); %%%luser为空且lresource不为空,类型为result/error,直接返回ok Type == result; Type == error -> ok; %%%luser为空且lresource不为空,其他 true -> ejabberd_hooks:run(local_send_to_resource_hook, To#jid.lserver, [From, To, Packet]) end.
-spec process_iq(jid(), jid(), iq()) -> any().process_iq(From, To, #iq{type = T, lang = Lang, sub_els = [El]} = Packet) when T == get; T == set -> XMLNS = xmpp:get_ns(El), Host = To#jid.lserver, %%%查找local_iqtable表映射关系 case ets:lookup(?IQTABLE, {XMLNS, Host}) of %%%若找到,通过gen_iq_handler:handler/7执行 [{_, Module, Function}] -> gen_iq_handler:handle(Host, Module, Function, no_queue, From, To, Packet); [{_, Module, Function, Opts}] -> gen_iq_handler:handle(Host, Module, Function, Opts, From, To, Packet); %%%未找到,路由错误信息 [] -> Txt = <<"No module is handling this query">>, Err = xmpp:err_service_unavailable(Txt, Lang), ejabberd_router:route_error(To, From, Packet, Err) end;%%%错误的请求process_iq(From, To, #iq{type = T} = Packet) when T == get; T == set -> Err = xmpp:err_bad_request(), ejabberd_router:route_error(To, From, Packet, Err);%%%result/errorprocess_iq(From, To, #iq{type = T} = Packet) when T == result; T == error -> process_iq_reply(From, To, Packet).
-spec process_iq_reply(jid(), jid(), iq()) -> any().process_iq_reply(From, To, #iq{id = ID} = IQ) -> case get_iq_callback(ID) of {ok, undefined, Function} -> Function(IQ), ok; {ok, Module, Function} -> Module:Function(From, To, IQ), ok; _ -> nothing end.
附加关联补充gen_iq_handler.erl相关内容
执行方式:
-spec handle(binary(), atom(), atom(), opts(), jid(), jid(), iq()) -> any().handle(Host, Module, Function, Opts, From, To, IQ) -> case Opts of %%%直接执行(当前包执行完毕后,其他包才能执行,不推荐) no_queue -> process_iq(Host, Module, Function, From, To, IQ); %%%Message_Send(创建一个队列,处理具有此规则的命名空间的IQ查询,推荐) {one_queue, Pid} -> Pid ! {process_iq, From, To, IQ}; %%%hash_term pid and Message_Send(创建多个单独队列处理查询) {queues, Pids} -> Pid = lists:nth(erlang:phash(p1_time_compat:unique_integer(), length(Pids)), Pids), Pid ! {process_iq, From, To, IQ}; %%%spawn临时进程,然后执行gen_qi_handler:process_iq/6 %%%可能会达到进程数上线(默认32000) parallel -> spawn(?MODULE, process_iq, [Host, Module, Function, From, To, IQ]); _ -> todo end.
%%%handle_info/4用于处理以上发送的消息handle_info({process_iq, From, To, IQ}, #state{host = Host, module = Module, function = Function} = State) -> process_iq(Host, Module, Function, From, To, IQ), {noreply, State};
process_iq(_Host, Module, Function, From, To, IQ0) -> IQ = xmpp:set_from_to(IQ0, From, To), try %%%检测模块Module是否已经加载并且是否包含一个输出的函数Function/1 ResIQ = case erlang:function_exported(Module, Function, 1) of %%%已加载,调用process_iq/3,直接MFC true -> process_iq(Module, Function, IQ); %%%未加载,调用process_iq/5 false -> process_iq(Module, Function, From, To, jlib:iq_query_info(xmpp:encode(IQ))) end, %%%若结果不等于ignore,则调换from/to位置,将结果路由出去 if ResIQ /= ignore -> ejabberd_router:route(To, From, ResIQ); true -> ok end catch E:R -> ?ERROR_MSG("failed to process iq:~n~s~nReason = ~p", [xmpp:pp(IQ), {E, {R, erlang:get_stacktrace()}}]), Txt = <<"Module failed to handle the query">>, Err = xmpp:err_internal_server_error(Txt, IQ#iq.lang), ejabberd_router:route_error(To, From, IQ, Err) end.-spec process_iq(module(), atom(), iq()) -> ignore | iq().process_iq(Module, Function, #iq{lang = Lang, sub_els = [El]} = IQ) -> try Pkt = case erlang:function_exported(Module, decode_iq_subel, 1) of true -> Module:decode_iq_subel(El); false -> xmpp:decode(El) end, Module:Function(IQ#iq{sub_els = [Pkt]}) catch error:{xmpp_codec, Why} -> Txt = xmpp:format_error(Why), xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang)) end.-spec process_iq(module(), atom(), jid(), jid(), term()) -> iq().process_iq(Module, Function, From, To, IQ) -> case Module:Function(From, To, IQ) of ignore -> ignore; ResIQ -> xmpp:decode(jlib:iq_to_xml(ResIQ), ?NS_CLIENT, [ignore_els]) end.
附加:local_iqtable部分数据展示(数据通过gen_iq_handler:add_iq_handler/6添加)
主要目的:{xmlns, host} 映射为{module, function, opts}
{{<<"http://jabber.org/protocol/commands">>,<<"irc.localhost">>}, mod_irc,process_command, {one_queue,<0.633.0>}}{{<<"http://jabber.org/protocol/commands">>,<<"localhost">>}, mod_adhoc,process_local_iq, {one_queue,<0.423.0>}}
最后再附一张图
- Ejabberd源码解读-ejabberd_router模块
- Ejabberd源码解读-ejabberd_listener模块
- Ejabberd源码解读-ejabberd_c2s模块
- Ejabberd源码解读-ejabberd_sm模块
- Ejabberd源码解读-ejabberd_hooks模块
- ejabberd模块开发
- ejabberd模块开发
- AMPS:Trace模块源码解读
- AMPS:MD5模块源码解读
- AMPS:AES模块源码解读
- AMPS:日志模块源码解读
- AMPS:Cache模块源码解读
- AMPS:定时器模块源码解读
- ejabberd 源码编译
- 【erlang】【rebar】【Elixir】【ejabberd】【模块】
- AMPS:数据库访问模块源码解读
- AMPS:MySQL数据库操作模块源码解读
- AMPS:Oracle数据库操作模块源码解读
- Ubuntu 启动activemq 报/data/activemq.pid: Permission denied 信息
- JVM学习之:调优总结 -Xms -Xmx -Xmn -Xss
- MongoDB_8天学通MongoDB——第三天 细说高级操作
- HTML转PDF工具(wkhtmltopdf)介绍,支持widows和linux
- HTTP协议详解
- Ejabberd源码解读-ejabberd_router模块
- studio 常用插件汇总
- 将不带www的域名301重定向到带www域名
- OSGI实战(1)-初识OSGI-到底什么是OSGI
- JAVA环境变量
- UDP的分析与UDP一次性能发多大的数据
- 数据分析师的“钱途”
- Cocos2d-JS 2017.1.17-代码阅读笔记(一)
- Web 内容,表现和行为分离