Skip to content

Notify chunked connection pid of EventSource client closing connection (or crashing) #228

@zampino

Description

@zampino

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.

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