Merge branch 'reduce-arcs' into routes
This commit is contained in:
commit
f572209dfa
|
@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
- build time and size by [@Alch-Emi]
|
- build time and size by [@Alch-Emi]
|
||||||
### Changed
|
### Changed
|
||||||
- Added route API [@Alch-Emi](https://github.com/Alch-Emi)
|
- Added route API [@Alch-Emi](https://github.com/Alch-Emi)
|
||||||
|
- API for adding handlers now accepts async handlers [@Alch-Emi](https://github.com/Alch-Emi)
|
||||||
|
|
||||||
## [0.3.0] - 2020-11-14
|
## [0.3.0] - 2020-11-14
|
||||||
### Added
|
### Added
|
||||||
|
|
|
@ -20,7 +20,6 @@ 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-core = "0.3.7"
|
|
||||||
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"
|
||||||
|
@ -28,5 +27,4 @@ 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"] }
|
tokio = { version = "0.3.1", features = ["macros", "rt-multi-thread", "sync"] }
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
use anyhow::*;
|
use anyhow::*;
|
||||||
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};
|
||||||
|
@ -31,44 +29,42 @@ async fn main() -> Result<()> {
|
||||||
/// selecting a username. They'll then get a message confirming their account creation.
|
/// selecting a username. They'll then get a message confirming their account creation.
|
||||||
/// Any time this user visits the site in the future, they'll get a personalized welcome
|
/// Any time this user visits the site in the future, they'll get a personalized welcome
|
||||||
/// message.
|
/// message.
|
||||||
fn handle_request(users: Arc<RwLock<HashMap<CertBytes, String>>>, request: Request) -> BoxFuture<'static, Result<Response>> {
|
async fn handle_request(users: Arc<RwLock<HashMap<CertBytes, String>>>, request: Request) -> Result<Response> {
|
||||||
async move {
|
if let Some(Certificate(cert_bytes)) = request.certificate() {
|
||||||
if let Some(Certificate(cert_bytes)) = request.certificate() {
|
// The user provided a certificate
|
||||||
// The user provided a certificate
|
let users_read = users.read().await;
|
||||||
let users_read = users.read().await;
|
if let Some(user) = users_read.get(cert_bytes) {
|
||||||
if let Some(user) = users_read.get(cert_bytes) {
|
// The user has already registered
|
||||||
// The user has already registered
|
Ok(
|
||||||
|
Response::success_with_body(
|
||||||
|
&GEMINI_MIME,
|
||||||
|
format!("Welcome {}!", user)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// The user still needs to register
|
||||||
|
drop(users_read);
|
||||||
|
if let Some(query_part) = request.uri().query() {
|
||||||
|
// The user provided some input (a username request)
|
||||||
|
let username = query_part.as_str();
|
||||||
|
let mut users_write = users.write().await;
|
||||||
|
users_write.insert(cert_bytes.clone(), username.to_owned());
|
||||||
Ok(
|
Ok(
|
||||||
Response::success_with_body(
|
Response::success_with_body(
|
||||||
&GEMINI_MIME,
|
&GEMINI_MIME,
|
||||||
format!("Welcome {}!", user)
|
format!(
|
||||||
|
"Your account has been created {}! Welcome!",
|
||||||
|
username
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
// The user still needs to register
|
// The user didn't provide input, and should be prompted
|
||||||
drop(users_read);
|
Response::input("What username would you like?")
|
||||||
if let Some(query_part) = request.uri().query() {
|
|
||||||
// The user provided some input (a username request)
|
|
||||||
let username = query_part.as_str();
|
|
||||||
let mut users_write = users.write().await;
|
|
||||||
users_write.insert(cert_bytes.clone(), username.to_owned());
|
|
||||||
Ok(
|
|
||||||
Response::success_with_body(
|
|
||||||
&GEMINI_MIME,
|
|
||||||
format!(
|
|
||||||
"Your account has been created {}! Welcome!",
|
|
||||||
username
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
// The user didn't provide input, and should be prompted
|
|
||||||
Response::input("What username would you like?")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// The user didn't provide a certificate
|
|
||||||
Ok(Response::client_certificate_required())
|
|
||||||
}
|
}
|
||||||
}.boxed()
|
} else {
|
||||||
|
// The user didn't provide a certificate
|
||||||
|
Ok(Response::client_certificate_required())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
use anyhow::*;
|
use anyhow::*;
|
||||||
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::*;
|
||||||
|
@ -17,34 +15,31 @@ async fn main() -> Result<()> {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_request(_request: Request) -> BoxFuture<'static, Result<Response>> {
|
async fn handle_request(_request: Request) -> Result<Response> {
|
||||||
async move {
|
let response = Document::new()
|
||||||
let response = Document::new()
|
.add_preformatted(include_str!("northstar_logo.txt"))
|
||||||
.add_preformatted(include_str!("northstar_logo.txt"))
|
.add_blank_line()
|
||||||
.add_blank_line()
|
.add_link("https://docs.rs/northstar", "Documentation")
|
||||||
.add_link("https://docs.rs/northstar", "Documentation")
|
.add_link("https://github.com/panicbit/northstar", "GitHub")
|
||||||
.add_link("https://github.com/panicbit/northstar", "GitHub")
|
.add_blank_line()
|
||||||
.add_blank_line()
|
.add_heading(H1, "Usage")
|
||||||
.add_heading(H1, "Usage")
|
.add_blank_line()
|
||||||
.add_blank_line()
|
.add_text("Add the latest version of northstar to your `Cargo.toml`.")
|
||||||
.add_text("Add the latest version of northstar to your `Cargo.toml`.")
|
.add_blank_line()
|
||||||
.add_blank_line()
|
.add_heading(H2, "Manually")
|
||||||
.add_heading(H2, "Manually")
|
.add_blank_line()
|
||||||
.add_blank_line()
|
.add_preformatted_with_alt("toml", r#"northstar = "0.3.0" # check crates.io for the latest version"#)
|
||||||
.add_preformatted_with_alt("toml", r#"northstar = "0.3.0" # check crates.io for the latest version"#)
|
.add_blank_line()
|
||||||
.add_blank_line()
|
.add_heading(H2, "Automatically")
|
||||||
.add_heading(H2, "Automatically")
|
.add_blank_line()
|
||||||
.add_blank_line()
|
.add_preformatted_with_alt("sh", "cargo add northstar")
|
||||||
.add_preformatted_with_alt("sh", "cargo add northstar")
|
.add_blank_line()
|
||||||
.add_blank_line()
|
.add_heading(H1, "Generating a key & certificate")
|
||||||
.add_heading(H1, "Generating a key & certificate")
|
.add_blank_line()
|
||||||
.add_blank_line()
|
.add_preformatted_with_alt("sh", concat!(
|
||||||
.add_preformatted_with_alt("sh", concat!(
|
"mkdir cert && cd cert\n",
|
||||||
"mkdir cert && cd cert\n",
|
"openssl req -x509 -nodes -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365",
|
||||||
"openssl req -x509 -nodes -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365",
|
))
|
||||||
))
|
.into();
|
||||||
.into();
|
Ok(response)
|
||||||
Ok(response)
|
|
||||||
}
|
|
||||||
.boxed()
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
use anyhow::*;
|
use anyhow::*;
|
||||||
use futures_core::future::BoxFuture;
|
|
||||||
use futures_util::FutureExt;
|
|
||||||
use log::LevelFilter;
|
use log::LevelFilter;
|
||||||
use northstar::{Document, document::HeadingLevel, Request, Response, GEMINI_PORT};
|
use northstar::{Document, document::HeadingLevel, Request, Response, GEMINI_PORT};
|
||||||
|
|
||||||
|
@ -18,25 +16,19 @@ async fn main() -> Result<()> {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_base(req: Request) -> BoxFuture<'static, Result<Response>> {
|
async fn handle_base(req: Request) -> Result<Response> {
|
||||||
let doc = generate_doc("base", &req);
|
let doc = generate_doc("base", &req);
|
||||||
async move {
|
Ok(Response::document(doc))
|
||||||
Ok(Response::document(doc))
|
|
||||||
}.boxed()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_short(req: Request) -> BoxFuture<'static, Result<Response>> {
|
async fn handle_short(req: Request) -> Result<Response> {
|
||||||
let doc = generate_doc("short", &req);
|
let doc = generate_doc("short", &req);
|
||||||
async move {
|
Ok(Response::document(doc))
|
||||||
Ok(Response::document(doc))
|
|
||||||
}.boxed()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_long(req: Request) -> BoxFuture<'static, Result<Response>> {
|
async fn handle_long(req: Request) -> Result<Response> {
|
||||||
let doc = generate_doc("long", &req);
|
let doc = generate_doc("long", &req);
|
||||||
async move {
|
Ok(Response::document(doc))
|
||||||
Ok(Response::document(doc))
|
|
||||||
}.boxed()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_doc(route_name: &str, req: &Request) -> Document {
|
fn generate_doc(route_name: &str, req: &Request) -> Document {
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
use anyhow::*;
|
use anyhow::*;
|
||||||
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};
|
||||||
|
|
||||||
|
@ -16,12 +14,9 @@ async fn main() -> Result<()> {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_request(request: Request) -> BoxFuture<'static, Result<Response>> {
|
async fn handle_request(request: Request) -> Result<Response> {
|
||||||
async move {
|
let path = request.path_segments();
|
||||||
let path = request.path_segments();
|
let response = northstar::util::serve_dir("public", &path).await?;
|
||||||
let response = northstar::util::serve_dir("public", &path).await?;
|
|
||||||
|
|
||||||
Ok(response)
|
Ok(response)
|
||||||
}
|
|
||||||
.boxed()
|
|
||||||
}
|
}
|
||||||
|
|
23
src/lib.rs
23
src/lib.rs
|
@ -7,8 +7,9 @@ use std::{
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
time::Duration,
|
time::Duration,
|
||||||
|
pin::Pin,
|
||||||
};
|
};
|
||||||
use futures_core::future::BoxFuture;
|
use std::future::Future;
|
||||||
use tokio::{
|
use tokio::{
|
||||||
prelude::*,
|
prelude::*,
|
||||||
io::{self, BufStream},
|
io::{self, BufStream},
|
||||||
|
@ -36,13 +37,12 @@ pub use types::*;
|
||||||
pub const REQUEST_URI_MAX_LEN: usize = 1024;
|
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 = Box<dyn Fn(Request) -> HandlerResponse + Send + Sync>;
|
||||||
pub (crate) type HandlerResponse = BoxFuture<'static, Result<Response>>;
|
pub (crate) type HandlerResponse = Pin<Box<dyn Future<Output = Result<Response>> + Send>>;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Server {
|
pub struct Server {
|
||||||
tls_acceptor: TlsAcceptor,
|
tls_acceptor: TlsAcceptor,
|
||||||
listener: Arc<TcpListener>,
|
|
||||||
routes: Arc<RoutingNode<Handler>>,
|
routes: Arc<RoutingNode<Handler>>,
|
||||||
timeout: Duration,
|
timeout: Duration,
|
||||||
complex_timeout: Option<Duration>,
|
complex_timeout: Option<Duration>,
|
||||||
|
@ -53,9 +53,9 @@ impl Server {
|
||||||
Builder::bind(addr)
|
Builder::bind(addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn serve(self) -> Result<()> {
|
async fn serve(self, listener: TcpListener) -> Result<()> {
|
||||||
loop {
|
loop {
|
||||||
let (stream, _addr) = self.listener.accept().await
|
let (stream, _addr) = listener.accept().await
|
||||||
.context("Failed to accept client")?;
|
.context("Failed to accept client")?;
|
||||||
let this = self.clone();
|
let this = self.clone();
|
||||||
|
|
||||||
|
@ -293,11 +293,13 @@ impl<A: ToSocketAddrs> Builder<A> {
|
||||||
/// "endpoint". Entering a relative or malformed path will result in a panic.
|
/// "endpoint". Entering a relative or malformed path will result in a panic.
|
||||||
///
|
///
|
||||||
/// For more information about routing mechanics, see the docs for [`RoutingNode`].
|
/// For more information about routing mechanics, see the docs for [`RoutingNode`].
|
||||||
pub fn add_route<H>(mut self, path: &'static str, handler: H) -> Self
|
pub fn add_route<F, H>(mut self, path: &'static str, handler: H) -> Self
|
||||||
where
|
where
|
||||||
H: Fn(Request) -> HandlerResponse + Send + Sync + 'static,
|
H: Send + Sync + 'static + Fn(Request) -> F,
|
||||||
|
F: Send + Sync + 'static + Future<Output = Result<Response>>
|
||||||
{
|
{
|
||||||
self.routes.add_route(path, Arc::new(handler));
|
let wrapped = Box::new(move|req| Box::pin((handler)(req)) as HandlerResponse);
|
||||||
|
self.routes.add_route(path, wrapped);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -312,13 +314,12 @@ impl<A: ToSocketAddrs> Builder<A> {
|
||||||
|
|
||||||
let server = Server {
|
let server = Server {
|
||||||
tls_acceptor: TlsAcceptor::from(config),
|
tls_acceptor: TlsAcceptor::from(config),
|
||||||
listener: Arc::new(listener),
|
|
||||||
routes: Arc::new(self.routes),
|
routes: Arc::new(self.routes),
|
||||||
timeout: self.timeout,
|
timeout: self.timeout,
|
||||||
complex_timeout: self.complex_body_timeout_override,
|
complex_timeout: self.complex_body_timeout_override,
|
||||||
};
|
};
|
||||||
|
|
||||||
server.serve().await
|
server.serve(listener).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ use crate::types::{Document, document::HeadingLevel::*};
|
||||||
use crate::types::Response;
|
use crate::types::Response;
|
||||||
use std::panic::{catch_unwind, AssertUnwindSafe};
|
use std::panic::{catch_unwind, AssertUnwindSafe};
|
||||||
use std::task::Poll;
|
use std::task::Poll;
|
||||||
use futures_core::future::Future;
|
use std::future::Future;
|
||||||
use tokio::time;
|
use tokio::time;
|
||||||
|
|
||||||
#[cfg(feature="serve_dir")]
|
#[cfg(feature="serve_dir")]
|
||||||
|
|
Loading…
Reference in a new issue