aboutsummaryrefslogtreecommitdiffstats
path: root/src/file_upload.rs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/file_upload.rs233
1 files changed, 100 insertions, 133 deletions
diff --git a/src/file_upload.rs b/src/file_upload.rs
index 5faa67f..6fa99ef 100644
--- a/src/file_upload.rs
+++ b/src/file_upload.rs
@@ -2,11 +2,10 @@ use actix_web::{
http::{header, StatusCode},
HttpRequest, HttpResponse,
};
-use futures::{future, Future, FutureExt, Stream, TryStreamExt};
+use futures::TryStreamExt;
use std::{
io::Write,
path::{Component, PathBuf},
- pin::Pin,
};
use crate::errors::{self, ContextualError};
@@ -14,88 +13,62 @@ use crate::listing::{self, SortingMethod, SortingOrder};
use crate::renderer;
/// Create future to save file.
-fn save_file(
+async fn save_file(
field: actix_multipart::Field,
file_path: PathBuf,
overwrite_files: bool,
-) -> Pin<Box<dyn Future<Output = Result<i64, ContextualError>>>> {
+) -> Result<u64, ContextualError> {
if !overwrite_files && file_path.exists() {
- return Box::pin(future::err(ContextualError::DuplicateFileError));
+ return Err(ContextualError::DuplicateFileError);
}
- let mut file = match std::fs::File::create(&file_path) {
- Ok(file) => file,
- Err(e) => {
- return Box::pin(future::err(ContextualError::IoError(
- format!("Failed to create {}", file_path.display()),
- e,
- )));
- }
- };
- Box::pin(
- field
- .map_err(ContextualError::MultipartError)
- .try_fold(0i64, move |acc, bytes| {
- let rt = file
- .write_all(bytes.as_ref())
- .map(|_| acc + bytes.len() as i64)
- .map_err(|e| {
- ContextualError::IoError("Failed to write to file".to_string(), e)
- });
- future::ready(rt)
- }),
- )
+ let file = std::fs::File::create(&file_path).map_err(|e| {
+ ContextualError::IoError(format!("Failed to create {}", file_path.display()), e)
+ })?;
+
+ let (_, written_len) = field
+ .map_err(ContextualError::MultipartError)
+ .try_fold((file, 0u64), |(mut file, written_len), bytes| async move {
+ file.write_all(bytes.as_ref())
+ .map_err(|e| ContextualError::IoError("Failed to write to file".to_string(), e))?;
+ Ok((file, written_len + bytes.len() as u64))
+ })
+ .await?;
+
+ Ok(written_len)
}
/// Create new future to handle file as multipart data.
-fn handle_multipart(
+async fn handle_multipart(
field: actix_multipart::Field,
- mut file_path: PathBuf,
+ file_path: PathBuf,
overwrite_files: bool,
-) -> Pin<Box<dyn Stream<Item = Result<i64, ContextualError>>>> {
+) -> Result<u64, ContextualError> {
let filename = field
- .headers()
- .get(header::CONTENT_DISPOSITION)
- .ok_or(ContextualError::ParseError)
- .and_then(|cd| {
- header::ContentDisposition::from_raw(cd).map_err(|_| ContextualError::ParseError)
- })
- .and_then(|content_disposition| {
- content_disposition
- .get_filename()
- .ok_or(ContextualError::ParseError)
- .map(String::from)
- });
- let err = |e: ContextualError| Box::pin(future::err(e).into_stream());
- match filename {
- Ok(f) => {
- match std::fs::metadata(&file_path) {
- Ok(metadata) => {
- if !metadata.is_dir() {
- 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(ContextualError::InsufficientPermissionsError(
- file_path.display().to_string(),
- ));
- }
- }
- Err(_) => {
- return err(ContextualError::InsufficientPermissionsError(
- file_path.display().to_string(),
- ));
- }
- }
- file_path = file_path.join(f);
- Box::pin(save_file(field, file_path, overwrite_files).into_stream())
- }
- Err(e) => err(e(
- "HTTP header".to_string(),
- "Failed to retrieve the name of the file to upload".to_string(),
+ .content_disposition()
+ .and_then(|cd| cd.get_filename().map(String::from))
+ .ok_or_else(|| {
+ ContextualError::ParseError(
+ "HTTP header".to_string(),
+ "Failed to retrieve the name of the file to upload".to_string(),
+ )
+ })?;
+
+ match std::fs::metadata(&file_path) {
+ Err(_) => Err(ContextualError::InsufficientPermissionsError(
+ file_path.display().to_string(),
)),
- }
+ Ok(metadata) if !metadata.is_dir() => Err(ContextualError::InvalidPathError(format!(
+ "cannot upload file to {}, since it's not a directory",
+ &file_path.display()
+ ))),
+ Ok(metadata) if metadata.permissions().readonly() => Err(
+ ContextualError::InsufficientPermissionsError(file_path.display().to_string()),
+ ),
+ Ok(_) => Ok(()),
+ }?;
+
+ save_file(field, file_path.join(filename), overwrite_files).await
}
/// Handle incoming request to upload file.
@@ -104,16 +77,16 @@ fn handle_multipart(
/// invalid.
/// This method returns future.
#[allow(clippy::too_many_arguments)]
-pub fn upload_file(
+pub async fn upload_file(
req: HttpRequest,
payload: actix_web::web::Payload,
uses_random_route: bool,
favicon_route: String,
css_route: String,
- default_color_scheme: &str,
- default_color_scheme_dark: &str,
+ default_color_scheme: String,
+ default_color_scheme_dark: String,
hide_version_footer: bool,
-) -> Pin<Box<dyn Future<Output = Result<HttpResponse, actix_web::Error>>>> {
+) -> Result<HttpResponse, actix_web::Error> {
let conf = req.app_data::<crate::MiniserveConfig>().unwrap();
let return_path = if let Some(header) = req.headers().get(header::REFERER) {
header.to_str().unwrap_or("/").to_owned()
@@ -131,7 +104,7 @@ pub fn upload_file(
let err = ContextualError::InvalidHttpRequestError(
"Missing query parameter 'path'".to_string(),
);
- return Box::pin(create_error_response(
+ return Ok(create_error_response(
&err.to_string(),
StatusCode::BAD_REQUEST,
&return_path,
@@ -140,8 +113,8 @@ pub fn upload_file(
uses_random_route,
&favicon_route,
&css_route,
- default_color_scheme,
- default_color_scheme_dark,
+ &default_color_scheme,
+ &default_color_scheme_dark,
hide_version_footer,
));
}
@@ -154,7 +127,7 @@ pub fn upload_file(
"Failed to resolve path served by miniserve".to_string(),
e,
);
- return Box::pin(create_error_response(
+ return Ok(create_error_response(
&err.to_string(),
StatusCode::INTERNAL_SERVER_ERROR,
&return_path,
@@ -163,8 +136,8 @@ pub fn upload_file(
uses_random_route,
&favicon_route,
&css_route,
- default_color_scheme,
- default_color_scheme_dark,
+ &default_color_scheme,
+ &default_color_scheme_dark,
hide_version_footer,
));
}
@@ -177,7 +150,7 @@ pub fn upload_file(
let err = ContextualError::InvalidHttpRequestError(
"Invalid value for 'path' parameter".to_string(),
);
- return Box::pin(create_error_response(
+ return Ok(create_error_response(
&err.to_string(),
StatusCode::BAD_REQUEST,
&return_path,
@@ -186,8 +159,8 @@ pub fn upload_file(
uses_random_route,
&favicon_route,
&css_route,
- default_color_scheme,
- default_color_scheme_dark,
+ &default_color_scheme,
+ &default_color_scheme_dark,
hide_version_footer,
));
}
@@ -196,33 +169,29 @@ pub fn upload_file(
let default_color_scheme = conf.default_color_scheme.clone();
let default_color_scheme_dark = conf.default_color_scheme_dark.clone();
- Box::pin(
- actix_multipart::Multipart::new(req.headers(), payload)
- .map_err(ContextualError::MultipartError)
- .map_ok(move |item| handle_multipart(item, target_dir.clone(), overwrite_files))
- .try_flatten()
- .try_collect::<Vec<_>>()
- .then(move |e| match e {
- Ok(_) => future::ok(
- HttpResponse::SeeOther()
- .append_header((header::LOCATION, return_path))
- .finish(),
- ),
- Err(e) => create_error_response(
- &e.to_string(),
- StatusCode::INTERNAL_SERVER_ERROR,
- &return_path,
- query_params.sort,
- query_params.order,
- uses_random_route,
- &favicon_route,
- &css_route,
- &default_color_scheme,
- &default_color_scheme_dark,
- hide_version_footer,
- ),
- }),
- )
+ match actix_multipart::Multipart::new(req.headers(), payload)
+ .map_err(ContextualError::MultipartError)
+ .and_then(move |field| handle_multipart(field, target_dir.clone(), overwrite_files))
+ .try_collect::<Vec<u64>>()
+ .await
+ {
+ Ok(_) => Ok(HttpResponse::SeeOther()
+ .append_header((header::LOCATION, return_path))
+ .finish()),
+ Err(e) => Ok(create_error_response(
+ &e.to_string(),
+ StatusCode::INTERNAL_SERVER_ERROR,
+ &return_path,
+ query_params.sort,
+ query_params.order,
+ uses_random_route,
+ &favicon_route,
+ &css_route,
+ &default_color_scheme,
+ &default_color_scheme_dark,
+ hide_version_footer,
+ )),
+ }
}
/// Convenience method for creating response errors, if file upload fails.
@@ -239,27 +208,25 @@ fn create_error_response(
default_color_scheme: &str,
default_color_scheme_dark: &str,
hide_version_footer: bool,
-) -> future::Ready<Result<HttpResponse, actix_web::Error>> {
+) -> HttpResponse {
errors::log_error_chain(description.to_string());
- future::ok(
- HttpResponse::BadRequest()
- .content_type("text/html; charset=utf-8")
- .body(
- renderer::render_error(
- description,
- error_code,
- return_path,
- sorting_method,
- sorting_order,
- true,
- !uses_random_route,
- favicon_route,
- css_route,
- default_color_scheme,
- default_color_scheme_dark,
- hide_version_footer,
- )
- .into_string(),
- ),
- )
+ HttpResponse::BadRequest()
+ .content_type("text/html; charset=utf-8")
+ .body(
+ renderer::render_error(
+ description,
+ error_code,
+ return_path,
+ sorting_method,
+ sorting_order,
+ true,
+ !uses_random_route,
+ favicon_route,
+ css_route,
+ default_color_scheme,
+ default_color_scheme_dark,
+ hide_version_footer,
+ )
+ .into_string(),
+ )
}