Some more error handling
This commit is contained in:
@ -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()?)),
|
||||
}
|
||||
}
|
||||
|
111
src/error.rs
111
src/error.rs
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for Error {
|
||||
fn to_string(&self) -> String {
|
||||
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 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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!(
|
||||
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) {
|
||||
|
17
src/main.rs
17
src/main.rs
@ -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 {
|
||||
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());
|
||||
|
@ -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)
|
||||
}
|
||||
|
Reference in New Issue
Block a user