elixir-plug / plug

Compose web applications with functions
https://hex.pm/packages/plug
Other
2.84k stars 582 forks source link

Provide single-file websocket example #1135

Closed josevalim closed 1 year ago

josevalim commented 1 year ago

Hi @mtrudel,

Can you please help me with this one?

Currently in the README.md we have an example using HTTP, but I would love one additional example that uses WebSocket (via websock adapter) + Bandit. A simple ping/pong with WS is enough. Can you please provide a single file with Mix.install that does so? Then I will add and ship a new release.

No worries if you cannot.

Thank you! :)

Gazler commented 1 year ago

Something like this?

Mix.install([
  {:bandit, ">= 0.0.0"},
  {:websock_adapter, ">= 0.0.0"}
])

defmodule EchoServer do
  def init(args) do
    {:ok, []}
  end

  def handle_in({"ping", [opcode: :text]}, state) do
    {:reply, :ok, {:text, "pong"}, state}
  end

  def terminate(:timeout, state) do
    {:ok, state}
  end
end

defmodule Router do
  use Plug.Router
  plug(Plug.Logger)
  plug(:match)
  plug(:dispatch)

  get "/" do
    response = """
    Use the JavaScript console to interact using websockets

    sock  = new WebSocket("ws://localhost:4000/websocket")
    sock.addEventListener("message", console.log)
    sock.addEventListener("open", () => sock.send("ping"))
    """
    send_resp(conn, 200, response)
  end

  get "/websocket" do
    conn
    |> WebSockAdapter.upgrade(EchoServer, [], timeout: 60_000)
    |> halt()
  end

  match _ do
    send_resp(conn, 404, "not found")
  end
end

bandit = {Bandit, plug: Router, scheme: :http, port: 4000}
require Logger
Logger.info("starting #{inspect(bandit)}")
{:ok, _} = Supervisor.start_link([bandit], strategy: :one_for_one)

# unless running from IEx, sleep idenfinitely so we can serve requests
unless IEx.started?() do
  Process.sleep(:infinity)
end
josevalim commented 1 year ago

Thank you!!!!!!!

mtrudel commented 1 year ago

LMAO. I've had this as a low-priority TODO in the back of my brain for months, and finally got around to adding the following into Things literally last night:

image

I guess great minds think alike! I'll copy that same example over to websock_adapters this morning. Also note that this is going to be one of the examples that I use in my ElixirConf EU talk next month (which is really just a larger public debut of the websock stack), so there'll finally be some publicity around this!