107 lines
2.7 KiB
Rust
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(())
|
|
}
|