|
|
|
@ -5,9 +5,11 @@ mod config;
|
|
|
|
|
mod error; |
|
|
|
|
mod gemini; |
|
|
|
|
|
|
|
|
|
use gemini::GeminiResponse; |
|
|
|
|
|
|
|
|
|
use native_tls::{Identity, TlsAcceptor, TlsStream}; |
|
|
|
|
use std::fs::File; |
|
|
|
|
use std::io::{self, Read, Write}; |
|
|
|
|
use std::io::{self, Read}; |
|
|
|
|
use std::net::{TcpListener, TcpStream}; |
|
|
|
|
use std::path; |
|
|
|
|
use std::sync::Arc; |
|
|
|
@ -69,9 +71,14 @@ fn main() -> Result<(), error::SimpleError> {
|
|
|
|
|
let static_root = static_root.clone(); |
|
|
|
|
|
|
|
|
|
thread::spawn(move || match acceptor.accept(stream) { |
|
|
|
|
Ok(stream) => handle_client(stream, &static_root) |
|
|
|
|
.unwrap_or_else(|e| println!("Error: {}", e)), |
|
|
|
|
Err(e) => println!("Error: can't handle stream: {}", e), |
|
|
|
|
Ok(stream) => { |
|
|
|
|
if let Err(e) = handle_client(stream, &static_root) { |
|
|
|
|
println!("Error: can't handle client: {}", e); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
Err(e) => { |
|
|
|
|
println!("Error: can't handle stream: {}", e); |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
Err(err) => println!("Error: {}", err), |
|
|
|
@ -92,28 +99,20 @@ fn read_file(file_path: &str) -> Result<Vec<u8>, io::Error> {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Send file as a response
|
|
|
|
|
fn send_file(path: &str, response: &mut gemini::GeminiResponse) { |
|
|
|
|
fn write_file(path: &str) -> GeminiResponse { |
|
|
|
|
match read_file(path) { |
|
|
|
|
Ok(buf) => { |
|
|
|
|
response.body = Some(buf); |
|
|
|
|
} |
|
|
|
|
Ok(buf) => GeminiResponse::success(buf), |
|
|
|
|
Err(err) => { |
|
|
|
|
// Cannot read file or it doesn't exist
|
|
|
|
|
|
|
|
|
|
println!("Error [{}]: {}", path, err); |
|
|
|
|
|
|
|
|
|
response.status = [b'5', b'1']; |
|
|
|
|
response.meta = "Resource not found".into(); |
|
|
|
|
GeminiResponse::not_found() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn not_found(response: &mut gemini::GeminiResponse) { |
|
|
|
|
response.status = [b'5', b'1']; |
|
|
|
|
response.meta = "Resource not found".into(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn handle_client(mut stream: TlsStream<TcpStream>, static_root: &str) -> Result<(), String> { |
|
|
|
|
fn handle_client(mut stream: TlsStream<TcpStream>, static_root: &str) -> Result<usize, String> { |
|
|
|
|
let mut buffer = [0; 1024]; |
|
|
|
|
if let Err(e) = stream.read(&mut buffer) { |
|
|
|
|
return Err(format!("could not read from stream: {}", e)); |
|
|
|
@ -127,14 +126,12 @@ fn handle_client(mut stream: TlsStream<TcpStream>, static_root: &str) -> Result<
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let request = gemini::GeminiRequest::from_string(&raw_request).unwrap(); |
|
|
|
|
let mut response = gemini::GeminiResponse::new(); |
|
|
|
|
|
|
|
|
|
let url_path = request.file_path(); |
|
|
|
|
let file_path = path::Path::new(url_path); |
|
|
|
|
|
|
|
|
|
if file_path.has_root() { |
|
|
|
|
// File starts with `/` (*nix) or `\\` (Windows), decline it
|
|
|
|
|
not_found(&mut response); |
|
|
|
|
return GeminiResponse::not_found().send(stream); |
|
|
|
|
} else { |
|
|
|
|
let path = path::Path::new(&static_root) |
|
|
|
|
.join(&file_path) |
|
|
|
@ -151,21 +148,12 @@ fn handle_client(mut stream: TlsStream<TcpStream>, static_root: &str) -> Result<
|
|
|
|
|
.ok_or("invalid Unicode".to_owned())? |
|
|
|
|
.to_owned(); |
|
|
|
|
|
|
|
|
|
send_file(&index_path, &mut response); |
|
|
|
|
return write_file(&index_path).send(stream); |
|
|
|
|
} else { |
|
|
|
|
send_file( |
|
|
|
|
path.to_str().ok_or("invalid Unicode".to_owned())?, |
|
|
|
|
&mut response, |
|
|
|
|
); |
|
|
|
|
return write_file(path.to_str().ok_or("invalid Unicode".to_owned())?).send(stream); |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
not_found(&mut response); |
|
|
|
|
return GeminiResponse::not_found().send(stream); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if let Err(e) = stream.write(&response.build()) { |
|
|
|
|
return Err(format!("could not write to stream: {}", e)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Ok(()) |
|
|
|
|
} |
|
|
|
|