From 87d61457bdc33676475803937ccd39e1a6c661de Mon Sep 17 00:00:00 2001 From: Jan Wolff Date: Sun, 17 May 2020 00:16:57 +0200 Subject: [PATCH] working file serving --- src/main.rs | 14 ++++++++++-- src/server/handler.rs | 29 +++++++++++++++++------ src/server/mod.rs | 44 ++++++++++++++++++++--------------- src/server/response.rs | 52 ++++++++++-------------------------------- 4 files changed, 71 insertions(+), 68 deletions(-) diff --git a/src/main.rs b/src/main.rs index 59b6d00..9d1fdcf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,18 @@ mod server; +use std::env; use std::path::Path; +fn read_config() -> server::ServerConfig { + let mut config = server::ServerConfig::new( + "klockenschooster.de".to_string(), + "/home/jw/code/projects/sheldond/doc".to_string(), + ); + config.add_addr("127.0.0.1:1965".to_string()); + config.add_addr("[::1]:1965".to_string()); + return config; +} + fn main() { - let wwwRoot = Path::new("/var/www/gemini/"); - let server = server::Server::new("gemini://localhost", &wwwRoot); + let server = server::Server::new(&read_config()); server.serve(); } diff --git a/src/server/handler.rs b/src/server/handler.rs index ceb6551..4a593e8 100644 --- a/src/server/handler.rs +++ b/src/server/handler.rs @@ -1,17 +1,32 @@ use crate::server::response; use crate::server::ServerConfig; +use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod, SslStream}; +use std::fs::File; +use std::io::{copy, BufReader, BufWriter}; +use std::net::TcpStream; use std::path::Path; use url::Url; -pub fn handle(config: &ServerConfig, url: Url) -> Option { +pub fn handle(config: &ServerConfig, url: Url, stream: &mut SslStream) { if url.scheme() != "gemini" { - return Some(response::invalid_protocol()); + stream.ssl_write(&response::invalid_protocol().to_vec()); + return; } - let path = Path::new(url.path()); - if !path.has_root() { - return Some(response::not_understood()); - } + let rel_path = Path::new(url.path()).strip_prefix("/").unwrap(); + let path = config.www_root.join(rel_path); - None + let file = match File::open(&path) { + Ok(file) => file, + Err(_) => { + stream.ssl_write(&response::not_found().to_vec()); + return; + } + }; + + stream.ssl_write(&response::Header::new(response::Status::Success, "text/gemini").to_vec()); + + let mut buf_file = BufReader::new(file); + let mut buf_stream = BufWriter::new(stream); + copy(&mut buf_file, &mut buf_stream); } diff --git a/src/server/mod.rs b/src/server/mod.rs index d7826f6..545fbeb 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -1,4 +1,3 @@ -use crate::server::response::Response; use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod, SslStream}; use std::collections::HashMap; use std::net::{SocketAddr, TcpListener, TcpStream}; @@ -13,8 +12,26 @@ pub mod response; #[derive(Clone)] pub struct ServerConfig { - defaultHost: Url, - wwwRoot: PathBuf, + default_host: Url, + www_root: PathBuf, + addrs: Vec, +} + +impl ServerConfig { + pub fn new(default_host: String, www_root: String) -> ServerConfig { + let mut url = Url::parse("gemini://default").unwrap(); + url.set_host(Some(default_host.as_str())); + + ServerConfig { + default_host: url, + www_root: PathBuf::from(www_root), + addrs: Vec::new(), + } + } + + pub fn add_addr(&mut self, addr: String) { + self.addrs.push(addr.parse().unwrap()); + } } pub struct Server { @@ -23,21 +40,15 @@ pub struct Server { } impl Server { - pub fn new(host: &str, wwwRoot: &Path) -> Server { - let config = ServerConfig { - defaultHost: Url::parse(host).unwrap(), - wwwRoot: PathBuf::from(wwwRoot), - }; - + pub fn new(config: &ServerConfig) -> Server { Server { acceptor: build_acceptor(), - config: config, + config: config.clone(), } } pub fn serve(&self) { - let addrs = [SocketAddr::from(([127, 0, 0, 1], 1965))]; - let listener = TcpListener::bind(&addrs[..]).unwrap(); + let listener = TcpListener::bind(&self.config.addrs[..]).unwrap(); for stream in listener.incoming() { match stream { @@ -62,15 +73,10 @@ fn handle_client(config: &ServerConfig, mut stream: SslStream) { let location = match Url::parse(&request) { Ok(url) => url, - Err(e) => config.defaultHost.join(&request).unwrap(), + Err(e) => config.default_host.join(&request).unwrap(), }; - let response = match handler::handle(config, location) { - Some(response) => response, - None => response::internal_error(), - }; - - stream.ssl_write(&response.format()); + handler::handle(config, location, &mut stream); } fn build_acceptor() -> std::sync::Arc { diff --git a/src/server/response.rs b/src/server/response.rs index 36d5d15..8b087ba 100644 --- a/src/server/response.rs +++ b/src/server/response.rs @@ -15,11 +15,6 @@ pub struct Header { meta: String, } -pub struct Response { - header: Header, - data: Vec, -} - impl Header { pub fn new(status: Status, meta: &str) -> Header { Header { @@ -31,47 +26,24 @@ impl Header { pub fn format(&self) -> String { format!("{} {}\r\n", self.status as u8, self.meta) } -} -impl Response { - pub fn new(header: Header, data: Vec) -> Response { - Response { - header: header, - data: data, - } - } - - pub fn new_empty(header: Header) -> Response { - Response { - header: header, - data: Vec::new(), - } - } - - pub fn format(&self) -> Vec { - let mut resp: Vec = self.header.format().as_bytes().to_vec(); - resp.extend(&self.data); - return resp; + pub fn to_vec(&self) -> Vec { + self.format().as_bytes().to_vec() } } -pub fn invalid_protocol() -> Response { - Response::new_empty(Header::new( - Status::PermanentFailure, - "this protocol is not supported", - )) +pub fn invalid_protocol() -> Header { + 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 not_understood() -> Header { + Header::new(Status::PermanentFailure, "request not understood") } -pub fn internal_error() -> Response { - Response::new_empty(Header::new( - Status::PermanentFailure, - "internal server error", - )) +pub fn not_found() -> Header { + Header::new(Status::PermanentFailure, "resource not found") +} + +pub fn internal_error() -> Header { + Header::new(Status::PermanentFailure, "internal server error") }