1
2use iron::{Iron, IronResult, Listening, status};
3use iron::error::HttpResult;
4use iron::response::Response;
5#[cfg(feature = "gzip")]
6use iron::response::WriteBody;
7use iron::request::Request;
8use iron::middleware::Handler;
9use iron::mime::Mime;
10
11use sysinfo::{System, SystemExt};
12
13#[cfg(feature = "gzip")]
14use flate2::Compression;
15#[cfg(feature = "gzip")]
16use flate2::write::GzEncoder;
17
18use std::sync::{Arc, Mutex, RwLock};
19use std::thread;
20use std::time::{Duration, SystemTime};
21#[cfg(feature = "gzip")]
22use std::io::{self, Write};
23
24use SysinfoExt;
25use serde_json;
26
27
28const INDEX_HTML: &'static [u8] = include_bytes!("index.html");
29const FAVICON: &'static [u8] = include_bytes!("../resources/favicon.ico");
30const REFRESH_DELAY: u64 = 60 * 10; #[cfg(feature = "gzip")]
34struct GzipContent(Box<WriteBody>);
35
36#[cfg(feature = "gzip")]
37impl WriteBody for GzipContent {
38 fn write_body(&mut self, w: &mut Write) -> io::Result<()> {
39 let mut w = GzEncoder::new(w, Compression::default());
40 self.0.write_body(&mut w)?;
41 w.finish().map(|_| ())
42 }
43}
44
45struct SysinfoIronHandler(Arc<DataHandler>);
46
47struct DataHandler {
48 system: RwLock<System>,
49 last_connection: Mutex<SystemTime>,
50 json_output: RwLock<String>,
51}
52
53impl DataHandler {
54 fn can_update_system_info(&self) -> bool {
55 SystemTime::now().duration_since(*self.last_connection.lock().unwrap())
56 .unwrap()
57 .as_secs() < REFRESH_DELAY
58 }
59
60 fn update_last_connection(&self) {
61 *self.last_connection.lock().unwrap() = SystemTime::now();
62 }
63}
64
65#[cfg(feature = "gzip")]
66macro_rules! return_gzip_or_not {
67 ($req:expr, $content:expr, $typ:expr) => {{
68 let mut use_gzip = false;
69
70 if let Some(raw_accept_encoding) = $req.headers.get_raw("accept-encoding") {
71 for accept_encoding in raw_accept_encoding {
72 match ::std::str::from_utf8(accept_encoding).map(|s| s.to_lowercase()) {
73 Ok(ref s) if s.contains("gzip") => {
74 use_gzip = true;
75 break;
76 }
77 _ => continue,
78 }
79 }
80 }
81 if !use_gzip {
82 Ok(Response::with((status::Ok, $typ.parse::<Mime>().unwrap(), $content)))
83 } else {
84 use iron::headers::{ContentType, ContentEncoding, Encoding};
85 let mut res = Response::new();
86 res.status = Some(status::Ok);
87 res.body = Some(Box::new(GzipContent(Box::new($content))));
88 res.headers.set(ContentType($typ.parse::<Mime>().unwrap()));
89 res.headers.set(ContentEncoding(vec![Encoding::Gzip]));
90 Ok(res)
91 }
92 }}
93}
94
95#[cfg(not(feature = "gzip"))]
96macro_rules! return_gzip_or_not {
97 ($req:expr, $content:expr, $typ:expr) => {{
98 Ok(Response::with((status::Ok, $typ.parse::<Mime>().unwrap(), $content)))
99 }}
100}
101
102impl Handler for SysinfoIronHandler {
103 fn handle(&self, req: &mut Request) -> IronResult<Response> {
104 match match req.url.path().last() {
105 Some(path) => {
106 if *path == "" {
107 1
108 } else if *path == "favicon.ico" {
109 2
110 } else {
111 3
112 }
113 }
114 None => 0,
115 } {
116 1 => return_gzip_or_not!(req, INDEX_HTML, "text/html"),
117 2 => return_gzip_or_not!(req, FAVICON, "image/x-icon"),
118 3 => {
119 self.0.update_last_connection();
120 return_gzip_or_not!(req,
121 self.0.json_output.read().unwrap().clone(),
122 "application/json")
123 }
124 _ => Ok(Response::with((status::NotFound, "Not found"))),
125 }
126 }
127}
128
129pub fn start_web_server(sock_addr: Option<String>) -> HttpResult<Listening> {
130 let data_handler = Arc::new(DataHandler {
131 system: RwLock::new(System::new()),
132 last_connection: Mutex::new(SystemTime::now()),
133 json_output: RwLock::new(String::from("[]")),
134 });
135 let data_handler_clone = data_handler.clone();
136 thread::spawn(move || {
137 let mut sleeping = false;
138 loop {
139 if data_handler_clone.can_update_system_info() {
140 {
141 let mut system = data_handler_clone.system.write().unwrap();
142 system.refresh_all();
143 if sleeping {
145 system.refresh_all();
146 sleeping = false;
147 }
148 let sysinfo = SysinfoExt::new(&system);
149 let mut json_output = data_handler_clone.json_output.write().unwrap();
150 json_output.clear();
151 use std::fmt::Write;
152 json_output.write_str(&serde_json::to_string(&sysinfo)
153 .unwrap_or(String::from("[]"))).unwrap();
154 }
155 thread::sleep(Duration::new(5, 0));
156 } else {
157 thread::sleep(Duration::from_millis(500));
159 sleeping = true;
160 }
161 }
162 });
163 let mut iron = Iron::new(SysinfoIronHandler(data_handler));
164 iron.threads = 4;
165 iron.http(sock_addr.unwrap_or("localhost:3000".to_owned()))
166}