aboutsummaryrefslogtreecommitdiffstats
path: root/src/file_upload.rs
diff options
context:
space:
mode:
authorequal-l2 <eng.equall2@gmail.com>2020-07-17 20:26:31 +0000
committerequal-l2 <eng.equall2@gmail.com>2020-07-21 16:53:04 +0000
commitdde31288bdfb00cf325ae1f669d5fc098cee98ea (patch)
tree28585f88d0ff0c29591ab8d4bbd38815b842f02b /src/file_upload.rs
parentMerge pull request #331 from svenstaro/dependabot/cargo/select-0.5.0 (diff)
downloadminiserve-dde31288bdfb00cf325ae1f669d5fc098cee98ea.tar.gz
miniserve-dde31288bdfb00cf325ae1f669d5fc098cee98ea.zip
Update to actix 2 and futures 0.3
Diffstat (limited to 'src/file_upload.rs')
-rw-r--r--src/file_upload.rs139
1 files changed, 65 insertions, 74 deletions
diff --git a/src/file_upload.rs b/src/file_upload.rs
index f9bf002..136bd45 100644
--- a/src/file_upload.rs
+++ b/src/file_upload.rs
@@ -1,13 +1,12 @@
use actix_web::{
- dev,
http::{header, StatusCode},
- multipart, FutureResponse, HttpMessage, HttpRequest, HttpResponse,
+ HttpRequest, HttpResponse,
};
-use futures::{future, future::FutureResult, Future, Stream};
+use futures::{future, Future, FutureExt, Stream, TryStreamExt};
use std::{
- fs,
io::Write,
path::{Component, PathBuf},
+ pin::Pin,
};
use crate::errors::{self, ContextualError};
@@ -17,12 +16,12 @@ use crate::themes::ColorScheme;
/// Create future to save file.
fn save_file(
- field: multipart::Field<dev::Payload>,
+ field: actix_multipart::Field,
file_path: PathBuf,
overwrite_files: bool,
-) -> Box<dyn Future<Item = i64, Error = ContextualError>> {
+) -> Pin<Box<dyn Future<Output = Result<i64, ContextualError>>>> {
if !overwrite_files && file_path.exists() {
- return Box::new(future::err(ContextualError::CustomError(
+ return Box::pin(future::err(ContextualError::CustomError(
"File already exists, and the overwrite_files option has not been set".to_string(),
)));
}
@@ -30,85 +29,75 @@ fn save_file(
let mut file = match std::fs::File::create(&file_path) {
Ok(file) => file,
Err(e) => {
- return Box::new(future::err(ContextualError::IOError(
+ return Box::pin(future::err(ContextualError::IOError(
format!("Failed to create {}", file_path.display()),
e,
)));
}
};
- Box::new(
+ Box::pin(
field
.map_err(ContextualError::MultipartError)
- .fold(0i64, move |acc, bytes| {
+ .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::result(rt)
+ future::ready(rt)
}),
)
}
/// Create new future to handle file as multipart data.
fn handle_multipart(
- item: multipart::MultipartItem<dev::Payload>,
+ field: actix_multipart::Field,
mut file_path: PathBuf,
overwrite_files: bool,
-) -> Box<dyn Stream<Item = i64, Error = ContextualError>> {
- match item {
- multipart::MultipartItem::Field(field) => {
- let filename = field
- .headers()
- .get(header::CONTENT_DISPOSITION)
+) -> Pin<Box<dyn Stream<Item = Result<i64, 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)
- .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::new(future::err(e).into_stream());
- match filename {
- Ok(f) => {
- match 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(),
- ));
- }
+ .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(),
+ ));
}
- file_path = file_path.join(f);
- Box::new(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(),
- )),
+ 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())
}
- multipart::MultipartItem::Nested(mp) => Box::new(
- mp.map_err(ContextualError::MultipartError)
- .map(move |item| handle_multipart(item, file_path.clone(), overwrite_files))
- .flatten(),
- ),
+ Err(e) => err(e(
+ "HTTP header".to_string(),
+ "Failed to retrieve the name of the file to upload".to_string(),
+ )),
}
}
@@ -118,17 +107,19 @@ fn handle_multipart(
/// invalid.
/// This method returns future.
pub fn upload_file(
- req: &HttpRequest<crate::MiniserveConfig>,
+ req: HttpRequest,
+ payload: actix_web::web::Payload,
default_color_scheme: ColorScheme,
uses_random_route: bool,
-) -> FutureResponse<HttpResponse> {
+) -> Pin<Box<dyn Future<Output = 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()
} else {
"/".to_string()
};
- let query_params = listing::extract_query_parameters(req);
+ let query_params = listing::extract_query_parameters(&req);
let color_scheme = query_params.theme.unwrap_or(default_color_scheme);
let upload_path = match query_params.path.clone() {
Some(path) => match path.strip_prefix(Component::RootDir) {
@@ -139,7 +130,7 @@ pub fn upload_file(
let err = ContextualError::InvalidHTTPRequestError(
"Missing query parameter 'path'".to_string(),
);
- return Box::new(create_error_response(
+ return Box::pin(create_error_response(
&err.to_string(),
StatusCode::BAD_REQUEST,
&return_path,
@@ -152,14 +143,14 @@ pub fn upload_file(
}
};
- let app_root_dir = match req.state().path.canonicalize() {
+ let app_root_dir = match conf.path.canonicalize() {
Ok(dir) => dir,
Err(e) => {
let err = ContextualError::IOError(
"Failed to resolve path served by miniserve".to_string(),
e,
);
- return Box::new(create_error_response(
+ return Box::pin(create_error_response(
&err.to_string(),
StatusCode::INTERNAL_SERVER_ERROR,
&return_path,
@@ -179,7 +170,7 @@ pub fn upload_file(
let err = ContextualError::InvalidHTTPRequestError(
"Invalid value for 'path' parameter".to_string(),
);
- return Box::new(create_error_response(
+ return Box::pin(create_error_response(
&err.to_string(),
StatusCode::BAD_REQUEST,
&return_path,
@@ -191,13 +182,13 @@ pub fn upload_file(
));
}
};
- let overwrite_files = req.state().overwrite_files;
- Box::new(
- req.multipart()
+ let overwrite_files = conf.overwrite_files;
+ Box::pin(
+ actix_multipart::Multipart::new(req.headers(), payload)
.map_err(ContextualError::MultipartError)
- .map(move |item| handle_multipart(item, target_dir.clone(), overwrite_files))
- .flatten()
- .collect()
+ .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()
@@ -229,7 +220,7 @@ fn create_error_response(
color_scheme: ColorScheme,
default_color_scheme: ColorScheme,
uses_random_route: bool,
-) -> FutureResult<HttpResponse, actix_web::error::Error> {
+) -> future::Ready<Result<HttpResponse, actix_web::Error>> {
errors::log_error_chain(description.to_string());
future::ok(
HttpResponse::BadRequest()