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

View file

@ -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<response::Response> {
pub fn handle(config: &ServerConfig, url: Url, stream: &mut SslStream<TcpStream>) {
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);
}

View file

@ -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<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 {
@ -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<TcpStream>) {
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<SslAcceptor> {

View file

@ -15,11 +15,6 @@ pub struct Header {
meta: String,
}
pub struct Response {
header: Header,
data: Vec<u8>,
}
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<u8>) -> Response {
Response {
header: header,
data: data,
pub fn to_vec(&self) -> Vec<u8> {
self.format().as_bytes().to_vec()
}
}
pub fn new_empty(header: Header) -> Response {
Response {
header: header,
data: Vec::new(),
}
pub fn invalid_protocol() -> Header {
Header::new(Status::PermanentFailure, "this protocol is not supported")
}
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 not_understood() -> Header {
Header::new(Status::PermanentFailure, "request not understood")
}
pub fn invalid_protocol() -> Response {
Response::new_empty(Header::new(
Status::PermanentFailure,
"this protocol is not supported",
))
pub fn not_found() -> Header {
Header::new(Status::PermanentFailure, "resource not found")
}
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",
))
pub fn internal_error() -> Header {
Header::new(Status::PermanentFailure, "internal server error")
}