PageRenderTime 45ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/cds-tool/src/server.rs

https://bitbucket.org/BarisYuncu/fcds-lab-2018
Rust | 272 lines | 222 code | 46 blank | 4 comment | 14 complexity | e61e8a0878e719cb842cbc745dc2bd4c MD5 | raw file
  1. extern crate router;
  2. extern crate rustc_serialize;
  3. extern crate base64;
  4. use iron;
  5. use iron::prelude::*;
  6. use iron::status;
  7. use self::router::Router;
  8. use self::rustc_serialize::json::Json;
  9. use serde_json;
  10. use std::thread;
  11. use std::fs::File;
  12. use std::path::Path;
  13. use std::sync::{Arc, Mutex};
  14. use std::time::Instant;
  15. use std::process::{Command, Stdio};
  16. use std::io::{Write, Read};
  17. use std::ops::DerefMut;
  18. use errors::*;
  19. #[derive(Serialize, Deserialize, Debug)]
  20. pub struct InvokeRequestBody {
  21. pub stdin: String,
  22. }
  23. #[derive(Serialize, Deserialize, Debug, Default)]
  24. pub struct InvokeResponseBody {
  25. pub stdout: String,
  26. pub stderr: String,
  27. pub exit_status: i32,
  28. pub duration: u64,
  29. pub error: Option<String>,
  30. }
  31. pub struct Server {
  32. port: u16,
  33. config: Vec<(String, String)>,
  34. }
  35. fn extract_config(json: Json) -> Result<Vec<(String, String)>> {
  36. let mut config = Vec::new();
  37. match json.as_array() {
  38. Some(arr) => {
  39. for i in 0..arr.len() {
  40. match arr[i].as_array() {
  41. Some(entry) => {
  42. ensure!(entry.len() == 2, "Entry {} of server config file doesn't contain two values!", i+1);
  43. ensure!(entry[0].is_string(), "First value of entry {} of server config file isn't a string!", i+1);
  44. ensure!(entry[1].is_string(), "Second value of entry {} of server config file isn't a string!", i+1);
  45. config.push((entry[0].as_string().unwrap_or("UNEXPECTED ERROR").to_owned(), entry[1].as_string().unwrap_or("UNEXPECTED_ERROR").to_owned()))
  46. //TODO: make sure the path leads to an executable
  47. }
  48. None => bail!("Entry {} of server config file isn't an array!", i+1)
  49. }
  50. }
  51. },
  52. None => bail!("Server config file doesn't contain array!")
  53. }
  54. Ok(config)
  55. }
  56. struct HumanHelpHandler {
  57. server: Arc<Server>,
  58. }
  59. impl iron::Handler for HumanHelpHandler {
  60. fn handle(&self, _: &mut Request) -> IronResult<Response> {
  61. let mut body = String::new();
  62. body.push_str("CDS Lab server\nThis server forwards request to native applications\nThe following programs are known by this server:\n");
  63. for entry in self.server.as_ref().config.iter() {
  64. body.push_str(entry.0.as_str());
  65. body.push_str("\n");
  66. }
  67. let mut resp = Response::new();
  68. resp.status = Some(status::Ok);
  69. resp.body = Some(Box::new(body));
  70. Ok(resp)
  71. }
  72. }
  73. struct InvokeHandler {
  74. server: Arc<Server>,
  75. }
  76. impl InvokeHandler {
  77. fn json_error(&self, error_msg: String) -> String {
  78. let mut body: InvokeResponseBody = Default::default();
  79. body.error = Some(error_msg.clone());
  80. match serde_json::to_string(&body) {
  81. Ok(s) => s,
  82. Err(e) => {
  83. warn!("Error occurred during request handling, but unable to serialize error message ({}).", error_msg);
  84. String::new()
  85. }
  86. }
  87. }
  88. }
  89. impl iron::Handler for InvokeHandler {
  90. fn handle(&self, req: &mut Request) -> IronResult<Response> {
  91. let router = req.extensions.get::<Router>()
  92. .expect("internal error: InvokeHandler request has no Router extension!");
  93. let program = router
  94. .find("program")
  95. .expect("internal error: InvokerHandler request contains no :program param!");
  96. debug!("Received run request for program {}", program);
  97. debug!("Reading request's body");
  98. let mut req_body = String::new();
  99. itry!(req.body.read_to_string(&mut req_body), self.json_error(format!("Unable to read request's body")));
  100. debug!("Parsing JSON");
  101. let req_body: InvokeRequestBody = itry!(
  102. serde_json::from_str(req_body.as_str()),
  103. self.json_error(format!("Unable to decode request's JSON body"))
  104. );
  105. debug!("Looking up location of {} ...", program);
  106. let location = match self.server.as_ref().config.iter().find(|ref x| x.0 == program) {
  107. Some(entry) => &entry.1,
  108. None => {
  109. warn!("No such program in server configuration file!");
  110. let msg = format!("Unable to resolve location of program {}! No such entry in server configuration", program);
  111. return Err(IronError::new(Error::from_kind(ErrorKind::Msg(msg.clone())), self.json_error(msg)))
  112. }
  113. };
  114. debug!("Program is located at {}. Invoking program ...", location);
  115. let mut child = itry!(Command::new(location)
  116. .stdin(Stdio::piped())
  117. .stdout(Stdio::piped())
  118. .stderr(Stdio::piped())
  119. .spawn(),
  120. self.json_error(format!("Unable to invoke program {} (location in container: {})", program, location)));
  121. // let child_id = child.id();
  122. // alternative: open /proc/:child_id/stat regularly parse for Zombie state, collect user, sys
  123. // issue with alternative: how to measure wall time?
  124. debug!("Decoding base64 encoded stdin message ...");
  125. let stdin_decoded = itry!(base64::decode(req_body.stdin.as_str()), self.json_error("Unable to decode base64 stdin content".to_owned()));
  126. debug!("Sending input to stdin ...");
  127. match child.stdin.take() {
  128. Some(mut stdin) => {
  129. itry!(
  130. stdin.write_all(stdin_decoded.as_slice()),
  131. self.json_error("Unable to write to stdin of invoked program".to_owned())
  132. );
  133. itry!(stdin.flush(), self.json_error("Unable to flush stdin of invoked program".to_owned()));
  134. drop(stdin);
  135. },
  136. None => {
  137. itry!(child.kill(), self.json_error("Unable to send kill signal to invoked program".to_owned()));
  138. let msg = format!("Unable to open stdin of invoked program");
  139. return Err(IronError::new(Error::from_kind(ErrorKind::Msg(msg.clone())), self.json_error(msg)));
  140. }
  141. };
  142. debug!("Starting timer ...");
  143. let start = Instant::now();
  144. let mut stdout = match child.stdout.take() {
  145. Some(stdout) => stdout,
  146. None => {
  147. let msg = format!("Unable to obtain stdout of invoked program");
  148. return Err(IronError::new(Error::from_kind(ErrorKind::Msg(msg.clone())), self.json_error(msg)));
  149. }
  150. };
  151. debug!("Spawning stdout & stderr collector threads");
  152. let stdout_str = Arc::new(Mutex::new(String::new()));
  153. let stdout_str2 = stdout_str.clone();
  154. let stdout_collector = thread::spawn(move || {
  155. match stdout_str2.lock() {
  156. Ok(mut stdout_guard) => {
  157. if let Err(e) = stdout.read_to_string(stdout_guard.deref_mut()) {
  158. warn!("Unable to read stdout of program: {:?}", e);
  159. }
  160. },
  161. Err(e) => warn!("Unable to lock stdout collector stream: {:?}", e)
  162. };
  163. });
  164. let stderr_str = Arc::new(Mutex::new(String::new()));
  165. let stderr_str2 = stderr_str.clone();
  166. let stderr_collector = match child.stderr.take() {
  167. Some(mut stderr) => {
  168. Some(thread::spawn(move || {
  169. match stderr_str2.lock() {
  170. Ok(mut stderr_guard) => match stderr.read_to_string(stderr_guard.deref_mut()) {
  171. Err(e) => warn!("Unable to read stderr of program: {:?}", e),
  172. _ => {},
  173. },
  174. Err(e) => warn!("Unable to lock stderr collector stream: {:?}", e)
  175. };
  176. }))
  177. },
  178. None => {
  179. warn!("Unable to obtain program's stderr!");
  180. None
  181. },
  182. };
  183. debug!("Waiting for program to terminate ...");
  184. let exit_status = itry!(child.wait(), self.json_error("Waiting for child program failed".to_owned()));
  185. let duration = start.elapsed();
  186. debug!("Program terminated. Joining collector threads ...");
  187. if let Err(e) = stdout_collector.join() {
  188. warn!("Unable to join stdout collector thread: {:?}", e);
  189. }
  190. if let Some(t) = stderr_collector {
  191. if let Err(e) = t.join() {
  192. warn!("Unable to join stderr collector thread: {:?}", e);
  193. }
  194. };
  195. debug!("Collector threads joined. Generating response ...");
  196. let mut resp_body: InvokeResponseBody = Default::default();
  197. resp_body.exit_status = exit_status.code().unwrap_or(-1);
  198. resp_body.duration = duration.as_secs() * 1000 * 1000 + duration.subsec_nanos() as u64 / 1000;
  199. if let Ok(stdout) = stdout_str.lock() {
  200. resp_body.stdout = stdout.to_string();
  201. };
  202. if let Ok(stderr) = stderr_str.lock() {
  203. resp_body.stderr = stderr.to_string();
  204. }
  205. Ok(Response::with((status::Ok, serde_json::to_string(&resp_body).unwrap())))
  206. }
  207. }
  208. impl Server {
  209. pub fn new(config_path: &Path, port: u16) -> Result<Server> {
  210. let mut config_file = try!(File::open(config_path)
  211. .chain_err(|| format!("Unable to open server config file {}", config_path.display())));
  212. let config_json = try!(Json::from_reader(&mut config_file)
  213. .chain_err(|| format!("Unable to parse server config file {}", config_path.display())));
  214. let config = try!(extract_config(config_json)
  215. .chain_err(|| format!("Unable to read server config file {}", config_path.display())));
  216. Ok(Server{port: port, config: config})
  217. }
  218. pub fn run(self) -> Result<Arc<Server>> {
  219. let arc = Arc::new(self);
  220. let mut router = Router::new();
  221. router.post("/run/:program", InvokeHandler{server: arc.clone()} , "invoke");
  222. router.any("/*", HumanHelpHandler{server: arc.clone()}, "human");
  223. try!(Iron::new(router).http(("0.0.0.0", arc.as_ref().port)).chain_err(|| "Unable to start server!"));
  224. Ok(arc)
  225. }
  226. }