diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e32277..46e7fed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - `Meta::new` rejects strings exceeding `Meta::MAX_LEN` (`1024`) - Some `Response` and `Status` constructors are now infallible +- Improve error messages ### Deprecated - Instead of `gemini_mime()` use `GEMINI_MIME` diff --git a/Cargo.toml b/Cargo.toml index 6c4874c..7643e4a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,3 +19,6 @@ itertools = "0.9.0" log = "0.4.11" webpki = "0.21.0" lazy_static = "1.4.0" + +[dev-dependencies] +env_logger = "0.8.1" diff --git a/examples/certificates.rs b/examples/certificates.rs index e3c18e7..2da1e68 100644 --- a/examples/certificates.rs +++ b/examples/certificates.rs @@ -1,5 +1,6 @@ use anyhow::*; use futures::{future::BoxFuture, FutureExt}; +use log::LevelFilter; use tokio::sync::RwLock; use northstar::{Certificate, GEMINI_MIME, GEMINI_PORT, Request, Response, Server}; use std::collections::HashMap; @@ -10,6 +11,10 @@ type CertBytes = Vec; #[tokio::main] async fn main() -> Result<()> { + env_logger::builder() + .filter_module("northstar", LevelFilter::Debug) + .init(); + let users = Arc::>>::default(); Server::bind(("0.0.0.0", GEMINI_PORT)) diff --git a/examples/serve_dir.rs b/examples/serve_dir.rs index d4a6bb8..47145e1 100644 --- a/examples/serve_dir.rs +++ b/examples/serve_dir.rs @@ -1,9 +1,14 @@ use anyhow::*; use futures::{future::BoxFuture, FutureExt}; +use log::LevelFilter; use northstar::{Server, Request, Response, GEMINI_PORT}; #[tokio::main] async fn main() -> Result<()> { + env_logger::builder() + .filter_module("northstar", LevelFilter::Debug) + .init(); + Server::bind(("localhost", GEMINI_PORT)) .serve(handle_request) .await diff --git a/src/lib.rs b/src/lib.rs index 2a58256..c217c79 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -44,22 +44,25 @@ impl Server { async fn serve(self) -> Result<()> { loop { - let (stream, _addr) = self.listener.accept().await?; + let (stream, _addr) = self.listener.accept().await + .context("Failed to accept client")?; let this = self.clone(); tokio::spawn(async move { if let Err(err) = this.serve_client(stream).await { - error!("{}", err); + error!("{:?}", err); } }); } } async fn serve_client(self, stream: TcpStream) -> Result<()> { - let stream = self.tls_acceptor.accept(stream).await?; + let stream = self.tls_acceptor.accept(stream).await + .context("Failed to establish TLS session")?; let mut stream = BufStream::new(stream); - let mut request = receive_request(&mut stream).await?; + let mut request = receive_request(&mut stream).await + .context("Failed to receive request")?; debug!("Client requested: {}", request.uri()); // Identify the client certificate from the tls stream. This is the first @@ -78,13 +81,16 @@ impl Server { let response = handler.catch_unwind().await .unwrap_or_else(|_| Response::server_error("")) .or_else(|err| { - error!("Handler: {}", err); + error!("Handler failed: {:?}", err); Response::server_error("") - })?; + }) + .context("Request handler failed")?; - send_response(response, &mut stream).await?; + send_response(response, &mut stream).await + .context("Failed to send response")?; - stream.flush().await?; + stream.flush().await + .context("Failed to flush response data")?; Ok(()) } @@ -103,11 +109,15 @@ impl Builder { where F: Fn(Request) -> HandlerResponse + Send + Sync + 'static, { - let config = tls_config()?; + let config = tls_config() + .context("Failed to create TLS config")?; + + let listener = TcpListener::bind(self.addr).await + .context("Failed to create socket")?; let server = Server { tls_acceptor: TlsAcceptor::from(config), - listener: Arc::new(TcpListener::bind(self.addr).await?), + listener: Arc::new(listener), handler: Arc::new(handler), }; @@ -134,17 +144,22 @@ async fn receive_request(stream: &mut (impl AsyncBufRead + Unpin)) -> Result Result<()> { - send_response_header(response.header(), stream).await?; + send_response_header(response.header(), stream).await + .context("Failed to send response header")?; if let Some(body) = response.take_body() { - send_response_body(body, stream).await?; + send_response_body(body, stream).await + .context("Failed to send response body")?; } Ok(()) @@ -174,26 +189,34 @@ async fn send_response_body(body: Body, stream: &mut (impl AsyncWrite + Unpin)) fn tls_config() -> Result> { let mut config = ServerConfig::new(AllowAnonOrSelfsignedClient::new()); - let cert_chain = load_cert_chain()?; - let key = load_key()?; - config.set_single_cert(cert_chain, key)?; + let cert_chain = load_cert_chain() + .context("Failed to load TLS certificate")?; + let key = load_key() + .context("Failed to load TLS key")?; + config.set_single_cert(cert_chain, key) + .context("Failed to use loaded TLS certificate")?; Ok(config.into()) } fn load_cert_chain() -> Result> { - let certs = std::fs::File::open("cert/cert.pem")?; + let cert_path = "cert/cert.pem"; + let certs = std::fs::File::open(cert_path) + .with_context(|| format!("Failed to open `{}`", cert_path))?; let mut certs = BufReader::new(certs); let certs = rustls::internal::pemfile::certs(&mut certs) - .map_err(|_| anyhow!("failed to load certs"))?; + .map_err(|_| anyhow!("failed to load certs `{}`", cert_path))?; Ok(certs) } fn load_key() -> Result { - let mut keys = BufReader::new(std::fs::File::open("cert/key.pem")?); + let key_path = "cert/key.pem"; + let keys = std::fs::File::open(key_path) + .with_context(|| format!("Failed to open `{}`", key_path))?; + let mut keys = BufReader::new(keys); let mut keys = rustls::internal::pemfile::pkcs8_private_keys(&mut keys) - .map_err(|_| anyhow!("failed to load key"))?; + .map_err(|_| anyhow!("failed to load key `{}`", key_path))?; ensure!(!keys.is_empty(), "no key found"); diff --git a/src/types.rs b/src/types.rs index 15316f5..9b2d538 100644 --- a/src/types.rs +++ b/src/types.rs @@ -27,7 +27,8 @@ impl Request { None => None, Some(query) => { let input = percent_decode_str(query.as_str()) - .decode_utf8()? + .decode_utf8() + .context("Request URI query contains invalid UTF-8")? .into_owned(); Some(input) } @@ -84,7 +85,7 @@ impl ResponseHeader { pub fn input(prompt: impl AsRef + Into) -> Result { Ok(Self { status: Status::INPUT, - meta: Meta::new(prompt)?, + meta: Meta::new(prompt).context("Invalid input prompt")?, }) } @@ -105,7 +106,7 @@ impl ResponseHeader { pub fn server_error(reason: impl AsRef + Into) -> Result { Ok(Self { status: Status::PERMANENT_FAILURE, - meta: Meta::new(reason)?, + meta: Meta::new(reason).context("Invalid server error reason")?, }) } @@ -269,7 +270,8 @@ impl Meta { } pub fn to_mime(&self) -> Result { - let mime = self.as_str().parse::()?; + let mime = self.as_str().parse::() + .context("Meta is not a valid MIME")?; Ok(mime) } } diff --git a/src/util.rs b/src/util.rs index 0236fbd..c1265e4 100644 --- a/src/util.rs +++ b/src/util.rs @@ -25,14 +25,16 @@ pub async fn serve_file>(path: P, mime: &Mime) -> Result, P: AsRef>(dir: D, virtual_path: &[P]) -> Result { debug!("Dir: {}", dir.as_ref().display()); - let dir = dir.as_ref().canonicalize()?; + let dir = dir.as_ref().canonicalize() + .context("Failed to canonicalize directory")?; let mut path = dir.to_path_buf(); for segment in virtual_path { path.push(segment); } - let path = path.canonicalize()?; + let path = path.canonicalize() + .context("Failed to canonicalize path")?; if !path.starts_with(&dir) { return Ok(Response::not_found()); @@ -67,10 +69,12 @@ async fn serve_dir_listing, B: AsRef>(path: P, virtual_path writeln!(listing, "=> .. 📁 ../")?; } - while let Some(entry) = dir.next_entry().await? { + while let Some(entry) = dir.next_entry().await.context("Failed to list directory")? { let file_name = entry.file_name(); let file_name = file_name.to_string_lossy(); - let is_dir = entry.file_type().await?.is_dir(); + let is_dir = entry.file_type().await + .with_context(|| format!("Failed to get file type of `{}`", entry.path().display()))? + .is_dir(); writeln!( listing,