Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I send data asynchronously over a websocket with Cowboy?

I have a Cowboy websocket server and I'd like to register a gen_event handler that sends something over the websocket. I also need to be able to reply to regular synchronous requests with websocket_handle/3. I didn't see anything obvious in cowboy_http_websocket_handler.erl and cowboy_http_websocket:websocket_send/3 isn't exported. Am I missing an easy way to send something over an open socket?

like image 701
nmichaels Avatar asked Nov 15 '11 13:11

nmichaels


2 Answers

@nmichaels answer pointed me in the right direction and I used gen_event successfully in a cowboy app to send internal messages to the websocket_info. But the answer is a bit dated and cowboy has changed a lot so I would like to add to it and provide a solution that works on the latest cowboy version. Hopefully this will help some one new to Erlang and cowboy.

There are three steps needed in order to implement a gen_event in cowboy

  • Start the gen_event and register you handlers

    start(_Type, _Args) ->
    Dispatch = cowboy_router:compile(wrinqle_routes:routes_configuration()),
    {ok, _} = cowboy:start_http(http, 100, [{port, 3000}],
                                [{env, [{dispatch, Dispatch}]}]),
      pg2:start(),
    
      gen_event:start({global,my_events}),
      gen_event:add_handler({global,my_events},my_event_handler,[]).
    

Here I have registered the event called my_events globally (note: you can register the events locally as well) and added handler in the module my_event_handler

  • Create an event handler.

  • Now you can notify your event handler of the events from anywhere in cowboy. As an example the code below raises events from the websocket_handler

    { _,_ }->
    
       gen_event: notify(global:whereis_name(my_events),{event_name,self()}),
        {ok,Req,State};
    

All this code is doing is notifying the event registered under my_events globally of the event. That's it.

Another problem the OP had trouble with was how to send messages to open connections and connections for which pid is not known at the time of initialization. To solve this problem you can make use of pg2 which registers process id under channels. It is a very useful module for manging PIDs. So the above code can be transformed to something like this

  [H|T] = pg2:get_members(Name)
  gen_event: notify(global:whereis_name(my_events),{event_name, H}).

And this way you can send message to a particular pid and by extension to a particular socket.

like image 164
Akshat Jiwan Sharma Avatar answered Sep 30 '22 10:09

Akshat Jiwan Sharma


In the example websocket handler, websocket_info/3 is used to send stuff like this. Combine gen_event:add_sup_handler/3 in the websocket's init code with websocket_info/3. Keep the pid of the connection in the handler's state and just send a message with the asynchronous event.

like image 22
nmichaels Avatar answered Sep 30 '22 09:09

nmichaels