From 26a8231fa3cdd7329bdd9c4d53e7a02be99d6e80 Mon Sep 17 00:00:00 2001 From: Tiffany Bennett Date: Wed, 5 Oct 2016 16:01:31 -0400 Subject: [PATCH] web: Use json serialization for templating --- web/src/main.rs | 14 +++++++-- web/src/worker.rs | 75 +++++++++++++++++++++++++++++++---------------- 2 files changed, 61 insertions(+), 28 deletions(-) diff --git a/web/src/main.rs b/web/src/main.rs index f2e27d7..e5ddfb6 100644 --- a/web/src/main.rs +++ b/web/src/main.rs @@ -2,6 +2,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. +#![feature(rustc_macro)] + extern crate rink; extern crate iron; extern crate router; @@ -12,6 +14,11 @@ extern crate staticfile; extern crate mount; extern crate ipc_channel; extern crate libc; +extern crate rustc_serialize; +extern crate serde; +extern crate serde_json; +#[macro_use] +extern crate serde_derive; pub mod worker; @@ -26,7 +33,7 @@ use staticfile::Static; use std::collections::BTreeMap; use params::{Params, Value}; use std::env; -use worker::eval; +use worker::{eval_text, eval_json}; fn root(req: &mut Request) -> IronResult { let mut data = BTreeMap::new(); @@ -34,7 +41,8 @@ fn root(req: &mut Request) -> IronResult { let map = req.get_ref::().unwrap(); match map.find(&["q"]) { Some(&Value::String(ref query)) => { - let reply = eval(query); + let reply = eval_json(query); + println!("{}", reply.pretty()); data.insert("content".to_owned(), reply); }, _ => (), @@ -52,7 +60,7 @@ fn api(req: &mut Request) -> IronResult { _ => return Ok(Response::with((acao, status::BadRequest))), }; - let reply = eval(query); + let reply = eval_text(query); Ok(Response::with((acao, status::Ok, reply))) } diff --git a/web/src/worker.rs b/web/src/worker.rs index 2c3d233..cce3985 100644 --- a/web/src/worker.rs +++ b/web/src/worker.rs @@ -1,15 +1,32 @@ use libc; -use std::io::Error; -use ipc_channel::ipc::{IpcOneShotServer, IpcSender}; +use ipc_channel::ipc::{IpcOneShotServer, IpcSender, IpcReceiver}; use std::process::{Command, Stdio}; use std::env; use rink; +use rink::reply::{QueryReply, QueryError}; use std::os::unix::process::ExitStatusExt; +use std::io; +use rustc_serialize; +use serde_json; + +#[derive(Debug, Serialize)] +pub enum Error { + Rink(QueryError), + Time, + Memory, + Generic(String), +} + +impl From for Error { + fn from(err: io::Error) -> Error { + Error::Generic(format!("{}", err)) + } +} pub fn worker(server_name: &str, query: &str) -> ! { let tx = IpcSender::connect(server_name.to_owned()).unwrap(); - tx.send("".to_owned()).unwrap(); + tx.send(Err(QueryError::Generic("".to_owned()))).unwrap(); unsafe { let limit = libc::rlimit { @@ -19,7 +36,7 @@ pub fn worker(server_name: &str, query: &str) -> ! { }; let res = libc::setrlimit(libc::RLIMIT_AS, &limit); if res == -1 { - panic!("Setrlimit RLIMIT_AS failed: {}", Error::last_os_error()) + panic!("Setrlimit RLIMIT_AS failed: {}", io::Error::last_os_error()) } let limit = libc::rlimit { // 15 seconds @@ -28,22 +45,21 @@ pub fn worker(server_name: &str, query: &str) -> ! { }; let res = libc::setrlimit(libc::RLIMIT_CPU, &limit); if res == -1 { - panic!("Setrlimit RLIMIT_AS failed: {}", Error::last_os_error()) + panic!("Setrlimit RLIMIT_AS failed: {}", io::Error::last_os_error()) } } let mut ctx = rink::load().unwrap(); ctx.short_output = true; - let reply = match rink::one_line(&mut ctx, query) { - Ok(v) => v, - Err(e) => e - }; + let mut iter = rink::text_query::TokenIterator::new(query).peekable(); + let expr = rink::text_query::parse_query(&mut iter); + let reply = ctx.eval_outer(&expr); tx.send(reply).unwrap(); ::std::process::exit(0) } -pub fn eval(query: &str) -> String { +pub fn eval(query: &str) -> Result { let (server, server_name) = IpcOneShotServer::new().unwrap(); let res = Command::new(env::current_exe().unwrap()) @@ -54,28 +70,22 @@ pub fn eval(query: &str) -> String { .stderr(Stdio::piped()) .stdout(Stdio::piped()) .env("RUST_BACKTRACE", "1") - .spawn() - .map_err(|x| format!("{}", x)); - let child = match res { - Ok(s) => s, - Err(e) => return format!("Failed to run sandbox: {}", e) - }; + .spawn(); + let child = try!(res); let (rx, _) = server.accept().unwrap(); + let rx: IpcReceiver> = rx; match rx.recv() { - Ok(s) => s, + Ok(s) => s.map_err(Error::Rink), Err(e) => { - let output = match child.wait_with_output() { - Ok(v) => v, - Err(e) => return format!("{}", e) - }; + let output = try!(child.wait_with_output()); match output.status.signal() { - Some(libc::SIGXCPU) => return format!("Calculation went over time limit"), + Some(libc::SIGXCPU) => return Err(Error::Time), // SIGABRT doesn't necessarily mean OOM, but GMP will raise it when it happens - Some(libc::SIGABRT) => return format!("Calculation ran out of memory"), + Some(libc::SIGABRT) => return Err(Error::Memory), _ => () }; - format!( + Err(Error::Generic(format!( "Receiving reply from sandbox failed: {}\n\ Signal: {}\n\ Sandbox stdout: {}\n\ @@ -84,7 +94,22 @@ pub fn eval(query: &str) -> String { output.status.signal().map(|x| format!("{}", x)).unwrap_or("None".to_owned()), String::from_utf8_lossy(&output.stdout), String::from_utf8_lossy(&output.stderr) - ) + ))) } } } + +pub fn eval_text(query: &str) -> String { + match eval(query) { + Ok(v) => format!("{}", v), + Err(Error::Generic(e)) => format!("{}", e), + Err(Error::Memory) => format!("Calculation ran out of memory"), + Err(Error::Time) => format!("Calculation timed out"), + Err(Error::Rink(e)) => format!("{}", e), + } +} + +pub fn eval_json(query: &str) -> rustc_serialize::json::Json { + let res = eval(query); + rustc_serialize::json::Json::from_str(&serde_json::ser::to_string(&res).unwrap()).unwrap() +}