aboutsummaryrefslogtreecommitdiff
path: root/src/api/s3_list.rs
diff options
context:
space:
mode:
authorAlex Auvolat <alex@adnab.me>2021-05-03 22:45:42 +0200
committerAlex Auvolat <alex@adnab.me>2021-05-06 22:37:15 +0200
commit6ccffc316228bb056fa135cc5fceb2ed75f545f5 (patch)
tree109e9334aaee5b7865a5d29fcfaea23010da0cc4 /src/api/s3_list.rs
parente4b9e4e24d006373a20cfcbdac9dba5e399ee182 (diff)
downloadgarage-better_xml.tar.gz
garage-better_xml.zip
Improved XML serializationbetter_xml
- Use quick_xml and serde for all XML response returned by the S3 API. - Include tests for all structs used to generate XML - Remove old manual XML escaping function which was unsafe
Diffstat (limited to 'src/api/s3_list.rs')
-rw-r--r--src/api/s3_list.rs147
1 files changed, 51 insertions, 96 deletions
diff --git a/src/api/s3_list.rs b/src/api/s3_list.rs
index 80fefd56..384346e0 100644
--- a/src/api/s3_list.rs
+++ b/src/api/s3_list.rs
@@ -1,5 +1,4 @@
use std::collections::{BTreeMap, BTreeSet, HashMap};
-use std::fmt::Write;
use std::sync::Arc;
use hyper::{Body, Response};
@@ -14,6 +13,7 @@ use garage_table::DeletedFilter;
use crate::encoding::*;
use crate::error::*;
+use crate::s3_xml;
#[derive(Debug)]
pub struct ListObjectsQuery {
@@ -163,126 +163,81 @@ pub async fn handle_list(
}
}
- let mut xml = String::new();
- writeln!(&mut xml, r#"<?xml version="1.0" encoding="UTF-8"?>"#).unwrap();
- writeln!(
- &mut xml,
- r#"<ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">"#
- )
- .unwrap();
-
- writeln!(&mut xml, "\t<Name>{}</Name>", query.bucket).unwrap();
-
- // TODO: in V1, is this supposed to be urlencoded when encoding-type is URL??
- writeln!(
- &mut xml,
- "\t<Prefix>{}</Prefix>",
- xml_encode_key(&query.prefix, query.urlencode_resp),
- )
- .unwrap();
-
- if let Some(delim) = &query.delimiter {
- // TODO: in V1, is this supposed to be urlencoded when encoding-type is URL??
- writeln!(
- &mut xml,
- "\t<Delimiter>{}</Delimiter>",
- xml_encode_key(delim, query.urlencode_resp),
- )
- .unwrap();
- }
-
- writeln!(&mut xml, "\t<MaxKeys>{}</MaxKeys>", query.max_keys).unwrap();
- if query.urlencode_resp {
- writeln!(&mut xml, "\t<EncodingType>url</EncodingType>").unwrap();
- }
-
- writeln!(
- &mut xml,
- "\t<KeyCount>{}</KeyCount>",
- result_keys.len() + result_common_prefixes.len()
- )
- .unwrap();
- writeln!(
- &mut xml,
- "\t<IsTruncated>{}</IsTruncated>",
- truncated.is_some()
- )
- .unwrap();
+ let mut result = s3_xml::ListBucketResult {
+ xmlns: (),
+ name: s3_xml::Value(query.bucket.to_string()),
+ prefix: uriencode_maybe(&query.prefix, query.urlencode_resp),
+ marker: None,
+ next_marker: None,
+ start_after: None,
+ continuation_token: None,
+ next_continuation_token: None,
+ max_keys: s3_xml::IntValue(query.max_keys as i64),
+ delimiter: query
+ .delimiter
+ .as_ref()
+ .map(|x| uriencode_maybe(x, query.urlencode_resp)),
+ encoding_type: match query.urlencode_resp {
+ true => Some(s3_xml::Value("url".to_string())),
+ false => None,
+ },
+
+ key_count: Some(s3_xml::IntValue(
+ result_keys.len() as i64 + result_common_prefixes.len() as i64,
+ )),
+ is_truncated: s3_xml::Value(format!("{}", truncated.is_some())),
+ contents: vec![],
+ common_prefixes: vec![],
+ };
if query.is_v2 {
if let Some(ct) = &query.continuation_token {
- writeln!(&mut xml, "\t<ContinuationToken>{}</ContinuationToken>", ct).unwrap();
+ result.continuation_token = Some(s3_xml::Value(ct.to_string()));
}
if let Some(sa) = &query.start_after {
- writeln!(
- &mut xml,
- "\t<StartAfter>{}</StartAfter>",
- xml_encode_key(sa, query.urlencode_resp)
- )
- .unwrap();
+ result.start_after = Some(uriencode_maybe(sa, query.urlencode_resp));
}
if let Some(nct) = truncated {
- writeln!(
- &mut xml,
- "\t<NextContinuationToken>{}</NextContinuationToken>",
- base64::encode(nct.as_bytes())
- )
- .unwrap();
+ result.next_continuation_token = Some(s3_xml::Value(base64::encode(nct.as_bytes())));
}
} else {
// TODO: are these supposed to be urlencoded when encoding-type is URL??
if let Some(mkr) = &query.marker {
- writeln!(
- &mut xml,
- "\t<Marker>{}</Marker>",
- xml_encode_key(mkr, query.urlencode_resp)
- )
- .unwrap();
+ result.marker = Some(uriencode_maybe(mkr, query.urlencode_resp));
}
if let Some(next_marker) = truncated {
- writeln!(
- &mut xml,
- "\t<NextMarker>{}</NextMarker>",
- xml_encode_key(&next_marker, query.urlencode_resp)
- )
- .unwrap();
+ result.next_marker = Some(uriencode_maybe(&next_marker, query.urlencode_resp));
}
}
for (key, info) in result_keys.iter() {
- let last_modif = msec_to_rfc3339(info.last_modified);
- writeln!(&mut xml, "\t<Contents>").unwrap();
- writeln!(
- &mut xml,
- "\t\t<Key>{}</Key>",
- xml_encode_key(key, query.urlencode_resp),
- )
- .unwrap();
- writeln!(&mut xml, "\t\t<LastModified>{}</LastModified>", last_modif).unwrap();
- writeln!(&mut xml, "\t\t<Size>{}</Size>", info.size).unwrap();
- if !info.etag.is_empty() {
- writeln!(&mut xml, "\t\t<ETag>\"{}\"</ETag>", info.etag).unwrap();
- }
- writeln!(&mut xml, "\t\t<StorageClass>STANDARD</StorageClass>").unwrap();
- writeln!(&mut xml, "\t</Contents>").unwrap();
+ result.contents.push(s3_xml::ListBucketItem {
+ key: uriencode_maybe(key, query.urlencode_resp),
+ last_modified: s3_xml::Value(msec_to_rfc3339(info.last_modified)),
+ size: s3_xml::IntValue(info.size as i64),
+ etag: s3_xml::Value(info.etag.to_string()),
+ storage_class: s3_xml::Value("STANDARD".to_string()),
+ });
}
for pfx in result_common_prefixes.iter() {
- writeln!(&mut xml, "\t<CommonPrefixes>").unwrap();
//TODO: in V1, are these urlencoded when urlencode_resp is true ?? (proably)
- writeln!(
- &mut xml,
- "\t\t<Prefix>{}</Prefix>",
- xml_encode_key(pfx, query.urlencode_resp),
- )
- .unwrap();
- writeln!(&mut xml, "\t</CommonPrefixes>").unwrap();
+ result.common_prefixes.push(s3_xml::CommonPrefix {
+ prefix: uriencode_maybe(pfx, query.urlencode_resp),
+ });
}
- writeln!(&mut xml, "</ListBucketResult>").unwrap();
- debug!("{}", xml);
+ let xml = s3_xml::to_xml_with_header(&result)?;
Ok(Response::builder()
.header("Content-Type", "application/xml")
.body(Body::from(xml.into_bytes()))?)
}
+
+fn uriencode_maybe(s: &str, yes: bool) -> s3_xml::Value {
+ if yes {
+ s3_xml::Value(uri_encode(s, true))
+ } else {
+ s3_xml::Value(s.to_string())
+ }
+}