Niceties for constructing headers

This commit is contained in:
soup 2024-10-14 23:24:20 -04:00
parent 29a2fd3c7f
commit 9e2664ec36
No known key found for this signature in database
4 changed files with 100 additions and 33 deletions

View file

@ -14,20 +14,21 @@ pub fn main() -> Result<(), Box<dyn Error>> {
.and_then(|h| Some((h, args.next()?, args.next()?, args.next()?)))
.ok_or_else(|| Box::<dyn Error>::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<dyn Error>> {
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<dyn Error>> {
kind: uhttp::ErrorKind::NeedMoreData,
..
}) => {
buf.read_from(&mut stream)?;
continue;
},
Err(e) => panic!("{e:?}"),

View file

@ -23,6 +23,37 @@ pub enum Event<'a> {
StatusLine(StatusLine<'a>),
SendDone,
}
impl<'a> From<HeaderSpecial> for Event<'a> {
fn from(value: HeaderSpecial) -> Self {
Self::Header(value.into())
}
}
impl<'a> From<HeaderOther<'a>> for Event<'a> {
fn from(value: HeaderOther<'a>) -> Self {
Self::Header(value.into())
}
}
impl<'a> From<Header<'a>> for Event<'a> {
fn from(value: Header<'a>) -> Self {
Self::Header(value)
}
}
impl<'a> From<RequestLine<'a>> for Event<'a> {
fn from(value: RequestLine<'a>) -> Self {
Self::RequestLine(value)
}
}
impl<'a> From<StatusLine<'a>> 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 {

View file

@ -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<Header> {
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<Header> {
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)

View file

@ -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<str> + ?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<HeaderSpecial> for Header<'a> {
fn from(value: HeaderSpecial) -> Self {
Self::Special(value)
}
}
impl<'a> From<HeaderOther<'a>> 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<str> + ?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 }),
}
}
}