use super::{APPLICATION_JSON, ServerError};
pub(super) fn parse_request_line(request: &str) -> Option<(&str, &str)> {
let request_line = request.lines().next()?;
let mut parts = request_line.split_whitespace();
let method = parts.next()?;
let path = parts.next()?;
parts.next()?;
Some((method, path))
}
pub(super) fn json_response<T>(status: StatusCode, value: &T) -> Result<Vec<u8>, ServerError>
where
T: serde::Serialize,
{
let body = serde_json::to_vec(value).map_err(|error| ServerError::HealthEndpoint {
message: format!("failed to serialize health response: {error}"),
})?;
Ok(response(status, Some(APPLICATION_JSON), &body))
}
pub(super) fn empty_response(status: StatusCode) -> Vec<u8> {
response(status, None, &[])
}
pub(super) fn response(status: StatusCode, content_type: Option<&str>, body: &[u8]) -> Vec<u8> {
let mut response = Vec::new();
let status_line = format!("HTTP/1.1 {} {}\r\n", status.code(), status.reason());
response.extend_from_slice(status_line.as_bytes());
response.extend_from_slice(format!("Content-Length: {}\r\n", body.len()).as_bytes());
response.extend_from_slice(b"Connection: close\r\n");
if let Some(content_type) = content_type {
response.extend_from_slice(format!("Content-Type: {content_type}\r\n").as_bytes());
}
response.extend_from_slice(b"\r\n");
response.extend_from_slice(body);
response
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum StatusCode {
Ok,
BadRequest,
NotFound,
MethodNotAllowed,
Conflict,
ServiceUnavailable,
}
impl StatusCode {
const fn code(self) -> u16 {
match self {
Self::Ok => 200,
Self::BadRequest => 400,
Self::NotFound => 404,
Self::MethodNotAllowed => 405,
Self::Conflict => 409,
Self::ServiceUnavailable => 503,
}
}
const fn reason(self) -> &'static str {
match self {
Self::Ok => "OK",
Self::BadRequest => "Bad Request",
Self::NotFound => "Not Found",
Self::MethodNotAllowed => "Method Not Allowed",
Self::Conflict => "Conflict",
Self::ServiceUnavailable => "Service Unavailable",
}
}
}