Pastelli is a colorful Plug adapter for Elli
with a focus on streaming over chunked
connections (read EventSource
).
For the moment, this is quite alpha and
it implements almost all (see below) of the Plug.Conn.Adapter
behaviour.
As you would do with your beloved Plug.Adapters.Cowboy
,
you'll type:
Pastelli.http MyPlug.Router, [], [port: 4001]
Now setup your router (or simpler plug) as usual. Pastelli changes the semantics of EventSource chunked responses, in which it doesn't block your router dispatch:
defmodule MyPlug.Router do
use Plug.Router
plug :match
plug :dispatch
get "/connections/:id" do
put_resp_content_type(conn, "text/event-stream")
|> send_chunked(200)
|> register_stream(id)
# dispatch doesn't need to block execution,
# it enters a chunk loop just after pipeline resolution,
# waiting for chunk messages
end
defp register_stream(conn, id) do
{:ok, pid} = MyPlug.Connections.register id, conn
# usually a :simple_one_for_one supervised
# event manager
Process.link pid
# we link the process to the streaming manager!
# once the chunk is complete (client closes socket or crashes)
# pastelli handler will send a `chunk_complete` exit signal
# to the connection process.
# It is your responsibility to monitor the event manager and
# react on such exits
conn
end
end
Pastelli.Router
wraps an extra stream
macro around Plug.Router
and
imports Pastelli.Conn
, a module with a few extra functions to manipulate
Plug.Conn
chunked-state structs.
defmodule MyPlug.Router do
use Pastelli.Router
plug :match
plug :dispatch
stream "/connections" do
init_chunk(conn, %{text: "hallo!"}, event: :handshake, retry: 6000, id: 1234)
# sends an initial chunk to event source as early as plug pipeline ends
|> register_stream()
end
end
from wherever you can access the connection struct,
Pastelli.Conn.event/2,3
serializes non-binary message body and
meta data.
Pastelli.Conn.event(conn, %{some: "map"}, event: "message", id: "x4x3", retry: 6000)
Event Source based remote control backend for remote controlling presentation slides.
Pastelli upgrades to Web Sockets thanks to mmzeeman's elli_websockets.
Pass an elli_websocket handler in the private map of your connection. This will receive the current connection as option argument.
get "/ws" do
put_private(conn, :upgrade, {:websocket, WebSocketHandler})
end
defmodule WebSocketHandler do
def websocket_init(request, conn: %Plug.Conn{} = conn) do
# ...
end
def websocket_handle() # handle callback
def websocket_info() # info callback
end
With version 1.1.0
or newer of
Phoenix
we can allow different servers other than Cowboy at the heart of the framework.
It's enough to provide a suitable
endpoint handler behaviour.
With Pastelli.Phoenix
you
can have Elli serve Phoenix. Check also this example out.
The current built-in Plug cowboy adapter does not notify the connection owner process of the EventSource client closing the socket (or just crashing). More precisely, Pastelli tries to address this issue.
- send_resp
- send_file
- send_chunked
- chunk
- read_req_body
- parse_req_multipart
- init_chunk/2, init_chunk/3
- event/2, event/3
- close_chunk/0
- run http
- run https
- websocket upgrade via mmzeeman/elli_websocket
- Plug.Conn extensions
- Pastelli.Router streaming DSL
- ExDoc
- hex package