add CGI support
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
This commit is contained in:
parent
ccb142d8b3
commit
1da65dcfeb
|
@ -1,11 +1,11 @@
|
|||
use anyhow::{anyhow, Result};
|
||||
use atom_syndication as atom;
|
||||
use chrono::{prelude::*, Duration};
|
||||
use maj::gemini::Node;
|
||||
use rustls::ClientConfig;
|
||||
use std::io::{self, BufReader, Cursor, Write};
|
||||
use std::ops::Sub;
|
||||
use std::str;
|
||||
use chrono::{Duration, prelude::*};
|
||||
|
||||
mod tls;
|
||||
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
echo "20 text/plain"
|
||||
echo "The following is the CGI environment of this program:"
|
||||
echo
|
||||
env
|
|
@ -37,6 +37,10 @@ struct Options {
|
|||
#[structopt(short = "s", long, env = "STATIC_PATH", default_value = "./static")]
|
||||
static_path: PathBuf,
|
||||
|
||||
/// CGI path
|
||||
#[structopt(short = "C", long, env = "CGI_PATH", default_value = "./cgi-bin")]
|
||||
cgi_path: PathBuf,
|
||||
|
||||
/// server hostname
|
||||
#[structopt(
|
||||
long = "hostname",
|
||||
|
@ -83,6 +87,7 @@ fn main() -> Result<(), maj::server::Error> {
|
|||
let h = Arc::new(server::Handler {
|
||||
hostname: opts.hostname,
|
||||
files: maj::server::files::Handler::new(opts.static_path),
|
||||
cgi: maj::server::cgi::Handler::new(opts.cgi_path),
|
||||
});
|
||||
|
||||
{
|
||||
|
|
|
@ -13,6 +13,7 @@ mod tarot;
|
|||
pub struct Handler {
|
||||
pub hostname: String,
|
||||
pub files: maj::server::files::Handler,
|
||||
pub cgi: maj::server::cgi::Handler,
|
||||
}
|
||||
|
||||
async fn dice(req: Request) -> Result<Response, Error> {
|
||||
|
@ -81,6 +82,7 @@ impl MajHandler for Handler {
|
|||
route!(req.url.path(), {
|
||||
(/"dice") => dice(req).await;
|
||||
(/"tools"/"character_gen") => tarot::character().await;
|
||||
(/"cgi-bin"[/rest..]) => self.cgi.handle(req).await;
|
||||
});
|
||||
|
||||
self.files.handle(req).await
|
||||
|
|
|
@ -4,10 +4,7 @@ use tokio::{
|
|||
io::{AsyncReadExt, AsyncWriteExt},
|
||||
net::TcpStream,
|
||||
};
|
||||
use tokio_rustls::{
|
||||
rustls::{TLSError},
|
||||
TlsConnector,
|
||||
};
|
||||
use tokio_rustls::{rustls::TLSError, TlsConnector};
|
||||
use url::Url;
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
/// A simple handler for disk based files. Will optionally chop off a prefix.
|
||||
use super::{Handler as MajHandler, Request, Result};
|
||||
use crate::Response;
|
||||
use crate::{route, seg, split};
|
||||
use async_trait::async_trait;
|
||||
use std::collections::HashMap;
|
||||
use std::io::Cursor;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
|
||||
pub struct Handler {
|
||||
base_dir: PathBuf,
|
||||
}
|
||||
|
||||
const APPLICATION_NAME: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"));
|
||||
|
||||
#[async_trait]
|
||||
impl MajHandler for Handler {
|
||||
async fn handle(&self, r: Request) -> Result<Response> {
|
||||
route!(r.url.path(), {
|
||||
(/"cgi-bin"/[prog_name: String][/rest..]) => self.do_cgi(prog_name, rest.to_string(), r).await;
|
||||
});
|
||||
|
||||
Ok(Response::not_found())
|
||||
}
|
||||
}
|
||||
|
||||
impl Handler {
|
||||
pub fn new(base_dir: PathBuf) -> Self {
|
||||
Handler { base_dir: base_dir }
|
||||
}
|
||||
|
||||
async fn do_cgi(&self, prog_name: String, rest: String, r: Request) -> Result<Response> {
|
||||
let mut path = PathBuf::from(&self.base_dir);
|
||||
path.push(&prog_name);
|
||||
|
||||
log::debug!("path: {:?}", path);
|
||||
let query = {
|
||||
match r.url.query() {
|
||||
Some(q) => q.clone(),
|
||||
None => "",
|
||||
}
|
||||
};
|
||||
|
||||
let filtered_env: HashMap<String, String> = std::env::vars()
|
||||
.filter(|&(ref k, _)| k == "TERM" || k == "TZ" || k == "LANG" || k == "PATH")
|
||||
.collect();
|
||||
|
||||
let output = Command::new(path.clone())
|
||||
.env_clear()
|
||||
.envs(filtered_env)
|
||||
.env("GATEWAY_INTERFACE", "CGI/1.1")
|
||||
.env("SERVER_PROTOCOL", "GEMINI")
|
||||
.env("SERVER_SOFTWARE", APPLICATION_NAME)
|
||||
.env("GEMINI_URL", format!("{}", r.url))
|
||||
.env("SCRIPT_NAME", path)
|
||||
.env("PATH_INFO", rest)
|
||||
.env("QUERY_STRING", query)
|
||||
.env("SERVER_NAME", r.url.host_str().unwrap())
|
||||
.env("SERVER_HOSTNAME", r.url.host_str().unwrap())
|
||||
.env("SERVER_PORT", format!("{}", r.url.port().unwrap_or(1965)))
|
||||
.env("REMOTE_HOST", "127.0.0.1")
|
||||
.env("REMOTE_ADDR", "127.0.0.1")
|
||||
.env("TLS_CIPHER", "Secure")
|
||||
.env("TLS_VERSION", "TLSv1.3")
|
||||
.output()?;
|
||||
|
||||
let resp = Response::parse(&mut Cursor::new(output.stdout))?;
|
||||
|
||||
Ok(resp)
|
||||
}
|
||||
}
|
|
@ -12,9 +12,7 @@ pub struct Handler {
|
|||
impl Handler {
|
||||
/// Serves static files from an OS directory with a given prefix chopped off.
|
||||
pub fn new(base_dir: PathBuf) -> Self {
|
||||
Handler {
|
||||
base_dir: base_dir,
|
||||
}
|
||||
Handler { base_dir: base_dir }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ enum RequestParsingError {
|
|||
mod routes;
|
||||
pub use routes::*;
|
||||
|
||||
pub mod cgi;
|
||||
pub mod files;
|
||||
|
||||
#[async_trait]
|
||||
|
@ -119,7 +120,9 @@ pub async fn write_header<W: Write + Unpin>(
|
|||
status: StatusCode,
|
||||
meta: &str,
|
||||
) -> Result {
|
||||
stream.write(format!("{} {}\r\n", status as u8, meta).as_bytes()).await?;
|
||||
stream
|
||||
.write(format!("{} {}\r\n", status as u8, meta).as_bytes())
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -175,7 +178,14 @@ async fn handle<T>(
|
|||
}
|
||||
Err(why) => {
|
||||
let _ = stream
|
||||
.write(format!("{} {}\r\n", StatusCode::PermanentFailure as u8, why.to_string()).as_bytes())
|
||||
.write(
|
||||
format!(
|
||||
"{} {}\r\n",
|
||||
StatusCode::PermanentFailure as u8,
|
||||
why.to_string()
|
||||
)
|
||||
.as_bytes(),
|
||||
)
|
||||
.await;
|
||||
log::error!("{}: {}: {:?}", addr, u, why);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue