input test and static file serving with majsite

This commit is contained in:
Cadey Ratio 2020-07-31 13:13:51 -04:00
parent d2af2c5f08
commit 28ae14ffa7
5 changed files with 61 additions and 27 deletions

View File

@ -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"

View File

@ -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<Vec<PrivateKey>> {
}
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<maj::Response, maj::server::Error> {
@ -110,6 +117,26 @@ async fn need_cert(req: Request) -> Result<Response, Error> {
}
}
async fn input(req: Request) -> Result<Response, Error> {
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<Response, Error> {
@ -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())

3
site/static/index.gmi Normal file
View File

@ -0,0 +1,3 @@
# test
Hi there

View File

@ -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<String> {
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<Response> {
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('/') {
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!("{}/", url)));
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));
}

View File

@ -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<T>(
.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);
}