use crate::{
fail_details,
parts::{RequestLine, Version},
Error, ErrorKind, Header, StatusLine, Tup,
};
pub type Parse<'a, T> = Result<(&'a [u8], T), (&'a [u8], Error)>;
pub fn split_crlf(d: &[u8]) -> Option<(&[u8], &[u8])> {
let p = d.windows(2).position(|w| w == b"\r\n")?;
Some((&d[..p], &d[p + 2..]))
}
pub fn request_line(d: &[u8]) -> Parse<RequestLine> {
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 (method, target, version) = match (it.next(), it.next(), it.next()) {
(Some(m), Some(t), Some(v)) => (m, t, v),
_ => {
return fail_details(
ErrorKind::Parse,
"request line doesn't have required number of elements",
);
},
};
let method = match core::str::from_utf8(method) {
Ok(m) => m,
_ => {
return fail_details(ErrorKind::Parse, "expected method to be ascii");
},
};
let target = match core::str::from_utf8(target) {
Ok(m) => m,
_ => {
return fail_details(ErrorKind::Parse, "expected target to be ascii");
},
};
let version = match () {
_ if version.eq_ignore_ascii_case(b"http/1.1") => Version::HTTP1_1,
_ => {
return fail_details(ErrorKind::Parse, "unknown http version");
},
};
Ok(RequestLine {
method,
target,
version,
})
};
go().tup(rest)
}
pub fn status_line(d: &[u8]) -> Parse<StatusLine> {
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 (version, status_code, status_text) = match (it.next(), it.next(), it.next()) {
(Some(m), Some(t), Some(v)) => (m, t, v),
_ => {
return fail_details(
ErrorKind::Parse,
"status line doesn't have required number of elements",
);
},
};
let version = match () {
_ if version.eq_ignore_ascii_case(b"http/1.1") => Version::HTTP1_1,
_ => {
return fail_details(ErrorKind::Parse, "unknown http version");
},
};
let status_code = core::str::from_utf8(status_code)
.ok()
.and_then(|s| s.parse().ok())
.ok_or_else(|| Error::with_details(ErrorKind::Parse, "invalid status code"))?;
let status_text = core::str::from_utf8(status_text)
.ok()
.ok_or_else(|| Error::with_details(ErrorKind::Parse, "invalid status text"))?;
Ok(StatusLine {
version,
status_code,
status_text,
})
};
go().tup(rest)
}
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 == b':').filter(|bs| !bs.is_empty());
let (name, value) = match (it.next(), it.next()) {
(Some(n), Some(v)) => (n, v),
_ => {
return fail_details(
ErrorKind::Parse,
"header doesn't have required number of elements",
);
},
};
let name = match core::str::from_utf8(name) {
Ok(m) => m,
_ => return fail_details(ErrorKind::Parse, "expected target to be ascii"),
};
let name = name.trim();
let value = value.trim_ascii();
Ok(Header::from((name, value)))
};
go().tup(rest)
}