Niceties for constructing headers
This commit is contained in:
parent
29a2fd3c7f
commit
9e2664ec36
|
|
@ -14,20 +14,21 @@ pub fn main() -> Result<(), Box<dyn Error>> {
|
||||||
.and_then(|h| Some((h, args.next()?, args.next()?, args.next()?)))
|
.and_then(|h| Some((h, args.next()?, args.next()?, args.next()?)))
|
||||||
.ok_or_else(|| Box::<dyn Error>::from(String::from("Missing required argument")))?;
|
.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 buf = vec![0; 4096];
|
||||||
let mut buf = uhttp_ext::Buf::new(buf);
|
let mut buf = uhttp_ext::Buf::new(buf);
|
||||||
let mut conn = uhttp::Connection::new(uhttp::Role::Client);
|
let mut conn = uhttp::Connection::new(uhttp::Role::Client);
|
||||||
let events = &[
|
let events = &[
|
||||||
uhttp::Event::RequestLine(uhttp::RequestLine {
|
uhttp::RequestLine {
|
||||||
version: uhttp::Version::HTTP1_1,
|
version: uhttp::Version::HTTP1_1,
|
||||||
method: "POST",
|
method: "POST",
|
||||||
target: &path,
|
target: &path,
|
||||||
}),
|
}
|
||||||
uhttp::Event::Header(uhttp::Header::Special(uhttp::HeaderSpecial::ContentLength(
|
.into(),
|
||||||
data.len(),
|
uhttp::HeaderOther::from(("Host", host.as_bytes())).into(),
|
||||||
))),
|
uhttp::HeaderOther::from(("Accept", "*/*")).into(),
|
||||||
|
uhttp::HeaderSpecial::ContentLength(data.len()).into(),
|
||||||
uhttp::Event::HeadersDone,
|
uhttp::Event::HeadersDone,
|
||||||
uhttp::Event::BodyChunk(data.as_bytes()),
|
uhttp::Event::BodyChunk(data.as_bytes()),
|
||||||
uhttp::Event::SendDone,
|
uhttp::Event::SendDone,
|
||||||
|
|
@ -42,7 +43,6 @@ pub fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
|
||||||
buf.clear();
|
buf.clear();
|
||||||
loop {
|
loop {
|
||||||
buf.read_from(&mut stream)?;
|
|
||||||
let data = buf.filled();
|
let data = buf.filled();
|
||||||
let (d, r) = conn.handle_recv(data).lift();
|
let (d, r) = conn.handle_recv(data).lift();
|
||||||
|
|
||||||
|
|
@ -51,6 +51,7 @@ pub fn main() -> Result<(), Box<dyn Error>> {
|
||||||
kind: uhttp::ErrorKind::NeedMoreData,
|
kind: uhttp::ErrorKind::NeedMoreData,
|
||||||
..
|
..
|
||||||
}) => {
|
}) => {
|
||||||
|
buf.read_from(&mut stream)?;
|
||||||
continue;
|
continue;
|
||||||
},
|
},
|
||||||
Err(e) => panic!("{e:?}"),
|
Err(e) => panic!("{e:?}"),
|
||||||
|
|
|
||||||
31
src/lib.rs
31
src/lib.rs
|
|
@ -23,6 +23,37 @@ pub enum Event<'a> {
|
||||||
StatusLine(StatusLine<'a>),
|
StatusLine(StatusLine<'a>),
|
||||||
SendDone,
|
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> {
|
impl<'a> core::fmt::Display for Event<'a> {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
|
|
|
||||||
31
src/parse.rs
31
src/parse.rs
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
fail_details,
|
fail_details,
|
||||||
parts::{RequestLine, Version},
|
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)>;
|
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 (line, rest) = split_crlf(d).ok_or((d, ErrorKind::NeedMoreData.into()))?;
|
||||||
|
|
||||||
let go = || {
|
let go = || {
|
||||||
let mut it = line
|
let mut it = line.split(|b| *b == b':').filter(|bs| !bs.is_empty());
|
||||||
.split(|b| b.is_ascii_whitespace())
|
|
||||||
.filter(|bs| !bs.is_empty());
|
|
||||||
|
|
||||||
let (name, value) = match (it.next(), it.next()) {
|
let (name, value) = match (it.next(), it.next()) {
|
||||||
(Some(n), Some(v)) => (n, v),
|
(Some(n), Some(v)) => (n, v),
|
||||||
|
|
@ -127,30 +125,11 @@ pub fn header(d: &[u8]) -> Parse<Header> {
|
||||||
Ok(m) => m,
|
Ok(m) => m,
|
||||||
_ => return fail_details(ErrorKind::Parse, "expected target to be ascii"),
|
_ => return fail_details(ErrorKind::Parse, "expected target to be ascii"),
|
||||||
};
|
};
|
||||||
let name = name
|
let name = name.trim();
|
||||||
.split_once(":")
|
|
||||||
.map(|(f, _)| f)
|
|
||||||
.ok_or(Error::with_details(ErrorKind::Parse, "invalid header name"))?;
|
|
||||||
|
|
||||||
let h = match () {
|
let value = value.trim_ascii();
|
||||||
_ 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 }),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(h)
|
Ok(Header::from((name, value)))
|
||||||
};
|
};
|
||||||
|
|
||||||
go().tup(rest)
|
go().tup(rest)
|
||||||
|
|
|
||||||
56
src/parts.rs
56
src/parts.rs
|
|
@ -37,8 +37,64 @@ pub struct HeaderOther<'a> {
|
||||||
pub value: &'a [u8],
|
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)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||||
pub enum Header<'a> {
|
pub enum Header<'a> {
|
||||||
Special(HeaderSpecial),
|
Special(HeaderSpecial),
|
||||||
Other(HeaderOther<'a>),
|
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 }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue