luciders/src/http/responses.rs
2024-11-02 18:24:57 +11:00

107 lines
2.7 KiB
Rust

use std::{error::Error, io::prelude::*, net::TcpStream};
use super::requests::Request;
pub enum Body {
String(String),
Static(&'static str),
Bytes(Vec<u8>, &'static str),
}
pub struct Response {
request: Request,
pub body: Body,
pub status: u16,
}
impl Response {
/// Create a new response from a request
///
/// request: the request to respond to
/// status: the HTTP status code
/// body: the body of the response (see Body enum)
pub fn new(request: Request, status: u16, body: Body) -> Self {
Response {
request,
body,
status,
}
}
/// Send off this response (write to the stream)
///
/// Consumes the response and returns a Result with either () or a Boxed Error
pub fn send(self) -> UnitOrBoxedError {
send_response(self.request.mut_stream(), self.status, self.body)
}
}
pub type UnitOrBoxedError = Result<(), Box<dyn Error>>;
fn send_response(mut stream: TcpStream, status: u16, body: Body) -> UnitOrBoxedError {
let status_line = match status {
200 => "200 OK",
400 => "400 BAD REQUEST",
404 => "404 NOT FOUND",
405 => "405 METHOD NOT ALLOWED",
_ => "500 INTERNAL SERVER ERROR",
};
let mut response = format!("HTTP/1.1 {}\r\n", status_line);
// Headers
response.push_str("Server: luciders/0.1.0\r\n");
response.push_str("Cache-Control: immutable, public, max-age=604800\r\n");
// CORS
response.push_str("Access-Control-Allow-Origin: *\r\n");
response.push_str("Access-Control-Allow-Metods: GET\r\n");
response.push_str(&format!(
"Content-Length: {}\r\n",
match &body {
Body::String(s) => s.len(),
Body::Static(s) => s.len(),
Body::Bytes(b, _) => b.len(),
}
));
match &body {
Body::Bytes(_, content_type) => {
response.push_str(&format!("Content-Type: {}\r\n", content_type))
}
Body::Static(_) => response.push_str("Content-Type: text/plain; charset=utf-8\r\n"),
_ => {}
}
response.push_str("\r\n");
// Body
match &body {
Body::String(s) => response.push_str(s),
Body::Static(s) => response.push_str(s),
_ => {}
}
match stream.write(response.as_bytes()) {
Ok(_) => {}
Err(e) => {
return Err(Box::new(e));
}
}
// Special handling for Bytes
if let Body::Bytes(b, _) = &body {
match stream.write(b) {
Ok(_) => {}
Err(e) => {
return Err(Box::new(e));
}
}
}
match stream.flush() {
Ok(_) => {}
Err(e) => {
return Err(Box::new(e));
}
}
Ok(())
}