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(())
}
pub fn client() -> Client {
pub fn client() -> Result<Client, Error> {
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,
JsonParseError,
DatabaseConnectionError,
DatabaseError,
HttpRequestParseError,
IoError,
}
#[derive(Copy, Clone)]
pub enum Class {
ClientError,
ServerError,
}
pub struct Error {
kind: ErrorKind,
kind: Kind,
msg: Option<String>,
desc: Option<String>,
class: Class,
}
impl Error {
pub fn new(kind: ErrorKind) -> Error {
Error { kind, desc: None }
pub fn new(kind: Kind, class: Class) -> Self {
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 {
fn to_string(&self) -> String {
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut error = match self.kind {
ErrorKind::InvalidEndpointError => "invalid endpoint",
ErrorKind::JsonParseError => "unable to parse JSON data",
ErrorKind::DatabaseError => "unable to connect to database",
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",
}
.to_string();
if let Some(desc) = &self.desc {
error += ": ";
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 {
fn from(error: serde_json::Error) -> Self {
Error {
kind: ErrorKind::JsonParseError,
kind: Kind::JsonParseError,
msg: Some("Unable to parse JSON data".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 {
fn from(error: r2d2::Error) -> Self {
Error {
kind: ErrorKind::DatabaseError,
kind: Kind::DatabaseConnectionError,
msg: Some("Unable to connect to database".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 crate::usimp;
use crate::websocket;
use crate::error::*;
use serde_json;
pub struct HttpStream {
@ -69,8 +70,8 @@ fn request_handler(client: &mut super::HttpStream) {
}
Err(e) => {
res.status(400);
res.error_info(format!("Unable to parser header: {}", &e));
println!("Unable to parser header: {}", &e);
res.error_info(format!("{}", &e));
println!("{}", &e);
client.server_keep_alive = false;
}
}
@ -90,14 +91,17 @@ fn endpoint_handler(
) {
res.add_header("Cache-Control", "no-store");
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());
let mut error = |error: Error, client: &mut super::HttpStream| {
println!("{}", error.to_string());
res.status(match &error.class() {
Class::ClientError => 400,
Class::ServerError => 500,
});
res.error_info(error.to_string());
let mut obj = serde_json::Value::Object(serde_json::Map::new());
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 length = buf.as_bytes().len();
@ -117,8 +121,8 @@ fn endpoint_handler(
Some(length) => length,
None => {
return error(
400,
"Unable to parse header: Content-Length missing",
Error::new(Kind::HttpRequestParseError, Class::ClientError)
.set_desc("field 'Content-Length' missing".to_string()),
client,
)
}
@ -128,8 +132,8 @@ fn endpoint_handler(
Ok(length) => length,
Err(e) => {
return error(
400,
format!("Unable to parse Content-Length: {}", &e).as_str(),
Error::new(Kind::HttpRequestParseError, Class::ClientError)
.set_desc(format!("unable to parse field 'Content-Length': {}", &e).to_string()),
client,
)
}
@ -142,17 +146,13 @@ fn endpoint_handler(
let input = match serde_json::from_slice(&buf[..length]) {
Ok(val) => val,
Err(e) => {
return error(
400,
format!("Unable to parse JSON: {}", &e).as_str(),
client,
)
return error(e.into(), client)
}
};
let buf = match usimp::endpoint(endpoint, input) {
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

View File

@ -1,9 +1,10 @@
use crate::error::*;
use crate::http;
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 size = stream.peek(&mut buf).unwrap();
let size = stream.peek(&mut buf)?;
if size == 0 {
return Ok(None);
}
@ -26,22 +27,25 @@ pub fn parse_request(stream: &mut http::Stream) -> Result<Option<http::Request>,
header_fields,
};
stream.read_exact(&mut buf[..header_size]).unwrap();
stream.read_exact(&mut buf[..header_size])?;
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 size = stream.peek(&mut buf).unwrap();
let size = stream.peek(&mut buf)?;
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 = match status_code.parse() {
let status_code = match status_code.parse::<u16>() {
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();
@ -58,7 +62,7 @@ pub fn parse_response(stream: &mut http::Stream) -> Result<http::Response, Strin
header_fields,
};
stream.read_exact(&mut buf[..header_size]).unwrap();
stream.read_exact(&mut buf[..header_size])?;
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 {
self.next(*char);
match self.state {
State::Finish => return Ok(self.header_size),
State::Error => {
return Err(format!(
"invalid character at position {}",
self.header_size - 1
))
return Err(Error::new(Kind::HttpRequestParseError, Class::ClientError)
.set_desc(format!(
"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) {

View File

@ -24,11 +24,15 @@ enum SocketType {
impl std::fmt::Display for SocketType {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", match self {
SocketType::Http => "http+ws",
SocketType::Https => "https+wss",
SocketType::Udp => "udp",
})
write!(
f,
"{}",
match self {
SocketType::Http => "http+ws",
SocketType::Https => "https+wss",
SocketType::Udp => "udp",
}
)
}
}
@ -77,7 +81,9 @@ fn main() {
eprintln!(
"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
);
@ -100,7 +106,10 @@ fn main() {
.set_certificate_chain_file("/home/lorenz/Certificates/chakotay.pem")
.unwrap();
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();
acceptor.check_private_key().unwrap();
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> {
match endpoint {
"echo" => Ok(serde_json::to_value(echo(serde_json::from_value(input)?))?),
_ => Err(Error::new(ErrorKind::InvalidEndpointError)),
"echo" => Ok(serde_json::to_value(echo(serde_json::from_value(input)?)?)?),
_ => Err(Error::new(Kind::InvalidEndpointError, Class::ClientError)),
}
}
@ -22,19 +22,19 @@ pub struct EchoOutput {
database: Option<i32>,
}
pub fn echo(input: EchoInput) -> EchoOutput {
let backend = database::client();
pub fn echo(input: EchoInput) -> Result<EchoOutput, Error> {
let backend = database::client()?;
let mut output = EchoOutput {
message: input.message,
database: None,
};
match backend {
database::Client::Postgres(mut client) => {
let res = client.query("SELECT * FROM test", &[]).unwrap();
let res = client.query("SELECT * FROM test", &[])?;
for row in res {
output.database = Some(row.get(0));
}
}
}
output
Ok(output)
}