input test and static file serving with majsite
This commit is contained in:
parent
d2af2c5f08
commit
28ae14ffa7
|
@ -11,7 +11,8 @@ anyhow = "1"
|
||||||
async-std = "1.5"
|
async-std = "1.5"
|
||||||
async-trait = "0"
|
async-trait = "0"
|
||||||
log = "0"
|
log = "0"
|
||||||
pretty_env_logger = "0.4"
|
env_logger = "0"
|
||||||
|
percent-encoding = "2"
|
||||||
rustls = { version = "0.18", features = ["dangerous_configuration"] }
|
rustls = { version = "0.18", features = ["dangerous_configuration"] }
|
||||||
structopt = "0.3"
|
structopt = "0.3"
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ use maj::{
|
||||||
server::{Error, Handler as MajHandler, Request},
|
server::{Error, Handler as MajHandler, Request},
|
||||||
split, Response,
|
split, Response,
|
||||||
};
|
};
|
||||||
|
use percent_encoding::percent_decode_str;
|
||||||
use rustls::internal::pemfile::{certs, rsa_private_keys};
|
use rustls::internal::pemfile::{certs, rsa_private_keys};
|
||||||
use rustls::{
|
use rustls::{
|
||||||
AllowAnyAnonymousOrAuthenticatedClient, Certificate, PrivateKey, RootCertStore, ServerConfig,
|
AllowAnyAnonymousOrAuthenticatedClient, Certificate, PrivateKey, RootCertStore, ServerConfig,
|
||||||
|
@ -33,6 +34,10 @@ struct Options {
|
||||||
#[structopt(short = "k", long = "key", env = "KEY_FILE")]
|
#[structopt(short = "k", long = "key", env = "KEY_FILE")]
|
||||||
key: PathBuf,
|
key: PathBuf,
|
||||||
|
|
||||||
|
/// static path
|
||||||
|
#[structopt(short = "s", long, env = "STATIC_PATH")]
|
||||||
|
static_path: PathBuf,
|
||||||
|
|
||||||
/// server hostname
|
/// server hostname
|
||||||
#[structopt(
|
#[structopt(
|
||||||
long = "hostname",
|
long = "hostname",
|
||||||
|
@ -53,7 +58,7 @@ fn load_keys(path: &Path) -> io::Result<Vec<PrivateKey>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<(), maj::server::Error> {
|
fn main() -> Result<(), maj::server::Error> {
|
||||||
pretty_env_logger::init();
|
env_logger::init();
|
||||||
let opts = Options::from_args();
|
let opts = Options::from_args();
|
||||||
let certs = load_certs(&opts.cert)?;
|
let certs = load_certs(&opts.cert)?;
|
||||||
let mut keys = load_keys(&opts.key)?;
|
let mut keys = load_keys(&opts.key)?;
|
||||||
|
@ -75,6 +80,7 @@ fn main() -> Result<(), maj::server::Error> {
|
||||||
task::block_on(maj::server::serve(
|
task::block_on(maj::server::serve(
|
||||||
Arc::new(Handler {
|
Arc::new(Handler {
|
||||||
hostname: opts.hostname,
|
hostname: opts.hostname,
|
||||||
|
files: maj::server::files::Handler::new(opts.static_path),
|
||||||
}),
|
}),
|
||||||
config,
|
config,
|
||||||
opts.host,
|
opts.host,
|
||||||
|
@ -86,6 +92,7 @@ fn main() -> Result<(), maj::server::Error> {
|
||||||
|
|
||||||
struct Handler {
|
struct Handler {
|
||||||
hostname: String,
|
hostname: String,
|
||||||
|
files: maj::server::files::Handler,
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn index() -> Result<maj::Response, maj::server::Error> {
|
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]
|
#[async_trait::async_trait]
|
||||||
impl MajHandler for Handler {
|
impl MajHandler for Handler {
|
||||||
async fn handle(&self, req: Request) -> Result<Response, Error> {
|
async fn handle(&self, req: Request) -> Result<Response, Error> {
|
||||||
|
@ -127,7 +154,9 @@ impl MajHandler for Handler {
|
||||||
route!(req.url.path(), {
|
route!(req.url.path(), {
|
||||||
(/) => index().await;
|
(/) => index().await;
|
||||||
(/"cert") => need_cert(req).await;
|
(/"cert") => need_cert(req).await;
|
||||||
|
(/"input") => input(req).await;
|
||||||
(/"majc") => majc().await;
|
(/"majc") => majc().await;
|
||||||
|
(/"static"[/rest..]) => self.files.handle(req).await;
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(Response::not_found())
|
Ok(Response::not_found())
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
# test
|
||||||
|
|
||||||
|
Hi there
|
|
@ -3,46 +3,45 @@ use super::{Handler as MajHandler, Request, Result};
|
||||||
use crate::Response;
|
use crate::Response;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
pub struct Handler {
|
pub struct Handler {
|
||||||
chop_off: String,
|
base_dir: PathBuf,
|
||||||
base_dir: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Handler {
|
impl Handler {
|
||||||
/// Serves static files from an OS directory with a given prefix chopped off.
|
/// 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 {
|
Handler {
|
||||||
chop_off: chop_off,
|
|
||||||
base_dir: base_dir,
|
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]
|
#[async_trait]
|
||||||
impl MajHandler for Handler {
|
impl MajHandler for Handler {
|
||||||
async fn handle(&self, r: Request) -> Result<Response> {
|
async fn handle(&self, r: Request) -> Result<Response> {
|
||||||
let mut path = std::path::PathBuf::from(&self.base_dir);
|
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() {
|
if let Some(segments) = r.url.path_segments() {
|
||||||
path.extend(segments);
|
path.extend(segments);
|
||||||
}
|
}
|
||||||
|
|
||||||
if async_std::fs::metadata(&path).await?.is_dir() {
|
log::debug!("opening file {:?}", path);
|
||||||
if url.as_str().ends_with('/') {
|
|
||||||
path.push("index.gmi");
|
match async_std::fs::metadata(&path).await {
|
||||||
} else {
|
Ok(stat) => {
|
||||||
// Send a redirect when the URL for a directory has no trailing slash.
|
if stat.is_dir() {
|
||||||
return Ok(Response::perm_redirect(format!("{}/", url)));
|
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?;
|
async_std::io::copy(&mut file, &mut buf).await?;
|
||||||
|
|
||||||
// Send header.
|
// 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));
|
return Ok(Response::gemini(buf));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -107,8 +107,8 @@ async fn handle_request(
|
||||||
handle(h, req, &mut stream, addr).await;
|
handle(h, req, &mut stream, addr).await;
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let _ = write_header(&mut stream, StatusCode::BadRequest, "Invalid request.").await;
|
let _ = write_header(&mut stream, StatusCode::BadRequest, "Invalid request").await;
|
||||||
log::error!("error from {}: {:?}", addr, e);
|
log::error!("error from {}: {}", addr, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -171,11 +171,11 @@ async fn handle<T>(
|
||||||
.write(format!("{} {}\r\n", resp.status as u8, resp.meta).as_bytes())
|
.write(format!("{} {}\r\n", resp.status as u8, resp.meta).as_bytes())
|
||||||
.await;
|
.await;
|
||||||
let _ = stream.write(&resp.body).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) => {
|
Err(why) => {
|
||||||
let _ = stream
|
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;
|
.await;
|
||||||
log::error!("{}: {}: {:?}", addr, u, why);
|
log::error!("{}: {}: {:?}", addr, u, why);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue