mirror of
https://github.com/rustfs/rustfs.git
synced 2026-01-17 01:30:33 +00:00
Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: loverustfs <hello@rustfs.com> Co-authored-by: houseme <housemecn@gmail.com> Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> Co-authored-by: houseme <4829346+houseme@users.noreply.github.com>
238 lines
8.0 KiB
Rust
238 lines
8.0 KiB
Rust
// Copyright 2024 RustFS Team
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
#![allow(unused_imports)]
|
|
#![allow(unused_variables)]
|
|
#![allow(unused_mut)]
|
|
#![allow(unused_assignments)]
|
|
#![allow(unused_must_use)]
|
|
#![allow(clippy::all)]
|
|
|
|
use super::constants::UNSIGNED_PAYLOAD;
|
|
use super::credentials::SignatureType;
|
|
use crate::client::{
|
|
api_error_response::http_resp_to_error_response,
|
|
transition_api::{CreateBucketConfiguration, LocationConstraint, TransitionClient},
|
|
};
|
|
use http::Request;
|
|
use hyper::StatusCode;
|
|
use rustfs_config::MAX_S3_CLIENT_RESPONSE_SIZE;
|
|
use rustfs_utils::hash::EMPTY_STRING_SHA256_HASH;
|
|
use s3s::Body;
|
|
use s3s::S3ErrorCode;
|
|
use std::collections::HashMap;
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct BucketLocationCache {
|
|
items: HashMap<String, String>,
|
|
}
|
|
|
|
impl BucketLocationCache {
|
|
pub fn new() -> BucketLocationCache {
|
|
BucketLocationCache { items: HashMap::new() }
|
|
}
|
|
|
|
pub fn get(&self, bucket_name: &str) -> Option<String> {
|
|
self.items.get(bucket_name).map(|s| s.clone())
|
|
}
|
|
|
|
pub fn set(&mut self, bucket_name: &str, location: &str) {
|
|
self.items.insert(bucket_name.to_string(), location.to_string());
|
|
}
|
|
|
|
pub fn delete(&mut self, bucket_name: &str) {
|
|
self.items.remove(bucket_name);
|
|
}
|
|
}
|
|
|
|
impl TransitionClient {
|
|
pub async fn get_bucket_location(&self, bucket_name: &str) -> Result<String, std::io::Error> {
|
|
Ok(self.get_bucket_location_inner(bucket_name).await?)
|
|
}
|
|
|
|
async fn get_bucket_location_inner(&self, bucket_name: &str) -> Result<String, std::io::Error> {
|
|
if self.region != "" {
|
|
return Ok(self.region.clone());
|
|
}
|
|
|
|
let mut location;
|
|
{
|
|
let mut bucket_loc_cache = self.bucket_loc_cache.lock().unwrap();
|
|
let ret = bucket_loc_cache.get(bucket_name);
|
|
if let Some(location) = ret {
|
|
return Ok(location);
|
|
}
|
|
//location = ret?;
|
|
}
|
|
|
|
let req = self.get_bucket_location_request(bucket_name)?;
|
|
|
|
let mut resp = self.doit(req).await?;
|
|
location = process_bucket_location_response(resp, bucket_name, &self.tier_type).await?;
|
|
{
|
|
let mut bucket_loc_cache = self.bucket_loc_cache.lock().unwrap();
|
|
bucket_loc_cache.set(bucket_name, &location);
|
|
}
|
|
Ok(location)
|
|
}
|
|
|
|
fn get_bucket_location_request(&self, bucket_name: &str) -> Result<http::Request<Body>, std::io::Error> {
|
|
let mut url_values = HashMap::new();
|
|
url_values.insert("location".to_string(), "".to_string());
|
|
|
|
let mut target_url = self.endpoint_url.clone();
|
|
let scheme = self.endpoint_url.scheme();
|
|
let h = target_url.host().expect("host is none.");
|
|
let default_port = if scheme == "https" { 443 } else { 80 };
|
|
let p = target_url.port().unwrap_or(default_port);
|
|
|
|
let is_virtual_style = self.is_virtual_host_style_request(&target_url, bucket_name);
|
|
|
|
let mut url_str: String = "".to_string();
|
|
|
|
if is_virtual_style {
|
|
url_str = scheme.to_string();
|
|
url_str.push_str("://");
|
|
url_str.push_str(bucket_name);
|
|
url_str.push_str(".");
|
|
url_str.push_str(target_url.host_str().expect("err"));
|
|
url_str.push_str("/?location");
|
|
} else {
|
|
let mut path = bucket_name.to_string();
|
|
path.push_str("/");
|
|
target_url.set_path(&path);
|
|
{
|
|
let mut q = target_url.query_pairs_mut();
|
|
for (k, v) in url_values {
|
|
q.append_pair(&k, &urlencoding::encode(&v));
|
|
}
|
|
}
|
|
url_str = target_url.to_string();
|
|
}
|
|
|
|
let Ok(mut req) = Request::builder().method(http::Method::GET).uri(url_str).body(Body::empty()) else {
|
|
return Err(std::io::Error::other("create request error"));
|
|
};
|
|
|
|
self.set_user_agent(&mut req);
|
|
|
|
let value;
|
|
{
|
|
let mut creds_provider = self.creds_provider.lock().unwrap();
|
|
value = match creds_provider.get_with_context(Some(self.cred_context())) {
|
|
Ok(v) => v,
|
|
Err(err) => {
|
|
return Err(std::io::Error::other(err));
|
|
}
|
|
};
|
|
}
|
|
|
|
let mut signer_type = value.signer_type.clone();
|
|
let mut access_key_id = value.access_key_id;
|
|
let mut secret_access_key = value.secret_access_key;
|
|
let mut session_token = value.session_token;
|
|
|
|
if self.override_signer_type != SignatureType::SignatureDefault {
|
|
signer_type = self.override_signer_type.clone();
|
|
}
|
|
|
|
if value.signer_type == SignatureType::SignatureAnonymous {
|
|
signer_type = SignatureType::SignatureAnonymous
|
|
}
|
|
|
|
if signer_type == SignatureType::SignatureAnonymous {
|
|
return Ok(req);
|
|
}
|
|
|
|
if signer_type == SignatureType::SignatureV2 {
|
|
let req = rustfs_signer::sign_v2(req, 0, &access_key_id, &secret_access_key, is_virtual_style);
|
|
return Ok(req);
|
|
}
|
|
|
|
let mut content_sha256 = EMPTY_STRING_SHA256_HASH.to_string();
|
|
if self.secure {
|
|
content_sha256 = UNSIGNED_PAYLOAD.to_string();
|
|
}
|
|
|
|
req.headers_mut()
|
|
.insert("X-Amz-Content-Sha256", content_sha256.parse().unwrap());
|
|
let req = rustfs_signer::sign_v4(req, 0, &access_key_id, &secret_access_key, &session_token, "us-east-1");
|
|
Ok(req)
|
|
}
|
|
}
|
|
|
|
async fn process_bucket_location_response(
|
|
mut resp: http::Response<Body>,
|
|
bucket_name: &str,
|
|
tier_type: &str,
|
|
) -> Result<String, std::io::Error> {
|
|
//if resp != nil {
|
|
if resp.status() != StatusCode::OK {
|
|
let err_resp = http_resp_to_error_response(&resp, vec![], bucket_name, "");
|
|
match err_resp.code {
|
|
S3ErrorCode::NotImplemented => {
|
|
match err_resp.server.as_str() {
|
|
"AmazonSnowball" => {
|
|
return Ok("snowball".to_string());
|
|
}
|
|
"cloudflare" => {
|
|
return Ok("us-east-1".to_string());
|
|
}
|
|
_ => {
|
|
return Err(std::io::Error::other(err_resp));
|
|
}
|
|
}
|
|
}
|
|
S3ErrorCode::AuthorizationHeaderMalformed |
|
|
//S3ErrorCode::InvalidRegion |
|
|
S3ErrorCode::AccessDenied => {
|
|
if err_resp.region == "" {
|
|
return Ok("us-east-1".to_string());
|
|
}
|
|
return Ok(err_resp.region);
|
|
}
|
|
_ => {
|
|
return Err(std::io::Error::other(err_resp));
|
|
}
|
|
}
|
|
}
|
|
//}
|
|
|
|
let b = resp
|
|
.body_mut()
|
|
.store_all_limited(MAX_S3_CLIENT_RESPONSE_SIZE)
|
|
.await
|
|
.unwrap()
|
|
.to_vec();
|
|
let mut location = "".to_string();
|
|
if tier_type == "huaweicloud" {
|
|
let d = quick_xml::de::from_str::<CreateBucketConfiguration>(&String::from_utf8(b).unwrap()).unwrap();
|
|
location = d.location_constraint;
|
|
} else {
|
|
if let Ok(LocationConstraint { field }) = quick_xml::de::from_str::<LocationConstraint>(&String::from_utf8(b).unwrap()) {
|
|
location = field;
|
|
}
|
|
}
|
|
//debug!("location: {}", location);
|
|
|
|
if location == "" {
|
|
location = "us-east-1".to_string();
|
|
}
|
|
|
|
if location == "EU" {
|
|
location = "eu-west-1".to_string();
|
|
}
|
|
|
|
Ok(location)
|
|
}
|