Skip to content

Commit 099458f

Browse files
authored
feat(services/s3): Add detect_region support for S3Builder (#2634)
* add detect_region code * update by tips * update
1 parent 58226a6 commit 099458f

File tree

1 file changed

+69
-0
lines changed

1 file changed

+69
-0
lines changed

core/src/services/s3/backend.rs

+69
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,69 @@ impl S3Builder {
517517

518518
self
519519
}
520+
521+
/// a helper function to make it easier to find region
522+
/// Reference: [Amazon S3 HeadBucket API](https://docs.aws.amazon.com/zh_cn/AmazonS3/latest/API/API_HeadBucket.html)
523+
/// # Args
524+
///
525+
/// endpoint: the endpoint of S3 service
526+
///
527+
/// bucket: the bucket of S3 service
528+
/// # Return
529+
/// if get the region of given inputs, return Some(region)
530+
/// else return None
531+
///
532+
/// # Usage
533+
/// let b = S3Builder::default();
534+
/// let region = b.detect_region("https://s3.amazonaws.com", "buckets").await;
535+
pub async fn detect_region(&self, endpoint: &str, bucket: &str) -> Option<String> {
536+
let mut endpoint = if endpoint.starts_with("http") {
537+
endpoint.to_string()
538+
} else {
539+
// Prefix https if endpoint doesn't start with scheme.
540+
format!("https://{}", endpoint)
541+
};
542+
543+
endpoint = endpoint.replace(&format!("//{0}.", bucket), "//");
544+
let url = format!("{0}/{1}", endpoint, bucket);
545+
546+
debug!("backend detect region with url: {url}");
547+
548+
let req = match http::Request::head(&url).body(AsyncBody::Empty) {
549+
Ok(reg) => reg,
550+
Err(_) => return None,
551+
};
552+
553+
let client = match HttpClient::new() {
554+
Ok(client) => client,
555+
Err(_) => return None,
556+
};
557+
let res = match client.send(req).await {
558+
Ok(res) => res,
559+
Err(_) => return None,
560+
};
561+
562+
debug!(
563+
"auto detect region got response: status {:?}, header: {:?}",
564+
res.status(),
565+
res.headers()
566+
);
567+
568+
match res.status() {
569+
// The endpoint works, return with not changed endpoint and
570+
// default region.
571+
StatusCode::OK | StatusCode::FORBIDDEN | StatusCode::MOVED_PERMANENTLY => {
572+
let region = res.headers().get("x-amz-bucket-region").unwrap().to_str();
573+
if let Ok(regin) = region {
574+
Some(regin.to_string())
575+
} else {
576+
None
577+
}
578+
}
579+
// Unexpected status code
580+
_ => None,
581+
}
582+
}
520583
}
521584

522585
impl Builder for S3Builder {
@@ -1049,4 +1112,10 @@ mod tests {
10491112
assert_eq!(endpoint, "https://test.s3.us-east-2.amazonaws.com");
10501113
}
10511114
}
1115+
#[tokio::test]
1116+
async fn test_detect_region() {
1117+
let b = S3Builder::default();
1118+
let region = b.detect_region("https://s3.amazonaws.com", "buckets").await;
1119+
assert_eq!(region, Some("us-east-1".to_string()))
1120+
}
10521121
}

0 commit comments

Comments
 (0)