From 087776bda1cb52d81bd52760453811916cf72954 Mon Sep 17 00:00:00 2001 From: Joonas Koivunen Date: Wed, 8 Jul 2020 19:17:58 +0300 Subject: [PATCH] refactor: try_only_named_multipart out of put_block --- http/src/v0/block.rs | 70 ++++++++++++++++++++++++-------------------- 1 file changed, 39 insertions(+), 31 deletions(-) diff --git a/http/src/v0/block.rs b/http/src/v0/block.rs index 32d2dd52..0a673be9 100644 --- a/http/src/v0/block.rs +++ b/http/src/v0/block.rs @@ -96,7 +96,37 @@ async fn inner_put( .get_param("boundary") .map(|v| v.to_string()) .ok_or_else(|| StringError::from("missing 'boundary' on content-type"))?; - let mut stream = MultipartStream::new(boundary, body.map_ok(|mut buf| buf.to_bytes())); + + let buffer = try_only_named_multipart(&["data", "file"], 1024 * 1024, boundary, body).await?; + + // bad thing about Box<[u8]>: converting to it forces an reallocation + let data = buffer.into_boxed_slice(); + + let digest = opts.digest()?(&data); + let cid = Cid::new(opts.version()?, opts.format()?, digest).map_err(StringError::from)?; + + let size = data.len(); + let key = cid.to_string(); + + let block = ipfs::Block { cid, data }; + + ipfs.put_block(block).await.map_err(StringError::from)?; + + Ok(reply::json(&serde_json::json!({ + "Key": key, + "Size": size, + }))) +} + +pub async fn try_only_named_multipart<'a>( + allowed_names: &'a [impl AsRef + 'a], + size_limit: usize, + boundary: String, + st: impl Stream> + Unpin + 'a, +) -> Result, Rejection> { + use bytes::Bytes; + let mut stream = + MultipartStream::new(Bytes::from(boundary), st.map_ok(|mut buf| buf.to_bytes())); // store the first good field here; optimally this would just be an Option but couldn't figure // out a way to handle the "field matched", "field not matched" cases while supporting empty @@ -114,21 +144,15 @@ async fn inner_put( .name() .map_err(|_| StringError::from("invalid field name"))?; - let mut target = match name { - "data" => Some(&mut buffer), - name @ "file" => { - log::warn!("block/put: processing part with deprecated name {:?}", name); - Some(&mut buffer) - } - other => { - log::warn!("block/put: ignoring part named {:?}", other); - None - } + let mut target = if allowed_names.iter().any(|s| s.as_ref() == name) { + Some(&mut buffer) + } else { + None }; if matched { // per spec: only one block should be uploaded at once - return Err(StringError::from("multiple blocks (blocks/put only accepts one)").into()); + return Err(StringError::from("multiple blocks (expecting at most one)").into()); } matched = target.is_some(); @@ -140,10 +164,10 @@ async fn inner_put( match (next, target.as_mut()) { (Some(bytes), Some(target)) => { - if target.len() + bytes.len() > 1024 * 1024 { + if target.len() + bytes.len() > size_limit { return Err(StringError::from("block is too large").into()); } else if target.is_empty() { - target.reserve(1024 * 1024); + target.reserve(size_limit); } target.extend_from_slice(bytes.as_ref()); } @@ -164,23 +188,7 @@ async fn inner_put( return Err(StringError::from("missing field: \"data\" (or \"file\")").into()); } - // bad thing about Box<[u8]>: converting to it forces an reallocation - let data = buffer.into_boxed_slice(); - - let digest = opts.digest()?(&data); - let cid = Cid::new(opts.version()?, opts.format()?, digest).map_err(StringError::from)?; - - let size = data.len(); - let key = cid.to_string(); - - let block = ipfs::Block { cid, data }; - - ipfs.put_block(block).await.map_err(StringError::from)?; - - Ok(reply::json(&serde_json::json!({ - "Key": key, - "Size": size, - }))) + Ok(buffer) } #[derive(Debug, Serialize)]