diff --git a/README.md b/README.md index e5ed9a0..207a78c 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,10 @@ And you can check all of our open-source projects at [inaka.github.io](http://in - You must have installed an updated Openssl version or, at least, be sure it supports TLS 1.2+. New APNs server only supports connections over TLS 1.2+. - Erlang R19+ +## Important Links + +- [Pool of connections Example](examples/apns_pool/README.md) + ## How to use it? First we have to fill our `config` data. There are two ways for do this, one is filling a `config` file. This is an example you can find at `test/test.config`: diff --git a/examples/apns_pool/README.md b/examples/apns_pool/README.md new file mode 100644 index 0000000..208238c --- /dev/null +++ b/examples/apns_pool/README.md @@ -0,0 +1,54 @@ +# apns_pool + +This is an example of how to use `apns4erl` with a pool of connections. We are using [worker_pool](https://github.com/inaka/worker_pool) for creating the pool. + +## Run + +After cloning this repo in your local you must replace the `priv/cert2.pem` and `priv/key2-noenc.pem` files by your own provided by Apple. + +You need to replace the `apns-topic` in the module `apns_pool:push/2` also: + +```erlang +push(DeviceId, Message) -> + Notification = create_notification(Message), + ApnsTopic = <<"com.inaka.myapp">>, % Replace by your own topic + wpool:call(pool_name(), {push, DeviceId, ApnsTopic, Notification}). +``` + +Now the configuration is done, lets compile: + +``` +$ rebar3 compile +``` + +and run + +``` + $ erl erl -pa _build/default/lib/*/ebin -s apns_pool + Erlang/OTP 19 [erts-8.3] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false] [dtrace] + + Eshell V8.3 (abort with ^G) + 1> 09:13:28.408 [info] Application lager started on node nonode@nohost + 09:13:28.408 [info] Application chatterbox started on node nonode@nohost + 09:13:28.408 [info] Application base64url started on node nonode@nohost + 09:13:28.411 [info] Application apns started on node nonode@nohost + 09:13:28.413 [info] Application worker_pool started on node nonode@nohost + 09:13:44.760 [info] Application apns_pool started on node nonode@nohost +``` + +you must wait until the last message is printed (`apns_pool started`), it could take some seconds until `apns_pool` loads completely. + +## Pushing messages + +You need a device id and calling `apns_pool:push/2` function: + +``` +1> DeviceId = <<"bd5c3ad01bbe4d884bf2fe8801ed77e94a71bc2e9de937c84f745f54eb4cb2f4">>. +<<"bd5c3ad01bbe4d884bf2fe8801ed77e94a71bc2e9de937c84f745f54eb4cb2f4">> +2> apns_pool:push(DeviceId, <<"hello from a pool">>). +{200, + [{<<"apns-id">>,<<"1EF4ED9F-42FC-BA8B-450F-3BF6B09C72CB">>}], + no_body} +``` + +Thats all! diff --git a/examples/apns_pool/priv/cert2.pem b/examples/apns_pool/priv/cert2.pem new file mode 100644 index 0000000..9d0a40d --- /dev/null +++ b/examples/apns_pool/priv/cert2.pem @@ -0,0 +1 @@ +get your own! diff --git a/examples/apns_pool/priv/key2-noenc.pem b/examples/apns_pool/priv/key2-noenc.pem new file mode 100644 index 0000000..9d0a40d --- /dev/null +++ b/examples/apns_pool/priv/key2-noenc.pem @@ -0,0 +1 @@ +get your own! diff --git a/examples/apns_pool/rebar.config b/examples/apns_pool/rebar.config new file mode 100644 index 0000000..47b25db --- /dev/null +++ b/examples/apns_pool/rebar.config @@ -0,0 +1,10 @@ +%% == Erlang Compiler == + +{minimum_otp_vsn, "19"}. + +%% == Dependencies == + +{deps, [ + {apns, "2.1.1", {pkg, apns4erl}}, + {worker_pool, "2.2.3"} +]}. diff --git a/examples/apns_pool/rebar.lock b/examples/apns_pool/rebar.lock new file mode 100644 index 0000000..06d176b --- /dev/null +++ b/examples/apns_pool/rebar.lock @@ -0,0 +1,20 @@ +{"1.1.0", +[{<<"apns">>,{pkg,<<"apns4erl">>,<<"2.1.1">>},0}, + {<<"base64url">>,{pkg,<<"base64url">>,<<"0.0.1">>},1}, + {<<"chatterbox">>,{pkg,<<"chatterbox">>,<<"0.4.2">>},1}, + {<<"goldrush">>,{pkg,<<"goldrush">>,<<"0.1.9">>},3}, + {<<"hpack">>,{pkg,<<"hpack_erl">>,<<"0.2.3">>},2}, + {<<"jsx">>,{pkg,<<"jsx">>,<<"2.8.1">>},1}, + {<<"lager">>,{pkg,<<"lager">>,<<"3.2.4">>},2}, + {<<"worker_pool">>,{pkg,<<"worker_pool">>,<<"2.2.3">>},0}]}. +[ +{pkg_hash,[ + {<<"apns">>, <<"D2F851C803DC546212FEBB6F23EB40DBF86539864F7DB0FB8304DFC1C1F1384B">>}, + {<<"base64url">>, <<"36A90125F5948E3AFD7BE97662A1504B934DD5DAC78451CA6E9ABF85A10286BE">>}, + {<<"chatterbox">>, <<"BA68296FA79F0CA31139713C688C350A26C8844E1244F1736D7845C1C72E5F87">>}, + {<<"goldrush">>, <<"F06E5D5F1277DA5C413E84D5A2924174182FB108DABB39D5EC548B27424CD106">>}, + {<<"hpack">>, <<"17670F83FF984AE6CD74B1C456EDDE906D27FF013740EE4D9EFAA4F1BF999633">>}, + {<<"jsx">>, <<"1453B4EB3615ACB3E2CD0A105D27E6761E2ED2E501AC0B390F5BBEC497669846">>}, + {<<"lager">>, <<"A6DEB74DAE7927F46BD13255268308EF03EB206EC784A94EAF7C1C0F3B811615">>}, + {<<"worker_pool">>, <<"2CD7B2C289B900940297D283922D7E119C540CB8F29B5254639ABB9BFB100CAE">>}]} +]. diff --git a/examples/apns_pool/src/apns_pool.app.src b/examples/apns_pool/src/apns_pool.app.src new file mode 100644 index 0000000..4577bda --- /dev/null +++ b/examples/apns_pool/src/apns_pool.app.src @@ -0,0 +1,13 @@ +{application, apns_pool, [ + {description, "pool of connections with apns4erl"}, + {vsn, "0.0.1"}, + {registered, []}, + {applications, [ + kernel, + stdlib, + apns, + worker_pool + ]}, + {modules, []}, + {mod, {apns_pool, []}} +]}. diff --git a/examples/apns_pool/src/apns_pool.erl b/examples/apns_pool/src/apns_pool.erl new file mode 100644 index 0000000..b9febcd --- /dev/null +++ b/examples/apns_pool/src/apns_pool.erl @@ -0,0 +1,49 @@ +-module(apns_pool). +-author("Felipe Ripoll "). + +-behaviour(application). + +%% Application callbacks +-export([ start/0 + , push/2 + ]). + +%% Application callbacks +-export([ start/2 + , stop/1 + ]). + +%%%=================================================================== +%%% Application callbacks +%%%=================================================================== + +start(_StartType, _StartArgs) -> + apns_pool_sup:start_link(pool_name()). + +stop(_State) -> + ok. + +%%%=================================================================== +%%% API +%%%=================================================================== + +start() -> + application:ensure_all_started(apns_pool). + +push(DeviceId, Message) -> + Notification = create_notification(Message), + ApnsTopic = <<"com.inaka.myapp">>, % Replace by your own topic + wpool:call(pool_name(), {push, DeviceId, ApnsTopic, Notification}). + +%%%=================================================================== +%%% private functions +%%%=================================================================== + +create_notification(Message) -> + #{<<"aps">> => + #{ <<"alert">> => Message + , <<"sound">> => <<"default">> + , <<"badge">> => 1} + }. + +pool_name() -> ?MODULE. diff --git a/examples/apns_pool/src/apns_pool_sup.erl b/examples/apns_pool/src/apns_pool_sup.erl new file mode 100644 index 0000000..584290c --- /dev/null +++ b/examples/apns_pool/src/apns_pool_sup.erl @@ -0,0 +1,59 @@ +-module(apns_pool_sup). +-author("Felipe Ripoll "). +-behaviour(supervisor). + +%% API +-export([ start_link/1 + ]). + +%% Supervisor callbacks +-export([ init/1 + ]). + +%%%=================================================================== +%%% API functions +%%%=================================================================== + +start_link(PoolName) -> + supervisor:start_link({local, ?MODULE}, ?MODULE, PoolName). + +%%%=================================================================== +%%% Supervisor callbacks +%%%=================================================================== + +init(PoolName) -> + WPoolOptions = [ {overrun_warning, infinity} + , {overrun_handler, {error_logger, warning_report}} + , {workers, 50} + , {worker, {apns_pool_worker, apns4erl_config()}} + ], + + SupFlags = #{ strategy => one_for_one + , intensity => 1000 + , period => 3600 + }, + + Children = [#{ id => wpool + , start => {wpool, start_pool, [PoolName, WPoolOptions]} + , restart => permanent + , shutdown => 5000 + , type => supervisor + , modules => [wpool] + }], + + {ok, {SupFlags, Children}}. + +%%%=================================================================== +%%% Private functions +%%%=================================================================== + +apns4erl_config() -> + % this should be in a config file but its fine for an example :) + #{ name => undefined + , apple_host => "api.push.apple.com" + , apple_port => 443 + , certfile => "priv/cert2.pem" + , keyfile => "priv/key2-noenc.pem" + , timeout => 10000 + , type => cert + }. diff --git a/examples/apns_pool/src/apns_pool_worker.erl b/examples/apns_pool/src/apns_pool_worker.erl new file mode 100644 index 0000000..c319804 --- /dev/null +++ b/examples/apns_pool/src/apns_pool_worker.erl @@ -0,0 +1,41 @@ +-module(apns_pool_worker). +-author("Felipe Ripoll "). + +-behaviour(gen_server). + +%% gen_server callbacks +-export([ init/1 + , handle_call/3 + , handle_cast/2 + , handle_info/2 + , terminate/2 + , code_change/3 + ]). + +%%%=================================================================== +%%% gen_server callbacks +%%%=================================================================== + +init(ApnsConfig) -> + {ok, ConnectionPid} = apns:connect(ApnsConfig), + {ok, #{connection_pid => ConnectionPid}}. + +handle_call({push, DeviceId, ApnsTopic, Notification}, _From, State) -> + #{connection_pid := ConnectionPid} = State, + Headers = #{apns_topic => ApnsTopic}, + Response = apns:push_notification(ConnectionPid, DeviceId, Notification, Headers), + {reply, Response, State}; +handle_call(_Request, _From, State) -> + {reply, ok, State}. + +handle_cast(_Request, State) -> + {noreply, State}. + +handle_info(_Info, State) -> + {noreply, State}. + +terminate(_Reason, _State) -> + ok. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}.