Browse Source

Refactor responses

These responses should be more extendable later on.
master
Garrit Franke 4 years ago
parent
commit
438c975d9e
  1. 21
      src/gemini.rs
  2. 50
      src/main.rs

21
src/gemini.rs

@ -1,3 +1,6 @@
use native_tls::TlsStream;
use std::io::Write;
use std::net::TcpStream;
use url::Url;
pub struct GeminiRequest {
@ -35,18 +38,26 @@ pub struct GeminiResponse {
}
impl GeminiResponse {
pub fn new() -> Self {
pub fn success(body: Vec<u8>) -> Self {
GeminiResponse {
status: [b'2', b'0'],
meta: "text/gemini; charset=utf-8".as_bytes().to_vec(),
body: Some(body),
}
}
pub fn not_found() -> Self {
GeminiResponse {
status: [b'5', b'1'],
meta: "Resource not found".into(),
body: None,
}
}
pub fn build(&self) -> Vec<u8> {
pub fn send(&self, mut stream: TlsStream<TcpStream>) -> Result<usize, String> {
let mut buf: Vec<u8> = Vec::new();
// 20 SUCESS status
// <Status>
buf.extend(&self.status);
// <Space>
@ -61,6 +72,8 @@ impl GeminiResponse {
buf.extend(body);
}
buf
stream
.write(&buf)
.map_err(|e| format!("could not write to stream: {}", e))
}
}

50
src/main.rs

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

Loading…
Cancel
Save