hyper-sse 0.1.0

Server-Sent Events (SSE) library for hyper and Rocket
extern crate hyper_sse;

extern crate futures;
extern crate hyper;
#[macro_use]
extern crate lazy_static;
extern crate tokio;

use hyper::{Body, Request, Response};
use hyper_sse::Server;
use std::io::{self, BufRead};

lazy_static! {
    static ref PUSH_SERVER: Server<u8> = Server::new();
}

const HTML: &'static str = "\
<html>
<head>
    <title>hyper_sse demo</title>
    <meta charset=\"utf-8\" />
</head>
<body>
    <ol></ol>
    <script>
        var evtSource = new EventSource('http://[::1]:3000/push/0?%auth_token%');
        evtSource.addEventListener('update', event => {
            var newElement = document.createElement('li');
            var eventList = document.querySelector('ol');

            newElement.innerHTML = JSON.parse(event.data);
            eventList.appendChild(newElement);
        });
    </script>
</body>";

fn service_handler(req: Request<Body>) -> Response<Body> {
    match req.uri().path() {
        "/" => {
            let auth_cookie = PUSH_SERVER.generate_auth_token(Some(0)).unwrap();

            Response::builder()
                .body(HTML.replace("%auth_token%", &auth_cookie).into())
                .expect("Invalid header specification")
        },
        _ => {
            PUSH_SERVER.create_stream(&req)
        }
    }
}

fn spawn_server(server: &'static Server<u8>) {
    use futures::future;
    use hyper::rt::{Future, Stream};
    use hyper::service::service_fn_ok;
    use std::thread;
    use std::time::{Duration, Instant};
    use tokio::timer::Interval;

    let addr = ("[::1]:3000").parse().unwrap();

    let http_server = hyper::Server::bind(&addr)
        .serve(move || service_fn_ok(service_handler))
        .map_err(|e| panic!("Push server failed: {}", e));

    let maintenance = Interval::new(Instant::now(), Duration::from_secs(45))
        .for_each(move |_| {
            server.remove_stale_clients();
            server.send_heartbeats();
            future::ok(())
        })
        .map_err(|e| panic!("Push maintenance failed: {}", e));

    thread::spawn(move || {
        hyper::rt::run(
            http_server
            .join(maintenance)
            .map(|_| ())
        );
    });
}

fn main() {
    spawn_server(&PUSH_SERVER);

    println!("Open to following page in your browser: http://[::1]:3000/");
    println!("Enter push message and press enter:");

    let stdin = io::stdin();
    for line in stdin.lock().lines() {
        let line = line.unwrap();

        PUSH_SERVER.push(0, "update", &line).ok();
    }
}