From 39b9b6aa9e33205153afd4a7fe9d9e7c614d0420 Mon Sep 17 00:00:00 2001 From: Lorenz Stechauner Date: Sun, 16 May 2021 19:16:16 +0200 Subject: [PATCH] Some error handling --- src/http/handler.rs | 183 ++++++++++++++++++++++++++++++-------------- src/http/mod.rs | 10 ++- src/http/parser.rs | 9 ++- 3 files changed, 142 insertions(+), 60 deletions(-) diff --git a/src/http/handler.rs b/src/http/handler.rs index d76aaf8..179291f 100644 --- a/src/http/handler.rs +++ b/src/http/handler.rs @@ -31,65 +31,136 @@ pub fn connection_handler(client: super::Stream) { fn request_handler(client: &mut super::HttpStream) { let mut res = super::Response::new(); - let req = super::parser::parse_request(&mut client.stream).unwrap(); - println!("{} {}", req.method, req.uri); + match super::parser::parse_request(&mut client.stream) { + Ok(Some(req)) => { + println!("{} {}", req.method, req.uri); - if !req.uri.starts_with("/") - || req.uri.contains("/./") - || req.uri.contains("/../") - || req.uri.ends_with("/..") - { - res.status(400); - } else if req.uri.contains("/.") { - res.status(404); - } else if req.uri.eq("/") { - res.status(200); - } else if req.uri.eq("/_usimp/websocket") { - return websocket::connection_handler(client, &req); - } else if req.uri.starts_with("/_usimp/") { - let parts: Vec<&str> = req.uri.split('/').collect(); - match parts[2..] { - ["entity", entity] => res.status(501), - [endpoint] => match usimp::is_valid_endpoint(endpoint) { - true => match req.method { - Method::POST => { - let mut buf = [0; 8192]; - - let length = req.find_header("Content-Length"); - let length: usize = length.unwrap().parse().unwrap(); - - client.stream.read_exact(&mut buf[..length]); - - // TODO decompress - let input = - json::parse(std::str::from_utf8(&buf[..length]).unwrap()).unwrap(); - let output = usimp::endpoint(endpoint, input); - - // TODO compress - let buf = output.to_string() + "\n"; - let length = buf.as_bytes().len(); - res.add_header("Content-Length", length.to_string().as_str()); - res.add_header("Content-Type", "application/json; charset=utf-8"); - - res.status(200); - res.send(&mut client.stream); - client.stream.write_all(buf.as_bytes()); - - return; - } - _ => { - res.status(405); - res.add_header("Allow", "POST"); - } - }, - false => res.status(400), - }, - _ => res.status(400), + if !req.uri.starts_with("/") + || req.uri.contains("/./") + || req.uri.contains("/../") + || req.uri.ends_with("/..") + { + res.status(400); + } else if req.uri.contains("/.") { + res.status(404); + } else if req.uri.eq("/") { + res.status(200); + } else if req.uri.eq("/_usimp/websocket") { + return websocket::connection_handler(client, &req); + } else if req.uri.starts_with("/_usimp/") { + let parts: Vec<&str> = req.uri.split('/').collect(); + match parts[2..] { + ["entity", entity] => res.status(501), + [endpoint] => match usimp::is_valid_endpoint(endpoint) { + true => match req.method { + Method::POST => { + return endpoint_handler(client, &req, &mut res, endpoint) + } + _ => { + res.status(405); + res.add_header("Allow", "POST"); + } + }, + false => res.status(400), + }, + _ => res.status(400), + } + } else { + res.status(404); + } + } + Ok(None) => { + client.client_keep_alive = false; + return; + } + Err(e) => { + res.status(400); + res.error_info(format!("Unable to parser header: {}", &e)); + println!("Unable to parser header: {}", &e); + client.server_keep_alive = false; } - } else { - res.status(404); } - res.send(&mut client.stream).unwrap(); + if let Err(e) = res.send(&mut client.stream) { + println!("Unable to send: {}", e); + client.server_keep_alive = false; + } client.server_keep_alive = false; } + +fn endpoint_handler( + client: &mut super::HttpStream, + req: &super::Request, + res: &mut super::Response, + endpoint: &str, +) { + let mut buf = [0; 8192]; + + let mut error = |code: u16, err_str: &str, client: &mut super::HttpStream| { + println!("{}", err_str); + res.status(code); + res.error_info(err_str.to_string()); + + if let Err(e) = res.send(&mut client.stream) { + println!("Unable to send: {}", e); + client.server_keep_alive = false; + } + }; + + let length = req.find_header("Content-Length"); + let length: usize = match match length { + Some(length) => length, + None => { + return error( + 400, + "Unable to parse header: Content-Length missing", + client, + ) + }, + } + .parse() + { + Ok(length) => length, + Err(e) => { + return error( + 400, + format!("Unable to parse Content-Length: {}", &e).as_str(), + client, + ) + }, + }; + + client.stream.read_exact(&mut buf[..length]); + + // TODO decompress + let input = match json::parse(match std::str::from_utf8(&buf[..length]) { + Ok(source) => source, + Err(e) => { + return error( + 400, + format!("Unable to parse payload: {}", &e).as_str(), + client, + ) + }, + }) { + Ok(val) => val, + Err(e) => { + return error( + 400, + format!("Unable to parse JSON: {}", &e).as_str(), + client, + ) + }, + }; + let output = usimp::endpoint(endpoint, input); + + // TODO compress + let buf = output.to_string() + "\r\n"; + let length = buf.as_bytes().len(); + res.add_header("Content-Length", length.to_string().as_str()); + res.add_header("Content-Type", "application/json; charset=utf-8"); + + res.status(200); + res.send(&mut client.stream); + client.stream.write_all(buf.as_bytes()); +} diff --git a/src/http/mod.rs b/src/http/mod.rs index acdf06b..9968a0f 100644 --- a/src/http/mod.rs +++ b/src/http/mod.rs @@ -97,6 +97,7 @@ pub struct Status { message: String, desc: &'static str, class: StatusClass, + info: Option, } impl Status { @@ -108,6 +109,7 @@ impl Status { message: msg.to_string(), desc, class: class.clone(), + info: None, }); } } @@ -124,6 +126,7 @@ impl Status { message: message.to_string(), desc: status.desc, class: status.class, + info: None, } } else { Status { @@ -131,6 +134,7 @@ impl Status { message: message.to_string(), desc: "", class: StatusClass::from_code(status_code), + info: None, } } } @@ -188,6 +192,10 @@ impl Response { self.status = Status::from_code(status_code).unwrap() } + pub fn error_info(&mut self, info: String) { + self.status.info = Some(info); + } + pub fn add_header(&mut self, name: &str, value: &str) { self.header_fields.push(HeaderField { name: String::from(name), @@ -266,7 +274,7 @@ impl Response { doc.replace("{code}", self.status.code.to_string().as_str()) .replace("{message}", self.status.message.as_str()) .replace("{desc}", self.status.desc) - .replace("{info}", "") // TODO info string + .replace("{info}", self.status.info.as_ref().unwrap_or(&String::new()).as_str()) .as_str(), ) .replace("{{", "{") diff --git a/src/http/parser.rs b/src/http/parser.rs index 598e729..15bc12e 100644 --- a/src/http/parser.rs +++ b/src/http/parser.rs @@ -1,12 +1,15 @@ use crate::http; use crate::http::Status; -pub fn parse_request(stream: &mut http::Stream) -> Result { +pub fn parse_request(stream: &mut http::Stream) -> Result, String> { let mut buf = [0; 4096]; let size = stream.peek(&mut buf).unwrap(); + if size == 0 { + return Ok(None) + } let mut parser = Parser::new_request_parser(&buf[..size]); - let header_size = parser.parse().unwrap(); + let header_size = parser.parse()?; let mut header_fields = Vec::new(); for (name, value) in parser.headers { @@ -25,7 +28,7 @@ pub fn parse_request(stream: &mut http::Stream) -> Result stream.read_exact(&mut buf[..header_size]).unwrap(); - Ok(request) + Ok(Some(request)) } pub fn parse_response(stream: &mut http::Stream) -> Result {