use crate::framing::Framing;
use crate::headers;
pub(crate) const MAX_HEAD_BYTES: usize = 64 * 1024;
const MAX_HEADER_FIELDS: usize = 128;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum HeadError {
Malformed,
TooLarge,
ConflictingFraming,
UnsupportedTransferCoding,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct RequestHead {
pub(crate) method: String,
pub(crate) target: String,
pub(crate) headers: Vec<(String, String)>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct ResponseHead {
pub(crate) status: u16,
pub(crate) reason: String,
pub(crate) headers: Vec<(String, String)>,
}
pub(crate) fn parse_request(buf: &[u8]) -> Result<Option<(RequestHead, usize)>, HeadError> {
if buf.len() > MAX_HEAD_BYTES {
return Err(HeadError::TooLarge);
}
let mut fields = [httparse::EMPTY_HEADER; MAX_HEADER_FIELDS];
let mut req = httparse::Request::new(&mut fields);
match req.parse(buf) {
Ok(httparse::Status::Complete(consumed)) => {
let head = RequestHead {
method: req.method.ok_or(HeadError::Malformed)?.to_owned(),
target: req.path.ok_or(HeadError::Malformed)?.to_owned(),
headers: collect(req.headers)?,
};
Ok(Some((head, consumed)))
}
Ok(httparse::Status::Partial) => Ok(None),
Err(httparse::Error::TooManyHeaders) => Err(HeadError::TooLarge),
Err(_) => Err(HeadError::Malformed),
}
}
pub(crate) fn parse_response(buf: &[u8]) -> Result<Option<(ResponseHead, usize)>, HeadError> {
if buf.len() > MAX_HEAD_BYTES {
return Err(HeadError::TooLarge);
}
let mut fields = [httparse::EMPTY_HEADER; MAX_HEADER_FIELDS];
let mut res = httparse::Response::new(&mut fields);
match res.parse(buf) {
Ok(httparse::Status::Complete(consumed)) => {
let head = ResponseHead {
status: res.code.ok_or(HeadError::Malformed)?,
reason: res.reason.unwrap_or("").to_owned(),
headers: collect(res.headers)?,
};
Ok(Some((head, consumed)))
}
Ok(httparse::Status::Partial) => Ok(None),
Err(httparse::Error::TooManyHeaders) => Err(HeadError::TooLarge),
Err(_) => Err(HeadError::Malformed),
}
}
pub(crate) fn serialize_request(head: &RequestHead, framing: Framing) -> Vec<u8> {
let mut out = format!("{} {} HTTP/1.1\r\n", head.method, head.target).into_bytes();
write_fields(&mut out, &head.headers, framing);
out
}
pub(crate) fn serialize_response(head: &ResponseHead, framing: Framing) -> Vec<u8> {
let mut out = format!("HTTP/1.1 {} {}\r\n", head.status, head.reason).into_bytes();
write_fields(&mut out, &head.headers, framing);
out
}
fn write_fields(out: &mut Vec<u8>, fields: &[(String, String)], framing: Framing) {
for (name, value) in fields {
out.extend_from_slice(name.as_bytes());
out.extend_from_slice(b": ");
out.extend_from_slice(value.as_bytes());
out.extend_from_slice(b"\r\n");
}
if framing == Framing::Chunked {
out.extend_from_slice(b"Transfer-Encoding: chunked\r\n");
}
out.extend_from_slice(b"\r\n");
}
pub(crate) fn authorization(fields: &[(String, String)]) -> Option<&[u8]> {
fields
.iter()
.find(|(name, _)| name.eq_ignore_ascii_case("authorization"))
.map(|(_, value)| value.as_bytes())
}
pub(crate) fn path_only(target: &str) -> &str {
target
.split_once(['?', '#'])
.map_or(target, |(path, _)| path)
}
pub(crate) fn expects_continue(fields: &[(String, String)]) -> bool {
fields
.iter()
.filter(|(name, _)| name.eq_ignore_ascii_case("expect"))
.flat_map(|(_, value)| value.split(','))
.any(|expectation| expectation.trim().eq_ignore_ascii_case("100-continue"))
}
pub(crate) fn rewrite_for_backend(head: &mut RequestHead, authority: &str, peer: &str) {
headers::strip_hop_by_hop(&mut head.headers);
headers::strip_inbound_forwarded(&mut head.headers);
headers::set_host(&mut head.headers, authority);
headers::set_tunnel_markers(&mut head.headers, peer);
head.headers
.push(("Connection".to_owned(), "close".to_owned()));
}
fn collect(fields: &[httparse::Header<'_>]) -> Result<Vec<(String, String)>, HeadError> {
fields
.iter()
.map(|h| {
let value = std::str::from_utf8(h.value).map_err(|_| HeadError::Malformed)?;
Ok((h.name.to_owned(), value.to_owned()))
})
.collect()
}
#[cfg(test)]
#[path = "http_head_tests.rs"]
mod http_head_tests;