use std::fmt;
use crate::error::Error;
use crate::response::Response;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Method {
Get,
Post,
Put,
Delete,
Patch,
Head,
Options,
}
impl fmt::Display for Method {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Method::Get => write!(f, "GET"),
Method::Post => write!(f, "POST"),
Method::Put => write!(f, "PUT"),
Method::Delete => write!(f, "DELETE"),
Method::Patch => write!(f, "PATCH"),
Method::Head => write!(f, "HEAD"),
Method::Options => write!(f, "OPTIONS"),
}
}
}
pub(crate) fn serialize_request(
method: Method,
path: &str,
headers: &[(String, String)],
body: Option<&[u8]>,
) -> Vec<u8> {
let mut out = Vec::new();
out.extend_from_slice(format!("{method} {path} HTTP/1.1\r\n").as_bytes());
for (key, value) in headers {
out.extend_from_slice(format!("{key}: {value}\r\n").as_bytes());
}
if let Some(body) = body {
if !headers.iter().any(|(k, _)| k.eq_ignore_ascii_case("content-length")) {
out.extend_from_slice(
format!("Content-Length: {}\r\n", body.len()).as_bytes(),
);
}
}
out.extend_from_slice(b"\r\n");
if let Some(body) = body {
out.extend_from_slice(body);
}
out
}
pub(crate) fn try_parse_response_head(buf: &[u8]) -> Result<Option<(u16, usize)>, Error> {
let mut headers = [httparse::EMPTY_HEADER; 64];
let mut resp = httparse::Response::new(&mut headers);
match resp.parse(buf).map_err(|e| Error::HttpParse(e.to_string()))? {
httparse::Status::Complete(n) => {
let code = resp
.code
.ok_or_else(|| Error::HttpParse("Missing status code".into()))?;
Ok(Some((code, n)))
}
httparse::Status::Partial => Ok(None),
}
}
pub(crate) fn parse_response(raw: &[u8]) -> Result<Response, Error> {
let mut headers_buf = [httparse::EMPTY_HEADER; 64];
let mut resp = httparse::Response::new(&mut headers_buf);
let status = resp
.parse(raw)
.map_err(|e| Error::HttpParse(e.to_string()))?;
let header_len = match status {
httparse::Status::Complete(len) => len,
httparse::Status::Partial => {
return Err(Error::HttpParse("Incomplete HTTP response".into()));
}
};
let status_code = resp
.code
.ok_or_else(|| Error::HttpParse("Missing status code".into()))?;
let headers: Vec<(String, String)> = resp
.headers
.iter()
.map(|h| {
(
h.name.to_string(),
String::from_utf8_lossy(h.value).into_owned(),
)
})
.collect();
let body = raw[header_len..].to_vec();
Ok(Response::new(status_code, headers, body))
}