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_SendMFA_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}}
整理如下:

名称 Module route_way domain local ejabberd_local Message_Send/MFA_Call localhost muc mod_muc Message_Send conference.localhost irc mod_irc Message_Send irc.localhost pubsub mod_pubsub Message_Send pubsub.localhost

以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>}}

最后再附一张图
这里写图片描述

0 0
原创粉丝点击