use crate::app::{error_handler, PathAwareRequestHandler, RequestHandler};
use crate::http::headers::HeaderType;
use crate::http::mime::MimeType;
use crate::http::{Request, Response, StatusCode};
use crate::route::{try_find_path, LocatedPath};
use tokio::fs::File;
use tokio::io::AsyncReadExt;
use futures::Future;
use std::path::PathBuf;
use std::pin::Pin;
use std::sync::Arc;
const INDEX_FILES: [&str; 2] = ["index.html", "index.htm"];
pub fn serve_file<S>(file_path: &'static str) -> impl RequestHandler<S> {
let path_buf = PathBuf::from(file_path);
FileServer { path_buf }
}
struct FileServer {
path_buf: PathBuf,
}
impl<S> RequestHandler<S> for FileServer {
fn serve(&self, _: Request, _: Arc<S>) -> Pin<Box<dyn Future<Output = Response> + Send>> {
let path_buf = self.path_buf.clone();
Box::pin(async move {
if let Ok(mut file) = File::open(&path_buf).await {
let mut buf = Vec::new();
if file.read_to_end(&mut buf).await.is_ok() {
return if let Some(extension) = &path_buf.extension() {
Response::new(StatusCode::OK, buf).with_header(
HeaderType::ContentType,
MimeType::from_extension(extension.to_str().unwrap()).to_string(),
)
} else {
Response::new(StatusCode::OK, buf)
};
}
}
error_handler(StatusCode::NotFound)
})
}
}
pub fn serve_as_file_path<S>(directory_path: &'static str) -> impl RequestHandler<S> {
let directory_path = PathBuf::from(directory_path.strip_suffix('/').unwrap_or(directory_path));
FilePathServer { directory_path }
}
struct FilePathServer {
directory_path: PathBuf,
}
impl<S> RequestHandler<S> for FilePathServer {
fn serve(&self, request: Request, _: Arc<S>) -> Pin<Box<dyn Future<Output = Response> + Send>> {
let directory_path = self.directory_path.clone();
Box::pin(async move {
let file_path = request.uri.strip_prefix('/').unwrap_or(&request.uri);
let path = format!("{}/{}", directory_path.to_str().unwrap(), file_path);
let path_buf = PathBuf::from(path);
if let Ok(mut file) = File::open(&path_buf).await {
let mut buf = Vec::new();
if file.read_to_end(&mut buf).await.is_ok() {
return if let Some(extension) = path_buf.extension() {
Response::new(StatusCode::OK, buf).with_header(
HeaderType::ContentType,
MimeType::from_extension(extension.to_str().unwrap()).to_string(),
)
} else {
Response::new(StatusCode::OK, buf)
};
}
}
error_handler(StatusCode::NotFound)
})
}
}
pub fn serve_dir<S>(directory_path: &'static str) -> impl PathAwareRequestHandler<S> {
DirServer { directory_path }
}
struct DirServer {
directory_path: &'static str,
}
impl<S> PathAwareRequestHandler<S> for DirServer {
fn serve(
&self,
request: Request,
_: Arc<S>,
route: &'static str,
) -> Pin<Box<dyn Future<Output = Response> + Send>> {
let directory_path = self.directory_path;
Box::pin(async move {
let route_without_wildcard = route.strip_suffix('*').unwrap_or(route);
let uri_without_route = request
.uri
.strip_prefix(route_without_wildcard)
.unwrap_or(&request.uri);
let located = try_find_path(directory_path, uri_without_route, &INDEX_FILES);
if let Some(located) = located {
match located {
LocatedPath::Directory => Response::empty(StatusCode::MovedPermanently)
.with_header(HeaderType::Location, format!("{}/", &request.uri)),
LocatedPath::File(path) => {
if let Ok(mut file) = File::open(&path).await {
let mut buf = Vec::new();
if file.read_to_end(&mut buf).await.is_ok() {
return if let Some(extension) = path.extension() {
Response::new(StatusCode::OK, buf).with_header(
HeaderType::ContentType,
MimeType::from_extension(extension.to_str().unwrap())
.to_string(),
)
} else {
Response::new(StatusCode::OK, buf)
};
}
}
error_handler(StatusCode::InternalError)
}
}
} else {
error_handler(StatusCode::NotFound)
}
})
}
}
pub fn redirect<S>(location: &'static str) -> impl RequestHandler<S> {
RedirectServer { location }
}
struct RedirectServer {
location: &'static str,
}
impl<S> RequestHandler<S> for RedirectServer {
fn serve(&self, _: Request, _: Arc<S>) -> Pin<Box<dyn Future<Output = Response> + Send>> {
let location = self.location;
Box::pin(async move { Response::redirect(location) })
}
}