diff --git a/README.md b/README.md index 2446406..b1a4000 100644 --- a/README.md +++ b/README.md @@ -8,3 +8,6 @@ Why "Sheldon Director"? Because it is the real name of that villain in _Kim Possible_ who went by the pseudonym "Gemini". (https://kimpossible.fandom.com/wiki/Gemini) + +Though you are free to imagine the shortened name stands for Sheldon Daemon. +But in that case you'll have to come up with your own explanation. diff --git a/src/main.rs b/src/main.rs index ab50c02..df4f4d2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,49 +1,6 @@ -use std::net::{TcpListener, TcpStream, SocketAddr}; -use std::sync::Arc; -use std::thread; -use openssl::ssl::{SslMethod, SslAcceptor, SslStream, SslFiletype}; -use url::Url; mod server; -fn build_acceptor() -> std::sync::Arc { - let mut acceptor = SslAcceptor::mozilla_intermediate_v5(SslMethod::tls()).unwrap(); - acceptor.set_private_key_file("doc/key.pem", SslFiletype::PEM).unwrap(); - acceptor.set_certificate_chain_file("doc/cert.pem").unwrap(); - acceptor.check_private_key().unwrap(); - return Arc::new(acceptor.build()); -} - -fn build_server() { -} - -fn handle_client(mut stream: SslStream) { - let mut buffer = [0; 1026]; - stream.ssl_read(&mut buffer); - let request = String::from_utf8(buffer.to_vec()).unwrap(); - let url = Url::parse(&request).unwrap(); - - let data = server::handle(&url); - - stream.ssl_write(&data); -} - fn main() { - let addrs = [ - SocketAddr::from(([127, 0, 0, 1], 1965)), - ]; - let acceptor = build_acceptor(); - let listener = TcpListener::bind(&addrs[..]).unwrap(); - - for stream in listener.incoming() { - match stream { - Ok(stream) => { - let acceptor = acceptor.clone(); - thread::spawn(move || { - let stream = acceptor.accept(stream).unwrap(); - handle_client(stream); - }); - } - Err(e) => { /* connection failed */ } - } - } + let server = server::Server::new("gemini://localhost"); + server.serve(); } diff --git a/src/server/handler.rs b/src/server/handler.rs index ec4193d..fb07a4d 100644 --- a/src/server/handler.rs +++ b/src/server/handler.rs @@ -1,16 +1,7 @@ use url::Url; -use crate::server::response::{Status, Header, Response}; +use crate::server::response; +use crate::server::ServerConfig; -pub trait Handler { - fn handle(&self, url : Url) -> Option; -} - -pub struct TestHandler { -} - -impl Handler for TestHandler { - fn handle(&self, url : Url) -> Option { - let header = Header::new(Status::Success, "text/gemini"); - return Some(Response::new(header, Vec::new())); - } +pub fn handle(config: &ServerConfig, url: Url) -> Option { + Some(response::invalid_protocol()) } diff --git a/src/server/mod.rs b/src/server/mod.rs index 01a686d..5b7aedb 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -1,30 +1,79 @@ -use std::vec::Vec; +use crate::server::response::Response; +use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod, SslStream}; use std::collections::HashMap; +use std::net::{SocketAddr, TcpListener, TcpStream}; +use std::thread; +use std::vec::Vec; +use std::sync::Arc; use url::Url; -mod handler; -mod response; +pub mod response; +pub mod handler; + +#[derive(Clone)] +pub struct ServerConfig { + defaultHost: Url, +} pub struct Server { - handlers: Vec>, + acceptor: std::sync::Arc, + config: ServerConfig, } impl Server { - pub fn new() -> Server { - Server{handlers: Vec::>::new()} + pub fn new(host: &str) -> Server { + let config = ServerConfig{defaultHost: Url::parse(host).unwrap()}; + + Server { + acceptor: build_acceptor(), + config: config, + } + } + + pub fn serve(&self) { + let addrs = [SocketAddr::from(([127, 0, 0, 1], 1965))]; + let listener = TcpListener::bind(&addrs[..]).unwrap(); + + for stream in listener.incoming() { + match stream { + Ok(stream) => { + let acceptor = self.acceptor.clone(); + let config = self.config.clone(); + thread::spawn(move || { + let stream = acceptor.accept(stream).unwrap(); + handle_client(&config, stream); + }); + } + Err(e) => { /* connection failed */ } + } + } } } -pub fn register(host: &str, server: Server) { -//// servers.insert( -//// host.to_string(), -//// server, -//// ); +fn handle_client(config: &ServerConfig, mut stream: SslStream) { + let mut buffer = [0; 1026]; + stream.ssl_read(&mut buffer); + let request = String::from_utf8(buffer.to_vec()).unwrap(); + + let location = match Url::parse(&request) { + Ok(url) => url, + Err(e) => config.defaultHost.join(&request).unwrap(), + }; + + let response = match handler::handle(config, location) { + Some(response) => response, + None => response::internal_error(), + }; + + stream.ssl_write(&response.format()); } -pub fn handle(url: &Url) -> Vec { - return Vec::new(); +fn build_acceptor() -> std::sync::Arc { + let mut acceptor = SslAcceptor::mozilla_intermediate_v5(SslMethod::tls()).unwrap(); + acceptor + .set_private_key_file("doc/key.pem", SslFiletype::PEM) + .unwrap(); + acceptor.set_certificate_chain_file("doc/cert.pem").unwrap(); + acceptor.check_private_key().unwrap(); + return Arc::new(acceptor.build()); } - -//static mut servers : HashMap = HashMap::new(); - diff --git a/src/server/response.rs b/src/server/response.rs index f2df722..36d5d15 100644 --- a/src/server/response.rs +++ b/src/server/response.rs @@ -2,12 +2,12 @@ use std::vec::Vec; #[derive(Copy, Clone)] pub enum Status { - Input = 1, - Success = 2, - Redirect = 3, - TemporaryFailure = 4, - PermanentFailure = 5, - ClientCertificateRequired = 6, + Input = 10, + Success = 20, + Redirect = 30, + TemporaryFailure = 40, + PermanentFailure = 50, + ClientCertificateRequired = 60, } pub struct Header { @@ -22,28 +22,27 @@ pub struct Response { impl Header { pub fn new(status: Status, meta: &str) -> Header { - Header{ + Header { status: status, meta: meta.to_string(), } } pub fn format(&self) -> String { - let status: u8 = self.status as u8; - return format!("{} {}\r\n", status * 10, self.meta) + format!("{} {}\r\n", self.status as u8, self.meta) } } impl Response { pub fn new(header: Header, data: Vec) -> Response { - Response{ + Response { header: header, data: data, } } pub fn new_empty(header: Header) -> Response { - Response{ + Response { header: header, data: Vec::new(), } @@ -56,11 +55,23 @@ impl Response { } } -pub fn invalid_protocol () -> Response { - Response::new_empty( - Header::new( - Status::PermanentFailure, - "this protocol is not supported" - ), - ) +pub fn invalid_protocol() -> Response { + Response::new_empty(Header::new( + Status::PermanentFailure, + "this protocol is not supported", + )) +} + +pub fn not_understood() -> Response { + Response::new_empty(Header::new( + Status::PermanentFailure, + "request not understood", + )) +} + +pub fn internal_error() -> Response { + Response::new_empty(Header::new( + Status::PermanentFailure, + "internal server error", + )) }