3

I'm currently trying to create a push notification module for Erlang.

When the token is valid, everything works great... The issue is when an old device token (which is invalid by now) is rejected. I understand that invalid token will be rejected by the apns with a 6 bytes socket message, and invalidate the connection (Which I think is really dumb, whatever...)

The thing is I do not seems to get the 6 bytes socket message that APNS should give me with my module, like the controlling process is not listening the socket.

Here's my code :

-module(pushiphone).
-behaviour(gen_server).

-export([start/1, init/1, handle_call/3, handle_cast/2, code_change/3, handle_info/2, terminate/2]).

-import(ssl, [connect/4]).
-record(push, {socket, pid, state, cert, key}).

start(Provisioning) ->
    gen_server:start_link(?MODULE, [Provisioning], []).

init([Provisioning]) ->
    gen_server:cast(self(), {connect, Provisioning}),
    {ok, #push{pid=self()}}.

send(Socket, DT, Payload) ->
    PayloadLen = length(Payload),
    DTLen = size(DT),
    PayloadBin = list_to_binary(Payload),
    Packet = <<0:8, 
           DTLen:16/big, 
           DT/binary, 
           PayloadLen:16/big, 
           PayloadBin/binary>>,
    ssl:send(Socket, Packet).

handle_call(_, _, P) ->
    {noreply, P}.

handle_cast({connect, Provisioning}, P) ->
    case Provisioning of
    dev -> Address = "gateway.sandbox.push.apple.com";
    prod -> Address = "gateway.push.apple.com"
    end,
    Port = 2195,
    Cert="/apns-" ++ atom_to_list(Provisioning) ++ "-cert.pem",
    Key="/apns-" ++ atom_to_list(Provisioning) ++ "-key.pem",
    Options = [{certfile, Cert}, {keyfile, Key}, {password, "********"}, {mode, binary}, {active, false}],
    Timeout = 1000,
    {ok, Socket} = ssl:connect(Address, Port, Options, Timeout),
    ssl:controlling_process(Socket, self()), %% Necessary ??
    gproc:reg({n,l, pushiphone}),
    {noreply, P#push{socket=Socket}};
handle_cast(_, P) ->
    {noreply, P}.

handle_info({ssl, Socket, Data}, P) ->
    <<Command, Status, SomeID:32/big>> = Data,
    io:fwrite("[PUSH][ERROR]: ~p / ~p / ~p~n", [Command, Status, SomeID]),
    ssl:close(Socket),
    {noreply, P};
handle_info({push, message, DT, Badge, [Message]}, P) ->
    Payload = "{\"aps\":{\"alert\":\"" ++ Message ++ "\",\"badge\":" ++ Badge ++ ",\"sound\":\"" ++ "msg.caf" ++ "\"}}",
    send(P#push.socket, DT, Payload),
    {noreply, P};
handle_info({ssl_closed, _SslSocket}, P) ->
    io:fwrite("SSL CLOSED !!!!!!~n"),
    {stop, normal, P};
handle_info(AnythingElse, P) ->
    io:fwrite("[ERROR][PUSH][ANYTHING ELSE] : ~p~n", [AnythingElse]),
    {noreply, P}.

code_change(_, P, _) ->
    {ok, P}.

terminate(_, _) ->
    ok.

So When I start the module, and I push to a valid token, the push is received on the phone, but when I push to an invalid token, and then to a valid token, the valid token won't receive any push...

I am aware I should listen to the feedback service in order to remove the Device token from my database, but I also need to know if the push gateway as invalidated my connection in order to reconnect.

So Here's the real question : Why my gen-server doesn't receive the error-response packet (which should match the handle_info({ssl, Socket, Data}, P) )?

TheSquad
  • 7,385
  • 8
  • 40
  • 79

1 Answers1

4

Your socket is configured with active=false. You won't receive any messages unless you set it active=true (or repeatedly active=once). See the documentation for inet:setopts/2.

You also shouldn't have to set the controlling_process to self().

butter71
  • 2,703
  • 19
  • 12
  • You are right, now the gen_server call the ssl_close handle... Weird that I do not receive error-response packet, whatever, I can know when the socket is disconnected now, All I asked ;-) Valid +1 precise answer. – TheSquad Mar 21 '12 at 23:51
  • @TheSquad looks like you're using the legacy "simple notification format" where you don't get errors back, just a closed socket. Try using the also legacy now "enhanced" (fairly similar to the simple format) or the current binary format. Remember to retransmit pending notifications after the error-response-id. – Stefan L Jan 20 '14 at 16:36