extern crate native_tls;
mod error;
pub mod gemini;
mod io;
mod logger;
use std::io::Read;
use std::net::{TcpListener, TcpStream};
use std::path::PathBuf;
use std::sync::Arc;
use error::{GempressError, GempressResult};
use native_tls::{TlsAcceptor, TlsStream};
#[derive(Clone, Debug)]
pub struct Config {
identityPath: PathBuf,
password: String,
}
impl Config {
pub fn from_identity(identityPath: PathBuf, password: String) -> Self {
Self {
identityPath,
password,
}
}
}
pub type Handler = dyn Fn(Box<gemini::Request>, Box<gemini::Response>);
struct Layer {
handler: Box<Handler>,
path: String,
}
impl std::fmt::Debug for Layer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Layer").field("path", &self.path).finish()
}
}
pub struct Gempress {
pub config: Config,
stack: Vec<Layer>,
}
impl Gempress {
pub fn new(config: Config) -> Self {
Gempress {
config,
stack: Vec::new(),
}
}
pub fn on(&mut self, path: &str, handler: &'static Handler) {
let layer = Layer {
path: path.to_string(),
handler: Box::new(handler.to_owned()),
};
self.stack.push(layer);
}
pub fn listen<F: Fn()>(self, port: u16, callback: F) -> GempressResult<()> {
let identity = io::load_cert(
&self.config.identityPath.to_str().unwrap_or(""),
&self.config.password,
)?;
let address = format!("0.0.0.0:{}", port);
let listener = TcpListener::bind(address).map_err(GempressError::BindFailed)?;
let acceptor = TlsAcceptor::new(identity).unwrap();
let acceptor = Arc::new(acceptor);
logger::info(format!("Listening on port {}", port));
for stream in listener.incoming() {
match stream {
Ok(stream) => {
let acceptor = acceptor.clone();
match acceptor.accept(stream) {
Ok(stream) => {
if let Err(e) = self.handle_client(stream) {
logger::error(format!("Can't handle client: {}", e));
}
}
Err(e) => {
logger::error(format!("Can't handle stream: {}", e));
}
};
}
Err(err) => logger::error(err),
}
}
(callback)();
Ok(())
}
fn handle_client(&self, mut stream: TlsStream<TcpStream>) -> GempressResult<()> {
let mut buffer = [0; 1024];
stream
.read(&mut buffer)
.map_err(GempressError::StreamReadFailed)?;
let raw_request = String::from_utf8(buffer.to_vec())?;
let request = gemini::Request::parse(&raw_request)?;
let response = gemini::Response::new(stream);
let layer = self
.stack
.iter()
.find(|&l| l.path == request.url.path())
.unwrap();
(layer.handler)(Box::new(request), Box::new(response));
Ok(())
}
}