1

I see the basic example for Elixir's WebSockex library here but it doesn't really explain how I can expose my own websocket to the internet. This question has answers which explain how to chat to an existing websocket externally, but I want to expose my own websocket. I'm actually using websockex as part of a Phoenix application, so perhaps bits of Phoenix might help here?

I obviously know the ip:port combo of my phoenix application so given these, how do I expose a websockex websocket on that ip:port? In other words, what should I pass as the URL? in this basic example code:

defmodule WebSocketExample do
  use WebSockex

  def start_link(url, state) do
    WebSockex.start_link(url, __MODULE__, state)
  end

  def handle_frame({type, msg}, state) do
    IO.puts "Received Message - Type: #{inspect type} -- Message: #{inspect msg}"
    {:ok, state}
  end

  def handle_cast({:send, {type, msg} = frame}, state) do
    IO.puts "Sending #{type} frame with payload: #{msg}"
    {:reply, frame, state}
  end
end

Please note that I need to expose a raw websocket, not a Phoenix channel, as the consumer doesn't understand Phoenix channels. If Phoenix can expose a raw websocket then I'll consider that a solution too.

If neither Phoenix nor WebSockex can help, what are my options?

Thomas Browne
  • 23,824
  • 32
  • 78
  • 121

2 Answers2

2

Websockex is a client library, I don't think it has any code for exposing a websocket. Since you're already using phoenix, you probably can do what you need with phoenix channels.

If you're on cowboy (and you probably are, since it's the default), then you can also use it to expose a raw websocket. However, it requires some fiddling with routing. You will need to replace YourAppWeb.Endpoint with a manual configuration of cowboy:

{
  Plug.Cowboy,
  scheme: :http,
  plug: YourAppWeb.Endpoint,
  options: endpoint_options(),
  dispatch: [
    _: [
      # Dispatch paths beginning with /ws to a websocket handler
      {"/ws/[...]", YourApp.WebsocketHandler, []},
      # Dispatch other paths to the phoenix endpoint
      {:_, Plug.Cowboy.Handler, {YourAppWeb.Endpoint, endpoint_options()}}
    ]
  ]
}

I have honestly only done this with raw plug, so you might need to convert the endpoint to be a Plug instead of a Phoenix.Endpoint. Then, you need to implement YourApp.WebsocketHandler to conform to cowboy's API and perform a websocket upgrade (and handle sending/receiving messages), as described in cowboy docs. You can also see this gist for a more fleshed-out example.

Paweł Obrok
  • 22,568
  • 8
  • 74
  • 70
  • I need to exposre a raw websocket not a phoenix channel, as the data will be consumed by an application which doesn't understand phoenix channels. Can phoenix expose a raw websocket? – Thomas Browne Mar 01 '21 at 13:17
  • 1
    I added some info on how to expose a raw socket with `cowboy`, although it seems to me like it's more fiddly than either implementing parts of the phoenix protocol yourself, or just copying over `deps/phoenix/assets/js/phoenix.js` into your code if you're after a very low-tech solution - it's only 1.6k lines unminified. – Paweł Obrok Mar 01 '21 at 19:06
  • 1
    Also consider [Phoenix.Socket.Transport](https://hexdocs.pm/phoenix/Phoenix.Socket.Transport.html) for implementing a different protocol. Probably easier to get started with than adding a manual `cowboy` configuration. – Maarten van Vliet Mar 08 '21 at 09:54
2

WebSockex implements many callbacks, including, but not limited to WebSockex.handle_connect/2. It holds WebSockex.Conn in a state and passes it to all callbacks.

WebSockex.Conn is a plain old good struct, having socket field.

So from any callback (I’d do it from WebSockex.handle_connect/2) you might share this socket with the process which needs it and use it then from there.


Also, you can borrow some internals and check how the connection is being created.

You’ll see it uses WebSockex.Conn.new/2 that returns an initialized connection, that, in turn, holds a socket. In that case, you’ll be obliged to supervise the process that holds the socket manually.


The power of OSS is all answers are one mouse click far from questions.

Aleksei Matiushkin
  • 119,336
  • 10
  • 100
  • 160
  • Okay, so, what you are explaining here, will allow me to expose a websocket to the internet, right? Because that's what I need. Consumer is is going to be Javascript Node Red (https://flows.nodered.org/node/node-red-contrib-websocket-auth0) – Thomas Browne Mar 01 '21 at 17:20
  • This explains how to get to the underlying raw socket. This is what was asked. JS can perfectly read from Phoenix sockets, AFAIK. – Aleksei Matiushkin Mar 01 '21 at 17:27
  • JS needs to load phoenix library code to read from Phoenix channels, and that's not something that's available in the Node Red library as of now, and I'm not willing to write such a heavyweight addin. So what I wanted to ask is how to expose a raw websocket to the internet, using websockex, not just how to get access to the raw socket. So I'll edit my question to make this explicit. – Thomas Browne Mar 01 '21 at 17:54