|
|
|
@ -1,20 +1,20 @@
|
|
|
|
|
extern crate native_tls; |
|
|
|
|
|
|
|
|
|
mod logger; |
|
|
|
|
mod io; |
|
|
|
|
mod error; |
|
|
|
|
mod gemini; |
|
|
|
|
pub mod gemini; |
|
|
|
|
mod io; |
|
|
|
|
mod logger; |
|
|
|
|
|
|
|
|
|
use std::io::Read; |
|
|
|
|
use native_tls::Identity; |
|
|
|
|
use std::sync::Arc; |
|
|
|
|
use std::path::PathBuf; |
|
|
|
|
|
|
|
|
|
use std::net::{TcpListener, TcpStream}; |
|
|
|
|
use std::thread; |
|
|
|
|
use std::path; |
|
|
|
|
use std::path::PathBuf; |
|
|
|
|
use std::sync::Arc; |
|
|
|
|
|
|
|
|
|
use error::{GempressError, GempressResult}; |
|
|
|
|
use native_tls::{TlsAcceptor, TlsStream}; |
|
|
|
|
use error::{GempressResult, GempressError}; |
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug)] |
|
|
|
|
pub struct Config { |
|
|
|
|
// Path to the identity file
|
|
|
|
|
identityPath: PathBuf, |
|
|
|
@ -30,21 +30,47 @@ impl Config {
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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 listen<F: Fn()>(&self, port: u16, callback: F) -> GempressResult<()> { |
|
|
|
|
// Read certificate
|
|
|
|
|
// TODO: Can a password be optional?
|
|
|
|
|
let identity = io::load_cert(&self.config.identityPath.to_str().unwrap_or(""), &self.config.password)?; |
|
|
|
|
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<()> { |
|
|
|
|
// Read certificate
|
|
|
|
|
// TODO: Can a password be optional?
|
|
|
|
|
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)?; |
|
|
|
@ -58,38 +84,46 @@ impl Gempress {
|
|
|
|
|
Ok(stream) => { |
|
|
|
|
let acceptor = acceptor.clone(); |
|
|
|
|
|
|
|
|
|
thread::spawn(move || match acceptor.accept(stream) { |
|
|
|
|
match acceptor.accept(stream) { |
|
|
|
|
Ok(stream) => { |
|
|
|
|
if let Err(e) = handle_client(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), |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
(callback)(); |
|
|
|
|
|
|
|
|
|
Ok(()) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
fn handle_client(&self, mut stream: TlsStream<TcpStream>) -> GempressResult<()> { |
|
|
|
|
let mut buffer = [0; 1024]; |
|
|
|
|
|
|
|
|
|
fn handle_client(mut stream: TlsStream<TcpStream>) -> GempressResult<usize> { |
|
|
|
|
let mut buffer = [0; 1024]; |
|
|
|
|
stream |
|
|
|
|
.read(&mut buffer) |
|
|
|
|
.map_err(GempressError::StreamReadFailed)?; |
|
|
|
|
|
|
|
|
|
stream |
|
|
|
|
.read(&mut buffer) |
|
|
|
|
.map_err(GempressError::StreamReadFailed)?; |
|
|
|
|
let raw_request = String::from_utf8(buffer.to_vec())?; |
|
|
|
|
|
|
|
|
|
let raw_request = String::from_utf8(buffer.to_vec())?; |
|
|
|
|
let request = gemini::Request::parse(&raw_request)?; |
|
|
|
|
let response = gemini::Response::new(stream); |
|
|
|
|
|
|
|
|
|
let request = gemini::Request::parse(&raw_request)?; |
|
|
|
|
let url_path = request.file_path(); |
|
|
|
|
let file_path = path::Path::new(url_path); |
|
|
|
|
let layer = self |
|
|
|
|
.stack |
|
|
|
|
.iter() |
|
|
|
|
.find(|&l| l.path == request.url.path()) |
|
|
|
|
.unwrap(); |
|
|
|
|
|
|
|
|
|
gemini::Response::success("It Works!\n".into(), "text/gemini").send(stream) |
|
|
|
|
(layer.handler)(Box::new(request), Box::new(response)); |
|
|
|
|
|
|
|
|
|
Ok(()) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|