Files
rustfs/crates/ecstore/src/client/bucket_cache.rs
dependabot[bot] 0da943a6a4 build(deps): bump s3s from 0.12.0-rc.4 to 0.12.0-rc.5 in the s3s group (#1046)
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>
2025-12-11 15:20:36 +08:00

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)
}