Skip to content

[RFC] Plug.WebSocket #65

@jeregrine

Description

@jeregrine

This is a first draft and I intend to change and alter it from feedback

I've been doing a little research into making websockets a part of plug, and having built the websocket support for phoenix I figured I'd do an RFC for Plug.Websocket.

Why?

  • Plug supports every other major form of HTTP/Browser style specs outside of SPDY and Websockets.
  • Websockets are supported on every current browser and many former versions http://caniuse.com/websockets.
  • They are a great Usecase for Elixir.

How?
Because Plug has goals of being multi-backend we need to be cognizant of how possible backends might work. Currently we only support cowboy, but I assume we have plans to support:

  • Mochiweb
  • Yaws
  • Webmachine
  • inets
  • elli

Unfortunately only a subset of these natively support websockets, but all is not lost! For those that DONT support it out of the box we can implement connection hijacking as shown here by simple_bridge. I will be references simple_bridge often as they tackled the concept of supporting many of these backends with websockets.

Is there a common interface?

  • Mochiweb: doesn't support would require highjacking, meaning we can define our own interface.
  • Yaws: does support, check for a header and upgrade the connection, defing a custom handler module.

Interface

init([_Arg, State={Handler, Bridge}]) ->
handle_message(Data, State) ->j
handle_info(Data, State) ->
terminate(Reason, State) ->
  • Webmachine: doesn't support, would require highjacking.
  • Inets: doesn't support, would require highjacking.
  • elli: does support with third party lib, which implements highjacking.

Interface

websocket_init(Req, Opts) ->
websocket_handle(_Req, {text, Data}, State) ->
websocket_handle(_Req, {binary, Data}, State) ->
websocket_handle(_Req, _Frame, State) ->
websocket_info(Req, Message, State) ->
websocket_handle_event(Name, EventArgs, State) -> #open, close etc
  • Cowboy: does support, return upgrade to request and current module handles rest.

Interface

websocket_init(_Transport, Req, _Opts) ->
websocket_handle(Data, Req, State) ->
websocket_info(Data, Req, State) ->
websocket_terminate(Reason, _Req, State) ->

Proposed Plug Interface
Plug.Conn.upgrade_to_websocket(conn, HandlerModule) this function will call the adaper.upgrade_to_websocket which has one responsibility to verify and upgrade a connection. I see this working two ways: for built in functions it would attach a value to conn.assigns and match on it in init or it would attempt to hijack the request similar to how simple_bridge and elli do. It would be up to the adapter to make the decision.

The HandlerModule would be a module that implements the behavior based on the common interfaces from above.

#initalizing the socket. 
defcallback init(request, options)
#handling client data
defcallback data(data, req, state)
#handling messages from erlang process
defcallback info(data, req, state)
#handling terminations from users or erlang
defcallback terminate(reason, req state)

There would be another plug module called Plug.WebSocket which defines reply, terminate, hibernate and info.

In each case the adapter would call the HandlerModule function when appropriate. Plug.WebSocket also calls the adapter module/s definitions to help build the proper response for reply/hibernate/info/terminate.

With minimal changes to the current plug adapter interface, and a new method on Plug.Conn I believe we can have Websocket support.

The first step is adding cowboy support since thats the only backend we currently support. If we can decide on a common interface I could do it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions