use std::fmt::{Display, Error, Formatter};
use std::str::Utf8Error;
pub mod headers;
pub mod sock_ctrl_msg;
pub mod ascii {
pub const CR: u8 = b'\r';
pub const COLON: u8 = b':';
pub const LF: u8 = b'\n';
pub const SP: u8 = b' ';
pub const CRLF_LEN: usize = 2;
}
#[derive(Debug, Eq, PartialEq)]
pub enum HttpHeaderError {
InvalidFormat(String),
InvalidUtf8String(Utf8Error),
InvalidValue(String, String),
SizeLimitExceeded(String),
UnsupportedFeature(String, String),
UnsupportedName(String),
UnsupportedValue(String, String),
}
impl Display for HttpHeaderError {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
match self {
Self::InvalidFormat(header_key) => {
write!(f, "Header is incorrectly formatted. Key: {}", header_key)
}
Self::InvalidUtf8String(header_key) => {
write!(f, "Header contains invalid characters. Key: {}", header_key)
}
Self::InvalidValue(header_name, value) => {
write!(f, "Invalid value. Key:{}; Value:{}", header_name, value)
}
Self::SizeLimitExceeded(inner) => {
write!(f, "Invalid content length. Header: {}", inner)
}
Self::UnsupportedFeature(header_key, header_value) => write!(
f,
"Unsupported feature. Key: {}; Value: {}",
header_key, header_value
),
Self::UnsupportedName(inner) => write!(f, "Unsupported header name. Key: {}", inner),
Self::UnsupportedValue(header_key, header_value) => write!(
f,
"Unsupported value. Key:{}; Value:{}",
header_key, header_value
),
}
}
}
#[derive(Debug, Eq, PartialEq)]
pub enum RequestError {
BodyWithoutPendingRequest,
HeaderError(HttpHeaderError),
HeadersWithoutPendingRequest,
InvalidHttpMethod(&'static str),
InvalidHttpVersion(&'static str),
InvalidRequest,
InvalidUri(&'static str),
Overflow,
Underflow,
SizeLimitExceeded(usize, usize),
}
impl Display for RequestError {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
match self {
Self::BodyWithoutPendingRequest => write!(
f,
"No request was pending while the request body was being parsed."
),
Self::HeaderError(inner) => write!(f, "Invalid header. Reason: {}", inner),
Self::HeadersWithoutPendingRequest => write!(
f,
"No request was pending while the request headers were being parsed."
),
Self::InvalidHttpMethod(inner) => write!(f, "Invalid HTTP Method: {}", inner),
Self::InvalidHttpVersion(inner) => write!(f, "Invalid HTTP Version: {}", inner),
Self::InvalidRequest => write!(f, "Invalid request."),
Self::InvalidUri(inner) => write!(f, "Invalid URI: {}", inner),
Self::Overflow => write!(f, "Overflow occurred when parsing a request."),
Self::Underflow => write!(f, "Underflow occurred when parsing a request."),
Self::SizeLimitExceeded(limit, size) => write!(
f,
"Request payload with size {} is larger than the limit of {} \
allowed by server.",
size, limit
),
}
}
}
#[derive(Debug)]
pub enum ConnectionError {
ConnectionClosed,
InvalidWrite,
ParseError(RequestError),
StreamReadError(SysError),
StreamWriteError(std::io::Error),
}
impl Display for ConnectionError {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
match self {
Self::ConnectionClosed => write!(f, "Connection closed."),
Self::InvalidWrite => write!(f, "Invalid write attempt."),
Self::ParseError(inner) => write!(f, "Parsing error: {}", inner),
Self::StreamReadError(inner) => write!(f, "Reading stream error: {}", inner),
Self::StreamWriteError(inner) => write!(f, "Writing stream error: {}", inner),
}
}
}
#[derive(Debug)]
#[allow(dead_code)]
pub enum RouteError {
HandlerExist(String),
}
impl Display for RouteError {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
match self {
RouteError::HandlerExist(p) => write!(f, "handler for {} already exists", p),
}
}
}
#[derive(Debug)]
pub enum ServerError {
ConnectionError(ConnectionError),
IOError(std::io::Error),
Overflow,
ServerFull,
Underflow,
}
impl Display for ServerError {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
match self {
Self::ConnectionError(inner) => write!(f, "Connection error: {}", inner),
Self::IOError(inner) => write!(f, "IO error: {}", inner),
Self::Overflow => write!(f, "Overflow occured while processing messages."),
Self::ServerFull => write!(f, "Server is full."),
Self::Underflow => write!(f, "Underflow occured while processing messages."),
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Body {
pub body: Vec<u8>,
}
impl Body {
pub fn new<T: Into<Vec<u8>>>(body: T) -> Self {
Self { body: body.into() }
}
pub fn raw(&self) -> &[u8] {
self.body.as_slice()
}
pub fn len(&self) -> usize {
self.body.len()
}
pub fn is_empty(&self) -> bool {
self.body.len() == 0
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub enum Method {
Get,
Head,
Post,
Put,
Patch,
Delete,
}
impl Method {
pub fn try_from(bytes: &[u8]) -> Result<Self, RequestError> {
match bytes {
b"GET" => Ok(Self::Get),
b"HEAD" => Ok(Self::Head),
b"POST" => Ok(Self::Post),
b"PUT" => Ok(Self::Put),
b"PATCH" => Ok(Self::Patch),
b"DELETE" => Ok(Self::Delete),
_ => Err(RequestError::InvalidHttpMethod("Unsupported HTTP method.")),
}
}
pub fn raw(self) -> &'static [u8] {
match self {
Self::Get => b"GET",
Self::Head => b"HEAD",
Self::Post => b"POST",
Self::Put => b"PUT",
Self::Patch => b"PATCH",
Self::Delete => b"DELETE",
}
}
pub fn to_str(self) -> &'static str {
match self {
Method::Get => "GET",
Method::Head => "HEAD",
Method::Post => "POST",
Method::Put => "PUT",
Method::Patch => "PATCH",
Method::Delete => "DELETE",
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Version {
Http10,
Http11,
}
impl Default for Version {
fn default() -> Self {
Self::Http11
}
}
impl Version {
pub fn raw(self) -> &'static [u8] {
match self {
Self::Http10 => b"HTTP/1.0",
Self::Http11 => b"HTTP/1.1",
}
}
pub fn try_from(bytes: &[u8]) -> Result<Self, RequestError> {
match bytes {
b"HTTP/1.0" => Ok(Self::Http10),
b"HTTP/1.1" => Ok(Self::Http11),
_ => Err(RequestError::InvalidHttpVersion(
"Unsupported HTTP version.",
)),
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct SysError(i32);
impl SysError {
pub fn new(errno: i32) -> SysError {
SysError(errno)
}
pub fn last() -> SysError {
SysError(std::io::Error::last_os_error().raw_os_error().unwrap())
}
pub fn errno(self) -> i32 {
self.0
}
}
impl Display for SysError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
std::io::Error::from_raw_os_error(self.0).fmt(f)
}
}
impl std::error::Error for SysError {}
impl From<std::io::Error> for SysError {
fn from(e: std::io::Error) -> Self {
SysError::new(e.raw_os_error().unwrap_or_default())
}
}
impl From<SysError> for std::io::Error {
fn from(err: SysError) -> std::io::Error {
std::io::Error::from_raw_os_error(err.0)
}
}
pub type SysResult<T> = std::result::Result<T, SysError>;
#[cfg(test)]
mod tests {
use super::*;
impl PartialEq for ConnectionError {
fn eq(&self, other: &Self) -> bool {
use self::ConnectionError::*;
match (self, other) {
(ParseError(ref e), ParseError(ref other_e)) => e.eq(other_e),
(ConnectionClosed, ConnectionClosed) => true,
(StreamReadError(ref e), StreamReadError(ref other_e)) => {
format!("{}", e).eq(&format!("{}", other_e))
}
(StreamWriteError(ref e), StreamWriteError(ref other_e)) => {
format!("{}", e).eq(&format!("{}", other_e))
}
(InvalidWrite, InvalidWrite) => true,
_ => false,
}
}
}
#[test]
fn test_version() {
assert_eq!(Version::Http10.raw(), b"HTTP/1.0");
assert_eq!(Version::Http11.raw(), b"HTTP/1.1");
assert_eq!(Version::try_from(b"HTTP/1.0").unwrap(), Version::Http10);
assert_eq!(Version::try_from(b"HTTP/1.1").unwrap(), Version::Http11);
assert_eq!(
Version::try_from(b"HTTP/2.0").unwrap_err(),
RequestError::InvalidHttpVersion("Unsupported HTTP version.")
);
assert_eq!(Version::default(), Version::Http11);
}
#[test]
fn test_method() {
assert_eq!(Method::Get.raw(), b"GET");
assert_eq!(Method::Head.raw(), b"HEAD");
assert_eq!(Method::Post.raw(), b"POST");
assert_eq!(Method::Put.raw(), b"PUT");
assert_eq!(Method::Patch.raw(), b"PATCH");
assert_eq!(Method::Post.raw(), b"POST");
assert_eq!(Method::Delete.raw(), b"DELETE");
assert_eq!(Method::try_from(b"GET").unwrap(), Method::Get);
assert_eq!(Method::try_from(b"HEAD").unwrap(), Method::Head);
assert_eq!(Method::try_from(b"POST").unwrap(), Method::Post);
assert_eq!(Method::try_from(b"PUT").unwrap(), Method::Put);
assert_eq!(Method::try_from(b"PATCH").unwrap(), Method::Patch);
assert_eq!(Method::try_from(b"DELETE").unwrap(), Method::Delete);
assert_eq!(
Method::try_from(b"CONNECT").unwrap_err(),
RequestError::InvalidHttpMethod("Unsupported HTTP method.")
);
assert_eq!(Method::try_from(b"POST").unwrap(), Method::Post);
assert_eq!(Method::try_from(b"DELETE").unwrap(), Method::Delete);
}
#[test]
fn test_body() {
let body = Body::new("".to_string());
assert!(body.is_empty());
let body = Body::new("This is a body.".to_string());
assert_eq!(body.len(), 15);
assert_eq!(body.raw(), b"This is a body.");
}
#[test]
fn test_display_request_error() {
assert_eq!(
format!("{}", RequestError::BodyWithoutPendingRequest),
"No request was pending while the request body was being parsed."
);
assert_eq!(
format!("{}", RequestError::HeadersWithoutPendingRequest),
"No request was pending while the request headers were being parsed."
);
assert_eq!(
format!("{}", RequestError::InvalidHttpMethod("test")),
"Invalid HTTP Method: test"
);
assert_eq!(
format!("{}", RequestError::InvalidHttpVersion("test")),
"Invalid HTTP Version: test"
);
assert_eq!(
format!("{}", RequestError::InvalidRequest),
"Invalid request."
);
assert_eq!(
format!("{}", RequestError::InvalidUri("test")),
"Invalid URI: test"
);
assert_eq!(
format!("{}", RequestError::Overflow),
"Overflow occurred when parsing a request."
);
assert_eq!(
format!("{}", RequestError::Underflow),
"Underflow occurred when parsing a request."
);
assert_eq!(
format!("{}", RequestError::SizeLimitExceeded(4, 10)),
"Request payload with size 10 is larger than the limit of 4 allowed by server."
);
}
#[test]
fn test_display_header_error() {
assert_eq!(
format!(
"{}",
RequestError::HeaderError(HttpHeaderError::InvalidFormat("test".to_string()))
),
"Invalid header. Reason: Header is incorrectly formatted. Key: test"
);
let value = String::from_utf8(vec![0, 159]);
assert_eq!(
format!(
"{}",
RequestError::HeaderError(HttpHeaderError::InvalidUtf8String(
value.unwrap_err().utf8_error()
))
),
"Invalid header. Reason: Header contains invalid characters. Key: invalid utf-8 sequence of 1 bytes from index 1"
);
assert_eq!(
format!(
"{}",
RequestError::HeaderError(HttpHeaderError::SizeLimitExceeded("test".to_string()))
),
"Invalid header. Reason: Invalid content length. Header: test"
);
assert_eq!(
format!(
"{}",
RequestError::HeaderError(HttpHeaderError::UnsupportedFeature(
"test".to_string(),
"test".to_string()
))
),
"Invalid header. Reason: Unsupported feature. Key: test; Value: test"
);
assert_eq!(
format!(
"{}",
RequestError::HeaderError(HttpHeaderError::UnsupportedName("test".to_string()))
),
"Invalid header. Reason: Unsupported header name. Key: test"
);
assert_eq!(
format!(
"{}",
RequestError::HeaderError(HttpHeaderError::UnsupportedValue(
"test".to_string(),
"test".to_string()
))
),
"Invalid header. Reason: Unsupported value. Key:test; Value:test"
);
}
#[test]
fn test_display_connection_error() {
assert_eq!(
format!("{}", ConnectionError::ConnectionClosed),
"Connection closed."
);
assert_eq!(
format!(
"{}",
ConnectionError::ParseError(RequestError::InvalidRequest)
),
"Parsing error: Invalid request."
);
assert_eq!(
format!("{}", ConnectionError::InvalidWrite),
"Invalid write attempt."
);
#[cfg(target_os = "linux")]
assert_eq!(
format!(
"{}",
ConnectionError::StreamWriteError(std::io::Error::from_raw_os_error(11))
),
"Writing stream error: Resource temporarily unavailable (os error 11)"
);
#[cfg(target_os = "macos")]
assert_eq!(
format!(
"{}",
ConnectionError::StreamWriteError(std::io::Error::from_raw_os_error(11))
),
"Writing stream error: Resource deadlock avoided (os error 11)"
);
}
#[test]
fn test_display_server_error() {
assert_eq!(
format!(
"{}",
ServerError::ConnectionError(ConnectionError::ConnectionClosed)
),
"Connection error: Connection closed."
);
#[cfg(target_os = "linux")]
assert_eq!(
format!(
"{}",
ServerError::IOError(std::io::Error::from_raw_os_error(11))
),
"IO error: Resource temporarily unavailable (os error 11)"
);
#[cfg(target_os = "macos")]
assert_eq!(
format!(
"{}",
ServerError::IOError(std::io::Error::from_raw_os_error(11))
),
"IO error: Resource deadlock avoided (os error 11)"
);
assert_eq!(
format!("{}", ServerError::Overflow),
"Overflow occured while processing messages."
);
assert_eq!(format!("{}", ServerError::ServerFull), "Server is full.");
assert_eq!(
format!("{}", ServerError::Underflow),
"Underflow occured while processing messages."
);
}
#[test]
fn test_display_route_error() {
assert_eq!(
format!("{}", RouteError::HandlerExist("test".to_string())),
"handler for test already exists"
);
}
#[test]
fn test_method_to_str() {
let val = Method::Get;
assert_eq!(val.to_str(), "GET");
let val = Method::Head;
assert_eq!(val.to_str(), "HEAD");
let val = Method::Post;
assert_eq!(val.to_str(), "POST");
let val = Method::Put;
assert_eq!(val.to_str(), "PUT");
let val = Method::Patch;
assert_eq!(val.to_str(), "PATCH");
let val = Method::Delete;
assert_eq!(val.to_str(), "DELETE");
}
}