use crate::enums::char_set::CharSet;
use crate::enums::http_error::HttpError;
use crate::structures::http_body_part::HttpBodyPart;
use crate::utils::parse_util;
use async_regex::{CRLF, read_until_pattern_async};
use futures::AsyncBufRead;
use std::io::{self, Write};
pub const MULTIPART_EPILOGUE_MARKER: &[u8] = &[45, 45, 13, 10];
#[derive(Clone)]
pub struct HttpMultipartBody {
pub parts: Vec<HttpBodyPart>,
pub boundary: String,
pub subtype: String,
pub preamble: Vec<u8>,
pub epilogue: Vec<u8>,
}
impl HttpMultipartBody {
pub fn new(parts: Vec<HttpBodyPart>, boundary: String) -> Self {
HttpMultipartBody {
parts,
boundary,
subtype: "form-data".to_string(),
preamble: vec![],
epilogue: vec![],
}
}
pub async fn read(
mut reader: impl AsyncBufRead + Unpin,
boundary: &str,
subtype: &str,
) -> Result<HttpMultipartBody, HttpError> {
let delimiter = format!("\r\n--{}", boundary);
let mut buf = Vec::new();
let (_, len) = read_until_pattern_async(&mut reader, &delimiter, &mut buf).await?;
let preamble = buf[..len - delimiter.len()].to_vec();
let mut epilogue: Vec<u8> = vec![];
buf.truncate(0);
let mut parts: Vec<HttpBodyPart> = Vec::new();
let (_, mut len) = read_until_pattern_async(&mut reader, &delimiter, &mut buf).await?;
loop {
if len > 1 && &buf[..MULTIPART_EPILOGUE_MARKER.len()] == MULTIPART_EPILOGUE_MARKER {
epilogue = buf[MULTIPART_EPILOGUE_MARKER.len()..].to_vec();
break;
}
if len == 0 {
break;
}
parts.push(HttpBodyPart::read(&mut &buf[2..len - delimiter.len()]).await?);
buf.truncate(0);
(_, len) = read_until_pattern_async(&mut reader, &delimiter, &mut buf).await?;
}
Ok(HttpMultipartBody {
parts,
boundary: boundary.to_string(),
subtype: subtype.to_string(),
preamble,
epilogue,
})
}
pub fn write_to<W: Write>(&self, mut writer: W) -> io::Result<()> {
for part in self.parts.iter() {
writer.write_all(b"--")?;
writer.write_all(self.boundary.as_bytes())?;
writer.write_all(CRLF)?;
part.write_to(&mut writer)?;
writer.write_all(CRLF)?;
}
writer.write_all(b"--")?;
writer.write_all(self.boundary.as_bytes())?;
writer.write_all(b"--")?;
writer.write_all(CRLF)?;
Ok(())
}
pub fn as_bytes(&self) -> Vec<u8> {
let boundary_overhead = self.boundary.len() + 6;
let estimated_capacity = self
.parts
.iter()
.map(|p| p.content.len() + 128)
.sum::<usize>()
+ boundary_overhead * (self.parts.len() + 1)
+ 4;
let mut result = Vec::with_capacity(estimated_capacity);
self.write_to(&mut result).expect("Vec write cannot fail");
result
}
pub fn to_string(&self, charset: CharSet) -> Result<String, HttpError> {
let bytes = self.as_bytes();
let str = parse_util::bytes_to_string(&bytes, charset)?;
Ok(str.to_string())
}
}