You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

95 lines
2.5 KiB

extern crate native_tls;
mod logger;
mod io;
mod error;
mod gemini;
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 native_tls::{TlsAcceptor, TlsStream};
use error::{GempressResult, GempressError};
pub struct Config {
// Path to the identity file
identityPath: PathBuf,
password: String,
}
impl Config {
pub fn with_identity(identityPath: PathBuf, password: String) -> Self {
Self {
identityPath,
password,
}
}
}
pub struct Gempress {
pub config: Config,
}
impl Gempress {
pub fn new(config: Config) -> Self {
Gempress {
config,
}
}
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)?;
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();
thread::spawn(move || match acceptor.accept(stream) {
Ok(stream) => {
if let Err(e) = 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(())
}
}
fn handle_client(mut stream: TlsStream<TcpStream>) -> GempressResult<usize> {
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 url_path = request.file_path();
let file_path = path::Path::new(url_path);
gemini::Response::success("It Works!\n".into(), "text/gemini").send(stream)
}