use std::{error::Error, io::prelude::*, net::TcpStream}; use super::requests::Request; pub enum Body { String(String), Static(&'static str), Bytes(Vec, &'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>; 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(()) }