Browse Source

gempress: add route handling

main
Garrit Franke 3 years ago
parent
commit
8bcc04fb6f
Signed by: garrit
GPG Key ID: 65586C4DDA55EA2C
  1. 12
      lib/gempress/examples/simple_server.rs
  2. 34
      lib/gempress/src/gemini.rs
  3. 90
      lib/gempress/src/lib.rs

12
lib/gempress/examples/simple.rs → lib/gempress/examples/simple_server.rs

@ -1,15 +1,23 @@
extern crate gempress;
use gempress::gemini;
use gempress::Gempress;
use std::path::PathBuf;
fn index_handler(req: Box<gemini::Request>, mut res: Box<gemini::Response>) {
res.send("Hello from index route!".as_bytes());
}
fn main() {
// Run make_cert.sh to generate a certificate
let config = gempress::Config::with_identity(PathBuf::from("identity.pfx"), "qqqq".into());
let app = Gempress::new(config);
let mut app = Gempress::new(config);
app.on("/", &index_handler);
app.listen(1965, || {
println!("Listening on port 1965");
}).unwrap();
})
.unwrap();
}

34
lib/gempress/src/gemini.rs

@ -5,7 +5,7 @@ use url::Url;
#[derive(Debug, PartialEq, Eq)]
pub struct Request {
url: Url,
pub url: Url,
}
impl Request {
@ -61,29 +61,23 @@ impl FromStr for Request {
}
pub struct Response {
stream: TlsStream<TcpStream>,
pub status: [u8; 2],
pub meta: Vec<u8>,
pub body: Option<Vec<u8>>,
pub body: Vec<u8>,
}
impl Response {
pub fn success(body: Vec<u8>, mime_type: &str) -> Self {
Response {
pub(crate) fn new(stream: TlsStream<TcpStream>) -> Self {
Self {
status: [b'2', b'0'],
meta: mime_type.as_bytes().to_vec(),
body: Some(body),
meta: "text/gemini".into(),
body: Vec::new(),
stream,
}
}
pub fn not_found() -> Self {
Response {
status: [b'5', b'1'],
meta: "Resource not found".into(),
body: None,
}
}
pub fn send(&self, mut stream: TlsStream<TcpStream>) -> GempressResult<usize> {
pub fn send(&mut self, text: &[u8]) -> GempressResult<usize> {
let mut buf: Vec<u8> = Vec::new();
// <Status>
@ -97,11 +91,13 @@ impl Response {
buf.extend(b"\r\n");
if let Some(body) = &self.body {
buf.extend(body);
}
buf.extend(self.body.clone());
buf.extend(text);
stream.write(&buf).map_err(GempressError::StreamWriteFailed)
self.stream
.write(&buf)
.map_err(GempressError::StreamWriteFailed)
}
}

90
lib/gempress/src/lib.rs

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

Loading…
Cancel
Save