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?

- 49,466
- 12
- 107
- 135
-
2The easiest way to answer this topic is create an issue at cowboy's github and ask author. Loïc Hoguin is very open for discussion. – Sergey Miryanov Nov 15 '11 at 18:28
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.

- 15,430
- 13
- 50
- 60
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.

- 49,466
- 12
- 107
- 135
-
I know you answered this a long time ago but I have a question... Why do you add event handler in websocket's int code and not in the start of your cowboy app? – Akshat Jiwan Sharma Nov 15 '13 at 20:35
-
The websocket's init code gets run when a session is set up. Cowboy is only initialized once. This needs to send messages to open connections, and the pids for future connections aren't known when the app is started. That said, a new version of Cowboy came out recently so this might be obsolete. – nmichaels Nov 17 '13 at 16:55
-
1I had a similar problem to yours and I have added the way I solved it. Thank you for pointing me in the right direction. It could be useful for someone else facing a similar problem. – Akshat Jiwan Sharma Nov 18 '13 at 16:59