agate

Simple gemini server for static files
git clone https://github.com/mbrubeck/agate.git
Log | Files | Refs | README

commit edf2ebffdce103db34a46cfce6957d684cc86137
parent 847434d844c337d8833468a11f9964969bae1b77
Author: Matt Brubeck <mbrubeck@limpet.net>
Date:   Thu, 31 Dec 2020 16:20:57 -0800

Refactor error handling and logging

Diffstat:
Msrc/main.rs | 65+++++++++++++++++++++++++++++++----------------------------------
1 file changed, 31 insertions(+), 34 deletions(-)

diff --git a/src/main.rs b/src/main.rs @@ -73,9 +73,9 @@ fn args() -> Result<Args> { opts.optflag("s", "silent", "Disable logging output"); opts.optflag("h", "help", "Print this help menu"); - let usage = opts.usage(&format!("Usage: {} FILE [options]", &args[0])); let matches = opts.parse(&args[1..]).map_err(|f| f.to_string())?; if matches.opt_present("h") { + let usage = opts.usage(&format!("Usage: {} [options]", &args[0])); Err(usage)?; } let hostname = match matches.opt_str("hostname") { @@ -107,18 +107,10 @@ async fn handle_request(stream: TcpStream) -> Result { static TLS: Lazy<TlsAcceptor> = Lazy::new(|| acceptor().unwrap()); let stream = &mut TLS.accept(stream).await?; - let url = match parse_request(stream).await { - Ok(url) => url, - Err((status, msg)) => { - send_header(stream, status, &[&msg]).await?; - Err(msg)? - } - }; - if let Err(e) = send_response(url, stream).await { - send_header(stream, 51, &["Not found, sorry."]).await?; - Err(e)? + match parse_request(stream).await { + Ok(url) => send_response(url, stream).await, + Err((status, msg)) => send_header(stream, status, &[msg]).await, } - Ok(()) } /// TLS configuration. @@ -147,10 +139,7 @@ async fn parse_request<R: Read + Unpin>( // Read until CRLF, end-of-stream, or there's no buffer space left. loop { - let bytes_read = stream - .read(buf) - .await - .map_err(|_| (59, "Request ended unexpectedly"))?; + let bytes_read = stream.read(buf).await.or(Err((59, "Request ended unexpectedly")))?; len += bytes_read; if request[..len].ends_with(b"\r\n") { break; @@ -159,24 +148,24 @@ async fn parse_request<R: Read + Unpin>( } buf = &mut request[len..]; } - let request = std::str::from_utf8(&request[..len - 2]).map_err(|_| (59, "Invalid URL"))?; + let request = std::str::from_utf8(&request[..len - 2]).or(Err((59, "Invalid URL")))?; log::info!("Got request for {:?}", request); // Handle scheme-relative URLs. let url = if request.starts_with("//") { - Url::parse(&format!("gemini:{}", request)).map_err(|_| (59, "Invalid URL"))? + Url::parse(&format!("gemini:{}", request)) } else { - Url::parse(request).map_err(|_| (59, "Invalid URL"))? - }; + Url::parse(request) + }.or(Err((59, "Invalid URL")))?; // Validate the URL, host and port. if url.scheme() != "gemini" { - return Err((53, "unsupported URL scheme")); + return Err((53, "Unsupported URL scheme")); } // TODO: Can be simplified by https://github.com/servo/rust-url/pull/651 if let (Some(Host::Domain(expected)), Some(Host::Domain(host))) = (url.host(), &ARGS.hostname) { if host != expected { - return Err((53, "proxy request refused")); + return Err((53, "Proxy request refused")); } } if let Some(port) = url.port() { @@ -195,23 +184,31 @@ async fn send_response<W: Write + Unpin>(url: Url, stream: &mut W) -> Result { path.push(&*percent_decode_str(segment).decode_utf8()?); } } - if async_std::fs::metadata(&path).await?.is_dir() { - if url.path().ends_with('/') || url.path().is_empty() { - // if the path ends with a slash or the path is empty, the links will work the same - // without a redirect - path.push("index.gmi"); - if !path.exists() && path.with_file_name(".directory-listing-ok").exists() { - path.pop(); - return list_directory(stream, &path).await; + if let Ok(metadata) = async_std::fs::metadata(&path).await { + if metadata.is_dir() { + if url.path().ends_with('/') || url.path().is_empty() { + // if the path ends with a slash or the path is empty, the links will work the same + // without a redirect + path.push("index.gmi"); + if !path.exists() && path.with_file_name(".directory-listing-ok").exists() { + path.pop(); + return list_directory(stream, &path).await; + } + } else { + // if client is not redirected, links may not work as expected without trailing slash + return send_header(stream, 31, &[url.as_str(), "/"]).await; } - } else { - // if client is not redirected, links may not work as expected without trailing slash - return send_header(stream, 31, &[url.as_str(), "/"]).await; } } // Make sure the file opens successfully before sending the success header. - let mut file = async_std::fs::File::open(&path).await?; + let mut file = match async_std::fs::File::open(&path).await { + Ok(file) => file, + Err(e) => { + send_header(stream, 51, &["Not found, sorry."]).await?; + Err(e)? + } + }; // Send header. if path.extension() == Some(OsStr::new("gmi")) {