Skip to content
This repository was archived by the owner on May 23, 2024. It is now read-only.

Commit 2a9074f

Browse files
committed
support multiple backends by specifying a backend_ref in config
1 parent 3898e23 commit 2a9074f

File tree

3 files changed

+97
-44
lines changed

3 files changed

+97
-44
lines changed

README.md

+34-4
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,24 @@ modules:
6767
type: apns
6868
# make sure this pem file contains only one(!) certificate + key pair
6969
certfile: "/etc/ssl/private/apns_example_app.pem"
70-
# sandbox is for testing
7170
gateway: "gateway.push.apple.com"
72-
#gateway: "gateway.sandbox.push.apple.com"
71+
-
72+
type: apns
73+
# you can add more backends of each type by specifying backend_ref with unique names
74+
backend_ref: "sandbox"
75+
# make sure this pem file contains only one(!) certificate + key pair
76+
certfile: "/etc/ssl/private/apns_example_app_sandbox.pem"
77+
gateway: "gateway.sandbox.push.apple.com"
7378
-
7479
type: fcm
7580
gateway: "https://fcm.googleapis.com/fcm/send"
7681
api_key: "API_KEY"
82+
-
83+
type: fcm
84+
# you can add more backends of each type by specifying backend_ref with unique names
85+
backend_ref: "fcm2"
86+
gateway: "https://fcm.googleapis.com/fcm/send"
87+
api_key: "API_KEY_2"
7788
```
7889
7990
## Client Applications
@@ -94,15 +105,34 @@ Example (note, `to='localhost'` contain the your user's server name):
94105
node='register-push-apns'
95106
action='execute'>
96107
<x xmlns='jabber:x:data' type='submit'>
97-
<field
98-
var='token'>
108+
<field var='token'>
99109
<value>urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6uro=</value>
100110
</field>
101111
</x>
102112
</command>
103113
</iq>
104114
```
105115

116+
You need to specify a `backend_ref` field to route your subscription to a particular backend:
117+
118+
```xml
119+
<iq type='set' to='localhost' id='randomrandomrequestid2'>
120+
<command xmlns='http://jabber.org/protocol/commands'
121+
node='register-push-apns'
122+
action='execute'>
123+
<x xmlns='jabber:x:data' type='submit'>
124+
<field var='backend_ref'>
125+
<value>sandbox</value>
126+
</field>
127+
<field var='token'>
128+
<value>urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6uro=</value>
129+
</field>
130+
</x>
131+
</command>
132+
</iq>
133+
```
134+
135+
106136
See [client.py](client.py) for a reference client.
107137

108138
You can use `babababababababababababababababababababababababababababababababa` (`urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6uro=` in base64) token to test APNS.

include/mod_pushoff.hrl

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
-type bare_jid() :: {binary(), binary()}.
2-
-type backend_type() :: apns | fcm.
3-
-type backend_id() :: {binary(), backend_type()}.
2+
-type backend_ref() :: apns | fcm | {apns, binary()} | {fcm, binary()}.
3+
-type backend_id() :: {binary(), backend_ref()}.
44

55
-record(pushoff_registration, {bare_jid :: bare_jid(),
66
token :: binary(),

src/mod_pushoff.erl

+61-38
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555
-type fcm_config() :: #fcm_config{}.
5656

5757
-record(backend_config,
58-
{type :: backend_type(),
58+
{ref :: backend_ref(),
5959
config :: apns_config() | fcm_config()}).
6060

6161
-type backend_config() :: #backend_config{}.
@@ -145,18 +145,34 @@ adhoc_local_commands(Acc, _From, _To, _Request) ->
145145

146146

147147
adhoc_perform_action(<<"register-push-apns">>, #jid{lserver = LServer} = From, XData) ->
148-
case xmpp_util:get_xdata_values(<<"token">>, XData) of
149-
[Base64Token] ->
150-
case catch base64:decode(Base64Token) of
151-
{'EXIT', _} -> {error, xmpp:err_bad_request()};
152-
Token -> mod_pushoff_mnesia:register_client(From, {LServer, apns}, Token)
153-
end;
154-
_ -> {error, xmpp:err_bad_request()}
148+
BackendRef = case xmpp_util:get_xdata_values(<<"backend_ref">>, XData) of
149+
[Key] -> {apns, Key};
150+
_ -> apns
151+
end,
152+
case validate_backend_ref(LServer, BackendRef) of
153+
{error, E} -> {error, E};
154+
{ok, BackendRef} ->
155+
case xmpp_util:get_xdata_values(<<"token">>, XData) of
156+
[Base64Token] ->
157+
case catch base64:decode(Base64Token) of
158+
{'EXIT', _} -> {error, xmpp:err_bad_request()};
159+
Token -> mod_pushoff_mnesia:register_client(From, {LServer, BackendRef}, Token)
160+
end;
161+
_ -> {error, xmpp:err_bad_request()}
162+
end
155163
end;
156164
adhoc_perform_action(<<"register-push-fcm">>, #jid{lserver = LServer} = From, XData) ->
157-
case xmpp_util:get_xdata_values(<<"token">>, XData) of
158-
[AsciiToken] -> mod_pushoff_mnesia:register_client(From, {LServer, fcm}, AsciiToken);
159-
_ -> {error, xmpp:err_bad_request()}
165+
BackendRef = case xmpp_util:get_xdata_values(<<"backend_ref">>, XData) of
166+
[Key] -> {fcm, Key};
167+
_ -> fcm
168+
end,
169+
case validate_backend_ref(LServer, BackendRef) of
170+
{error, E} -> {error, E};
171+
{ok, BackendRef} ->
172+
case xmpp_util:get_xdata_values(<<"token">>, XData) of
173+
[AsciiToken] -> mod_pushoff_mnesia:register_client(From, {LServer, BackendRef}, AsciiToken);
174+
_ -> {error, xmpp:err_bad_request()}
175+
end
160176
end;
161177
adhoc_perform_action(<<"unregister-push">>, From, _) ->
162178
mod_pushoff_mnesia:unregister_client(From, undefined);
@@ -193,10 +209,10 @@ stop(Host) ->
193209
ok = ejabberd_hooks:delete(remove_user, Host, ?MODULE, remove_user, 50),
194210

195211
[begin
196-
Worker = backend_worker({Host, Type}),
212+
Worker = backend_worker({Host, Ref}),
197213
supervisor:terminate_child(ejabberd_gen_mod_sup, Worker),
198214
supervisor:delete_child(ejabberd_gen_mod_sup, Worker)
199-
end || #backend_config{type=Type} <- backend_configs(Host)],
215+
end || #backend_config{ref=Ref} <- backend_configs(Host)],
200216
ok.
201217

202218
depends(_, _) ->
@@ -205,27 +221,33 @@ depends(_, _) ->
205221
mod_opt_type(backends) -> fun ?MODULE:parse_backends/1;
206222
mod_opt_type(_) -> [backends].
207223

224+
validate_backend_ref(Host, Ref) ->
225+
case [R || #backend_config{ref=R} <- backend_configs(Host), R == Ref] of
226+
[R] -> {ok, R};
227+
_ -> {error, xmpp:err_bad_request()}
228+
end.
229+
230+
backend_ref(apns, undefined) -> apns;
231+
backend_ref(fcm, undefined) -> fcm;
232+
backend_ref(apns, K) -> {apns, K};
233+
backend_ref(fcm, K) -> {fcm, K}.
234+
235+
backend_type({Type, _}) -> Type;
236+
backend_type(Type) -> Type.
237+
208238
parse_backends(Plists) ->
209239
[parse_backend(Plist) || Plist <- Plists].
210240

211241
parse_backend(Opts) ->
212-
RawType = proplists:get_value(type, Opts),
213-
Type =
214-
case lists:member(RawType, [apns, fcm]) of
215-
true -> RawType
216-
end,
217-
Gateway = proplists:get_value(gateway, Opts),
218-
CertFile = proplists:get_value(certfile, Opts),
219-
ApiKey = proplists:get_value(api_key, Opts),
220-
242+
Ref = backend_ref(proplists:get_value(type, Opts), proplists:get_value(backend_ref, Opts)),
221243
#backend_config{
222-
type = Type,
244+
ref = Ref,
223245
config =
224-
case Type of
246+
case backend_type(Ref) of
225247
apns ->
226-
#apns_config{certfile = CertFile, gateway = Gateway};
248+
#apns_config{certfile = proplists:get_value(certfile, Opts), gateway = proplists:get_value(gateway, Opts)};
227249
fcm ->
228-
#fcm_config{gateway = Gateway, api_key = ApiKey}
250+
#fcm_config{gateway = proplists:get_value(gateway, Opts), api_key = proplists:get_value(api_key, Opts)}
229251
end
230252
}.
231253

@@ -235,32 +257,33 @@ parse_backend(Opts) ->
235257

236258
-spec(backend_worker(backend_id()) -> atom()).
237259

238-
backend_worker({Host, Type}) -> gen_mod:get_module_proc(Host, Type).
260+
backend_worker({Host, {T, R}}) -> gen_mod:get_module_proc(Host, binary_to_atom(<<(erlang:atom_to_binary(T, latin1))/binary, "_", R/binary>>, latin1));
261+
backend_worker({Host, Ref}) -> gen_mod:get_module_proc(Host, Ref).
239262

240263
backend_configs(Host) ->
241264
gen_mod:get_module_opt(Host, ?MODULE, backends,
242265
fun(O) when is_list(O) -> O end, []).
243266

267+
backend_module(apns) -> ?MODULE_APNS;
268+
backend_module(fcm) -> ?MODULE_FCM.
269+
244270
-spec(start_worker(Host :: binary(), Backend :: backend_config()) -> ok).
245271

246-
start_worker(Host, #backend_config{type = Type, config = TypeConfig}) ->
247-
Module = proplists:get_value(Type, [{apns, ?MODULE_APNS}, {fcm, ?MODULE_FCM}]),
248-
Worker = backend_worker({Host, Type}),
249-
BackendSpec =
250-
case Type of
272+
start_worker(Host, #backend_config{ref = Ref, config = Config}) ->
273+
Worker = backend_worker({Host, Ref}),
274+
BackendSpec =
275+
case backend_type(Ref) of
251276
apns ->
252277
{Worker,
253278
{gen_server, start_link,
254-
[{local, Worker}, Module,
255-
%% TODO: mb i should send one record like BackendConfig#backend_config.config and parse it in each module
256-
[TypeConfig#apns_config.certfile, TypeConfig#apns_config.gateway], []]},
279+
[{local, Worker}, backend_module(backend_type(Ref)),
280+
[Config#apns_config.certfile, Config#apns_config.gateway], []]},
257281
permanent, 1000, worker, [?MODULE]};
258282
fcm ->
259283
{Worker,
260284
{gen_server, start_link,
261-
[{local, Worker}, Module,
262-
%% TODO: mb i should send one record like BackendConfig#backend_config.config and parse it in each module
263-
[TypeConfig#fcm_config.gateway, TypeConfig#fcm_config.api_key], []]},
285+
[{local, Worker}, backend_module(backend_type(Ref)),
286+
[Config#fcm_config.gateway, Config#fcm_config.api_key], []]},
264287
permanent, 1000, worker, [?MODULE]}
265288
end,
266289

0 commit comments

Comments
 (0)