1
0
mirror of https://github.com/actix/actix-extras.git synced 2024-11-24 07:53:00 +01:00

Optimize multipart handling #634, #769

This commit is contained in:
Nikolay Kim 2019-04-21 15:41:01 -07:00
parent f0789aad05
commit 895e409d57
3 changed files with 90 additions and 62 deletions

View File

@ -1,7 +1,9 @@
# Changes # Changes
## [0.1.0-alpha.1] - 2019-04-xx ## [0.1.0-beta.1] - 2019-04-21
* Do not support nested multipart * Do not support nested multipart
* Split multipart support to separate crate * Split multipart support to separate crate
* Optimize multipart handling #634, #769

View File

@ -1,6 +1,6 @@
[package] [package]
name = "actix-multipart" name = "actix-multipart"
version = "0.1.0-alpha.1" version = "0.1.0-beta.1"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"] authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Multipart support for actix web framework." description = "Multipart support for actix web framework."
readme = "README.md" readme = "README.md"
@ -18,8 +18,8 @@ name = "actix_multipart"
path = "src/lib.rs" path = "src/lib.rs"
[dependencies] [dependencies]
actix-web = "1.0.0-alpha.6" actix-web = "1.0.0-beta.1"
actix-service = "0.3.4" actix-service = "0.3.6"
bytes = "0.4" bytes = "0.4"
derive_more = "0.14" derive_more = "0.14"
httparse = "1.3" httparse = "1.3"
@ -31,4 +31,4 @@ twoway = "0.2"
[dev-dependencies] [dev-dependencies]
actix-rt = "0.2.2" actix-rt = "0.2.2"
actix-http = "0.1.0" actix-http = "0.1.1"

View File

@ -168,7 +168,7 @@ impl InnerMultipart {
match payload.readline() { match payload.readline() {
None => { None => {
if payload.eof { if payload.eof {
Err(MultipartError::Incomplete) Ok(Some(true))
} else { } else {
Ok(None) Ok(None)
} }
@ -201,8 +201,7 @@ impl InnerMultipart {
match payload.readline() { match payload.readline() {
Some(chunk) => { Some(chunk) => {
if chunk.is_empty() { if chunk.is_empty() {
//ValueError("Could not find starting boundary %r" return Err(MultipartError::Boundary);
//% (self._boundary))
} }
if chunk.len() < boundary.len() { if chunk.len() < boundary.len() {
continue; continue;
@ -505,47 +504,73 @@ impl InnerField {
payload: &mut PayloadBuffer, payload: &mut PayloadBuffer,
boundary: &str, boundary: &str,
) -> Poll<Option<Bytes>, MultipartError> { ) -> Poll<Option<Bytes>, MultipartError> {
match payload.read_until(b"\r") { let mut pos = 0;
None => {
if payload.eof { let len = payload.buf.len();
Err(MultipartError::Incomplete) if len == 0 {
return Ok(Async::NotReady);
}
// check boundary
if len > 4 && payload.buf[0] == b'\r' {
let b_len = if &payload.buf[..2] == b"\r\n" && &payload.buf[2..4] == b"--" {
Some(4)
} else if &payload.buf[1..3] == b"--" {
Some(3)
} else {
None
};
if let Some(b_len) = b_len {
let b_size = boundary.len() + b_len;
if len < b_size {
return Ok(Async::NotReady);
} else {
if &payload.buf[b_len..b_size] == boundary.as_bytes() {
// found boundary
payload.buf.split_to(b_size);
return Ok(Async::Ready(None));
} else {
pos = b_size;
}
}
}
}
loop {
return if let Some(idx) = twoway::find_bytes(&payload.buf[pos..], b"\r") {
let cur = pos + idx;
// check if we have enough data for boundary detection
if cur + 4 > len {
if cur > 0 {
Ok(Async::Ready(Some(payload.buf.split_to(cur).freeze())))
} else { } else {
Ok(Async::NotReady) Ok(Async::NotReady)
} }
}
Some(mut chunk) => {
if chunk.len() == 1 {
payload.unprocessed(chunk);
match payload.read_exact(boundary.len() + 4) {
None => {
if payload.eof {
Err(MultipartError::Incomplete)
} else { } else {
Ok(Async::NotReady) // check boundary
} if (&payload.buf[cur..cur + 2] == b"\r\n"
} && &payload.buf[cur + 2..cur + 4] == b"--")
Some(mut chunk) => { || (&payload.buf[cur..cur + 1] == b"\r"
if &chunk[..2] == b"\r\n" && &payload.buf[cur + 1..cur + 3] == b"--")
&& &chunk[2..4] == b"--"
&& &chunk[4..] == boundary.as_bytes()
{ {
payload.unprocessed(chunk); if cur != 0 {
Ok(Async::Ready(None)) // return buffer
Ok(Async::Ready(Some(payload.buf.split_to(cur).freeze())))
} else { } else {
// \r might be part of data stream pos = cur + 1;
let ch = chunk.split_to(1); continue;
payload.unprocessed(chunk);
Ok(Async::Ready(Some(ch)))
} }
} else {
// not boundary
pos = cur + 1;
continue;
} }
} }
} else { } else {
let to = chunk.len() - 1; return Ok(Async::Ready(Some(payload.buf.take().freeze())));
let ch = chunk.split_to(to); };
payload.unprocessed(chunk);
Ok(Async::Ready(Some(ch)))
}
}
} }
} }
@ -555,6 +580,7 @@ impl InnerField {
} }
let result = if let Some(payload) = self.payload.as_ref().unwrap().get_mut(s) { let result = if let Some(payload) = self.payload.as_ref().unwrap().get_mut(s) {
if !self.eof {
let res = if let Some(ref mut len) = self.length { let res = if let Some(ref mut len) = self.length {
InnerField::read_len(payload, len)? InnerField::read_len(payload, len)?
} else { } else {
@ -562,10 +588,12 @@ impl InnerField {
}; };
match res { match res {
Async::NotReady => Async::NotReady, Async::NotReady => return Ok(Async::NotReady),
Async::Ready(Some(bytes)) => Async::Ready(Some(bytes)), Async::Ready(Some(bytes)) => return Ok(Async::Ready(Some(bytes))),
Async::Ready(None) => { Async::Ready(None) => self.eof = true,
self.eof = true; }
}
match payload.readline() { match payload.readline() {
None => Async::Ready(None), None => Async::Ready(None),
Some(line) => { Some(line) => {
@ -575,8 +603,6 @@ impl InnerField {
Async::Ready(None) Async::Ready(None)
} }
} }
}
}
} else { } else {
Async::NotReady Async::NotReady
}; };
@ -704,7 +730,7 @@ impl PayloadBuffer {
} }
/// Read exact number of bytes /// Read exact number of bytes
#[inline] #[cfg(test)]
fn read_exact(&mut self, size: usize) -> Option<Bytes> { fn read_exact(&mut self, size: usize) -> Option<Bytes> {
if size <= self.buf.len() { if size <= self.buf.len() {
Some(self.buf.split_to(size).freeze()) Some(self.buf.split_to(size).freeze())