working file serving

This commit is contained in:
Jan Wolff 2020-05-17 00:16:57 +02:00
parent 177d12b5b8
commit 87d61457bd
4 changed files with 71 additions and 68 deletions

View file

@ -1,8 +1,18 @@
mod server; mod server;
use std::env;
use std::path::Path; 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() { fn main() {
let wwwRoot = Path::new("/var/www/gemini/"); let server = server::Server::new(&read_config());
let server = server::Server::new("gemini://localhost", &wwwRoot);
server.serve(); server.serve();
} }

View file

@ -1,17 +1,32 @@
use crate::server::response; use crate::server::response;
use crate::server::ServerConfig; 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 std::path::Path;
use url::Url; use url::Url;
pub fn handle(config: &ServerConfig, url: Url) -> Option<response::Response> { pub fn handle(config: &ServerConfig, url: Url, stream: &mut SslStream<TcpStream>) {
if url.scheme() != "gemini" { if url.scheme() != "gemini" {
return Some(response::invalid_protocol()); stream.ssl_write(&response::invalid_protocol().to_vec());
return;
} }
let path = Path::new(url.path()); let rel_path = Path::new(url.path()).strip_prefix("/").unwrap();
if !path.has_root() { let path = config.www_root.join(rel_path);
return Some(response::not_understood());
}
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);
} }

View file

@ -1,4 +1,3 @@
use crate::server::response::Response;
use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod, SslStream}; use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod, SslStream};
use std::collections::HashMap; use std::collections::HashMap;
use std::net::{SocketAddr, TcpListener, TcpStream}; use std::net::{SocketAddr, TcpListener, TcpStream};
@ -13,8 +12,26 @@ pub mod response;
#[derive(Clone)] #[derive(Clone)]
pub struct ServerConfig { pub struct ServerConfig {
defaultHost: Url, default_host: Url,
wwwRoot: PathBuf, www_root: PathBuf,
addrs: Vec<SocketAddr>,
}
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 { pub struct Server {
@ -23,21 +40,15 @@ pub struct Server {
} }
impl Server { impl Server {
pub fn new(host: &str, wwwRoot: &Path) -> Server { pub fn new(config: &ServerConfig) -> Server {
let config = ServerConfig {
defaultHost: Url::parse(host).unwrap(),
wwwRoot: PathBuf::from(wwwRoot),
};
Server { Server {
acceptor: build_acceptor(), acceptor: build_acceptor(),
config: config, config: config.clone(),
} }
} }
pub fn serve(&self) { pub fn serve(&self) {
let addrs = [SocketAddr::from(([127, 0, 0, 1], 1965))]; let listener = TcpListener::bind(&self.config.addrs[..]).unwrap();
let listener = TcpListener::bind(&addrs[..]).unwrap();
for stream in listener.incoming() { for stream in listener.incoming() {
match stream { match stream {
@ -62,15 +73,10 @@ fn handle_client(config: &ServerConfig, mut stream: SslStream<TcpStream>) {
let location = match Url::parse(&request) { let location = match Url::parse(&request) {
Ok(url) => url, 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) { handler::handle(config, location, &mut stream);
Some(response) => response,
None => response::internal_error(),
};
stream.ssl_write(&response.format());
} }
fn build_acceptor() -> std::sync::Arc<SslAcceptor> { fn build_acceptor() -> std::sync::Arc<SslAcceptor> {

View file

@ -15,11 +15,6 @@ pub struct Header {
meta: String, meta: String,
} }
pub struct Response {
header: Header,
data: Vec<u8>,
}
impl Header { impl Header {
pub fn new(status: Status, meta: &str) -> Header { pub fn new(status: Status, meta: &str) -> Header {
Header { Header {
@ -31,47 +26,24 @@ impl Header {
pub fn format(&self) -> String { pub fn format(&self) -> String {
format!("{} {}\r\n", self.status as u8, self.meta) format!("{} {}\r\n", self.status as u8, self.meta)
} }
}
impl Response { pub fn to_vec(&self) -> Vec<u8> {
pub fn new(header: Header, data: Vec<u8>) -> Response { self.format().as_bytes().to_vec()
Response {
header: header,
data: data,
}
}
pub fn new_empty(header: Header) -> Response {
Response {
header: header,
data: Vec::new(),
}
}
pub fn format(&self) -> Vec<u8> {
let mut resp: Vec<u8> = self.header.format().as_bytes().to_vec();
resp.extend(&self.data);
return resp;
} }
} }
pub fn invalid_protocol() -> Response { pub fn invalid_protocol() -> Header {
Response::new_empty(Header::new( Header::new(Status::PermanentFailure, "this protocol is not supported")
Status::PermanentFailure,
"this protocol is not supported",
))
} }
pub fn not_understood() -> Response { pub fn not_understood() -> Header {
Response::new_empty(Header::new( Header::new(Status::PermanentFailure, "request not understood")
Status::PermanentFailure,
"request not understood",
))
} }
pub fn internal_error() -> Response { pub fn not_found() -> Header {
Response::new_empty(Header::new( Header::new(Status::PermanentFailure, "resource not found")
Status::PermanentFailure, }
"internal server error",
)) pub fn internal_error() -> Header {
Header::new(Status::PermanentFailure, "internal server error")
} }