diff options
author | Sven-Hendrik Haase <svenstaro@gmail.com> | 2019-04-27 19:31:07 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-04-27 19:31:07 +0000 |
commit | 18fcc8e699897ceca75920722531b79d909f28fc (patch) | |
tree | 9e855f3afbbe8237a09c9c364832f8c5aa4e57e8 | |
parent | Describe hashed password feature in README (diff) | |
parent | Combine ContextualError and ContextualErrorKind into one (diff) | |
download | miniserve-18fcc8e699897ceca75920722531b79d909f28fc.tar.gz miniserve-18fcc8e699897ceca75920722531b79d909f28fc.zip |
Merge pull request #88 from KSXGitHub/combine-contextual-error
Combine ContextualError and ContextualErrorKind into one
-rw-r--r-- | src/archive.rs | 38 | ||||
-rw-r--r-- | src/args.rs | 14 | ||||
-rw-r--r-- | src/auth.rs | 12 | ||||
-rw-r--r-- | src/errors.rs | 58 | ||||
-rw-r--r-- | src/file_upload.rs | 32 | ||||
-rw-r--r-- | src/main.rs | 26 |
6 files changed, 62 insertions, 118 deletions
diff --git a/src/archive.rs b/src/archive.rs index b5788f5..a76446a 100644 --- a/src/archive.rs +++ b/src/archive.rs @@ -7,7 +7,7 @@ use std::path::PathBuf; use strum_macros::{Display, EnumIter, EnumString}; use tar::Builder; -use crate::errors::{ContextualError, ContextualErrorKind}; +use crate::errors::{ContextualError}; /// Available compression methods #[derive(Deserialize, Clone, EnumIter, EnumString, Display)] @@ -62,17 +62,17 @@ fn tgz_compress(dir: &PathBuf, skip_symlinks: bool) -> Result<(String, Bytes), C let mut tgz_data = Bytes::new(); let tar_data = tar(src_dir, directory.to_string(), skip_symlinks).map_err(|e| { - ContextualError::new(ContextualErrorKind::ArchiveCreationError( + ContextualError::ArchiveCreationError( "tarball".to_string(), Box::new(e), - )) + ) })?; let gz_data = gzip(&tar_data).map_err(|e| { - ContextualError::new(ContextualErrorKind::ArchiveCreationError( + ContextualError::ArchiveCreationError( "GZIP archive".to_string(), Box::new(e), - )) + ) })?; tgz_data.extend_from_slice(&gz_data); @@ -80,15 +80,15 @@ fn tgz_compress(dir: &PathBuf, skip_symlinks: bool) -> Result<(String, Bytes), C Ok((dst_tgz_filename, tgz_data)) } else { // https://doc.rust-lang.org/std/ffi/struct.OsStr.html#method.to_str - Err(ContextualError::new(ContextualErrorKind::InvalidPathError( + Err(ContextualError::InvalidPathError( "Directory name contains invalid UTF-8 characters".to_string(), - ))) + )) } } else { // https://doc.rust-lang.org/std/path/struct.Path.html#method.file_name - Err(ContextualError::new(ContextualErrorKind::InvalidPathError( + Err(ContextualError::InvalidPathError( "Directory name terminates in \"..\"".to_string(), - ))) + )) } } @@ -105,20 +105,20 @@ fn tar( tar_builder .append_dir_all(inner_folder, &src_dir) .map_err(|e| { - ContextualError::new(ContextualErrorKind::IOError( + ContextualError::IOError( format!( "Failed to append the content of {} to the TAR archive", &src_dir ), e, - )) + ) })?; let tar_content = tar_builder.into_inner().map_err(|e| { - ContextualError::new(ContextualErrorKind::IOError( + ContextualError::IOError( "Failed to finish writing the TAR archive".to_string(), e, - )) + ) })?; Ok(tar_content) @@ -127,22 +127,22 @@ fn tar( /// Compresses a stream of bytes using the GZIP algorithm, and returns the resulting stream fn gzip(mut data: &[u8]) -> Result<Vec<u8>, ContextualError> { let mut encoder = Encoder::new(Vec::new()).map_err(|e| { - ContextualError::new(ContextualErrorKind::IOError( + ContextualError::IOError( "Failed to create GZIP encoder".to_string(), e, - )) + ) })?; io::copy(&mut data, &mut encoder).map_err(|e| { - ContextualError::new(ContextualErrorKind::IOError( + ContextualError::IOError( "Failed to write GZIP data".to_string(), e, - )) + ) })?; let data = encoder.finish().into_result().map_err(|e| { - ContextualError::new(ContextualErrorKind::IOError( + ContextualError::IOError( "Failed to write GZIP trailer".to_string(), e, - )) + ) })?; Ok(data) diff --git a/src/args.rs b/src/args.rs index 4077f35..63799a0 100644 --- a/src/args.rs +++ b/src/args.rs @@ -3,7 +3,7 @@ use std::path::PathBuf; use structopt::StructOpt; use crate::auth; -use crate::errors::{ContextualError, ContextualErrorKind}; +use crate::errors::{ContextualError}; use crate::themes; /// Possible characters for random routes @@ -80,9 +80,7 @@ fn parse_interface(src: &str) -> Result<IpAddr, std::net::AddrParseError> { /// Checks wether the auth string is valid, i.e. it follows the syntax username:password fn parse_auth(src: &str) -> Result<auth::RequiredAuth, ContextualError> { let mut split = src.splitn(3, ':'); - let invalid_auth_format = Err( - ContextualError::new(ContextualErrorKind::InvalidAuthFormat) - ); + let invalid_auth_format = Err(ContextualError::InvalidAuthFormat); let username = match split.next() { Some(username) => username, @@ -100,16 +98,14 @@ fn parse_auth(src: &str) -> Result<auth::RequiredAuth, ContextualError> { let hash_bin = if let Ok(hash_bin) = hex::decode(hash_hex) { hash_bin } else { - return Err(ContextualError::new(ContextualErrorKind::InvalidPasswordHash)) + return Err(ContextualError::InvalidPasswordHash) }; match second_part { "sha256" => auth::RequiredAuthPassword::Sha256(hash_bin.to_owned()), "sha512" => auth::RequiredAuthPassword::Sha512(hash_bin.to_owned()), _ => { - return Err(ContextualError::new( - ContextualErrorKind::InvalidHashMethod(second_part.to_owned()) - )) + return Err(ContextualError::InvalidHashMethod(second_part.to_owned())) }, } } else { @@ -117,7 +113,7 @@ fn parse_auth(src: &str) -> Result<auth::RequiredAuth, ContextualError> { // After 255 characters, Windows will truncate the value. // As for the username, the spec does not mention a limit in length if second_part.len() > 255 { - return Err(ContextualError::new(ContextualErrorKind::PasswordTooLongError)); + return Err(ContextualError::PasswordTooLongError); } auth::RequiredAuthPassword::Plain(second_part.to_owned()) diff --git a/src/auth.rs b/src/auth.rs index e75f498..8e6532b 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -3,7 +3,7 @@ use actix_web::middleware::{Middleware, Response}; use actix_web::{HttpRequest, HttpResponse, Result}; use sha2::{Digest, Sha256, Sha512}; -use crate::errors::{ContextualError, ContextualErrorKind}; +use crate::errors::{ContextualError}; pub struct Auth; @@ -36,13 +36,13 @@ pub fn parse_basic_auth( let basic_removed = authorization_header .to_str() .map_err(|e| { - ContextualError::new(ContextualErrorKind::ParseError( + ContextualError::ParseError( "HTTP authentication header".to_string(), e.to_string(), - )) + ) })? .replace("Basic ", ""); - let decoded = base64::decode(&basic_removed).map_err(ContextualErrorKind::Base64DecodeError)?; + let decoded = base64::decode(&basic_removed).map_err(ContextualError::Base64DecodeError)?; let decoded_str = String::from_utf8_lossy(&decoded); let credentials: Vec<&str> = decoded_str.splitn(2, ':').collect(); @@ -97,9 +97,7 @@ impl Middleware<crate::MiniserveConfig> for Auth { let auth_req = match parse_basic_auth(auth_headers) { Ok(auth_req) => auth_req, Err(err) => { - let auth_err = ContextualError::new( - ContextualErrorKind::HTTPAuthenticationError(Box::new(err)), - ); + let auth_err = ContextualError::HTTPAuthenticationError(Box::new(err)); return Ok(Response::Done( HttpResponse::BadRequest().body(auth_err.to_string()), )); diff --git a/src/errors.rs b/src/errors.rs index 833e9c4..b8af25d 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -1,8 +1,7 @@ -use failure::{Backtrace, Context, Fail}; -use std::fmt::{self, Debug, Display}; +use failure::Fail; #[derive(Debug, Fail)] -pub enum ContextualErrorKind { +pub enum ContextualError { /// Fully customized errors, not inheriting from any error #[fail(display = "{}", _0)] CustomError(String), @@ -73,58 +72,9 @@ pub fn log_error_chain(description: String) { } } -/// Based on https://boats.gitlab.io/failure/error-errorkind.html -pub struct ContextualError { - inner: Context<ContextualErrorKind>, -} - -impl ContextualError { - pub fn new(kind: ContextualErrorKind) -> ContextualError { - ContextualError { - inner: Context::new(kind), - } - } -} - -impl Fail for ContextualError { - fn cause(&self) -> Option<&Fail> { - self.inner.cause() - } - - fn backtrace(&self) -> Option<&Backtrace> { - self.inner.backtrace() - } -} - -impl Display for ContextualError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - Display::fmt(&self.inner, f) - } -} - -impl Debug for ContextualError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - Debug::fmt(&self.inner, f) - } -} - -impl From<Context<ContextualErrorKind>> for ContextualError { - fn from(inner: Context<ContextualErrorKind>) -> ContextualError { - ContextualError { inner } - } -} - -impl From<ContextualErrorKind> for ContextualError { - fn from(kind: ContextualErrorKind) -> ContextualError { - ContextualError { - inner: Context::new(kind), - } - } -} - -/// This allows to create CustomErrors more simply +/// This makes creating CustomErrors easier impl From<String> for ContextualError { fn from(msg: String) -> ContextualError { - ContextualError::new(ContextualErrorKind::CustomError(msg)) + ContextualError::CustomError(msg) } } diff --git a/src/file_upload.rs b/src/file_upload.rs index 1618617..7f9cede 100644 --- a/src/file_upload.rs +++ b/src/file_upload.rs @@ -10,7 +10,7 @@ use std::{ path::{Component, PathBuf}, }; -use crate::errors::{self, ContextualErrorKind}; +use crate::errors::{self, ContextualError}; use crate::renderer; /// Query parameters @@ -24,9 +24,9 @@ fn save_file( field: multipart::Field<dev::Payload>, file_path: PathBuf, overwrite_files: bool, -) -> Box<Future<Item = i64, Error = ContextualErrorKind>> { +) -> Box<Future<Item = i64, Error = ContextualError>> { if !overwrite_files && file_path.exists() { - return Box::new(future::err(ContextualErrorKind::CustomError( + return Box::new(future::err(ContextualError::CustomError( "File already exists, and the overwrite_files option has not been set".to_string(), ))); } @@ -34,7 +34,7 @@ fn save_file( let mut file = match std::fs::File::create(&file_path) { Ok(file) => file, Err(e) => { - return Box::new(future::err(ContextualErrorKind::IOError( + return Box::new(future::err(ContextualError::IOError( format!("Failed to create file in {}", file_path.display()), e, ))); @@ -42,13 +42,13 @@ fn save_file( }; Box::new( field - .map_err(ContextualErrorKind::MultipartError) + .map_err(ContextualError::MultipartError) .fold(0i64, move |acc, bytes| { let rt = file .write_all(bytes.as_ref()) .map(|_| acc + bytes.len() as i64) .map_err(|e| { - ContextualErrorKind::IOError("Failed to write to file".to_string(), e) + ContextualError::IOError("Failed to write to file".to_string(), e) }); future::result(rt) }), @@ -60,41 +60,41 @@ fn handle_multipart( item: multipart::MultipartItem<dev::Payload>, mut file_path: PathBuf, overwrite_files: bool, -) -> Box<Stream<Item = i64, Error = ContextualErrorKind>> { +) -> Box<Stream<Item = i64, Error = ContextualError>> { match item { multipart::MultipartItem::Field(field) => { let filename = field .headers() .get(header::CONTENT_DISPOSITION) - .ok_or(ContextualErrorKind::ParseError) + .ok_or(ContextualError::ParseError) .and_then(|cd| { header::ContentDisposition::from_raw(cd) - .map_err(|_| ContextualErrorKind::ParseError) + .map_err(|_| ContextualError::ParseError) }) .and_then(|content_disposition| { content_disposition .get_filename() - .ok_or(ContextualErrorKind::ParseError) + .ok_or(ContextualError::ParseError) .map(String::from) }); - let err = |e: ContextualErrorKind| Box::new(future::err(e).into_stream()); + let err = |e: ContextualError| Box::new(future::err(e).into_stream()); match filename { Ok(f) => { match fs::metadata(&file_path) { Ok(metadata) => { if !metadata.is_dir() { - return err(ContextualErrorKind::InvalidPathError(format!( + return err(ContextualError::InvalidPathError(format!( "cannot upload file to {}, since it's not a directory", &file_path.display() ))); } else if metadata.permissions().readonly() { - return err(ContextualErrorKind::InsufficientPermissionsError( + return err(ContextualError::InsufficientPermissionsError( file_path.display().to_string(), )); } } Err(_) => { - return err(ContextualErrorKind::InsufficientPermissionsError( + return err(ContextualError::InsufficientPermissionsError( file_path.display().to_string(), )); } @@ -109,7 +109,7 @@ fn handle_multipart( } } multipart::MultipartItem::Nested(mp) => Box::new( - mp.map_err(ContextualErrorKind::MultipartError) + mp.map_err(ContextualError::MultipartError) .map(move |item| handle_multipart(item, file_path.clone(), overwrite_files)) .flatten(), ), @@ -156,7 +156,7 @@ pub fn upload_file(req: &HttpRequest<crate::MiniserveConfig>) -> FutureResponse< let overwrite_files = req.state().overwrite_files; Box::new( req.multipart() - .map_err(ContextualErrorKind::MultipartError) + .map_err(ContextualError::MultipartError) .map(move |item| handle_multipart(item, target_dir.clone(), overwrite_files)) .flatten() .collect() diff --git a/src/main.rs b/src/main.rs index bc8f3f0..c1bb0aa 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,7 +19,7 @@ mod listing; mod renderer; mod themes; -use crate::errors::{ContextualError, ContextualErrorKind}; +use crate::errors::{ContextualError}; #[derive(Clone)] /// Configuration of the Miniserve application @@ -84,10 +84,10 @@ fn run() -> Result<(), ContextualError> { .path .symlink_metadata() .map_err(|e| { - ContextualError::new(ContextualErrorKind::IOError( + ContextualError::IOError( "Failed to retrieve symlink's metadata".to_string(), e, - )) + ) })? .file_type() .is_symlink() @@ -117,10 +117,10 @@ fn run() -> Result<(), ContextualError> { .collect::<Vec<String>>(); let canon_path = miniserve_config.path.canonicalize().map_err(|e| { - ContextualError::new(ContextualErrorKind::IOError( + ContextualError::IOError( "Failed to resolve path to be served".to_string(), e, - )) + ) })?; let path_string = canon_path.to_string_lossy(); @@ -136,18 +136,18 @@ fn run() -> Result<(), ContextualError> { ); print!("Starting server in "); io::stdout().flush().map_err(|e| { - ContextualError::new(ContextualErrorKind::IOError( + ContextualError::IOError( "Failed to write data".to_string(), e, - )) + ) })?; for c in "3… 2… 1… \n".chars() { print!("{}", c); io::stdout().flush().map_err(|e| { - ContextualError::new(ContextualErrorKind::IOError( + ContextualError::IOError( "Failed to write data".to_string(), e, - )) + ) })?; thread::sleep(Duration::from_millis(500)); } @@ -196,10 +196,10 @@ fn run() -> Result<(), ContextualError> { // Note that this should never fail, since CLI parsing succeeded // This means the format of each IP address is valid, and so is the port // Valid IpAddr + valid port == valid SocketAddr - return Err(ContextualError::new(ContextualErrorKind::ParseError( + return Err(ContextualError::ParseError( "string as socket address".to_string(), e.to_string(), - ))); + )); } }; @@ -211,10 +211,10 @@ fn run() -> Result<(), ContextualError> { }) .bind(socket_addresses.as_slice()) .map_err(|e| { - ContextualError::new(ContextualErrorKind::IOError( + ContextualError::IOError( "Failed to bind server".to_string(), e, - )) + ) })? .shutdown_timeout(0) .start(); |