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
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) |
|
}
|
|
|