diff options
Diffstat (limited to 'src/api/s3_delete.rs')
-rw-r--r-- | src/api/s3_delete.rs | 169 |
1 files changed, 100 insertions, 69 deletions
diff --git a/src/api/s3_delete.rs b/src/api/s3_delete.rs index 001eb162..e77ab314 100644 --- a/src/api/s3_delete.rs +++ b/src/api/s3_delete.rs @@ -1,5 +1,5 @@ -use std::sync::Arc; use std::fmt::Write; +use std::sync::Arc; use hyper::{Body, Request, Response}; @@ -9,10 +9,14 @@ use garage_util::error::Error; use garage_core::garage::Garage; use garage_core::object_table::*; -use crate::http_util::*; use crate::encoding::*; +use crate::http_util::*; -async fn handle_delete_internal(garage: &Garage, bucket: &str, key: &str) -> Result<(UUID, UUID), Error> { +async fn handle_delete_internal( + garage: &Garage, + bucket: &str, + key: &str, +) -> Result<(UUID, UUID), Error> { let object = match garage .object_table .get(&bucket.to_string(), &key.to_string()) @@ -32,16 +36,16 @@ async fn handle_delete_internal(garage: &Garage, bucket: &str, key: &str) -> Res let mut must_delete = None; let mut timestamp = now_msec(); for v in interesting_versions { - if v.timestamp + 1 > timestamp || must_delete.is_none() { - must_delete = Some(v.uuid); - } + if v.timestamp + 1 > timestamp || must_delete.is_none() { + must_delete = Some(v.uuid); + } timestamp = std::cmp::max(timestamp, v.timestamp + 1); } - let deleted_version = match must_delete { - None => return Err(Error::NotFound), - Some(v) => v, - }; + let deleted_version = match must_delete { + None => return Err(Error::NotFound), + Some(v) => v, + }; let version_uuid = gen_uuid(); @@ -62,8 +66,13 @@ async fn handle_delete_internal(garage: &Garage, bucket: &str, key: &str) -> Res return Ok((deleted_version, version_uuid)); } -pub async fn handle_delete(garage: Arc<Garage>, bucket: &str, key: &str) -> Result<Response<BodyType>, Error> { - let (_deleted_version, delete_marker_version) = handle_delete_internal(&garage, bucket, key).await?; +pub async fn handle_delete( + garage: Arc<Garage>, + bucket: &str, + key: &str, +) -> Result<Response<BodyType>, Error> { + let (_deleted_version, delete_marker_version) = + handle_delete_internal(&garage, bucket, key).await?; Ok(Response::builder() .header("x-amz-version-id", hex::encode(delete_marker_version)) @@ -71,76 +80,98 @@ pub async fn handle_delete(garage: Arc<Garage>, bucket: &str, key: &str) -> Resu .unwrap()) } -pub async fn handle_delete_objects(garage: Arc<Garage>, bucket: &str, req: Request<Body>) -> Result<Response<BodyType>, Error> { - let body = hyper::body::to_bytes(req.into_body()).await?; - let cmd_xml = roxmltree::Document::parse(&std::str::from_utf8(&body)?)?; - let cmd = parse_delete_objects_xml(&cmd_xml) - .map_err(|e| Error::BadRequest(format!("Invald delete XML query: {}", e)))?; - - let mut retxml = String::new(); +pub async fn handle_delete_objects( + garage: Arc<Garage>, + bucket: &str, + req: Request<Body>, +) -> Result<Response<BodyType>, Error> { + let body = hyper::body::to_bytes(req.into_body()).await?; + let cmd_xml = roxmltree::Document::parse(&std::str::from_utf8(&body)?)?; + let cmd = parse_delete_objects_xml(&cmd_xml) + .map_err(|e| Error::BadRequest(format!("Invald delete XML query: {}", e)))?; + + let mut retxml = String::new(); writeln!(&mut retxml, r#"<?xml version="1.0" encoding="UTF-8"?>"#).unwrap(); writeln!(&mut retxml, "<DeleteObjectsOutput>").unwrap(); - for obj in cmd.objects.iter() { - match handle_delete_internal(&garage, bucket, &obj.key).await { - Ok((deleted_version, delete_marker_version)) => { - writeln!(&mut retxml, "\t<Deleted>").unwrap(); - writeln!(&mut retxml, "\t\t<Key>{}</Key>", obj.key).unwrap(); - writeln!(&mut retxml, "\t\t<VersionId>{}</VersionId>", hex::encode(deleted_version)).unwrap(); - writeln!(&mut retxml, "\t\t<DeleteMarkerVersionId>{}</DeleteMarkerVersionId>", hex::encode(delete_marker_version)).unwrap(); - writeln!(&mut retxml, "\t</Deleted>").unwrap(); - } - Err(e) => { - writeln!(&mut retxml, "\t<Error>").unwrap(); - writeln!(&mut retxml, "\t\t<Code>{}</Code>", e.http_status_code()).unwrap(); - writeln!(&mut retxml, "\t\t<Key>{}</Key>", obj.key).unwrap(); - writeln!(&mut retxml, "\t\t<Message>{}</Message>", xml_escape(&format!("{}", e))).unwrap(); - writeln!(&mut retxml, "\t</Error>").unwrap(); - } - } - } + for obj in cmd.objects.iter() { + match handle_delete_internal(&garage, bucket, &obj.key).await { + Ok((deleted_version, delete_marker_version)) => { + writeln!(&mut retxml, "\t<Deleted>").unwrap(); + writeln!(&mut retxml, "\t\t<Key>{}</Key>", obj.key).unwrap(); + writeln!( + &mut retxml, + "\t\t<VersionId>{}</VersionId>", + hex::encode(deleted_version) + ) + .unwrap(); + writeln!( + &mut retxml, + "\t\t<DeleteMarkerVersionId>{}</DeleteMarkerVersionId>", + hex::encode(delete_marker_version) + ) + .unwrap(); + writeln!(&mut retxml, "\t</Deleted>").unwrap(); + } + Err(e) => { + writeln!(&mut retxml, "\t<Error>").unwrap(); + writeln!(&mut retxml, "\t\t<Code>{}</Code>", e.http_status_code()).unwrap(); + writeln!(&mut retxml, "\t\t<Key>{}</Key>", obj.key).unwrap(); + writeln!( + &mut retxml, + "\t\t<Message>{}</Message>", + xml_escape(&format!("{}", e)) + ) + .unwrap(); + writeln!(&mut retxml, "\t</Error>").unwrap(); + } + } + } writeln!(&mut retxml, "</DeleteObjectsOutput>").unwrap(); - Ok(Response::new(Box::new(BytesBody::from(retxml.into_bytes())))) + Ok(Response::new(Box::new(BytesBody::from( + retxml.into_bytes(), + )))) } struct DeleteRequest { - objects: Vec<DeleteObject>, + objects: Vec<DeleteObject>, } struct DeleteObject { - key: String, + key: String, } fn parse_delete_objects_xml(xml: &roxmltree::Document) -> Result<DeleteRequest, String> { - let mut ret = DeleteRequest{objects: vec![]}; - - let root = xml.root(); - let delete = match root.first_child() { - Some(del) => del, - None => return Err(format!("Delete tag not found")), - }; - if !delete.has_tag_name("Delete") { - return Err(format!("Invalid root tag: {:?}", root)); - } - - for item in delete.children() { - if item.has_tag_name("Object") { - if let Some(key) = item.children().find(|e| e.has_tag_name("Key")) { - if let Some(key_str) = key.text() { - ret.objects.push(DeleteObject{key: key_str.to_string()}); - } else { - return Err(format!("No text for key: {:?}", key)); - } - } else { - return Err(format!("No delete key for item: {:?}", item)); - } - } else { - return Err(format!("Invalid delete item: {:?}", item)); - } - } - - Ok(ret) -} + let mut ret = DeleteRequest { objects: vec![] }; + + let root = xml.root(); + let delete = match root.first_child() { + Some(del) => del, + None => return Err(format!("Delete tag not found")), + }; + if !delete.has_tag_name("Delete") { + return Err(format!("Invalid root tag: {:?}", root)); + } + for item in delete.children() { + if item.has_tag_name("Object") { + if let Some(key) = item.children().find(|e| e.has_tag_name("Key")) { + if let Some(key_str) = key.text() { + ret.objects.push(DeleteObject { + key: key_str.to_string(), + }); + } else { + return Err(format!("No text for key: {:?}", key)); + } + } else { + return Err(format!("No delete key for item: {:?}", item)); + } + } else { + return Err(format!("Invalid delete item: {:?}", item)); + } + } + + Ok(ret) +} |