Browse Source

Better error handling with enums

Signed-off-by: Alexey Yerin <yerinalexey98fd@gmail.com>
master
Alexey Yerin 3 years ago committed by Garrit Franke
parent
commit
46d6cd2386
  1. 21
      Cargo.lock
  2. 1
      Cargo.toml
  3. 33
      src/error.rs
  4. 5
      src/gemini.rs
  5. 44
      src/main.rs

21
Cargo.lock generated

@ -392,6 +392,7 @@ dependencies = [
"mime_guess",
"native-tls",
"serde",
"thiserror",
"toml",
"url",
]
@ -419,6 +420,26 @@ dependencies = [
"unicode-width",
]
[[package]]
name = "thiserror"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e9ae34b84616eedaaf1e9dd6026dbe00dcafa92aa0c8077cb69df1fcfe5e53e"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ba20f23e85b10754cd195504aebf6a27e2e6cbe28c17778a0c930724628dd56"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tinyvec"
version = "0.3.4"

1
Cargo.toml

@ -15,3 +15,4 @@ toml = "0.5.7"
anyhow = "1.0.34"
clap = "2.33.3"
mime_guess = "2.0.3"
thiserror = "1.0.22"

33
src/error.rs

@ -1,13 +1,26 @@
pub struct SimpleError(String);
use std::io;
use thiserror::Error;
impl std::convert::From<String> for SimpleError {
fn from(string: String) -> Self {
Self(string)
}
}
#[derive(Error, Debug)]
pub enum TaurusError {
#[error("failed to read configuration file: {0:#?}")]
InvalidConfig(anyhow::Error),
#[error("failed to open identity file: {0}")]
NoIdentity(io::Error),
#[error("failed parse certificate: {0:#?}")]
InvalidCertificate(native_tls::Error),
#[error("failed to bind: {0}")]
BindFailed(io::Error),
#[error("invalid Unicode input")]
InvalidUnicode,
#[error("could not read the stream")]
StreamReadFailed(io::Error),
impl std::fmt::Debug for SimpleError {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
formatter.write_str(&self.0)
}
#[error("could not write to the stream")]
StreamWriteFailed(io::Error),
}

5
src/gemini.rs

@ -1,3 +1,4 @@
use crate::error;
use native_tls::TlsStream;
use std::io::Write;
use std::net::TcpStream;
@ -54,7 +55,7 @@ impl GeminiResponse {
}
}
pub fn send(&self, mut stream: TlsStream<TcpStream>) -> Result<usize, String> {
pub fn send(&self, mut stream: TlsStream<TcpStream>) -> Result<usize, error::TaurusError> {
let mut buf: Vec<u8> = Vec::new();
// <Status>
@ -74,6 +75,6 @@ impl GeminiResponse {
stream
.write(&buf)
.map_err(|e| format!("could not write to stream: {}", e))
.map_err(error::TaurusError::StreamWriteFailed)
}
}

44
src/main.rs

@ -17,7 +17,14 @@ use std::thread;
use clap::{crate_authors, crate_description, crate_name, crate_version, App, Arg};
fn main() -> Result<(), error::SimpleError> {
fn main() {
if let Err(e) = run() {
println!("Error: {}", e);
std::process::exit(1);
}
}
fn run() -> Result<(), error::TaurusError> {
// CLI
let matches = App::new(crate_name!())
.version(crate_version!())
@ -34,8 +41,8 @@ fn main() -> Result<(), error::SimpleError> {
.get_matches();
let config_path = matches.value_of("config").map(|v| v.to_owned());
let config: config::Config = config::Config::load(config_path)
.map_err(|err| format!("failed to read configuration file: {}", err))?;
let config: config::Config =
config::Config::load(config_path).map_err(error::TaurusError::InvalidConfig)?;
// Defaults for configuration file
let port = config.port.unwrap_or(1965);
@ -47,18 +54,13 @@ fn main() -> Result<(), error::SimpleError> {
.unwrap_or_else(|| "/var/www/gemini".to_owned());
// Read certificate
let mut file =
File::open(cert_file).map_err(|err| format!("failed to open identity file: {}", err))?;
let mut identity = vec![];
file.read_to_end(&mut identity)
.map_err(|err| format!("failed to read identity file: {}", err))?;
let identity = read_file(&cert_file).map_err(error::TaurusError::NoIdentity)?;
let identity = Identity::from_pkcs12(&identity, &config.certificate_password)
.map_err(|err| format!("failed to parse certificate: {}", err))?;
.map_err(error::TaurusError::InvalidCertificate)?;
let address = format!("0.0.0.0:{}", port);
let listener = TcpListener::bind(address).map_err(|err| format!("failed to bind: {}", err))?;
let listener = TcpListener::bind(address).map_err(|err| error::TaurusError::BindFailed(err))?;
let acceptor = TlsAcceptor::new(identity).unwrap();
let acceptor = Arc::new(acceptor);
@ -99,12 +101,12 @@ fn read_file(file_path: &str) -> Result<Vec<u8>, io::Error> {
}
/// Send file as a response
fn write_file(path: &str) -> Result<GeminiResponse, String> {
fn write_file(path: &str) -> Result<GeminiResponse, error::TaurusError> {
let extension = path::Path::new(path)
.extension()
.unwrap_or_else(|| std::ffi::OsStr::new(""))
.to_str()
.ok_or_else(|| "invalid Unicode".to_owned())?;
.ok_or(error::TaurusError::InvalidUnicode)?;
let mime_type = match extension {
"gmi" => "text/gemini; charset=utf-8",
@ -125,11 +127,15 @@ fn write_file(path: &str) -> Result<GeminiResponse, String> {
}
}
fn handle_client(mut stream: TlsStream<TcpStream>, static_root: &str) -> Result<usize, String> {
fn handle_client(
mut stream: TlsStream<TcpStream>,
static_root: &str,
) -> Result<usize, error::TaurusError> {
let mut buffer = [0; 1024];
if let Err(e) = stream.read(&mut buffer) {
return Err(format!("could not read from stream: {}", e));
}
stream
.read(&mut buffer)
.map_err(error::TaurusError::StreamReadFailed)?;
let mut raw_request = String::from_utf8_lossy(&buffer[..]).to_mut().to_owned();
@ -158,12 +164,12 @@ fn handle_client(mut stream: TlsStream<TcpStream>, static_root: &str) -> Result<
let index_path = path
.join("index.gmi")
.to_str()
.ok_or_else(|| "invalid Unicode".to_owned())?
.ok_or(error::TaurusError::InvalidUnicode)?
.to_owned();
write_file(&index_path)?.send(stream)
} else {
write_file(path.to_str().ok_or_else(|| "invalid Unicode".to_owned())?)?.send(stream)
write_file(path.to_str().ok_or(error::TaurusError::InvalidUnicode)?)?.send(stream)
}
} else {
GeminiResponse::not_found().send(stream)

Loading…
Cancel
Save