fix: incorrect place for, HTTP/1.1 + read desc

handle different body types
This commit is contained in:
Compositr 2024-11-01 21:59:38 +11:00
parent af18bf2ebe
commit 3d8837cdb1
Signed by: compositr
GPG key ID: 91E3DE20129A0B4A
3 changed files with 72 additions and 27 deletions

View file

@ -1,13 +1,13 @@
use crate::http::{ use crate::http::{
requests::{Request, RequestStatus, URL}, requests::{Request, RequestStatus, URL},
responses::{Response, UnitOrBoxedError}, responses::{Body, Response, UnitOrBoxedError},
}; };
use std::{ use std::{
collections::HashMap, collections::HashMap,
net::{Shutdown, TcpListener}, net::{Shutdown, TcpListener},
}; };
type DynHandlerFn = dyn Fn(Request) -> Response<'static>; type DynHandlerFn = dyn Fn(Request) -> Response;
type BoxedHandlerFn = Box<DynHandlerFn>; type BoxedHandlerFn = Box<DynHandlerFn>;
// Collection of handlers for requests // Collection of handlers for requests
@ -31,7 +31,7 @@ impl Handlers {
/// ///
/// path: Path to match (no trailing /) /// path: Path to match (no trailing /)
/// handler: Function to handle the request. Must return a Response /// handler: Function to handle the request. Must return a Response
pub fn add_handler(&mut self, path: &str, handler: impl Fn(Request) -> Response<'static> + 'static) { pub fn add_handler(&mut self, path: &str, handler: impl Fn(Request) -> Response + 'static) {
self.matchers.insert(path.to_string(), Box::from(handler)); self.matchers.insert(path.to_string(), Box::from(handler));
} }
@ -120,7 +120,7 @@ impl Handlers {
match self.match_handler(&req.url) { match self.match_handler(&req.url) {
Some(handler) => handler(req).send()?, Some(handler) => handler(req).send()?,
None => { None => {
return Response::new(req, 404, "Not Found").send(); return Response::new(req, 404, Body::Static("Not Found")).send();
} }
}; };

View file

@ -1,18 +1,25 @@
use std::error::Error; use std::{error::Error, io::prelude::*, net::TcpStream};
use std::io::prelude::*;
use std::net::TcpStream;
use super::requests::Request; use super::requests::Request;
pub struct Response<'a> { pub enum Body {
String(String),
Static(&'static str),
}
pub struct Response {
request: Request, request: Request,
pub body: &'a str, pub body: Body,
pub status: u16, pub status: u16,
} }
impl<'a> Response<'a> { impl Response {
pub fn new(request: Request, status: u16, body: &'a str) -> Self { pub fn new(request: Request, status: u16, body: Body) -> Self {
Response { request, body, status } Response {
request,
body,
status,
}
} }
pub fn send(self) -> UnitOrBoxedError { pub fn send(self) -> UnitOrBoxedError {
@ -22,7 +29,7 @@ impl<'a> Response<'a> {
pub type UnitOrBoxedError = Result<(), Box<dyn Error>>; pub type UnitOrBoxedError = Result<(), Box<dyn Error>>;
fn send_response(mut stream: TcpStream, status: u16, body: &str) -> UnitOrBoxedError { fn send_response(mut stream: TcpStream, status: u16, body: Body) -> UnitOrBoxedError {
let status_line = match status { let status_line = match status {
200 => "200 OK", 200 => "200 OK",
400 => "400 BAD REQUEST", 400 => "400 BAD REQUEST",
@ -31,12 +38,16 @@ fn send_response(mut stream: TcpStream, status: u16, body: &str) -> UnitOrBoxedE
_ => "500 INTERNAL SERVER ERROR", _ => "500 INTERNAL SERVER ERROR",
}; };
let response = format!( let mut response = format!("HTTP/1.1 {}\r\nServer: luciders/0.1.0\r\n", status_line);
"{}\r\nContent-Length: {}\r\nServer: luciders/0.1.0\r\n\r\nHTTP/1.1 {}",
status_line, response.push_str(&format!(
body.len(), "Content-Length: {}\r\n",
body match &body {
); Body::String(s) => s.len(),
Body::Static(s) => s.len(),
}
));
match stream.write(response.as_bytes()) { match stream.write(response.as_bytes()) {
Ok(_) => {} Ok(_) => {}

View file

@ -1,12 +1,29 @@
use std::{net::TcpListener, process}; use std::{env, ffi::OsString, fs, net::TcpListener, process};
use ctrlc; use ctrlc;
use http::responses::Response; use http::responses::{Response, Body};
mod handlers; mod handlers;
mod http; mod http;
fn main() { fn main() {
println!("luciders starting...");
let icons_dir_arg = env::args_os()
.nth(1)
.unwrap_or(OsString::from("vendor/lucide/icons"));
println!("Using icons from: {:?}", icons_dir_arg);
let icon_filenames: Vec<_> = fs::read_dir(icons_dir_arg)
.unwrap_or_else(|_| fatal("Failed to read icons directory! Does the path exist?"))
.map(|entry| entry.unwrap_or_else(|_| fatal("Failed to read icon entry")))
.map(|entry| entry.file_name())
.filter(|entry| entry.to_str().unwrap_or("").ends_with(".svg"))
.collect();
println!("Found {} icons", icon_filenames.len());
let listener = let listener =
TcpListener::bind("[::]:7878").unwrap_or_else(|_| fatal("Failed to bind to address")); TcpListener::bind("[::]:7878").unwrap_or_else(|_| fatal("Failed to bind to address"));
@ -19,11 +36,28 @@ fn main() {
let mut handlers = handlers::Handlers::new(); let mut handlers = handlers::Handlers::new();
handlers.add_handler("/", |req| { handlers.add_handler("/", |req| {
if req.method == "GET" { if req.method != "GET" {
Response::new(req, 200, "OK - luciders is running") return Response::new(req, 405, Body::Static("Method Not Allowed"));
} else {
Response::new(req, 405, "Method Not Allowed")
} }
Response::new(req, 200, Body::Static("OK - luciders is running"))
});
handlers.add_handler("/icons/[icon].png", move |req| {
if req.method != "GET" {
return Response::new(req, 405, Body::Static("Method Not Allowed"));
}
let icon_name = match req.url.get_path_segment(1) {
Some(icon) => icon,
None => return Response::new(req, 404, Body::Static("Icon Not Found")),
};
if !icon_filenames.contains(&OsString::from(&icon_name[..icon_name.len() - 4])) {
return Response::new(req, 404, Body::Static("Icon Not Found"));
}
return Response::new(req, 200, Body::Static("Icon Not Found"));
}); });
handlers.bind(listener); handlers.bind(listener);