From 9e217953b929a84c4c6578309cff153c040a290b Mon Sep 17 00:00:00 2001 From: Christine Dodrill Date: Fri, 31 Jul 2020 13:13:51 -0400 Subject: [PATCH] input test and static file serving with majsite --- site/Cargo.toml | 3 ++- site/src/main.rs | 31 ++++++++++++++++++++++++++++++- site/static/index.gmi | 3 +++ src/server/files.rs | 43 ++++++++++++++++++++++--------------------- src/server/mod.rs | 8 ++++---- 5 files changed, 61 insertions(+), 27 deletions(-) create mode 100644 site/static/index.gmi diff --git a/site/Cargo.toml b/site/Cargo.toml index d129d87..e33bb27 100644 --- a/site/Cargo.toml +++ b/site/Cargo.toml @@ -11,7 +11,8 @@ anyhow = "1" async-std = "1.5" async-trait = "0" log = "0" -pretty_env_logger = "0.4" +env_logger = "0" +percent-encoding = "2" rustls = { version = "0.18", features = ["dangerous_configuration"] } structopt = "0.3" diff --git a/site/src/main.rs b/site/src/main.rs index 95e8e10..dc2d35b 100644 --- a/site/src/main.rs +++ b/site/src/main.rs @@ -5,6 +5,7 @@ use maj::{ server::{Error, Handler as MajHandler, Request}, split, Response, }; +use percent_encoding::percent_decode_str; use rustls::internal::pemfile::{certs, rsa_private_keys}; use rustls::{ AllowAnyAnonymousOrAuthenticatedClient, Certificate, PrivateKey, RootCertStore, ServerConfig, @@ -33,6 +34,10 @@ struct Options { #[structopt(short = "k", long = "key", env = "KEY_FILE")] key: PathBuf, + /// static path + #[structopt(short = "s", long, env = "STATIC_PATH")] + static_path: PathBuf, + /// server hostname #[structopt( long = "hostname", @@ -53,7 +58,7 @@ fn load_keys(path: &Path) -> io::Result> { } fn main() -> Result<(), maj::server::Error> { - pretty_env_logger::init(); + env_logger::init(); let opts = Options::from_args(); let certs = load_certs(&opts.cert)?; let mut keys = load_keys(&opts.key)?; @@ -75,6 +80,7 @@ fn main() -> Result<(), maj::server::Error> { task::block_on(maj::server::serve( Arc::new(Handler { hostname: opts.hostname, + files: maj::server::files::Handler::new(opts.static_path), }), config, opts.host, @@ -86,6 +92,7 @@ fn main() -> Result<(), maj::server::Error> { struct Handler { hostname: String, + files: maj::server::files::Handler, } async fn index() -> Result { @@ -110,6 +117,26 @@ async fn need_cert(req: Request) -> Result { } } +async fn input(req: Request) -> Result { + match req.url.query() { + None => Ok(Response::input("test")), + Some(q) => Ok({ + use maj::gemini::Node::*; + let result = vec![ + Heading { + level: 1, + body: "Input test".to_string(), + }, + Text("".to_string()), + Text("You gave me:".to_string()), + Preformatted(format!("{}", percent_decode_str(q).decode_utf8()?)), + ]; + + Response::render(result) + }), + } +} + #[async_trait::async_trait] impl MajHandler for Handler { async fn handle(&self, req: Request) -> Result { @@ -127,7 +154,9 @@ impl MajHandler for Handler { route!(req.url.path(), { (/) => index().await; (/"cert") => need_cert(req).await; + (/"input") => input(req).await; (/"majc") => majc().await; + (/"static"[/rest..]) => self.files.handle(req).await; }); Ok(Response::not_found()) diff --git a/site/static/index.gmi b/site/static/index.gmi new file mode 100644 index 0000000..95552b1 --- /dev/null +++ b/site/static/index.gmi @@ -0,0 +1,3 @@ +# test + +Hi there diff --git a/src/server/files.rs b/src/server/files.rs index 999beb0..c0c2b01 100644 --- a/src/server/files.rs +++ b/src/server/files.rs @@ -3,46 +3,45 @@ use super::{Handler as MajHandler, Request, Result}; use crate::Response; use async_trait::async_trait; use std::ffi::OsStr; +use std::path::PathBuf; pub struct Handler { - chop_off: String, - base_dir: String, + base_dir: PathBuf, } impl Handler { /// Serves static files from an OS directory with a given prefix chopped off. - pub fn new(chop_off: String, base_dir: String) -> Self { + pub fn new(base_dir: PathBuf) -> Self { Handler { - chop_off: chop_off, base_dir: base_dir, } } - - fn chop(&self, path: String) -> Option { - if path.starts_with(&self.chop_off) { - path.strip_prefix(&self.chop_off).map(|val| val.to_string()) - } else { - Some(path) - } - } } #[async_trait] impl MajHandler for Handler { async fn handle(&self, r: Request) -> Result { let mut path = std::path::PathBuf::from(&self.base_dir); - let mut url = r.url.clone(); - url.set_path(&self.chop(r.url.path().to_string()).unwrap()); if let Some(segments) = r.url.path_segments() { path.extend(segments); } - if async_std::fs::metadata(&path).await?.is_dir() { - if url.as_str().ends_with('/') { - path.push("index.gmi"); - } else { - // Send a redirect when the URL for a directory has no trailing slash. - return Ok(Response::perm_redirect(format!("{}/", url))); + log::debug!("opening file {:?}", path); + + match async_std::fs::metadata(&path).await { + Ok(stat) => { + if stat.is_dir() { + if r.url.as_str().ends_with('/') { + path.push("index.gmi"); + } else { + // Send a redirect when the URL for a directory has no trailing slash. + return Ok(Response::perm_redirect(format!("{}/", r.url))); + } + } + } + Err(why) => { + log::error!("file {} not found: {}", path.to_str().unwrap(), why); + return Ok(Response::not_found()); } } @@ -51,7 +50,9 @@ impl MajHandler for Handler { async_std::io::copy(&mut file, &mut buf).await?; // Send header. - if path.extension() == Some(OsStr::new("gmi")) { + if path.extension() == Some(OsStr::new("gmi")) + || path.extension() == Some(OsStr::new("gemini")) + { return Ok(Response::gemini(buf)); } diff --git a/src/server/mod.rs b/src/server/mod.rs index 2e7fc7f..fb895b8 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -107,8 +107,8 @@ async fn handle_request( handle(h, req, &mut stream, addr).await; } Err(e) => { - let _ = write_header(&mut stream, StatusCode::BadRequest, "Invalid request.").await; - log::error!("error from {}: {:?}", addr, e); + let _ = write_header(&mut stream, StatusCode::BadRequest, "Invalid request").await; + log::error!("error from {}: {}", addr, e); } } Ok(()) @@ -171,11 +171,11 @@ async fn handle( .write(format!("{} {}\r\n", resp.status as u8, resp.meta).as_bytes()) .await; let _ = stream.write(&resp.body).await; - log::info!("{}: {} {} {:?}", addr, u, resp.meta, resp.status); + log::info!("{}: {} {:?} {}", addr, u, resp.status, resp.meta); } Err(why) => { let _ = stream - .write(format!("{} {:?}\r\n", StatusCode::PermanentFailure as u8, why).as_bytes()) + .write(format!("{} {}\r\n", StatusCode::PermanentFailure as u8, why.to_string()).as_bytes()) .await; log::error!("{}: {}: {:?}", addr, u, why); }