working file serving
This commit is contained in:
parent
177d12b5b8
commit
87d61457bd
4 changed files with 71 additions and 68 deletions
14
src/main.rs
14
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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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 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 to_vec(&self) -> Vec<u8> {
|
||||
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")
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue