2020-10-30 21:18:52 +00:00
|
|
|
use multipart::server::Multipart;
|
2020-10-30 16:20:09 +00:00
|
|
|
use rocket::data::{self, FromDataSimple};
|
2020-10-30 18:09:35 +00:00
|
|
|
use rocket::http::Status;
|
|
|
|
use rocket::{Data, Outcome, Outcome::*, Request};
|
2020-10-30 21:18:52 +00:00
|
|
|
use std::fs::{self, File};
|
|
|
|
use std::io::{Cursor, Read, Write};
|
|
|
|
use std::path::Path;
|
2020-10-30 16:20:09 +00:00
|
|
|
|
2020-10-30 21:18:52 +00:00
|
|
|
pub use mime::Mime;
|
2020-10-30 16:20:09 +00:00
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct TextPart {
|
2020-10-30 18:09:35 +00:00
|
|
|
pub key: String,
|
|
|
|
pub value: String,
|
2020-10-30 16:20:09 +00:00
|
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct FilePart {
|
2020-10-30 18:09:35 +00:00
|
|
|
pub name: String,
|
|
|
|
pub path: String,
|
|
|
|
pub filename: String,
|
|
|
|
pub content_type: Option<Mime>,
|
2020-10-30 16:20:09 +00:00
|
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct MultipartDatas {
|
2020-10-30 18:09:35 +00:00
|
|
|
pub texts: Vec<TextPart>,
|
|
|
|
pub files: Vec<FilePart>,
|
2020-10-30 16:20:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl FilePart {
|
2020-10-30 18:09:35 +00:00
|
|
|
pub fn persist(&self, p: &Path) {
|
|
|
|
let s = Path::join(p, &self.filename);
|
|
|
|
fs::copy(Path::new(&self.path), &s).unwrap();
|
|
|
|
}
|
2020-10-30 16:20:09 +00:00
|
|
|
}
|
|
|
|
impl Drop for FilePart {
|
2020-10-30 18:09:35 +00:00
|
|
|
fn drop(&mut self) {
|
|
|
|
fs::remove_file(Path::new(&self.path)).unwrap();
|
|
|
|
}
|
2020-10-30 16:20:09 +00:00
|
|
|
}
|
2020-10-30 21:18:52 +00:00
|
|
|
const TMP_PATH: &str = "/tmp/wasmcloud_upload/";
|
2020-10-30 16:20:09 +00:00
|
|
|
|
|
|
|
impl<'t> FromDataSimple for MultipartDatas {
|
2020-10-30 18:09:35 +00:00
|
|
|
type Error = String;
|
|
|
|
|
|
|
|
fn from_data(request: &Request, data: Data) -> data::Outcome<Self, String> {
|
|
|
|
let ct = request
|
|
|
|
.headers()
|
|
|
|
.get_one("Content-Type")
|
|
|
|
.expect("no content-type");
|
|
|
|
let idx = ct.find("boundary=").expect("no boundary");
|
|
|
|
let boundary = &ct[(idx + "boundary=".len())..];
|
|
|
|
|
|
|
|
let mut d = Vec::new();
|
|
|
|
data.stream_to(&mut d).expect("Unable to read");
|
|
|
|
|
|
|
|
let mut mp = Multipart::with_body(Cursor::new(d), boundary);
|
|
|
|
let mut texts = Vec::new();
|
|
|
|
let mut files = Vec::new();
|
|
|
|
|
|
|
|
let mut buffer = [0u8; 4096];
|
|
|
|
|
|
|
|
let mut err_out: Option<Outcome<_, (Status, _), _>> = None;
|
2020-10-30 21:18:52 +00:00
|
|
|
let temp_folder = format!("{}{}/", TMP_PATH, elfs::next());
|
2020-10-30 18:09:35 +00:00
|
|
|
|
|
|
|
mp.foreach_entry(|entry| {
|
2020-10-30 21:18:52 +00:00
|
|
|
tracing::debug!("part.headers: {:?}", entry.headers);
|
2020-10-30 18:09:35 +00:00
|
|
|
let mut data = entry.data;
|
|
|
|
if entry.headers.filename == None {
|
|
|
|
let mut text_buffer = Vec::new();
|
|
|
|
|
|
|
|
loop {
|
|
|
|
let c = match data.read(&mut buffer) {
|
|
|
|
Ok(c) => c,
|
|
|
|
Err(err) => {
|
|
|
|
err_out =
|
|
|
|
Some(Failure((Status::UnprocessableEntity, format!("{:?}", err))));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
if c == 0 {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
text_buffer.extend_from_slice(&buffer[..c]);
|
2020-10-30 16:20:09 +00:00
|
|
|
}
|
2020-10-30 18:09:35 +00:00
|
|
|
|
|
|
|
let text = match String::from_utf8(text_buffer) {
|
|
|
|
Ok(s) => s,
|
|
|
|
Err(_err) => {
|
|
|
|
err_out = Some(Failure((
|
|
|
|
Status::UnprocessableEntity,
|
|
|
|
": Data can not read as UTF-8".into(),
|
|
|
|
)));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
tracing::debug!("name: {:?}", entry.headers.name);
|
|
|
|
texts.push(TextPart {
|
|
|
|
key: entry.headers.name.to_string(),
|
|
|
|
value: text,
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
let filename = entry.headers.filename.clone().unwrap();
|
2020-10-30 21:18:52 +00:00
|
|
|
if !Path::new(&temp_folder).exists() {
|
|
|
|
fs::create_dir_all(&temp_folder).unwrap();
|
2020-10-30 18:09:35 +00:00
|
|
|
}
|
|
|
|
|
2020-10-30 21:18:52 +00:00
|
|
|
let target_path = Path::join(Path::new(&temp_folder), &filename);
|
2020-10-30 18:09:35 +00:00
|
|
|
|
|
|
|
let mut file = match File::create(&target_path) {
|
|
|
|
Ok(f) => f,
|
|
|
|
Err(err) => {
|
|
|
|
err_out =
|
|
|
|
Some(Failure((Status::InternalServerError, format!("{:?}", err))));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut sum_c = 0u64;
|
|
|
|
|
|
|
|
loop {
|
|
|
|
let c = match data.read(&mut buffer) {
|
|
|
|
Ok(c) => c,
|
|
|
|
Err(err) => {
|
|
|
|
try_delete(&target_path);
|
|
|
|
err_out =
|
|
|
|
Some(Failure((Status::UnprocessableEntity, format!("{:?}", err))));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
if c == 0 {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
sum_c = sum_c + c as u64;
|
|
|
|
|
|
|
|
match file.write(&buffer[..c]) {
|
|
|
|
Ok(_) => (),
|
|
|
|
Err(err) => {
|
|
|
|
try_delete(&target_path);
|
|
|
|
err_out =
|
|
|
|
Some(Failure((Status::InternalServerError, format!("{:?}", err))));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
tracing::debug!("filename: {:?}", entry.headers.name);
|
|
|
|
files.push(FilePart {
|
|
|
|
name: entry.headers.name.to_string(),
|
2020-10-30 21:18:52 +00:00
|
|
|
path: format!("{}{}", temp_folder, filename),
|
2020-10-30 18:09:35 +00:00
|
|
|
filename: entry.headers.filename.clone().unwrap(),
|
|
|
|
content_type: entry.headers.content_type.clone(),
|
|
|
|
})
|
2020-10-30 16:20:09 +00:00
|
|
|
}
|
|
|
|
})
|
2020-10-30 18:09:35 +00:00
|
|
|
.unwrap();
|
|
|
|
if let Some(failed) = err_out {
|
|
|
|
return failed;
|
|
|
|
} else {
|
|
|
|
let v = MultipartDatas {
|
|
|
|
texts: texts,
|
|
|
|
files: files,
|
|
|
|
};
|
|
|
|
return Outcome::Success(v);
|
|
|
|
}
|
2020-10-30 16:20:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn try_delete<P: AsRef<Path>>(path: P) {
|
|
|
|
if fs::remove_file(path.as_ref()).is_err() {}
|
|
|
|
}
|