@@ -517,6 +517,69 @@ impl S3Builder {
517
517
518
518
self
519
519
}
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
+ }
520
583
}
521
584
522
585
impl Builder for S3Builder {
@@ -1049,4 +1112,10 @@ mod tests {
1049
1112
assert_eq ! ( endpoint, "https://test.s3.us-east-2.amazonaws.com" ) ;
1050
1113
}
1051
1114
}
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
+ }
1052
1121
}
0 commit comments