1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
use std::default::Default;
use std::io::prelude::*;

use hyper::{Get, Post};
use hyper::status::StatusCode;
use hyper::server::{Server, Request, Response};
use hyper::uri::RequestUri::AbsolutePath;
use serde_json;

use errors::Result;
use panel::{Panel, panel_html};

/// Server configuration.
pub struct ServerConfig {
    /// Whether to print messages, such as the listen address.
    pub verbose: bool,

    /// IP address and port on which to listen.
    ///
    /// This is a string of the form `"127.0.0.1:1337"`.
    /// Use `"0.0.0.0:1337"` to listen on all network
    /// interfaces.
    pub listen_on: String,
}

impl Default for ServerConfig {
    fn default() -> ServerConfig {
        ServerConfig {
            verbose: true,
            listen_on: "127.0.0.1:1337".to_owned(),
        }
    }
}

/// Serve a panel.
///
/// `f` is the callback for panel updates.
pub fn serve<P, F>(f: F) -> Result<()>
    where P: Panel,
          F: Fn(P) + Sync + Send + 'static,
{
    serve_with_config::<P, _>(Default::default(), f)
}

/// Serve a panel and configure the server.
///
/// `f` is the callback for panel updates.
pub fn serve_with_config<P, F>(cfg: ServerConfig, f: F) -> Result<()>
    where P: Panel,
          F: Fn(P) + Sync + Send + 'static,
{
    let panel_html = panel_html::<P>();

    let server = Server::http(&cfg.listen_on)?;

    let handle_update = move |req: &mut Request| -> Result<()> {
        let mut json = String::new();
        req.read_to_string(&mut json)?;

        let panel = serde_json::from_str(&json)?;
        f(panel);

        Ok(())
    };

    let _guard = server.handle(move |mut req: Request, mut res: Response| {
        // Clone to avoid borrowck, idgaf
        match req.uri.clone() {
            AbsolutePath(ref path) => match (&req.method, &path[..]) {
                (&Get, "/") => {
                    if let Err(e) = res.send(panel_html.as_bytes()) {
                        error!("Could not send panel page: {}", e);
                    }
                },

                (&Post, "/update") => {
                    if let Err(e) = handle_update(&mut req) {
                        warn!("Ignoring erroneous update: {}", e);
                        *res.status_mut() = StatusCode::BadRequest;
                    }
                },

                _ => {
                    *res.status_mut() = StatusCode::NotFound;
                }
            },

            _ => {
                *res.status_mut() = StatusCode::NotFound;
            }
        }
    });

    if cfg.verbose {
        println!("Listening on {}", cfg.listen_on);
    }

    Ok(())
}