Files
rojo/src/web.rs
Lucien Greathouse f89d491f29 Run rustfmt
I ignored some odd formatting it introduced relating to putting braces on newlines in if-let blocks. This might be a bug, but I didn't find any way to turn that off.
2017-12-13 12:05:11 -08:00

171 lines
4.3 KiB
Rust

use std::io::Read;
use std::sync::{Arc, Mutex};
use std::thread;
use rouille;
use serde;
use serde_json;
use core::Config;
use project::Project;
use vfs::{Vfs, VfsChange, VfsItem};
static MAX_BODY_SIZE: usize = 25 * 1024 * 1025; // 25 MiB
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
struct ServerInfo<'a> {
server_version: &'static str,
protocol_version: u64,
server_id: &'a str,
project: &'a Project,
current_time: f64,
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
struct ReadResult<'a> {
items: Vec<Option<VfsItem>>,
server_id: &'a str,
current_time: f64,
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
struct ChangesResult<'a> {
changes: &'a [VfsChange],
server_id: &'a str,
current_time: f64,
}
fn json<T: serde::Serialize>(value: T) -> rouille::Response {
let data = serde_json::to_string(&value).unwrap();
rouille::Response::from_data("application/json", data)
}
fn read_json_text(request: &rouille::Request) -> Option<String> {
match request.header("Content-Type") {
Some(header) => if !header.starts_with("application/json") {
return None;
},
None => return None,
}
let body = match request.data() {
Some(v) => v,
None => return None,
};
let mut out = Vec::new();
match body.take(MAX_BODY_SIZE.saturating_add(1) as u64)
.read_to_end(&mut out)
{
Ok(_) => {},
Err(_) => return None,
}
if out.len() > MAX_BODY_SIZE {
return None;
}
let parsed = match String::from_utf8(out) {
Ok(v) => v,
Err(_) => return None,
};
Some(parsed)
}
fn read_json<T>(request: &rouille::Request) -> Option<T>
where
T: serde::de::DeserializeOwned,
{
let body = match read_json_text(&request) {
Some(v) => v,
None => return None,
};
let parsed = match serde_json::from_str(&body) {
Ok(v) => v,
Err(_) => return None,
};
Some(parsed)
}
pub fn start(config: Config, project: Project, vfs: Arc<Mutex<Vfs>>) {
let address = format!("localhost:{}", config.port);
let server_id = config.server_id.to_string();
thread::spawn(move || {
rouille::start_server(address, move |request| {
router!(request,
(GET) (/) => {
let current_time = {
let vfs = vfs.lock().unwrap();
vfs.current_time()
};
json(ServerInfo {
server_version: env!("CARGO_PKG_VERSION"),
protocol_version: 0,
server_id: &server_id,
project: &project,
current_time,
})
},
(GET) (/changes/{ last_time: f64 }) => {
let vfs = vfs.lock().unwrap();
let current_time = vfs.current_time();
let changes = vfs.changes_since(last_time);
json(ChangesResult {
changes,
server_id: &server_id,
current_time,
})
},
(POST) (/read) => {
let read_request: Vec<Vec<String>> = match read_json(&request) {
Some(v) => v,
None => return rouille::Response::empty_400(),
};
let (items, current_time) = {
let vfs = vfs.lock().unwrap();
let current_time = vfs.current_time();
let mut items = Vec::new();
for route in &read_request {
match vfs.read(&route) {
Ok(v) => items.push(Some(v)),
Err(_) => items.push(None),
}
}
(items, current_time)
};
json(ReadResult {
server_id: &server_id,
items,
current_time,
})
},
(POST) (/write) => {
rouille::Response::empty_404()
},
_ => rouille::Response::empty_404()
)
});
});
}