summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md3
-rw-r--r--src/main.rs47
-rw-r--r--src/server/handler.rs17
-rw-r--r--src/server/mod.rs81
-rw-r--r--src/server/response.rs47
5 files changed, 103 insertions, 92 deletions
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<SslAcceptor> {
- 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<TcpStream>) {
- 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<Response>;
-}
-
-pub struct TestHandler {
-}
-
-impl Handler for TestHandler {
- fn handle(&self, url : Url) -> Option<Response> {
- let header = Header::new(Status::Success, "text/gemini");
- return Some(Response::new(header, Vec::new()));
- }
+pub fn handle(config: &ServerConfig, url: Url) -> Option<response::Response> {
+ 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<Box<dyn handler::Handler>>,
+ acceptor: std::sync::Arc<SslAcceptor>,
+ config: ServerConfig,
}
impl Server {
- pub fn new() -> Server {
- Server{handlers: Vec::<Box<dyn handler::Handler>>::new()}
+ pub fn new(host: &str) -> Server {
+ let config = ServerConfig{defaultHost: Url::parse(host).unwrap()};
+
+ Server {
+ acceptor: build_acceptor(),
+ config: config,
+ }
}
-}
-pub fn register(host: &str, server: Server) {
-//// servers.insert(
-//// host.to_string(),
-//// server,
-//// );
-}
+ pub fn serve(&self) {
+ let addrs = [SocketAddr::from(([127, 0, 0, 1], 1965))];
+ let listener = TcpListener::bind(&addrs[..]).unwrap();
-pub fn handle(url: &Url) -> Vec<u8> {
- return Vec::new();
+ 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 */ }
+ }
+ }
+ }
}
-//static mut servers : HashMap<String, Server> = HashMap::new();
+fn handle_client(config: &ServerConfig, mut stream: SslStream<TcpStream>) {
+ 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());
+}
+
+fn build_acceptor() -> std::sync::Arc<SslAcceptor> {
+ 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());
+}
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<u8>) -> 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",
+ ))
}