From 9e2664ec36f36ec03cbd21da6e015ddafcf435b1 Mon Sep 17 00:00:00 2001 From: soup Date: Mon, 14 Oct 2024 23:24:20 -0400 Subject: [PATCH] Niceties for constructing headers --- examples/client.rs | 15 +++++++------ src/lib.rs | 31 +++++++++++++++++++++++++ src/parse.rs | 31 +++++-------------------- src/parts.rs | 56 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 100 insertions(+), 33 deletions(-) diff --git a/examples/client.rs b/examples/client.rs index ac92477..6635194 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -14,20 +14,21 @@ pub fn main() -> Result<(), Box> { .and_then(|h| Some((h, args.next()?, args.next()?, args.next()?))) .ok_or_else(|| Box::::from(String::from("Missing required argument")))?; - let mut stream = TcpStream::connect((host, port.parse()?))?; + let mut stream = TcpStream::connect((host.as_str(), port.parse()?))?; let buf = vec![0; 4096]; let mut buf = uhttp_ext::Buf::new(buf); let mut conn = uhttp::Connection::new(uhttp::Role::Client); let events = &[ - uhttp::Event::RequestLine(uhttp::RequestLine { + uhttp::RequestLine { version: uhttp::Version::HTTP1_1, method: "POST", target: &path, - }), - uhttp::Event::Header(uhttp::Header::Special(uhttp::HeaderSpecial::ContentLength( - data.len(), - ))), + } + .into(), + uhttp::HeaderOther::from(("Host", host.as_bytes())).into(), + uhttp::HeaderOther::from(("Accept", "*/*")).into(), + uhttp::HeaderSpecial::ContentLength(data.len()).into(), uhttp::Event::HeadersDone, uhttp::Event::BodyChunk(data.as_bytes()), uhttp::Event::SendDone, @@ -42,7 +43,6 @@ pub fn main() -> Result<(), Box> { buf.clear(); loop { - buf.read_from(&mut stream)?; let data = buf.filled(); let (d, r) = conn.handle_recv(data).lift(); @@ -51,6 +51,7 @@ pub fn main() -> Result<(), Box> { kind: uhttp::ErrorKind::NeedMoreData, .. }) => { + buf.read_from(&mut stream)?; continue; }, Err(e) => panic!("{e:?}"), diff --git a/src/lib.rs b/src/lib.rs index c31ddd9..fc27802 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,6 +23,37 @@ pub enum Event<'a> { StatusLine(StatusLine<'a>), SendDone, } + +impl<'a> From for Event<'a> { + fn from(value: HeaderSpecial) -> Self { + Self::Header(value.into()) + } +} + +impl<'a> From> for Event<'a> { + fn from(value: HeaderOther<'a>) -> Self { + Self::Header(value.into()) + } +} + +impl<'a> From> for Event<'a> { + fn from(value: Header<'a>) -> Self { + Self::Header(value) + } +} + +impl<'a> From> for Event<'a> { + fn from(value: RequestLine<'a>) -> Self { + Self::RequestLine(value) + } +} + +impl<'a> From> for Event<'a> { + fn from(value: StatusLine<'a>) -> Self { + Self::StatusLine(value) + } +} + impl<'a> core::fmt::Display for Event<'a> { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { diff --git a/src/parse.rs b/src/parse.rs index b0b3da9..319de87 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -1,7 +1,7 @@ use crate::{ fail_details, parts::{RequestLine, Version}, - Error, ErrorKind, Header, HeaderOther, HeaderSpecial, StatusLine, Tup, + Error, ErrorKind, Header, StatusLine, Tup, }; pub type Parse<'a, T> = Result<(&'a [u8], T), (&'a [u8], Error)>; @@ -109,9 +109,7 @@ pub fn header(d: &[u8]) -> Parse
{ let (line, rest) = split_crlf(d).ok_or((d, ErrorKind::NeedMoreData.into()))?; let go = || { - let mut it = line - .split(|b| b.is_ascii_whitespace()) - .filter(|bs| !bs.is_empty()); + let mut it = line.split(|b| *b == b':').filter(|bs| !bs.is_empty()); let (name, value) = match (it.next(), it.next()) { (Some(n), Some(v)) => (n, v), @@ -127,30 +125,11 @@ pub fn header(d: &[u8]) -> Parse
{ Ok(m) => m, _ => return fail_details(ErrorKind::Parse, "expected target to be ascii"), }; - let name = name - .split_once(":") - .map(|(f, _)| f) - .ok_or(Error::with_details(ErrorKind::Parse, "invalid header name"))?; + let name = name.trim(); - let h = match () { - _ if name.eq_ignore_ascii_case("transfer-encoding") - && value.eq_ignore_ascii_case(b"chunked") => - { - Header::Special(HeaderSpecial::TransferEncodingChunked) - }, - _ if name.eq_ignore_ascii_case("content-length") => { - match core::str::from_utf8(value) - .ok() - .and_then(|s| s.parse().ok()) - { - Some(v) => Header::Special(HeaderSpecial::ContentLength(v)), - _ => Header::Other(HeaderOther { name, value }), - } - }, - _ => Header::Other(HeaderOther { name, value }), - }; + let value = value.trim_ascii(); - Ok(h) + Ok(Header::from((name, value))) }; go().tup(rest) diff --git a/src/parts.rs b/src/parts.rs index 6513b04..84e0887 100644 --- a/src/parts.rs +++ b/src/parts.rs @@ -37,8 +37,64 @@ pub struct HeaderOther<'a> { pub value: &'a [u8], } +impl<'a, Name, Value> From<(&'a Name, &'a Value)> for HeaderOther<'a> +where + Name: AsRef + ?Sized, + Value: AsRef<[u8]> + ?Sized, +{ + fn from(value: (&'a Name, &'a Value)) -> Self { + let (name, value) = value; + let name = name.as_ref(); + let value = value.as_ref(); + + Self { name, value } + } +} + #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] pub enum Header<'a> { Special(HeaderSpecial), Other(HeaderOther<'a>), } + +impl<'a> From for Header<'a> { + fn from(value: HeaderSpecial) -> Self { + Self::Special(value) + } +} + +impl<'a> From> for Header<'a> { + fn from(value: HeaderOther<'a>) -> Self { + Self::Other(value) + } +} + +impl<'a, Name, Value> From<(&'a Name, &'a Value)> for Header<'a> +where + Name: AsRef + ?Sized, + Value: AsRef<[u8]> + ?Sized, +{ + fn from(value: (&'a Name, &'a Value)) -> Self { + let (name, value) = value; + let name = name.as_ref(); + let value = value.as_ref(); + + match () { + _ if name.eq_ignore_ascii_case("transfer-encoding") + && value.eq_ignore_ascii_case(b"chunked") => + { + Header::Special(HeaderSpecial::TransferEncodingChunked) + }, + _ if name.eq_ignore_ascii_case("content-length") => { + match core::str::from_utf8(value) + .ok() + .and_then(|s| s.parse().ok()) + { + Some(v) => Header::Special(HeaderSpecial::ContentLength(v)), + _ => Header::Other(HeaderOther { name, value }), + } + }, + _ => Header::Other(HeaderOther { name, value }), + } + } +}