This is an automated email from the ASF dual-hosted git repository.

xuanwo pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-opendal.git


The following commit(s) were added to refs/heads/main by this push:
     new 099458fba feat(services/s3): Add detect_region support for S3Builder 
(#2634)
099458fba is described below

commit 099458fbaf2e878f8f7fd3a1a12e31b6fb2ab720
Author: parkma99 <[email protected]>
AuthorDate: Fri Jul 14 20:46:36 2023 +0800

    feat(services/s3): Add detect_region support for S3Builder (#2634)
    
    * add detect_region code
    
    * update by tips
    
    * update
---
 core/src/services/s3/backend.rs | 69 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 69 insertions(+)

diff --git a/core/src/services/s3/backend.rs b/core/src/services/s3/backend.rs
index cade3c53f..236781794 100644
--- a/core/src/services/s3/backend.rs
+++ b/core/src/services/s3/backend.rs
@@ -517,6 +517,69 @@ impl S3Builder {
 
         self
     }
+
+    /// a helper function to make it easier to find region
+    /// Reference: [Amazon S3 HeadBucket 
API](https://docs.aws.amazon.com/zh_cn/AmazonS3/latest/API/API_HeadBucket.html)
+    /// # Args
+    ///
+    /// endpoint: the endpoint of S3 service
+    ///
+    /// bucket: the bucket of S3 service
+    /// # Return
+    /// if get the region of given inputs, return Some(region)
+    /// else return None
+    ///
+    /// # Usage
+    /// let b = S3Builder::default();
+    /// let region = b.detect_region("https://s3.amazonaws.com";, 
"buckets").await;
+    pub async fn detect_region(&self, endpoint: &str, bucket: &str) -> 
Option<String> {
+        let mut endpoint = if endpoint.starts_with("http") {
+            endpoint.to_string()
+        } else {
+            // Prefix https if endpoint doesn't start with scheme.
+            format!("https://{}";, endpoint)
+        };
+
+        endpoint = endpoint.replace(&format!("//{0}.", bucket), "//");
+        let url = format!("{0}/{1}", endpoint, bucket);
+
+        debug!("backend detect region with url: {url}");
+
+        let req = match http::Request::head(&url).body(AsyncBody::Empty) {
+            Ok(reg) => reg,
+            Err(_) => return None,
+        };
+
+        let client = match HttpClient::new() {
+            Ok(client) => client,
+            Err(_) => return None,
+        };
+        let res = match client.send(req).await {
+            Ok(res) => res,
+            Err(_) => return None,
+        };
+
+        debug!(
+            "auto detect region got response: status {:?}, header: {:?}",
+            res.status(),
+            res.headers()
+        );
+
+        match res.status() {
+            // The endpoint works, return with not changed endpoint and
+            // default region.
+            StatusCode::OK | StatusCode::FORBIDDEN | 
StatusCode::MOVED_PERMANENTLY => {
+                let region = 
res.headers().get("x-amz-bucket-region").unwrap().to_str();
+                if let Ok(regin) = region {
+                    Some(regin.to_string())
+                } else {
+                    None
+                }
+            }
+            // Unexpected status code
+            _ => None,
+        }
+    }
 }
 
 impl Builder for S3Builder {
@@ -1049,4 +1112,10 @@ mod tests {
             assert_eq!(endpoint, "https://test.s3.us-east-2.amazonaws.com";);
         }
     }
+    #[tokio::test]
+    async fn test_detect_region() {
+        let b = S3Builder::default();
+        let region = b.detect_region("https://s3.amazonaws.com";, 
"buckets").await;
+        assert_eq!(region, Some("us-east-1".to_string()))
+    }
 }

Reply via email to