sax

sax

Phoenix.Socket.Transport - custom websocket transports and the interop between phoenix and cowboy

I had a question for Phoenix maintainers about custom websocket transports, and the interop between phoenix and cowboy. It’s possible to implement a websocket extension via Phoenix.Socket.Transport , but certain aspects are undocumented, possibly because of expectations in the cowboy_websocket.erl implementation.

The problem is (hopefully) clearly demonstrated in this codebase: GitHub - absinthe-graphql/absinthe_graphql_ws: Add graphql-ws websocket transport for Absinthe · GitHub

Some simple things came out of my iteration on this library. Firstly, I needed to implement server-side keepalive via a ping on an interval. The Phoenix.Socket.Transport only declares the :text or :binary opcodes in its documentation, but I found that if I returned other opcodes like :ping or :pong , they’re passed through as-is through cowboy, and the keepalive works. I was wondering if this might be just an oversight on the typespecs, or if this was intentionally left out.

The other big thing was custom websocket errors. Websockets reserve 4000-4999 close codes for applications, and the graphql-ws protocol uses custom close codes in this range to specify errors in usage.

I traced through cowboy_websocket.erl to understand what happens when a custom phoenix socket transport returns {:stop, reason, socket} from a callback, and discovered that there does not seem to be a public API to return a custom close code with text.

More specifically, it looks like I should be able to return {:stop, {:remote, 4400, "some content"}, socket} , but cowboy does not respect the binary content: cowboy/src/cowboy_websocket.erl at db0d6f8d254f2cc01bd458dc41969e0b96991cc3 · ninenines/cowboy · GitHub.

websocket_send_close(State, Reason) ->
	_ = case Reason of
                # ... list of

		{remote, Code, _} ->
			transport_send(State, fin, frame({close, Code, <<>>}, State))
	end,
	ok.

I could be wrong about whether Phoenix would pass this through to cowboy, but if it doesn’t I’m sure it could be changed to do so.

In our code, we sidestep this by implementing a custom close mechanism: absinthe_graphql_ws/lib/absinthe/graphql_ws/transport.ex at 7d622da914fd2eefdc14466c6e0d733184696a3b · absinthe-graphql/absinthe_graphql_ws · GitHub

  defp close(code, message, socket) do
    queue_exit()
    {:reply, :ok, {:close, code, message}, socket}
  end

It seems to me that with minimal changes to cowboy, this could all be handled through callback responses, which could then be documented in Phoenix.Socket.Transport .Specifically, cowboy should respect any binary content that is passed to it with a custom close code. Phoenix might just “do the right thing” as-is, and it could just be clearly documented and added to the typespecs. That :remote tuple on :stop is not the most understandable thing to add to the API of Phoenix.Socket.Transport, so if Phoenix/cowboy accept changes I think that it might make sense to provide a type that declares clear intent, and then document it clearly.

I’m happy to contribute pull requests, but thought that this would be the place to start.

Most Liked

sax

sax

Turns out I misread the callbacks in cowboy_websocket. The code I referenced above is executed based on events received from clients. Callbacks from the server handle {:close, code, message} properly.

I’ve issued the following pull request to update Phoenix.Socket.Transport documentation:

https://github.com/phoenixframework/phoenix/pull/4350

Where Next?

Popular in Discussions Top

vans163
So useless benchmarks aside, Its possible to write a webserver that can serve 300k requests per second (perhaps more with optimizations)....
New
thojanssens1
It would be nice to be able to define a redirect from one route to another from the router.ex file. E.g.: redirect "/", UserController, ...
New
pillaiindu
In django there is a cache framework backed by memcached. Rails also puts a lot of emphasis on caching, and even the idea of russian-doll...
New
nburkley
AWS re:Invent is on at the moment with some interesting announcements. One new feature in particular is the Lambda Runtime API for AWS La...
New
sergio
There’s a new TIOBE index report that came out that shows Elixir is still not in the top 50 used languages. It also goes on to call Elix...
New
rms.mrcs
A couple of days ago I was discussing with a friend about different approaches to write microservices. He said that if he was going to w...
New
AstonJ
If so I (and hopefully others!) might have some tips for you :slight_smile: But first, please say which area you’re finding most challen...
New
New
dogweather
I wrote this comment on r/haskell, and it’s not popular there. :wink: But I think I’m on to something… Haskell reminds me of Java, and e...
New
Fl4m3Ph03n1x
Background A few days ago I was listening to The future of Elixir from Elixir Talks, with Dave Thomas (@pragdave ) and Brian Mitchell. I...
New

Other popular topics Top

malloryerik
Hi, this is for people who, like me, have had some friction using .html.heex templates in VSCode. The solution seems to be, in a hyphena...
New
AstonJ
Posting this to see if we can make things easier for people to get into Neovim. If you use Neovim and have a favourite distro please let ...
New
johnnyicon
Hi all, I’ve just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I’m trying to use Postgres...
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? Ecto.Repo — Ecto v3.14.0 has exampl...
New
dblack
I’ve got an issue with an app and I’ve no idea of how to troubleshoot it. I’m hoping someone here might have seen something similar. I p...
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
JakeBecker
TL;DR: I’ve just released an implementation of Microsoft’s IDE-independent Language Server Protocol for Elixir. It adds language support ...
1144 54120 245
New
AstonJ
Seen any cool LiveView demos, sample apps or examples? Please post them here! :003:
New
jaysoifer
Is there a way to rollback a specific migration and only that one (“skipping” all the other ones)? Would mix ecto.rollback -v 200809061...
New
vegabook
I’m brand new to Phoenix and I have stripped one of the demo applications to the bone. I just want to get an svg up on the screen. Here i...
New

Latest on Elixir Forum

Elixir Forum

We're in Beta

About us Mission Statement