Some more error handling

This commit is contained in:
2021-05-20 20:00:14 +02:00
parent 813660c022
commit b77c863065
6 changed files with 161 additions and 59 deletions

View File

@ -35,8 +35,8 @@ pub fn init() -> Result<(), Error> {
Ok(()) Ok(())
} }
pub fn client() -> Client { pub fn client() -> Result<Client, Error> {
match unsafe { POOL.as_ref().unwrap().clone().lock().unwrap().deref() } { match unsafe { POOL.as_ref().unwrap().clone().lock().unwrap().deref() } {
Pool::Postgres(pool) => Client::Postgres(pool.get().unwrap()), Pool::Postgres(pool) => Ok(Client::Postgres(pool.get()?)),
} }
} }

View File

@ -1,41 +1,113 @@
pub enum ErrorKind { use std::fmt;
use std::borrow::Borrow;
#[derive(Copy, Clone)]
pub enum Kind {
InvalidEndpointError, InvalidEndpointError,
JsonParseError, JsonParseError,
DatabaseConnectionError,
DatabaseError, DatabaseError,
HttpRequestParseError,
IoError,
}
#[derive(Copy, Clone)]
pub enum Class {
ClientError,
ServerError,
} }
pub struct Error { pub struct Error {
kind: ErrorKind, kind: Kind,
msg: Option<String>,
desc: Option<String>, desc: Option<String>,
class: Class,
} }
impl Error { impl Error {
pub fn new(kind: ErrorKind) -> Error { pub fn new(kind: Kind, class: Class) -> Self {
Error { kind, desc: None } Error {
kind,
msg: None,
desc: None,
class,
}
}
pub fn class(&self) -> &Class {
&self.class
}
pub fn set_msg(mut self, msg: String) -> Self {
self.msg = Some(msg);
self
}
pub fn msg(&self) -> &str {
match &self.msg {
Some(msg) => msg.as_str(),
None => match self.kind {
Kind::InvalidEndpointError => "Invalid endpoint",
Kind::JsonParseError => "Unable to parse JSON data",
Kind::DatabaseConnectionError => "Unable to connect to database",
Kind::DatabaseError => "Database error",
Kind::HttpRequestParseError => "Unable to parse http request",
Kind::IoError => "IO error",
},
}
}
pub fn set_desc(mut self, desc: String) -> Self {
self.desc = Some(desc);
self
}
pub fn desc(&self) -> Option<&str> {
match &self.desc {
Some(desc) => Some(desc.as_str()),
None => None,
}
} }
} }
impl ToString for Error { impl fmt::Display for Error {
fn to_string(&self) -> String { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut error = match self.kind { let mut error = match self.kind {
ErrorKind::InvalidEndpointError => "invalid endpoint", Kind::InvalidEndpointError => "invalid endpoint",
ErrorKind::JsonParseError => "unable to parse JSON data", Kind::JsonParseError => "unable to parse json data",
ErrorKind::DatabaseError => "unable to connect to database", Kind::DatabaseConnectionError => "unable to connect to database",
Kind::DatabaseError => "database error",
Kind::HttpRequestParseError => "unable to parse http request",
Kind::IoError => "io error",
} }
.to_string(); .to_string();
if let Some(desc) = &self.desc { if let Some(desc) = &self.desc {
error += ": "; error += ": ";
error += desc; error += desc;
} }
error write!(f, "{}", error);
Ok(())
}
}
impl From<std::io::Error> for Error {
fn from(error: std::io::Error) -> Self {
Error {
kind: Kind::IoError,
msg: Some(error.to_string()),
desc: Some(error.to_string()),
class: Class::ClientError,
}
} }
} }
impl From<serde_json::Error> for Error { impl From<serde_json::Error> for Error {
fn from(error: serde_json::Error) -> Self { fn from(error: serde_json::Error) -> Self {
Error { Error {
kind: ErrorKind::JsonParseError, kind: Kind::JsonParseError,
msg: Some("Unable to parse JSON data".to_string()),
desc: Some(error.to_string()), desc: Some(error.to_string()),
class: Class::ClientError,
} }
} }
} }
@ -43,8 +115,23 @@ impl From<serde_json::Error> for Error {
impl From<r2d2::Error> for Error { impl From<r2d2::Error> for Error {
fn from(error: r2d2::Error) -> Self { fn from(error: r2d2::Error) -> Self {
Error { Error {
kind: ErrorKind::DatabaseError, kind: Kind::DatabaseConnectionError,
msg: Some("Unable to connect to database".to_string()),
desc: Some(error.to_string()), desc: Some(error.to_string()),
class: Class::ServerError,
}
}
}
impl From<r2d2_postgres::postgres::Error> for Error {
fn from(error: r2d2_postgres::postgres::Error) -> Self {
// format: "db error: ERROR ..."
let msg = error.to_string().split(":").skip(1).collect::<String>();
Error {
kind: Kind::DatabaseError,
msg: Some("Database error".to_string()),
desc: Some(msg.trim().to_string()),
class: Class::ServerError,
} }
} }
} }

View File

@ -1,6 +1,7 @@
use super::Method; use super::Method;
use crate::usimp; use crate::usimp;
use crate::websocket; use crate::websocket;
use crate::error::*;
use serde_json; use serde_json;
pub struct HttpStream { pub struct HttpStream {
@ -69,8 +70,8 @@ fn request_handler(client: &mut super::HttpStream) {
} }
Err(e) => { Err(e) => {
res.status(400); res.status(400);
res.error_info(format!("Unable to parser header: {}", &e)); res.error_info(format!("{}", &e));
println!("Unable to parser header: {}", &e); println!("{}", &e);
client.server_keep_alive = false; client.server_keep_alive = false;
} }
} }
@ -90,14 +91,17 @@ fn endpoint_handler(
) { ) {
res.add_header("Cache-Control", "no-store"); res.add_header("Cache-Control", "no-store");
let mut error = |code: u16, err_str: &str, client: &mut super::HttpStream| { let mut error = |error: Error, client: &mut super::HttpStream| {
println!("{}", err_str); println!("{}", error.to_string());
res.status(code); res.status(match &error.class() {
res.error_info(err_str.to_string()); Class::ClientError => 400,
Class::ServerError => 500,
});
res.error_info(error.to_string());
let mut obj = serde_json::Value::Object(serde_json::Map::new()); let mut obj = serde_json::Value::Object(serde_json::Map::new());
obj["status"] = serde_json::Value::String("error".to_string()); obj["status"] = serde_json::Value::String("error".to_string());
obj["message"] = serde_json::Value::String(err_str.to_string()); obj["message"] = serde_json::Value::String(error.to_string());
let buf = obj.to_string() + "\r\n"; let buf = obj.to_string() + "\r\n";
let length = buf.as_bytes().len(); let length = buf.as_bytes().len();
@ -117,8 +121,8 @@ fn endpoint_handler(
Some(length) => length, Some(length) => length,
None => { None => {
return error( return error(
400, Error::new(Kind::HttpRequestParseError, Class::ClientError)
"Unable to parse header: Content-Length missing", .set_desc("field 'Content-Length' missing".to_string()),
client, client,
) )
} }
@ -128,8 +132,8 @@ fn endpoint_handler(
Ok(length) => length, Ok(length) => length,
Err(e) => { Err(e) => {
return error( return error(
400, Error::new(Kind::HttpRequestParseError, Class::ClientError)
format!("Unable to parse Content-Length: {}", &e).as_str(), .set_desc(format!("unable to parse field 'Content-Length': {}", &e).to_string()),
client, client,
) )
} }
@ -142,17 +146,13 @@ fn endpoint_handler(
let input = match serde_json::from_slice(&buf[..length]) { let input = match serde_json::from_slice(&buf[..length]) {
Ok(val) => val, Ok(val) => val,
Err(e) => { Err(e) => {
return error( return error(e.into(), client)
400,
format!("Unable to parse JSON: {}", &e).as_str(),
client,
)
} }
}; };
let buf = match usimp::endpoint(endpoint, input) { let buf = match usimp::endpoint(endpoint, input) {
Ok(output) => output.to_string() + "\r\n", Ok(output) => output.to_string() + "\r\n",
Err(e) => return error(500, e.to_string().as_str(), client), Err(e) => return error(e, client),
}; };
// TODO compress // TODO compress

View File

@ -1,9 +1,10 @@
use crate::error::*;
use crate::http; use crate::http;
use crate::http::Status; use crate::http::Status;
pub fn parse_request(stream: &mut http::Stream) -> Result<Option<http::Request>, String> { pub fn parse_request(stream: &mut http::Stream) -> Result<Option<http::Request>, Error> {
let mut buf = [0; 4096]; let mut buf = [0; 4096];
let size = stream.peek(&mut buf).unwrap(); let size = stream.peek(&mut buf)?;
if size == 0 { if size == 0 {
return Ok(None); return Ok(None);
} }
@ -26,22 +27,25 @@ pub fn parse_request(stream: &mut http::Stream) -> Result<Option<http::Request>,
header_fields, header_fields,
}; };
stream.read_exact(&mut buf[..header_size]).unwrap(); stream.read_exact(&mut buf[..header_size])?;
Ok(Some(request)) Ok(Some(request))
} }
pub fn parse_response(stream: &mut http::Stream) -> Result<http::Response, String> { pub fn parse_response(stream: &mut http::Stream) -> Result<http::Response, Error> {
let mut buf = [0; 4096]; let mut buf = [0; 4096];
let size = stream.peek(&mut buf).unwrap(); let size = stream.peek(&mut buf)?;
let mut parser = Parser::new_request_parser(&buf[..size]); let mut parser = Parser::new_request_parser(&buf[..size]);
let header_size = parser.parse().unwrap(); let header_size = parser.parse()?;
let status_code = parser.status_code.unwrap(); let status_code = parser.status_code.unwrap();
let status_code = match status_code.parse() { let status_code = match status_code.parse::<u16>() {
Ok(v) => v, Ok(v) => v,
Err(e) => return Err(format!("{}", e)), Err(error) => {
return Err(Error::new(Kind::HttpRequestParseError, Class::ClientError)
.set_desc(error.to_string()))
}
}; };
let mut header_fields = Vec::new(); let mut header_fields = Vec::new();
@ -58,7 +62,7 @@ pub fn parse_response(stream: &mut http::Stream) -> Result<http::Response, Strin
header_fields, header_fields,
}; };
stream.read_exact(&mut buf[..header_size]).unwrap(); stream.read_exact(&mut buf[..header_size])?;
Ok(response) Ok(response)
} }
@ -122,21 +126,23 @@ impl Parser<'_> {
} }
} }
fn parse(&mut self) -> Result<usize, String> { fn parse(&mut self) -> Result<usize, Error> {
for char in self.buf { for char in self.buf {
self.next(*char); self.next(*char);
match self.state { match self.state {
State::Finish => return Ok(self.header_size), State::Finish => return Ok(self.header_size),
State::Error => { State::Error => {
return Err(format!( return Err(Error::new(Kind::HttpRequestParseError, Class::ClientError)
"invalid character at position {}", .set_desc(format!(
self.header_size - 1 "invalid character at position {}",
)) self.header_size - 1
)))
} }
_ => {} _ => {}
} }
} }
return Err(String::from("input too short")); return Err(Error::new(Kind::HttpRequestParseError, Class::ClientError)
.set_desc("input too short".to_string()));
} }
fn next(&mut self, char: u8) { fn next(&mut self, char: u8) {

View File

@ -24,11 +24,15 @@ enum SocketType {
impl std::fmt::Display for SocketType { impl std::fmt::Display for SocketType {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", match self { write!(
SocketType::Http => "http+ws", f,
SocketType::Https => "https+wss", "{}",
SocketType::Udp => "udp", match self {
}) SocketType::Http => "http+ws",
SocketType::Https => "https+wss",
SocketType::Udp => "udp",
}
)
} }
} }
@ -77,7 +81,9 @@ fn main() {
eprintln!( eprintln!(
"Creating listening thread for {} ({})", "Creating listening thread for {} ({})",
ansi_term::Style::new().bold().paint(socket_config.address.to_string()), ansi_term::Style::new()
.bold()
.paint(socket_config.address.to_string()),
socket_config.socket_type socket_config.socket_type
); );
@ -100,7 +106,10 @@ fn main() {
.set_certificate_chain_file("/home/lorenz/Certificates/chakotay.pem") .set_certificate_chain_file("/home/lorenz/Certificates/chakotay.pem")
.unwrap(); .unwrap();
acceptor acceptor
.set_private_key_file("/home/lorenz/Certificates/priv/chakotay.key",SslFiletype::PEM) .set_private_key_file(
"/home/lorenz/Certificates/priv/chakotay.key",
SslFiletype::PEM,
)
.unwrap(); .unwrap();
acceptor.check_private_key().unwrap(); acceptor.check_private_key().unwrap();
let acceptor = Arc::new(acceptor.build()); let acceptor = Arc::new(acceptor.build());

View File

@ -6,8 +6,8 @@ use crate::error::*;
pub fn endpoint(endpoint: &str, input: serde_json::Value) -> Result<serde_json::Value, Error> { pub fn endpoint(endpoint: &str, input: serde_json::Value) -> Result<serde_json::Value, Error> {
match endpoint { match endpoint {
"echo" => Ok(serde_json::to_value(echo(serde_json::from_value(input)?))?), "echo" => Ok(serde_json::to_value(echo(serde_json::from_value(input)?)?)?),
_ => Err(Error::new(ErrorKind::InvalidEndpointError)), _ => Err(Error::new(Kind::InvalidEndpointError, Class::ClientError)),
} }
} }
@ -22,19 +22,19 @@ pub struct EchoOutput {
database: Option<i32>, database: Option<i32>,
} }
pub fn echo(input: EchoInput) -> EchoOutput { pub fn echo(input: EchoInput) -> Result<EchoOutput, Error> {
let backend = database::client(); let backend = database::client()?;
let mut output = EchoOutput { let mut output = EchoOutput {
message: input.message, message: input.message,
database: None, database: None,
}; };
match backend { match backend {
database::Client::Postgres(mut client) => { database::Client::Postgres(mut client) => {
let res = client.query("SELECT * FROM test", &[]).unwrap(); let res = client.query("SELECT * FROM test", &[])?;
for row in res { for row in res {
output.database = Some(row.get(0)); output.database = Some(row.get(0));
} }
} }
} }
output Ok(output)
} }