fix: payload too large, failed binary content type detection
This commit is contained in:
parent
3da9f1117e
commit
24c4307ce5
|
@ -424,7 +424,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "datatrash"
|
name = "datatrash"
|
||||||
version = "2.0.4"
|
version = "2.0.5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"actix-files",
|
"actix-files",
|
||||||
"actix-governor",
|
"actix-governor",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "datatrash"
|
name = "datatrash"
|
||||||
version = "2.0.4"
|
version = "2.0.5"
|
||||||
authors = ["neri"]
|
authors = ["neri"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
|
|
|
@ -3,11 +3,7 @@ use actix_multipart::{Field, Multipart};
|
||||||
use actix_web::{error, http::header::DispositionParam, Error};
|
use actix_web::{error, http::header::DispositionParam, Error};
|
||||||
use futures_util::{StreamExt, TryStreamExt};
|
use futures_util::{StreamExt, TryStreamExt};
|
||||||
use mime::{Mime, APPLICATION_OCTET_STREAM, TEXT_PLAIN};
|
use mime::{Mime, APPLICATION_OCTET_STREAM, TEXT_PLAIN};
|
||||||
use std::{
|
use std::{cmp::min, io::ErrorKind, path::Path};
|
||||||
cmp::{max, min},
|
|
||||||
io::ErrorKind,
|
|
||||||
path::Path,
|
|
||||||
};
|
|
||||||
use time::{Duration, OffsetDateTime};
|
use time::{Duration, OffsetDateTime};
|
||||||
use tokio::{
|
use tokio::{
|
||||||
fs::{self, File},
|
fs::{self, File},
|
||||||
|
@ -72,7 +68,8 @@ pub(crate) async fn parse_multipart_inner(
|
||||||
content_type = Some(
|
content_type = Some(
|
||||||
mime.filter(|mime| *mime != APPLICATION_OCTET_STREAM)
|
mime.filter(|mime| *mime != APPLICATION_OCTET_STREAM)
|
||||||
.map(mime_relations::get_alias)
|
.map(mime_relations::get_alias)
|
||||||
.unwrap_or_else(|| get_content_type(&first_bytes)),
|
.or_else(|| get_content_type(&first_bytes))
|
||||||
|
.unwrap_or(APPLICATION_OCTET_STREAM),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
"text" => {
|
"text" => {
|
||||||
|
@ -81,7 +78,7 @@ pub(crate) async fn parse_multipart_inner(
|
||||||
}
|
}
|
||||||
let first_bytes;
|
let first_bytes;
|
||||||
(size, first_bytes) = create_file(file_path, field, config.max_file_size).await?;
|
(size, first_bytes) = create_file(file_path, field, config.max_file_size).await?;
|
||||||
content_type = Some(get_content_type(&first_bytes));
|
content_type = Some(get_content_type(&first_bytes).unwrap_or(TEXT_PLAIN));
|
||||||
}
|
}
|
||||||
"delete_on_download" => {
|
"delete_on_download" => {
|
||||||
delete_on_download = parse_string(&name, &mut field).await? != "false";
|
delete_on_download = parse_string(&name, &mut field).await? != "false";
|
||||||
|
@ -193,16 +190,12 @@ async fn write_to_file(
|
||||||
let mut written_bytes: u64 = 0;
|
let mut written_bytes: u64 = 0;
|
||||||
while let Some(chunk) = field.next().await {
|
while let Some(chunk) = field.next().await {
|
||||||
let chunk = chunk.map_err(error::ErrorBadRequest)?;
|
let chunk = chunk.map_err(error::ErrorBadRequest)?;
|
||||||
let remaining_first_bytes = min(max(0, 2048 - written_bytes) as usize, chunk.len());
|
|
||||||
first_bytes.extend_from_slice(&chunk[0..remaining_first_bytes]);
|
|
||||||
written_bytes += chunk.len() as u64;
|
written_bytes += chunk.len() as u64;
|
||||||
if let Some(max_size) = max_size {
|
validate_max_size(written_bytes, max_size)?;
|
||||||
if written_bytes > max_size {
|
|
||||||
return Err(error::ErrorBadRequest(format!(
|
let remaining_first_bytes = min(2048 - first_bytes.len(), chunk.len());
|
||||||
"exceeded maximum file size of {max_size} bytes"
|
first_bytes.extend_from_slice(&chunk[..remaining_first_bytes]);
|
||||||
)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
file.write_all(&chunk).await.map_err(|write_err| {
|
file.write_all(&chunk).await.map_err(|write_err| {
|
||||||
log::error!("could not write file {:?}", write_err);
|
log::error!("could not write file {:?}", write_err);
|
||||||
error::ErrorInternalServerError("could not write file")
|
error::ErrorInternalServerError("could not write file")
|
||||||
|
@ -211,6 +204,17 @@ async fn write_to_file(
|
||||||
Ok((written_bytes, first_bytes))
|
Ok((written_bytes, first_bytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn validate_max_size(written_bytes: u64, max_size: Option<u64>) -> Result<(), Error> {
|
||||||
|
if let Some(max_size) = max_size {
|
||||||
|
if written_bytes > max_size {
|
||||||
|
return Err(error::ErrorPayloadTooLarge(format!(
|
||||||
|
"exceeded maximum file size of {max_size} bytes"
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn get_file_metadata(field: &actix_multipart::Field) -> (Option<Mime>, Option<String>) {
|
fn get_file_metadata(field: &actix_multipart::Field) -> (Option<Mime>, Option<String>) {
|
||||||
let mime = field.content_type().cloned();
|
let mime = field.content_type().cloned();
|
||||||
let filename = field
|
let filename = field
|
||||||
|
@ -224,9 +228,6 @@ fn get_file_metadata(field: &actix_multipart::Field) -> (Option<Mime>, Option<St
|
||||||
(mime, filename)
|
(mime, filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_content_type(bytes: &[u8]) -> Mime {
|
fn get_content_type(bytes: &[u8]) -> Option<Mime> {
|
||||||
tree_magic_mini::from_u8(bytes)
|
tree_magic_mini::from_u8(bytes).parse().ok()
|
||||||
.parse()
|
|
||||||
.ok()
|
|
||||||
.unwrap_or(TEXT_PLAIN)
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue