From 25a368fd98a7ed71a28ae937493e3673f60fa176 Mon Sep 17 00:00:00 2001 From: Pierre HUBERT Date: Sat, 20 Jun 2020 11:37:29 +0200 Subject: [PATCH] Handle multipart requests --- Cargo.lock | 1 + Cargo.toml | 3 ++- src/controllers/server.rs | 38 ++++++++++++++++++++++++++++++-- src/data/http_request_handler.rs | 29 +++++++++--------------- 4 files changed, 50 insertions(+), 21 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7f47935..7d77538 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -529,6 +529,7 @@ dependencies = [ "actix-multipart", "actix-rt", "actix-web", + "bytes", "chrono", "encoding_rs", "futures", diff --git a/Cargo.toml b/Cargo.toml index d004a55..a760607 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,4 +20,5 @@ percent-encoding = "2.1.0" mailchecker = "3.3.6" sha1 = "0.6.0" rand = "0.7.3" -chrono = "0.4.11" \ No newline at end of file +chrono = "0.4.11" +bytes = "0.5.4" \ No newline at end of file diff --git a/src/controllers/server.rs b/src/controllers/server.rs index 5a07c93..166f4e5 100644 --- a/src/controllers/server.rs +++ b/src/controllers/server.rs @@ -3,8 +3,9 @@ use std::pin::Pin; use actix_web::{App, FromRequest, http, HttpMessage, HttpRequest, HttpResponse, HttpServer, web}; use actix_web::dev::{Decompress, Payload, PayloadStream}; -use actix_web::error::{ErrorBadRequest, PayloadError}; +use actix_web::error::{ErrorBadRequest, ErrorInternalServerError, PayloadError}; use actix_web::web::{Bytes, BytesMut}; +use bytes::{Buf, BufMut}; use encoding_rs::UTF_8; use futures::{FutureExt, Stream, StreamExt}; use futures::future::LocalBoxFuture; @@ -133,18 +134,51 @@ impl FromRequest for CustomRequest { // Add the value to the body body_args.insert( percent_decode_str(args[0]).decode_utf8_lossy().to_string(), - RequestValue::string(percent_decode_str(args[1]).decode_utf8_lossy().to_string()), + RequestValue::String(percent_decode_str(args[1]).decode_utf8_lossy().to_string()), ); } } } + // Process "multipart/form-data" request else if req.content_type().starts_with("multipart/form-data") { let mut req = actix_multipart::Multipart::new(req.headers(), payload); + // Process the list of arguments while let Some(el) = req.next().await { let mut field = el?; + + let content_type = field.content_disposition().ok_or( + ErrorInternalServerError("F1"))?; + let name = content_type.get_name().ok_or( + ErrorInternalServerError("Missing field name!"))?; + + // Handle file upload + if content_type.get_filename().is_some() && name.eq("file") { + let filename = content_type.get_filename().unwrap(); + let mut buf = BytesMut::new(); + + while let Some(chunk) = field.next().await { + let data = chunk.unwrap(); + buf.put(data); + } + + body_args.insert(name.to_string(), + RequestValue::File(filename.to_string(), buf.to_bytes())); + } + + // It is a simple field + else { + let mut content = String::new(); + + // Get content + while let Some(chunk) = field.next().await { + content = format!("{}{}", content, String::from_utf8_lossy(chunk?.bytes())); + } + + body_args.insert(name.to_string(), RequestValue::String(content)); + } } } diff --git a/src/data/http_request_handler.rs b/src/data/http_request_handler.rs index c7dea90..d33d273 100644 --- a/src/data/http_request_handler.rs +++ b/src/data/http_request_handler.rs @@ -14,23 +14,16 @@ use crate::data::error::{ExecError, ResultBoxError}; use crate::data::user::UserID; use crate::helpers::{account_helper, api_helper, user_helper, conversations_helper}; use crate::utils::virtual_directories_utils::check_virtual_directory; +use bytes::Bytes; /// Http request handler /// /// @author Pierre Hubert /// Single request body value -pub struct RequestValue { - pub string: Option -} - -impl RequestValue { - /// Build a string value - pub fn string(s: String) -> RequestValue { - RequestValue { - string: Some(s) - } - } +pub enum RequestValue { + String(String), + File(String, Bytes), } #[derive(Serialize)] @@ -187,19 +180,19 @@ impl HttpRequestHandler { -> ResultBoxError { let param = self.post_parameter(name)?; - match (¶m.string, required) { - (None, true) => - Err(self.bad_request(format!("'{}' is not a string!", name)).unwrap_err()), - - (None, false) => Ok(String::new()), - - (Some(s), _) => { + match (¶m, required) { + (RequestValue::String(s), _) => { if s.len() >= min_length { Ok(s.to_string()) } else { Err(self.bad_request(format!("'{}' is too short!", name)).unwrap_err()) } } + + (_, false) => Ok(String::new()), + + (_, true) => + Err(self.bad_request(format!("'{}' is not a string!", name)).unwrap_err()), } }