#![no_std] pub mod common; pub mod parse; pub mod parts; pub mod util; pub mod write; pub use common::*; pub use parse::Parse; pub use parts::*; pub use util::*; pub use write::{Write, WriteCursor, Written}; #[derive(Default, Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] pub enum Event<'a> { #[default] Empty, RequestLine(RequestLine<'a>), Header(Header<'a>), HeadersDone, BodyChunk(&'a [u8]), RecvDone, StatusLine(StatusLine<'a>), SendDone, } impl<'a> core::fmt::Display for Event<'a> { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { Event::Empty | Event::HeadersDone | Event::RecvDone | Event::SendDone => Ok(()), Event::RequestLine(r) => write!(f, "{} {} {}", r.method, r.target, r.version), Event::StatusLine(s) => { write!(f, "{} {} {}", s.version, s.status_code, s.status_text) }, Event::Header(h) => match h { Header::Other(HeaderOther { name, value }) => { write!( f, "{}: {}", name, core::str::from_utf8(value).unwrap_or("") ) }, Header::Special(h) => match h { HeaderSpecial::ContentLength(cl) => write!(f, "Content-Length: {cl}"), HeaderSpecial::TransferEncodingChunked => { write!(f, "Transfer-Encoding: chunked") }, }, }, Event::BodyChunk(b) => { write!(f, "{}", core::str::from_utf8(b).unwrap_or("")) }, } } } #[derive(Debug, Copy, Clone)] pub enum Role { Client, Server, } #[derive(Debug, Copy, Clone)] enum BodyState { ContentLength(usize), Chunked, } #[derive(Debug, Copy, Clone)] enum StateRecv { StartLine, Headers(Option), Body(BodyState), ValidateDone, } #[derive(Debug, Copy, Clone)] enum StateSend { StartLine, Headers(Option), Body(BodyState), ValidateDone, } #[derive(Debug, Copy, Clone)] enum StateConnection { Recv(StateRecv), Send(StateSend), } #[derive(Debug, Copy, Clone)] pub struct Connection { role: Role, state: StateConnection, } impl Connection { pub fn new(role: Role) -> Self { let state = match role { Role::Server => StateConnection::Recv(StateRecv::StartLine), Role::Client => StateConnection::Send(StateSend::StartLine), }; Self { state, role } } pub fn is_sending(&self) -> bool { matches!(self.state, StateConnection::Send(_)) } pub fn is_recving(&self) -> bool { matches!(self.state, StateConnection::Recv(_)) } pub fn handle_recv<'a>(&mut self, bytes: &'a [u8]) -> Parse<'a, Event<'a>> { let recv = match &self.state { StateConnection::Recv(r) => r, _ => { return Err((bytes, ErrorKind::InvalidConnectionState.into())); }, }; let n = match (self.role, recv) { (Role::Server, StateRecv::StartLine) => parse::request_line(bytes).map2(|rl| { ( Event::RequestLine(rl), StateConnection::Recv(StateRecv::Headers(None)), ) }), (Role::Client, StateRecv::StartLine) => parse::status_line(bytes).map2(|rl| { ( Event::StatusLine(rl), StateConnection::Recv(StateRecv::Headers(None)), ) }), (_, StateRecv::Headers(body_state)) => { if bytes.starts_with(b"\r\n") { Ok(( &bytes[2..], ( Event::HeadersDone, match body_state { None => StateConnection::Recv(StateRecv::ValidateDone), Some(b) => StateConnection::Recv(StateRecv::Body(*b)), }, ), )) } else { parse::header(bytes).map2(|h| { let b = match h { Header::Special(HeaderSpecial::TransferEncodingChunked) => { Some(BodyState::Chunked) }, Header::Special(HeaderSpecial::ContentLength(c)) => { Some(BodyState::ContentLength(c)) }, _ => *body_state, }; ( Event::Header(h), StateConnection::Recv(StateRecv::Headers(b)), ) }) } }, (_, StateRecv::Body(body_state)) => match body_state { BodyState::ContentLength(remaining) => { if bytes.len() < *remaining { Ok(( &[] as &[u8], ( Event::BodyChunk(bytes), StateConnection::Recv(StateRecv::Body( BodyState::ContentLength(remaining - bytes.len()), )), ), )) } else { Ok(( &bytes[*remaining..], ( Event::BodyChunk(&bytes[..*remaining]), StateConnection::Recv(StateRecv::ValidateDone), ), )) } }, _ => todo!(), }, (_, StateRecv::ValidateDone) => { if bytes.is_empty() { Ok(( bytes, (Event::RecvDone, StateConnection::Send(StateSend::StartLine)), )) } else { fail(ErrorKind::TrailingBytes).tup(bytes) } }, }; n.map2(move |(ev, next_state)| { self.state = next_state; ev }) } pub fn handle_send(&mut self, event: &Event, mut w: impl Write) -> Written { let state = match self.state { StateConnection::Send(s) => s, _ => return fail(ErrorKind::InvalidConnectionState), }; let next = match (self.role, state, event) { (Role::Server, StateSend::StartLine, Event::StatusLine(sl)) => { write::status_line(sl, w) .map(|_| StateConnection::Send(StateSend::Headers(None))) }, (Role::Client, StateSend::StartLine, Event::RequestLine(rl)) => { write::request_line(rl, w) .map(|_| StateConnection::Send(StateSend::Headers(None))) }, (_, StateSend::Headers(body_state), Event::Header(h)) => { write::header(h, w)?; let bs = match h { Header::Other(_) => body_state, Header::Special(h) => match h { HeaderSpecial::TransferEncodingChunked => Some(BodyState::Chunked), HeaderSpecial::ContentLength(cl) => { Some(BodyState::ContentLength(*cl)) }, }, }; Ok(StateConnection::Send(StateSend::Headers(bs))) }, (_, StateSend::Headers(body_state), Event::HeadersDone) => { write!(w, "\r\n")?; match body_state { Some(bs) => Ok(StateConnection::Send(StateSend::Body(bs))), None => Ok(StateConnection::Send(StateSend::ValidateDone)), } }, (_, StateSend::Body(b), Event::BodyChunk(c)) => match b { BodyState::ContentLength(cl) => match () { _ if c.len() < cl => { w.write(c)?; Ok(StateConnection::Send(StateSend::Body( BodyState::ContentLength(cl - c.len()), ))) }, _ if c.len() == cl => { w.write(c)?; Ok(StateConnection::Send(StateSend::ValidateDone)) }, _ => { return fail(ErrorKind::BodySizeMismatch); }, }, BodyState::Chunked => todo!(), }, (_, StateSend::ValidateDone, Event::SendDone) => { Ok(StateConnection::Recv(StateRecv::StartLine)) }, _ => return Err(Error::from(ErrorKind::InvalidEventForConnectionState)), }?; self.state = next; Ok(()) } }