-
Star
(108)
You must be signed in to star a gist -
Fork
(35)
You must be signed in to fork a gist
-
-
Save alanpeabody/4fae12b420fb50376af4 to your computer and use it in GitHub Desktop.
defmodule MyApp do | |
use Application | |
def start(_type, _args) do | |
import Supervisor.Spec, warn: false | |
children = [ | |
Plug.Adapters.Cowboy.child_spec(:http, MyApp.Router, [], [ | |
dispatch: dispatch | |
]) | |
] | |
opts = [strategy: :one_for_one, name: Navis.Supervisor] | |
Supervisor.start_link(children, opts) | |
end | |
defp dispatch do | |
[ | |
{:_, [ | |
{"/ws", MyApp.SocketHandler, []}, | |
{:_, Plug.Adapters.Cowboy.Handler, {MyApp.Router, []}} | |
]} | |
] | |
end | |
end |
defmodule MyApp.Router do | |
use Plug.Router | |
plug :match | |
plug :dispatch | |
match _ do | |
send_resp(conn, 200, "Hello from plug") | |
end | |
end |
defmodule MyApp.SocketHandler | |
@behaviour :cowboy_websocket_handler | |
def init(_, _req, _opts) do | |
{:upgrade, :protocol, :cowboy_websocket} | |
end | |
@timeout 60000 # terminate if no activity for one minute | |
#Called on websocket connection initialization. | |
def websocket_init(_type, req, _opts) do | |
state = %{} | |
{:ok, req, state, @timeout} | |
end | |
# Handle 'ping' messages from the browser - reply | |
def websocket_handle({:text, "ping"}, req, state) do | |
{:reply, {:text, "pong"}, req, state} | |
end | |
# Handle other messages from the browser - don't reply | |
def websocket_handle({:text, message}, req, state) do | |
IO.puts(message) | |
{:ok, req, state} | |
end | |
# Format and forward elixir messages to client | |
def websocket_info(message, req, state) do | |
{:reply, {:text, message}, req, state} | |
end | |
# No matter why we terminate, remove all of this pids subscriptions | |
def websocket_terminate(_reason, _req, _state) do | |
:ok | |
end | |
end |
For anyone finding this now, with plug_cowboy
, the name of the handler module has changed.
def dispatch do
[
{:_, [
{"/socket", MyApp.Socket, []},
{:_, Plug.Cowboy.Handler, {MyApp.Router, []}}
]}
]
end
It looks like my_app.ex
is not working. I'm getting this error:
** (Mix) Could not start application spb_api: exited in: MyApp.Application.start(:normal, [])
** (EXIT) an exception was raised:
** (UndefinedFunctionError) function Plug.Cowboy.child_spec/4 is undefined or private
Using {:plug_cowboy, "~> 2.0"},
How can I reject a websocket connection from websocket_init/1
?
I want to send an {:error, reason}
tuple while rejecting so that I can pattern match the reason in the terminate/3
function and do some extra process there.
Found this tutorial that's more easily readable: https://medium.com/@loganbbres/elixir-websocket-chat-example-c72986ab5778
For anyone finding this now, with
plug_cowboy
, the name of the handler module has changed.def dispatch do [ {:_, [ {"/socket", MyApp.Socket, []}, {:_, Plug.Cowboy.Handler, {MyApp.Router, []}} ]} ] end
You solved my headache, buddy :)
I created a library to make the Cowboy
and Plug.Router
integration much easier.
https://github.com/kevinlang/plug_socket
Essentially:
defmodule MyApp.Router
use Plug.Router
use PlugSocket
socket "/ws", MyApp.SocketHandler
plug :match
plug :dispatch
match _ do
send_resp(conn, 200, "Hello from plug")
end
end
defmodule MyApp do
use Application
def start(_type, _args) do
children = [
{Plug.Cowboy, scheme: :http, plug: MyApp.Router, options: [
dispatch: PlugSocket.plug_cowboy_dispatch(MyApp.Router)
]}
]
opts = [strategy: :one_for_one, name: MyApp.Supervisor]
Supervisor.start_link(children, opts)
end
end
Thanks for this Gist. Also, 👍 @kevinlang for further reductions/simplifications.
@AquaGeek and others: the function definitions changed in Cowboy 2.0. If you use
@behaviour :cowboy_websocket
your compiler will warn you. Implementinit/2
instead: