Merge pull request #25 from Alch-Emi/trim-deps

Trim dependencies & move directory serving utils to feature
This commit is contained in:
panicbit 2020-11-19 18:29:52 +01:00 committed by GitHub
commit 4e251d0cb6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 68 additions and 17 deletions

View File

@ -8,20 +8,25 @@ description = "Gemini server implementation"
repository = "https://github.com/panicbit/northstar" repository = "https://github.com/panicbit/northstar"
documentation = "https://docs.rs/northstar" documentation = "https://docs.rs/northstar"
[features]
default = ["serve_dir"]
serve_dir = ["mime_guess", "tokio/fs"]
[dependencies] [dependencies]
anyhow = "1.0.33" anyhow = "1.0.33"
rustls = { version = "0.18.1", features = ["dangerous_configuration"] } rustls = { version = "0.18.1", features = ["dangerous_configuration"] }
tokio-rustls = "0.20.0" tokio-rustls = "0.20.0"
tokio = { version = "0.3.1", features = ["full"] } tokio = { version = "0.3.1", features = ["io-util","net","time", "rt"] }
mime = "0.3.16" mime = "0.3.16"
uriparse = "0.6.3" uriparse = "0.6.3"
percent-encoding = "2.1.0" percent-encoding = "2.1.0"
futures = "0.3.7" futures-core = "0.3.7"
itertools = "0.9.0"
log = "0.4.11" log = "0.4.11"
webpki = "0.21.0" webpki = "0.21.0"
lazy_static = "1.4.0" lazy_static = "1.4.0"
mime_guess = "2.0.3" mime_guess = { version = "2.0.3", optional = true }
[dev-dependencies] [dev-dependencies]
env_logger = "0.8.1" env_logger = "0.8.1"
futures-util = "0.3.7"
tokio = { version = "0.3.1", features = ["macros", "rt-multi-thread", "sync"] }

View File

@ -1,5 +1,6 @@
use anyhow::*; use anyhow::*;
use futures::{future::BoxFuture, FutureExt}; use futures_core::future::BoxFuture;
use futures_util::FutureExt;
use log::LevelFilter; use log::LevelFilter;
use tokio::sync::RwLock; use tokio::sync::RwLock;
use northstar::{Certificate, GEMINI_MIME, GEMINI_PORT, Request, Response, Server}; use northstar::{Certificate, GEMINI_MIME, GEMINI_PORT, Request, Response, Server};

View File

@ -1,5 +1,6 @@
use anyhow::*; use anyhow::*;
use futures::{future::BoxFuture, FutureExt}; use futures_core::future::BoxFuture;
use futures_util::FutureExt;
use log::LevelFilter; use log::LevelFilter;
use northstar::{Server, Request, Response, GEMINI_PORT, Document}; use northstar::{Server, Request, Response, GEMINI_PORT, Document};
use northstar::document::HeadingLevel::*; use northstar::document::HeadingLevel::*;

View File

@ -1,5 +1,6 @@
use anyhow::*; use anyhow::*;
use futures::{future::BoxFuture, FutureExt}; use futures_core::future::BoxFuture;
use futures_util::FutureExt;
use log::LevelFilter; use log::LevelFilter;
use northstar::{Server, Request, Response, GEMINI_PORT}; use northstar::{Server, Request, Response, GEMINI_PORT};

View File

@ -8,7 +8,7 @@ use std::{
path::PathBuf, path::PathBuf,
time::Duration, time::Duration,
}; };
use futures::{future::BoxFuture, FutureExt}; use futures_core::future::BoxFuture;
use tokio::{ use tokio::{
prelude::*, prelude::*,
io::{self, BufStream}, io::{self, BufStream},
@ -33,7 +33,7 @@ pub const REQUEST_URI_MAX_LEN: usize = 1024;
pub const GEMINI_PORT: u16 = 1965; pub const GEMINI_PORT: u16 = 1965;
type Handler = Arc<dyn Fn(Request) -> HandlerResponse + Send + Sync>; type Handler = Arc<dyn Fn(Request) -> HandlerResponse + Send + Sync>;
type HandlerResponse = BoxFuture<'static, Result<Response>>; pub (crate) type HandlerResponse = BoxFuture<'static, Result<Response>>;
#[derive(Clone)] #[derive(Clone)]
pub struct Server { pub struct Server {
@ -94,7 +94,7 @@ impl Server {
let handler = (self.handler)(request); let handler = (self.handler)(request);
let handler = AssertUnwindSafe(handler); let handler = AssertUnwindSafe(handler);
let response = handler.catch_unwind().await let response = util::HandlerCatchUnwind::new(handler).await
.unwrap_or_else(|_| Response::server_error("")) .unwrap_or_else(|_| Response::server_error(""))
.or_else(|err| { .or_else(|err| {
error!("Handler failed: {:?}", err); error!("Handler failed: {:?}", err);

View File

@ -1,4 +1,6 @@
use tokio::{io::AsyncRead, fs::File}; use tokio::io::AsyncRead;
#[cfg(feature="serve_dir")]
use tokio::fs::File;
use crate::types::Document; use crate::types::Document;
@ -37,6 +39,7 @@ impl<'a> From<&'a str> for Body {
} }
} }
#[cfg(feature="serve_dir")]
impl From<File> for Body { impl From<File> for Body {
fn from(file: File) -> Self { fn from(file: File) -> Self {
Self::Reader(Box::new(file)) Self::Reader(Box::new(file))

View File

@ -39,7 +39,6 @@
use std::convert::TryInto; use std::convert::TryInto;
use std::fmt; use std::fmt;
use itertools::Itertools;
use crate::types::URIReference; use crate::types::URIReference;
use crate::util::Cowy; use crate::util::Cowy;
@ -550,5 +549,6 @@ fn strip_newlines(text: impl Cowy<str>) -> String {
text.as_ref() text.as_ref()
.lines() .lines()
.filter(|part| !part.is_empty()) .filter(|part| !part.is_empty())
.collect::<Vec<_>>()
.join(" ") .join(" ")
} }

View File

@ -1,13 +1,21 @@
use std::path::Path; #[cfg(feature="serve_dir")]
use std::path::{Path, PathBuf};
#[cfg(feature="serve_dir")]
use mime::Mime; use mime::Mime;
use anyhow::*; use anyhow::*;
#[cfg(feature="serve_dir")]
use tokio::{ use tokio::{
fs::{self, File}, fs::{self, File},
io, io,
}; };
use crate::types::{Response, Document, document::HeadingLevel::*}; #[cfg(feature="serve_dir")]
use itertools::Itertools; use crate::types::{Document, document::HeadingLevel::*};
use crate::types::Response;
use std::panic::{catch_unwind, AssertUnwindSafe};
use std::task::Poll;
use futures_core::future::Future;
#[cfg(feature="serve_dir")]
pub async fn serve_file<P: AsRef<Path>>(path: P, mime: &Mime) -> Result<Response> { pub async fn serve_file<P: AsRef<Path>>(path: P, mime: &Mime) -> Result<Response> {
let path = path.as_ref(); let path = path.as_ref();
@ -22,6 +30,7 @@ pub async fn serve_file<P: AsRef<Path>>(path: P, mime: &Mime) -> Result<Response
Ok(Response::success_with_body(mime, file)) Ok(Response::success_with_body(mime, file))
} }
#[cfg(feature="serve_dir")]
pub async fn serve_dir<D: AsRef<Path>, P: AsRef<Path>>(dir: D, virtual_path: &[P]) -> Result<Response> { pub async fn serve_dir<D: AsRef<Path>, P: AsRef<Path>>(dir: D, virtual_path: &[P]) -> Result<Response> {
debug!("Dir: {}", dir.as_ref().display()); debug!("Dir: {}", dir.as_ref().display());
let dir = dir.as_ref().canonicalize() let dir = dir.as_ref().canonicalize()
@ -47,6 +56,7 @@ pub async fn serve_dir<D: AsRef<Path>, P: AsRef<Path>>(dir: D, virtual_path: &[P
serve_dir_listing(path, virtual_path).await serve_dir_listing(path, virtual_path).await
} }
#[cfg(feature="serve_dir")]
async fn serve_dir_listing<P: AsRef<Path>, B: AsRef<Path>>(path: P, virtual_path: &[B]) -> Result<Response> { async fn serve_dir_listing<P: AsRef<Path>, B: AsRef<Path>>(path: P, virtual_path: &[B]) -> Result<Response> {
let mut dir = match fs::read_dir(path).await { let mut dir = match fs::read_dir(path).await {
Ok(dir) => dir, Ok(dir) => dir,
@ -56,10 +66,10 @@ async fn serve_dir_listing<P: AsRef<Path>, B: AsRef<Path>>(path: P, virtual_path
} }
}; };
let breadcrumbs = virtual_path.iter().map(|segment| segment.as_ref().display()).join("/"); let breadcrumbs: PathBuf = virtual_path.iter().collect();
let mut document = Document::new(); let mut document = Document::new();
document.add_heading(H1, format!("Index of /{}", breadcrumbs)); document.add_heading(H1, format!("Index of /{}", breadcrumbs.display()));
document.add_blank_line(); document.add_blank_line();
if virtual_path.get(0).map(<_>::as_ref) != Some(Path::new("")) { if virtual_path.get(0).map(<_>::as_ref) != Some(Path::new("")) {
@ -85,6 +95,7 @@ async fn serve_dir_listing<P: AsRef<Path>, B: AsRef<Path>>(path: P, virtual_path
Ok(Response::document(document)) Ok(Response::document(document))
} }
#[cfg(feature="serve_dir")]
pub fn guess_mime_from_path<P: AsRef<Path>>(path: P) -> Mime { pub fn guess_mime_from_path<P: AsRef<Path>>(path: P) -> Mime {
let path = path.as_ref(); let path = path.as_ref();
let extension = path.extension().and_then(|s| s.to_str()); let extension = path.extension().and_then(|s| s.to_str());
@ -115,3 +126,32 @@ where
C: AsRef<T> + Into<T::Owned>, C: AsRef<T> + Into<T::Owned>,
T: ToOwned + ?Sized, T: ToOwned + ?Sized,
{} {}
/// A utility for catching unwinds on Futures.
///
/// This is adapted from the futures-rs CatchUnwind, in an effort to reduce the large
/// amount of dependencies tied into the feature that provides this simple struct.
#[must_use = "futures do nothing unless polled"]
pub (crate) struct HandlerCatchUnwind {
future: AssertUnwindSafe<crate::HandlerResponse>,
}
impl HandlerCatchUnwind {
pub(super) fn new(future: AssertUnwindSafe<crate::HandlerResponse>) -> Self {
Self { future }
}
}
impl Future for HandlerCatchUnwind {
type Output = Result<Result<Response>, Box<dyn std::any::Any + Send>>;
fn poll(
mut self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context
) -> Poll<Self::Output> {
match catch_unwind(AssertUnwindSafe(|| self.future.as_mut().poll(cx))) {
Ok(res) => res.map(Ok),
Err(e) => Poll::Ready(Err(e))
}
}
}