Some more error handling
This commit is contained in:
@ -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()?)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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,
|
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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
.set_desc(format!(
|
||||||
"invalid character at position {}",
|
"invalid character at position {}",
|
||||||
self.header_size - 1
|
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) {
|
||||||
|
17
src/main.rs
17
src/main.rs
@ -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!(
|
||||||
|
f,
|
||||||
|
"{}",
|
||||||
|
match self {
|
||||||
SocketType::Http => "http+ws",
|
SocketType::Http => "http+ws",
|
||||||
SocketType::Https => "https+wss",
|
SocketType::Https => "https+wss",
|
||||||
SocketType::Udp => "udp",
|
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());
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user