-
Notifications
You must be signed in to change notification settings - Fork 602
Description
With the current Plug.Adapters.Cowboy I am not able to react on tcp socket being closed
during chunked connections. Could be that I am doing something wrong, but it's maybe worth
to start a discussion on this.
Typical scenario: I register, monitor and 'simple_one_for_one'-supervise a
GenEvent process for each incoming event source connection:
defmodule Streaming.Router do
use Plug.Router
plug :match
plug :dispatch
def init(_), do: []
get "/connections/:id" do
put_resp_content_type(conn, "text/event-stream")
|> send_chunked(200)
|> register_and_link_events_manager(id)
|> listen
end
defp listen(conn) do
GenEvent.stream(conn.assigns.events_manager_pid)
|> Stream.each(&chunk(conn, format(&1)))
|> Stream.run
end
Since I link the registered process, when the connection closes
it would be enough to send a (non normal) exit signal to the process and let
my gen_server do the necessary cleanup. But I couldn't find a way to do that.
As far as I understand, to get this kind of feedback the actual tcp_socket must be marked as active.
Transport:setopts(Socket, [{active, once}])
This for instance happens in a cowboy handler which enters the loop behaviour in
its init phase. Actual cowboy handler uses the upgrade protocol.
On the other hand Elli server has a nice loop-like
response handler (which is also mentioned in #65). To be more precise,
based on the user-configured handler, the server decides to enter or not the
chunked loop based on the
current middleware environment and this sounds perfectly plug-ish.
I drafted an Elli adapter: Pastelli with a subset of the Plug.Conn adapter interface, focusing on the 'chunked' functionalities. I would be of course very happy to
contribute to plug itself if a built-in Elli adapter is planned.
Elli response model supports a.o.
handler callbacks on several "events" like "reponse_complete", "request_parse_error" and in particular for 'chunk_complete' which is what I need.
For a streaming scenario like the one above, Pastelli doesn't block the action dispatch
(which happens above by calling GenEvent.stream) but yield the connection to
the server which enters a receive loop waiting
for 'chunk' messages. Finally, passing the conn struct to your event handler we can send chunks to the client with the usual api.