use std::str::FromStr;
use http::Method;
use log::warn;
pub fn request_to_bytes(request: http::Request<Vec<u8>>) -> Vec<u8> {
let url = request.uri();
let mut request_string = format!("{} {} HTTP/1.1\r\n", request.method(), url.path(),);
if let Some(host) = url.host() {
request_string.push_str(&format!("Host: {}\r\n", host));
}
for (key, value) in request.headers() {
let Ok(value_str) = value.to_str() else {
warn!(
"Failed to convert header `{:?}`'s value to string: {:?}",
key, value
);
continue;
};
request_string.push_str(&format!("{}: {}\r\n", key, value_str));
}
request_string.push_str("\r\n");
let mut request_bytes = request_string.into_bytes();
if request.body().len() > 0 {
request_bytes.extend_from_slice(request.body());
}
request_bytes
}
pub fn bytes_to_request(request_bytes: &[u8]) -> http::Request<Vec<u8>> {
let request_str = String::from_utf8_lossy(request_bytes);
let (path_line, headers_str, body_start_index) = split_request(&request_str);
let (method, url) = parse_path_line(path_line);
let headers = parse_headers(headers_str);
let body = request_bytes[body_start_index..].to_vec();
let mut request = http::Request::builder().method(method).uri(url);
for (key, value) in headers {
request = request.header(key, value);
}
request.body(body).unwrap()
}
pub fn response_to_bytes(response: http::Response<Vec<u8>>) -> Vec<u8> {
let mut response_bytes = response_header_to_vec(&response);
response_bytes.extend_from_slice(response.body());
response_bytes
}
pub fn bytes_to_response(response_bytes: &[u8]) -> http::Response<Vec<u8>> {
let response_str = String::from_utf8_lossy(response_bytes);
let (status_line, headers_str, body_start_index) = split_response(&response_str);
let (status_code, _status_text) = parse_status_line(status_line);
let headers = parse_headers(headers_str);
let body = response_bytes[body_start_index..].to_vec();
let mut response = http::Response::builder().status(status_code);
for (key, value) in headers {
response = response.header(key, value);
}
response.body(body).unwrap()
}
fn split_request(request_str: &str) -> (&str, &str, usize) {
let mut parts = request_str.splitn(3, "\r\n\r\n");
let status_and_headers = parts.next().unwrap();
let mut pathline_and_headers_parts = status_and_headers.splitn(2, "\r\n");
let path_line = pathline_and_headers_parts.next().unwrap();
let headers = pathline_and_headers_parts.next().unwrap_or(""); let body_start_index = request_str
.find("\r\n\r\n")
.map(|idx| idx + 4)
.unwrap_or(request_str.len());
(path_line, headers, body_start_index)
}
fn split_response(response_str: &str) -> (&str, &str, usize) {
let mut parts = response_str.splitn(3, "\r\n\r\n");
let status_and_headers = parts.next().unwrap();
let mut status_and_headers_parts = status_and_headers.splitn(2, "\r\n");
let status_line = status_and_headers_parts.next().unwrap();
let headers = status_and_headers_parts.next().unwrap_or(""); let body_start_index = response_str
.find("\r\n\r\n")
.map(|idx| idx + 4)
.unwrap_or(response_str.len());
(status_line, headers, body_start_index)
}
fn parse_path_line(path_line: &str) -> (Method, String) {
let mut parts = path_line.splitn(3, ' ');
let method = parts.next().unwrap();
let path = parts.next().unwrap();
let _http_version = parts.next().unwrap();
let method = Method::from_str(method).unwrap();
(method, path.to_string())
}
fn parse_status_line(status_line: &str) -> (u16, String) {
let mut parts = status_line.splitn(3, ' ');
let _http_version = parts.next().unwrap();
let status_code = parts.next().unwrap();
let status_text = parts.next().unwrap_or("").to_string(); let status_code = status_code.parse::<u16>().unwrap();
(status_code, status_text)
}
fn parse_headers(headers: &str) -> Vec<(String, String)> {
let mut header_store: Vec<(String, String)> = Vec::new();
for line in headers.lines() {
let mut parts = line.splitn(2, ": ");
let key = parts.next().unwrap().to_lowercase();
let value = parts.next().unwrap().to_string();
header_store.push((key, value));
}
header_store
}
fn response_header_to_vec(r: &http::Response<Vec<u8>>) -> Vec<u8> {
let mut v = Vec::with_capacity(120);
write_response_header(r, &mut v).expect("unable to write response header to stream");
v
}
fn write_response_header(r: &http::Response<Vec<u8>>, output: &mut Vec<u8>) -> std::io::Result<()> {
let status = r.status().as_u16();
let code = status.to_string();
write_line(b"HTTP/1.1 ", output)?;
write_line(code.as_bytes(), output)?;
write_line(b"\r\n", output)?;
for (hn, hv) in r.headers() {
let Ok(hv) = hv.to_str() else {
warn!(
"Failed to convert header `{:?}`'s value to string: {:?}",
hn, hv
);
continue;
};
write_line(hn.as_str().as_bytes(), output)?;
write_line(b": ", output)?;
write_line(hv.as_bytes(), output)?;
write_line(b"\r\n", output)?;
}
write_line(b"\r\n", output)?;
Ok(())
}
fn write_line(buf: &[u8], io: &mut dyn std::io::Write) -> std::io::Result<()> {
io.write_all(buf)?;
Ok(())
}