rusoto/s3: Allow passing custom AWS-compatible regions

For the region property this would be provided as
    `region-name+https://region.end/point`
while for the URI this unfortunately has to be base32 encoded to allow
usage as the host part of the URI.
This commit is contained in:
Sebastian Dröge 2021-09-27 19:16:26 +03:00 committed by Sebastian Dröge
parent 502b336361
commit 69bb09f7ad
3 changed files with 66 additions and 6 deletions

View file

@ -30,6 +30,7 @@ serde = "1"
serde_derive = "1"
serde_json = "1"
atomic_refcell = "0.1"
base32 = "0.4"
[lib]
name = "gstrusoto"

View file

@ -95,7 +95,27 @@ impl Settings {
fn to_uri(&self) -> String {
format!(
"s3://{}/{}/{}",
self.region.name(),
match self.region {
Region::Custom {
ref name,
ref endpoint,
} => {
format!(
"{}+{}",
base32::encode(
base32::Alphabet::RFC4648 { padding: true },
name.as_bytes(),
),
base32::encode(
base32::Alphabet::RFC4648 { padding: true },
endpoint.as_bytes(),
),
)
}
_ => {
String::from(self.region.name())
}
},
self.bucket.as_ref().unwrap(),
self.key.as_ref().unwrap()
)
@ -500,9 +520,17 @@ impl ObjectImpl for S3Sink {
}
}
"region" => {
settings.region =
Region::from_str(&value.get::<String>().expect("type checked upstream"))
.unwrap();
let region = value.get::<String>().expect("type checked upstream");
settings.region = Region::from_str(&region)
.or_else(|_| {
let (name, endpoint) = region.split_once('+').ok_or(())?;
Ok(Region::Custom {
name: name.into(),
endpoint: endpoint.into(),
})
})
.unwrap_or_else(|_: ()| panic!("Invalid region '{}'", region));
if settings.key.is_some() && settings.bucket.is_some() {
let _ = self.set_uri(obj, Some(&settings.to_uri()));
}

View file

@ -31,7 +31,27 @@ impl ToString for GstS3Url {
fn to_string(&self) -> String {
format!(
"s3://{}/{}/{}{}",
self.region.name(),
match self.region {
Region::Custom {
ref name,
ref endpoint,
} => {
format!(
"{}+{}",
base32::encode(
base32::Alphabet::RFC4648 { padding: true },
name.as_bytes(),
),
base32::encode(
base32::Alphabet::RFC4648 { padding: true },
endpoint.as_bytes(),
),
)
}
_ => {
String::from(self.region.name())
}
},
self.bucket,
percent_encode(self.object.as_bytes(), PATH_SEGMENT),
if self.version.is_some() {
@ -55,7 +75,18 @@ pub fn parse_s3_url(url_str: &str) -> Result<GstS3Url, String> {
}
let host = url.host_str().unwrap();
let region = Region::from_str(host).map_err(|_| format!("Invalid region '{}'", host))?;
let region = Region::from_str(host)
.or_else(|_| {
let (name, endpoint) = host.split_once('+').ok_or(())?;
let name =
base32::decode(base32::Alphabet::RFC4648 { padding: true }, name).ok_or(())?;
let endpoint =
base32::decode(base32::Alphabet::RFC4648 { padding: true }, endpoint).ok_or(())?;
let name = String::from_utf8(name).map_err(|_| ())?;
let endpoint = String::from_utf8(endpoint).map_err(|_| ())?;
Ok(Region::Custom { name, endpoint })
})
.map_err(|_: ()| format!("Invalid region '{}'", host))?;
let mut path = url
.path_segments()