1
votes

I want to write a module which need to be able to push message to pusher in Erlang. I found this repo https://github.com/bradfordw/pusherl, which is not maintained anymore, but I think I can adapt to make it work. I followed the README, and I ran successfully all the Rebar command. Then I open the rebar console by the command

./rel/pusherl/bin/pusherl console

So the server should start. But then when I try to call

gen_server:call(pusherl_server, {push, {"ChannelName", "EventName", "Payload"}}).

Then it throws error:

exception exit: {noproc,{gen_server,call,
                                   [pusherl_server,
                                    {push, 

    {"ChannelName","EventName","Payload"}}]}}
     in function  gen_server:call/2 (gen_server.erl, line 182)

I'm quite new with Erlang and OTP, and it takes me half of the day to make it work but not successful. Please help me to solve this. I really appreciate.

By the way, if you know any other pusher client, please suggest me. Thanks a lot.

Here is the code to for gen_server callback:

-module(pusherl_server).
-behaviour(gen_server).
-define(SERVER, ?MODULE).
-define(JP, fun(K,V) -> string:join([K,V],"=") end).

-record(state,{app_id, key, secret}).

-export([start_link/0]).

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

start_link() ->
  gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).

init(_) ->
  {ok, PusherAppId} = application:get_env(pusher_app_id),
  {ok, PusherKey} = application:get_env(pusher_key),
  {ok, PusherSecret} = application:get_env(pusher_secret),
  {ok, #state{app_id=PusherAppId, key=PusherKey, secret=PusherSecret}}.

handle_call({push, {ChannelName, EventName, Payload}}, _From, State) ->
  case http_request(ChannelName, EventName, Payload, State) of
    {ok, _} -> {reply, ok, State};
    {error, _} -> {reply, error, State}
  end;
handle_call(_Request, _From, State) ->
  {noreply, ok, State}.

handle_cast({push, {ChannelName, EventName, Payload}}, State) ->
  case http_request(ChannelName, EventName, Payload, State) of
    {ok, _} -> {noreply, ok, State};
    {error, _} -> {noreply, error, State}
  end;
handle_cast(_Msg, State) ->
  {noreply, State}.

handle_info(_Info, State) ->
  {noreply, State}.

terminate(_Reason, _State) ->
  ok.

code_change(_OldVsn, State, _Extra) ->
  {ok, State}.

http_request(ChannelName, EventName, Payload, Config) when is_list(ChannelName), is_record(Config, state) ->
  {ok, ReqProps} = http_request_props(Payload, EventName, ChannelName, Config),
    httpc:request(post, ReqProps, [], []).

http_request_props(Payload, EventName, ChannelName, #state{app_id=AppId, key=AppKey, secret=AppSecret}) ->
    Md5String = lists:flatten([io_lib:format("~2.16.0b",[N]) || <<N>> <= crypto:md5(Payload)]),
  ToSign = ["POST",
                lists:flatten(["/apps/", AppId, "/channels/", ChannelName, "/events"]),
                string:join([?JP("auth_key", AppKey),
        ?JP("auth_timestamp", get_time_as_string()),
        ?JP("auth_version", "1.0"),
        ?JP("body_md5", Md5String),
                ?JP("name", EventName)
                ],"&")
  ],
    AuthSignature = signed_params(ToSign, AppSecret),
    QueryParams = [
        ?JP("auth_key", AppKey),
      ?JP("auth_timestamp", get_time_as_string()),
      ?JP("auth_version","1.0"),
      ?JP("body_md5", Md5String),
        ?JP("auth_signature", AuthSignature),
        ?JP("name", EventName)
    ],
  Url = http_api_url(AppId, ChannelName, QueryParams),
  {ok, {Url, [], "application/x-www-form-urlencoded", Payload}}.

http_api_url(AppId, ChannelName, QueryParams) ->
  QueryString = string:join(QueryParams,"&"),
  lists:flatten(["http://api.pusherapp.com/apps/",AppId,"/channels/",ChannelName,"/events?", QueryString]).

get_time_as_string() ->
  {M, S, _} = now(),
  integer_to_list(((M * 1000000) + S)).

signed_params(Params, Secret) ->
    lists:flatten([io_lib:format("~2.16.0b",[N]) || <<N:8>> <= sha2:hmac_sha256(Secret, string:join(Params,"\n"))]).
1
I still met the problem that I mentioned. Please anyone help me to solve this. Thanksle duc quang

1 Answers

0
votes

noproc means that the process you're trying to call is not running. You need to start it with:

pusherl_server:start_link().

Note that this will link the pusherl_server process to the calling process. If you're running this in the shell, and then do something that causes an error, the error will propagate across the link to the pusherl_server process and kill it, so then you have to start it again.

To avoid that, you can either unlink the process after starting it:

{ok, Pid} = pusherl_server:start_link().
unlink(Pid).

or add a start function to the module, that does the same as start_link except it calls gen_server:start instead of gen_server:start_link.