agate

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

commit b671ddf235d616ee7d72fb3ec2458834af168659
parent 7ef914153b1e2753ae6fee16c521130b3d2f65b4
Author: Johann150 <johann.galle@protonmail.com>
Date:   Mon, 16 Nov 2020 20:39:30 +0100

implement right status codes

Ran cargo fmt.
Implemented most recommendations of clippy.

Diffstat:
Msrc/main.rs | 79++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------
1 file changed, 52 insertions(+), 27 deletions(-)

diff --git a/src/main.rs b/src/main.rs @@ -1,7 +1,15 @@ -use async_std::{io::prelude::*, net::{TcpListener, TcpStream}, stream::StreamExt, task}; +use async_std::{ + io::prelude::*, + net::{TcpListener, TcpStream}, + stream::StreamExt, + task, +}; use async_tls::TlsAcceptor; use once_cell::sync::Lazy; -use rustls::{ServerConfig, NoClientAuth, internal::pemfile::{certs, pkcs8_private_keys}}; +use rustls::{ + internal::pemfile::{certs, pkcs8_private_keys}, + NoClientAuth, ServerConfig, +}; use std::{error::Error, ffi::OsStr, fs::File, io::BufReader, marker::Unpin, sync::Arc}; use url::Url; @@ -22,12 +30,14 @@ fn main() -> Result { }) } -type Result<T=()> = std::result::Result<T, Box<dyn Error + Send + Sync>>; +type Result<T = ()> = std::result::Result<T, Box<dyn Error + Send + Sync>>; -static ARGS: Lazy<Args> = Lazy::new(|| args().unwrap_or_else(|| { - eprintln!("usage: agate <addr:port> <dir> <cert> <key> [<domain to check>]"); - std::process::exit(1); -})); +static ARGS: Lazy<Args> = Lazy::new(|| { + args().unwrap_or_else(|| { + eprintln!("usage: agate <addr:port> <dir> <cert> <key> [<domain to check>]"); + std::process::exit(1); + }) +}); struct Args { sock_addr: String, @@ -56,16 +66,17 @@ async fn handle_request(stream: TcpStream) -> Result { let url = match parse_request(stream).await { Ok(url) => url, - Err(e) => { - respond(stream, "59", &["Invalid request."]).await?; - return Err(e) + Err((status, msg)) => { + respond(stream, &status.to_string(), &[&msg]).await?; + Err(msg)? } }; if let Err(e) = send_response(url, stream).await { respond(stream, "51", &["Not found, sorry."]).await?; - return Err(e) + Err(e) + } else { + Ok(()) } - Ok(()) } /// TLS configuration. @@ -82,7 +93,9 @@ fn acceptor() -> Result<TlsAcceptor> { } /// Return the URL requested by the client. -async fn parse_request<R: Read + Unpin>(stream: &mut R) -> Result<Url> { +async fn parse_request<R: Read + Unpin>( + stream: &mut R, +) -> std::result::Result<Url, (u8, &'static str)> { // Because requests are limited to 1024 bytes (plus 2 bytes for CRLF), we // can use a fixed-sized buffer on the stack, avoiding allocations and // copying, and stopping bad clients from making us use too much memory. @@ -92,36 +105,48 @@ async fn parse_request<R: Read + Unpin>(stream: &mut R) -> Result<Url> { // Read until CRLF, end-of-stream, or there's no buffer space left. loop { - let bytes_read = stream.read(buf).await?; + let bytes_read = stream + .read(buf) + .await + .map_err(|_| (59, "Request ended unexpectedly"))?; len += bytes_read; if request[..len].ends_with(b"\r\n") { break; } else if bytes_read == 0 { - Err("Request ended unexpectedly")? + return Err((59, "Request ended unexpectedly")); } buf = &mut request[len..]; } - let request = std::str::from_utf8(&request[..len - 2])?; + let request = std::str::from_utf8(&request[..len - 2]).map_err(|_| (59, "Invalid URL"))?; // Handle scheme-relative URLs. let url = if request.starts_with("//") { - Url::parse(&format!("gemini:{}", request))? + Url::parse(&format!("gemini:{}", request)).map_err(|_| (59, "Invalid URL"))? } else { - Url::parse(request)? + Url::parse(request).map_err(|_| (59, "Invalid URL"))? }; // Validate the URL, host and port. if url.scheme() != "gemini" { - // FIXME: This should return a 53 status code. - Err("unsupported URL scheme")? - } else if ARGS.domain.as_ref().map_or(false, |domain| url.host().map_or(false, |host| &host.to_string() != domain)) { - // FIXME: This should return a 53 status code. - Err("proxy request refused")? - } else if url.port().map_or(false, |port| port != ARGS.sock_addr.rsplitn(2, ':').next().unwrap().parse().unwrap()) { - Err("port did not match")? + Err((53, "unsupported URL scheme")) + } else if ARGS.domain.as_ref().map_or(false, |domain| { + url.host().map_or(false, |host| &host.to_string() != domain) + }) { + Err((53, "proxy request refused")) + } else if url.port().map_or(false, |port| { + port != ARGS + .sock_addr + .rsplitn(2, ':') + .next() + .unwrap() + .parse() + .unwrap() + }) { + Err((59, "port did not match")) + } else { + log::info!("Got request for {:?}", url); + Ok(url) } - log::info!("Got request for {:?}", url); - Ok(url) } /// Send the client the file located at the requested URL.