nicoolas25
11/7/2014 - 11:40 PM

A super simple Elixir server for sending Server Sent Events to the browser.

A super simple Elixir server for sending Server Sent Events to the browser.

defmodule Sse do
  import Plug.Conn
  use Plug.Router
  
  plug :match
  plug :dispatch
  
  get "/" do
    conn
    |> put_resp_header("content-type", "text/html")
    |> send_file(200, "priv/static/index.html")
  end
  
  get "/sse" do
    conn = put_resp_header(conn, "content-type", "text/event-stream")
    conn = send_chunked(conn, 200)
    
    send_message(conn, "Look, Ma'! I'm streaming!")
    :timer.sleep(1000)
    send_message(conn, "It only took two lines of code!")
    :timer.sleep(1000)
    send_message(conn, "All you have to do is set a header and chunk the response!")
    :timer.sleep(1000)
    send_message(conn, "Bye now!")
    conn
  end
  
  defp send_message(conn, message) do
    chunk(conn, "event: \"message\"\n\ndata: {\"message\": \"#{message}\"}\n\n")
  end
end

# Run with mix run --no-halt lib/sse.ex
Plug.Adapters.Cowboy.http Sse, [], port: 4000
<!DOCTYPE html>
<html>
	<head>
		<script type="text/javascript">
			function ready() {
				if (!!window.EventSource) {
					setupEventSource();
				} else {
					document.getElementById('status').innerHTML =
						"Sorry but your browser doesn't support the EventSource API";
				}
			}

			function setupEventSource() {
				var source = new EventSource('/sse');

				source.addEventListener('message', function(event) {
					addStatus("server sent the following: '" + event.data + "'");
					}, false);

					source.addEventListener('open', function(event) {
						addStatus('eventsource connected.')
					}, false);

					source.addEventListener('error', function(event) {
						if (event.eventPhase == EventSource.CLOSED) {
							addStatus('eventsource was closed.')
						}
					}, false);
			}

			function addStatus(text) {
				var date = new Date();
				document.getElementById('status').innerHTML
				= document.getElementById('status').innerHTML
				+ date + ": " + text + "<br/>";
			}
		</script>
	</head>
	<body onload="ready();">
		Hi!
		<div id="status"></div>
	</body>
</html>

Generate a new Elixir project using mix and add cowboy and plug as dependencies in mix.exs:

  defp deps do
    [
      {:cowboy, "~> 1.0.0"},
      {:plug, "~> 0.8.1"}
    ]
  end

Then add the sse.ex file below to lib/ and run the code with mix run --no-halt lib/sse.ex. Point your browser to http://localhost:4000 and you're done.

If you don't feel like writing your own EventSource JavaScript, you can use the example in index.html below that comes from Cowboy. Just create a priv/static directory inside of your project.