diff --git a/Cargo.toml b/Cargo.toml index d34192a..b566e35 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ branch = "master" rocket = "0.4.5" mime = "0.3.12" multipart = { version = "0.17", default-features = false, features = ["server"] } +backoff = "0.3" [dev-dependencies] rocket-include-static-resources = "0.9" diff --git a/src/multipart_form_data.rs b/src/multipart_form_data.rs index 68ae861..7743365 100644 --- a/src/multipart_form_data.rs +++ b/src/multipart_form_data.rs @@ -47,7 +47,7 @@ impl MultipartFormData { options.allowed_fields.sort_by_key(|e| e.field_name); - let mut multipart = Multipart::with_body(data.open(), boundary); + let mut multipart = Multipart::with_body(WouldBlockFreeRead(data.open()), boundary); let mut files: HashMap, Vec> = HashMap::new(); let mut raw: HashMap, Vec> = HashMap::new(); @@ -399,3 +399,37 @@ impl Drop for MultipartFormData { fn try_delete>(path: P) { if fs::remove_file(path.as_ref()).is_err() {} } + +// Work around an issue with non blocking I/O which is hard to fix all the through +// rocket + hyper-0.10. Rocket 0.5 seems not to suffer from that defect, probably due to some +// special treatment related to async I/O. +struct WouldBlockFreeRead(R); + +const DELAY_BEFORE_WOULD_BLOCK_RETRY: std::time::Duration = std::time::Duration::from_millis(200); + +impl Read for WouldBlockFreeRead { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + use backoff::retry; + let res = retry( + backoff::backoff::Constant::new(DELAY_BEFORE_WOULD_BLOCK_RETRY), + || -> Result> { + match self.0.read(buf) { + Ok(read) => Ok(read), + Err( + err + @ std::io::Error { + .. + }, + ) if err.kind() == std::io::ErrorKind::WouldBlock => { + Err(backoff::Error::Transient(err)) + } + Err(err) => Err(backoff::Error::Permanent(err)), + } + }, + ); + match res { + Ok(res) => Ok(res), + Err(backoff::Error::Permanent(err)) | Err(backoff::Error::Transient(err)) => Err(err), + } + } +}