1 |
|
-module(mongoose_iq_handler). |
2 |
|
-include("mongoose.hrl"). |
3 |
|
-include("jlib.hrl"). |
4 |
|
|
5 |
|
%%---------------------------------------------------------------------- |
6 |
|
%% Types |
7 |
|
%%---------------------------------------------------------------------- |
8 |
|
|
9 |
|
-record(iq_handler, {handler_fn :: handler_fn(), |
10 |
|
extra :: map(), |
11 |
|
execution_method :: execution_method()}). |
12 |
|
|
13 |
|
-type t() :: #iq_handler{}. |
14 |
|
|
15 |
|
-type handler_fn() :: fun((Acc :: mongoose_acc:t(), |
16 |
|
From :: jid:jid(), |
17 |
|
To :: jid:jid(), |
18 |
|
IQ :: jlib:iq(), |
19 |
|
Extra :: map()) -> {NewAcc :: mongoose_acc:t(), |
20 |
|
IQResp :: ignore | jlib:iq()}). |
21 |
|
|
22 |
|
-type execution_method() :: no_queue | parallel | {one_queue, pid()} | {queues, [pid()]}. |
23 |
|
|
24 |
|
-type execution_type() :: no_queue | parallel | one_queue | {queues, pos_integer()}. |
25 |
|
|
26 |
|
-export_type([t/0, handler_fn/0, execution_type/0]). |
27 |
|
|
28 |
|
%%---------------------------------------------------------------------- |
29 |
|
%% API |
30 |
|
%%---------------------------------------------------------------------- |
31 |
|
|
32 |
|
-export([new/3, |
33 |
|
delete/1, |
34 |
|
process_iq/5, |
35 |
|
add_extra/2, |
36 |
|
execute_handler/5]). |
37 |
|
%% Getters |
38 |
|
-export([module/1, extra/1]). |
39 |
|
|
40 |
|
-ignore_xref([module/1]). |
41 |
|
|
42 |
|
-spec new(IQHandlerFn :: handler_fn(), |
43 |
|
Extra :: map(), |
44 |
|
ExecutionType :: execution_type()) -> t(). |
45 |
|
new(IQHandlerFn, Extra, ExecutionType) -> |
46 |
6227 |
ExecutionMethod = execution_method(ExecutionType), |
47 |
6227 |
#iq_handler{handler_fn = IQHandlerFn, extra = Extra, |
48 |
|
execution_method = ExecutionMethod}. |
49 |
|
|
50 |
|
-spec delete(IQHandler :: t()) -> ok. |
51 |
|
delete(#iq_handler{execution_method = ExecutionMethod}) -> |
52 |
5475 |
case ExecutionMethod of |
53 |
3436 |
{one_queue, Pid} -> mongoose_iq_worker:stop(Pid); |
54 |
|
{queues, Pids} -> |
55 |
:-( |
[mongoose_iq_worker:stop(Pid) || Pid <- Pids], |
56 |
:-( |
ok; |
57 |
2039 |
_ -> ok |
58 |
|
end. |
59 |
|
|
60 |
|
-spec module(t()) -> module(). |
61 |
|
module(#iq_handler{handler_fn = Fn}) -> |
62 |
:-( |
{module, Mod} = erlang:fun_info(Fn, module), |
63 |
:-( |
Mod. |
64 |
|
|
65 |
|
-spec process_iq(Handler :: t(), |
66 |
|
Acc :: mongoose_acc:t(), |
67 |
|
From :: jid:jid(), |
68 |
|
To :: jid:jid(), |
69 |
|
IQ :: jlib:iq()) -> mongoose_acc:t(). |
70 |
|
process_iq(#iq_handler{execution_method = ExecutionMethod} = Handler, |
71 |
|
Acc, From, To, IQ) -> |
72 |
3723 |
case ExecutionMethod of |
73 |
|
no_queue -> |
74 |
1393 |
?MODULE:execute_handler(Handler, Acc, From, To, IQ); |
75 |
|
{one_queue, Pid} -> |
76 |
295 |
mongoose_iq_worker:process_iq(Pid, Handler, Acc, From, To, IQ), |
77 |
295 |
Acc; |
78 |
|
{queues, Pids} -> |
79 |
:-( |
Pid = lists:nth(rand:uniform(length(Pids)), Pids), |
80 |
:-( |
mongoose_iq_worker:process_iq(Pid, Handler, Acc, From, To, IQ), |
81 |
:-( |
Acc; |
82 |
|
parallel -> |
83 |
2035 |
spawn(?MODULE, execute_handler, [Handler, Acc, From, To, IQ]), |
84 |
2035 |
Acc |
85 |
|
end. |
86 |
|
|
87 |
|
-spec extra(t()) -> map(). |
88 |
|
extra(#iq_handler{ extra = Extra }) -> |
89 |
1239 |
Extra. |
90 |
|
|
91 |
|
-spec add_extra(t(), map()) -> t(). |
92 |
|
add_extra(#iq_handler{ extra = OldExtra } = Handler, Extra) -> |
93 |
|
%% KV pairs from the OldExtra map will remain unchanged, only |
94 |
|
%% the new keys from Extra map will be added to the NewExtra map |
95 |
6227 |
NewExtra = maps:merge(Extra, OldExtra), |
96 |
6227 |
Handler#iq_handler{extra = NewExtra}. |
97 |
|
|
98 |
|
-spec execute_handler(Handler :: t(), |
99 |
|
Acc :: mongoose_acc:t(), |
100 |
|
From :: jid:jid(), |
101 |
|
To :: jid:jid(), |
102 |
|
IQ :: jlib:iq()) -> mongoose_acc:t(). |
103 |
|
execute_handler(#iq_handler{handler_fn = IQHandlerFn, extra = Extra}, |
104 |
|
Acc, From, To, IQ = #iq{sub_el = SubEl, lang = Lang}) -> |
105 |
3723 |
try IQHandlerFn(Acc, From, To, IQ, Extra) of |
106 |
|
{Acc1, ignore} -> |
107 |
15 |
Acc1; |
108 |
|
{Acc1, ResIQ} -> |
109 |
3708 |
reply(From, To, Acc1, ResIQ) |
110 |
|
catch |
111 |
|
Class:Reason:StackTrace -> |
112 |
:-( |
?LOG_WARNING(#{what => process_iq_error, from => From, to => To, iq => IQ, |
113 |
|
acc => Acc, extra => Extra, handler_function => IQHandlerFn, |
114 |
:-( |
class => Class, reason => Reason, stacktrace => StackTrace}), |
115 |
:-( |
ErrorMsg = <<"The server could not process the IQ stanza">>, |
116 |
:-( |
ErrorEl = mongoose_xmpp_errors:internal_server_error(Lang, ErrorMsg), |
117 |
:-( |
ErrorIQ = IQ#iq{type = error, sub_el = [SubEl, ErrorEl]}, |
118 |
:-( |
reply(From, To, Acc, ErrorIQ) |
119 |
|
end. |
120 |
|
|
121 |
|
-spec reply(jid:jid(), jid:jid(), mongoose_acc:t(), jlib:iq()) -> mongoose_acc:t(). |
122 |
|
reply(From, To, Acc, IQReply) -> |
123 |
3708 |
ejabberd_router:route(To, From, Acc, jlib:iq_to_xml(IQReply)). |
124 |
|
|
125 |
|
%%-------------------------------------------------------------------- |
126 |
|
%% Internal functions |
127 |
|
%%-------------------------------------------------------------------- |
128 |
|
-spec execution_method(execution_type()) -> execution_method(). |
129 |
|
execution_method(ExecutionType) -> |
130 |
6227 |
case ExecutionType of |
131 |
868 |
no_queue -> no_queue; |
132 |
1171 |
parallel -> parallel; |
133 |
|
one_queue -> |
134 |
4188 |
{ok, Pid} = mongoose_iq_worker:start(), |
135 |
4188 |
{one_queue, Pid}; |
136 |
|
{queues, N} -> |
137 |
:-( |
Pids = lists:map(fun(_) -> |
138 |
:-( |
{ok, Pid} = mongoose_iq_worker:start(), |
139 |
:-( |
Pid |
140 |
|
end, lists:seq(1, N)), |
141 |
:-( |
{queues, Pids} |
142 |
|
end. |