mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-07 22:03:14 +00:00
refactor: improve code quality with safer error handling, trait decomposition, and dead code cleanup (#1997)
This commit is contained in:
@@ -21,7 +21,7 @@ use matchit::Params;
|
||||
use rustfs_credentials::get_global_action_cred;
|
||||
use rustfs_ecstore::bucket::versioning_sys::BucketVersioningSys;
|
||||
use rustfs_ecstore::new_object_layer_fn;
|
||||
use rustfs_ecstore::store_api::{BucketOptions, StorageAPI};
|
||||
use rustfs_ecstore::store_api::{BucketOperations, BucketOptions, StorageAPI};
|
||||
use rustfs_iam::store::MappedPolicy;
|
||||
use rustfs_policy::policy::BucketPolicy;
|
||||
use rustfs_policy::policy::default::DEFAULT_POLICIES;
|
||||
|
||||
@@ -25,7 +25,10 @@ use hyper::Method;
|
||||
use matchit::Params;
|
||||
use rustfs_config::MAX_BUCKET_METADATA_IMPORT_SIZE;
|
||||
use rustfs_ecstore::{
|
||||
StorageAPI,
|
||||
bucket::utils::{deserialize, serialize},
|
||||
store_api::MakeBucketOptions,
|
||||
};
|
||||
use rustfs_ecstore::{
|
||||
bucket::{
|
||||
metadata::{
|
||||
BUCKET_LIFECYCLE_CONFIG, BUCKET_NOTIFICATION_CONFIG, BUCKET_POLICY_CONFIG, BUCKET_QUOTA_CONFIG_FILE,
|
||||
@@ -38,11 +41,7 @@ use rustfs_ecstore::{
|
||||
},
|
||||
error::StorageError,
|
||||
new_object_layer_fn,
|
||||
store_api::BucketOptions,
|
||||
};
|
||||
use rustfs_ecstore::{
|
||||
bucket::utils::{deserialize, serialize},
|
||||
store_api::MakeBucketOptions,
|
||||
store_api::{BucketOperations, BucketOptions},
|
||||
};
|
||||
use rustfs_policy::policy::{
|
||||
BucketPolicy,
|
||||
|
||||
@@ -30,6 +30,7 @@ use rustfs_ecstore::{
|
||||
new_object_layer_fn,
|
||||
notification_sys::get_global_notification_sys,
|
||||
rebalance::{DiskStat, RebalSaveOpt},
|
||||
store_api::BucketOperations,
|
||||
store_api::BucketOptions,
|
||||
};
|
||||
use rustfs_policy::policy::action::{Action, AdminAction};
|
||||
|
||||
@@ -27,7 +27,7 @@ use rustfs_ecstore::bucket::metadata_sys;
|
||||
use rustfs_ecstore::bucket::target::BucketTarget;
|
||||
use rustfs_ecstore::global::global_rustfs_port;
|
||||
use rustfs_ecstore::new_object_layer_fn;
|
||||
use rustfs_ecstore::store_api::{BucketOptions, StorageAPI};
|
||||
use rustfs_ecstore::store_api::{BucketOperations, BucketOptions};
|
||||
use rustfs_policy::policy::action::{Action, AdminAction};
|
||||
use s3s::header::CONTENT_TYPE;
|
||||
use s3s::{Body, S3Error, S3ErrorCode, S3Request, S3Response, S3Result, s3_error};
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
// limitations under the License.
|
||||
|
||||
//! Admin application use-case contracts.
|
||||
#![allow(dead_code)]
|
||||
|
||||
use crate::app::context::{AppContext, get_global_app_context};
|
||||
use crate::error::ApiError;
|
||||
@@ -58,31 +57,13 @@ pub struct QueryPoolStatusRequest {
|
||||
pub by_id: bool,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
pub trait AdminUsecase: Send + Sync {
|
||||
async fn query_server_info(&self, req: QueryServerInfoRequest) -> AdminUsecaseResult<QueryServerInfoResponse>;
|
||||
|
||||
async fn query_storage_info(&self) -> AdminUsecaseResult<StorageInfo>;
|
||||
|
||||
async fn query_data_usage_info(&self) -> AdminUsecaseResult<DataUsageInfo>;
|
||||
|
||||
async fn list_pool_statuses(&self) -> AdminUsecaseResult<Vec<PoolStatus>>;
|
||||
|
||||
async fn query_pool_status(&self, req: QueryPoolStatusRequest) -> AdminUsecaseResult<PoolStatus>;
|
||||
|
||||
fn collect_dependency_readiness(&self) -> DependencyReadiness;
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct DefaultAdminUsecase {
|
||||
context: Option<Arc<AppContext>>,
|
||||
}
|
||||
|
||||
impl DefaultAdminUsecase {
|
||||
pub fn new(context: Arc<AppContext>) -> Self {
|
||||
Self { context: Some(context) }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn without_context() -> Self {
|
||||
Self { context: None }
|
||||
}
|
||||
@@ -93,10 +74,6 @@ impl DefaultAdminUsecase {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn context(&self) -> Option<Arc<AppContext>> {
|
||||
self.context.clone()
|
||||
}
|
||||
|
||||
fn endpoints(&self) -> Option<EndpointServerPools> {
|
||||
self.context.as_ref().and_then(|context| context.endpoints().handle())
|
||||
}
|
||||
@@ -292,33 +269,6 @@ impl DefaultAdminUsecase {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl AdminUsecase for DefaultAdminUsecase {
|
||||
async fn query_server_info(&self, req: QueryServerInfoRequest) -> AdminUsecaseResult<QueryServerInfoResponse> {
|
||||
self.execute_query_server_info(req).await
|
||||
}
|
||||
|
||||
async fn query_storage_info(&self) -> AdminUsecaseResult<StorageInfo> {
|
||||
self.execute_query_storage_info().await
|
||||
}
|
||||
|
||||
async fn query_data_usage_info(&self) -> AdminUsecaseResult<DataUsageInfo> {
|
||||
self.execute_query_data_usage_info().await
|
||||
}
|
||||
|
||||
async fn list_pool_statuses(&self) -> AdminUsecaseResult<Vec<PoolStatus>> {
|
||||
self.execute_list_pool_statuses().await
|
||||
}
|
||||
|
||||
async fn query_pool_status(&self, req: QueryPoolStatusRequest) -> AdminUsecaseResult<PoolStatus> {
|
||||
self.execute_query_pool_status(req).await
|
||||
}
|
||||
|
||||
fn collect_dependency_readiness(&self) -> DependencyReadiness {
|
||||
self.execute_collect_dependency_readiness()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
// limitations under the License.
|
||||
|
||||
//! Bucket application use-case contracts.
|
||||
#![allow(dead_code)]
|
||||
|
||||
use crate::app::context::{AppContext, default_notify_interface, get_global_app_context};
|
||||
use crate::auth::get_condition_values;
|
||||
@@ -46,7 +45,7 @@ use rustfs_ecstore::bucket::{
|
||||
use rustfs_ecstore::client::object_api_utils::to_s3s_etag;
|
||||
use rustfs_ecstore::error::StorageError;
|
||||
use rustfs_ecstore::new_object_layer_fn;
|
||||
use rustfs_ecstore::store_api::{BucketOptions, DeleteBucketOptions, MakeBucketOptions, StorageAPI};
|
||||
use rustfs_ecstore::store_api::{BucketOperations, BucketOptions, DeleteBucketOptions, ListOperations, MakeBucketOptions};
|
||||
use rustfs_policy::policy::{
|
||||
action::{Action, S3Action},
|
||||
{BucketPolicy, BucketPolicyArgs, Effect, Validator},
|
||||
@@ -65,8 +64,6 @@ use std::{fmt::Display, sync::Arc};
|
||||
use tracing::{debug, error, info, instrument, warn};
|
||||
use urlencoding::encode;
|
||||
|
||||
pub type BucketUsecaseResult<T> = Result<T, ApiError>;
|
||||
|
||||
fn serialize_config<T: xml::Serialize>(value: &T) -> S3Result<Vec<u8>> {
|
||||
serialize(value).map_err(to_internal_error)
|
||||
}
|
||||
@@ -75,72 +72,13 @@ fn to_internal_error(err: impl Display) -> S3Error {
|
||||
S3Error::with_message(S3ErrorCode::InternalError, format!("{err}"))
|
||||
}
|
||||
|
||||
fn default_region() -> Region {
|
||||
// RUSTFS_REGION is a compile-time constant ("us-east-1") guaranteed to be valid.
|
||||
Region::new(RUSTFS_REGION.into()).expect("RUSTFS_REGION constant must be a valid region")
|
||||
}
|
||||
|
||||
fn resolve_notification_region(global_region: Option<Region>, request_region: Option<Region>) -> Region {
|
||||
global_region.unwrap_or_else(|| request_region.unwrap_or_else(|| Region::new(RUSTFS_REGION.into()).expect("valid region")))
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct CreateBucketRequest {
|
||||
pub bucket: String,
|
||||
pub object_lock_enabled: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
||||
pub struct CreateBucketResponse;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct DeleteBucketRequest {
|
||||
pub bucket: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
||||
pub struct DeleteBucketResponse;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct HeadBucketRequest {
|
||||
pub bucket: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
||||
pub struct HeadBucketResponse;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ListObjectsV2Request {
|
||||
pub bucket: String,
|
||||
pub prefix: Option<String>,
|
||||
pub delimiter: Option<String>,
|
||||
pub continuation_token: Option<String>,
|
||||
pub max_keys: Option<i32>,
|
||||
pub fetch_owner: Option<bool>,
|
||||
pub start_after: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ListObjectsV2Item {
|
||||
pub key: String,
|
||||
pub etag: Option<String>,
|
||||
pub size: i64,
|
||||
pub version_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
||||
pub struct ListObjectsV2Response {
|
||||
pub objects: Vec<ListObjectsV2Item>,
|
||||
pub common_prefixes: Vec<String>,
|
||||
pub key_count: i32,
|
||||
pub is_truncated: bool,
|
||||
pub next_continuation_token: Option<String>,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
pub trait BucketUsecase: Send + Sync {
|
||||
async fn create_bucket(&self, req: CreateBucketRequest) -> BucketUsecaseResult<CreateBucketResponse>;
|
||||
|
||||
async fn delete_bucket(&self, req: DeleteBucketRequest) -> BucketUsecaseResult<DeleteBucketResponse>;
|
||||
|
||||
async fn head_bucket(&self, req: HeadBucketRequest) -> BucketUsecaseResult<HeadBucketResponse>;
|
||||
|
||||
async fn list_objects_v2(&self, req: ListObjectsV2Request) -> BucketUsecaseResult<ListObjectsV2Response>;
|
||||
global_region.or(request_region).unwrap_or_else(default_region)
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
@@ -149,10 +87,7 @@ pub struct DefaultBucketUsecase {
|
||||
}
|
||||
|
||||
impl DefaultBucketUsecase {
|
||||
pub fn new(context: Arc<AppContext>) -> Self {
|
||||
Self { context: Some(context) }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn without_context() -> Self {
|
||||
Self { context: None }
|
||||
}
|
||||
@@ -163,10 +98,6 @@ impl DefaultBucketUsecase {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn context(&self) -> Option<Arc<AppContext>> {
|
||||
self.context.clone()
|
||||
}
|
||||
|
||||
fn global_region(&self) -> Option<Region> {
|
||||
self.context.as_ref().and_then(|context| context.region().get())
|
||||
}
|
||||
@@ -466,7 +397,10 @@ impl DefaultBucketUsecase {
|
||||
list_bucket_infos = futures::stream::iter(list_bucket_infos)
|
||||
.filter_map(|info| async {
|
||||
let mut req_clone = req.clone();
|
||||
let req_info = req_clone.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let Some(req_info) = req_clone.extensions.get_mut::<ReqInfo>() else {
|
||||
debug!(bucket = %info.name, "ReqInfo missing in extensions, skipping bucket authorization");
|
||||
return None;
|
||||
};
|
||||
req_info.bucket = Some(info.name.clone());
|
||||
|
||||
if authorize_request(&mut req_clone, Action::S3Action(S3Action::ListBucketAction))
|
||||
@@ -1750,37 +1684,6 @@ impl DefaultBucketUsecase {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl BucketUsecase for DefaultBucketUsecase {
|
||||
async fn create_bucket(&self, req: CreateBucketRequest) -> BucketUsecaseResult<CreateBucketResponse> {
|
||||
let _ = req;
|
||||
Err(ApiError::from(StorageError::other(
|
||||
"DefaultBucketUsecase::create_bucket DTO path is not implemented yet",
|
||||
)))
|
||||
}
|
||||
|
||||
async fn delete_bucket(&self, req: DeleteBucketRequest) -> BucketUsecaseResult<DeleteBucketResponse> {
|
||||
let _ = req;
|
||||
Err(ApiError::from(StorageError::other(
|
||||
"DefaultBucketUsecase::delete_bucket DTO path is not implemented yet",
|
||||
)))
|
||||
}
|
||||
|
||||
async fn head_bucket(&self, req: HeadBucketRequest) -> BucketUsecaseResult<HeadBucketResponse> {
|
||||
let _ = req;
|
||||
Err(ApiError::from(StorageError::other(
|
||||
"DefaultBucketUsecase::head_bucket DTO path is not implemented yet",
|
||||
)))
|
||||
}
|
||||
|
||||
async fn list_objects_v2(&self, req: ListObjectsV2Request) -> BucketUsecaseResult<ListObjectsV2Response> {
|
||||
let _ = req;
|
||||
Err(ApiError::from(StorageError::other(
|
||||
"DefaultBucketUsecase::list_objects_v2 DTO path is not implemented yet",
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
//! Application-layer dependency context.
|
||||
//! This module introduces explicit dependency injection entry points
|
||||
//! for storage, IAM, and KMS handles.
|
||||
#![allow(dead_code)]
|
||||
|
||||
use crate::config::workload_profiles::{RustFSBufferConfig, get_global_buffer_config};
|
||||
use async_trait::async_trait;
|
||||
@@ -34,11 +33,13 @@ use tokio::sync::RwLock;
|
||||
|
||||
/// IAM interface for application-layer use-cases.
|
||||
pub trait IamInterface: Send + Sync {
|
||||
#[allow(dead_code)]
|
||||
fn handle(&self) -> Arc<IamSys<ObjectStore>>;
|
||||
fn is_ready(&self) -> bool;
|
||||
}
|
||||
|
||||
/// KMS interface for application-layer use-cases.
|
||||
#[allow(dead_code)]
|
||||
pub trait KmsInterface: Send + Sync {
|
||||
fn handle(&self) -> Arc<KmsServiceManager>;
|
||||
}
|
||||
@@ -95,6 +96,7 @@ pub trait BufferConfigInterface: Send + Sync {
|
||||
|
||||
/// Default IAM interface adapter.
|
||||
pub struct IamHandle {
|
||||
#[allow(dead_code)]
|
||||
iam: Arc<IamSys<ObjectStore>>,
|
||||
}
|
||||
|
||||
@@ -115,6 +117,7 @@ impl IamInterface for IamHandle {
|
||||
}
|
||||
|
||||
/// Default KMS interface adapter.
|
||||
#[allow(dead_code)]
|
||||
pub struct KmsHandle {
|
||||
kms: Arc<KmsServiceManager>,
|
||||
}
|
||||
@@ -230,6 +233,7 @@ impl BufferConfigInterface for BufferConfigHandle {
|
||||
pub struct AppContext {
|
||||
object_store: Arc<ECStore>,
|
||||
iam: Arc<dyn IamInterface>,
|
||||
#[allow(dead_code)]
|
||||
kms: Arc<dyn KmsInterface>,
|
||||
kms_runtime: Arc<dyn KmsRuntimeInterface>,
|
||||
notify: Arc<dyn NotifyInterface>,
|
||||
@@ -274,6 +278,7 @@ impl AppContext {
|
||||
self.iam.clone()
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn kms(&self) -> Arc<dyn KmsInterface> {
|
||||
self.kms.clone()
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
// limitations under the License.
|
||||
|
||||
//! Multipart application use-case contracts.
|
||||
#![allow(dead_code)]
|
||||
|
||||
use crate::app::context::{AppContext, get_global_app_context};
|
||||
use crate::error::ApiError;
|
||||
@@ -28,7 +27,6 @@ use crate::storage::*;
|
||||
use bytes::Bytes;
|
||||
use futures::StreamExt;
|
||||
use rustfs_config::RUSTFS_REGION;
|
||||
use rustfs_ecstore::StorageAPI;
|
||||
use rustfs_ecstore::bucket::quota::checker::QuotaChecker;
|
||||
use rustfs_ecstore::bucket::{
|
||||
metadata_sys,
|
||||
@@ -41,6 +39,7 @@ use rustfs_ecstore::error::{StorageError, is_err_object_not_found, is_err_versio
|
||||
use rustfs_ecstore::new_object_layer_fn;
|
||||
use rustfs_ecstore::set_disk::{MAX_PARTS_COUNT, is_valid_storage_class};
|
||||
use rustfs_ecstore::store_api::{CompletePart, MultipartUploadResult, ObjectIO, ObjectOptions, PutObjReader};
|
||||
use rustfs_ecstore::store_api::{MultipartOperations, ObjectOperations};
|
||||
use rustfs_filemeta::{ReplicationStatusType, ReplicationType};
|
||||
use rustfs_rio::{CompressReader, HashReader, Reader, WarpReader};
|
||||
use rustfs_targets::EventName;
|
||||
@@ -50,6 +49,7 @@ use rustfs_utils::http::{
|
||||
headers::{AMZ_DECODED_CONTENT_LENGTH, AMZ_OBJECT_TAGGING, RESERVED_METADATA_PREFIX_LOWER},
|
||||
};
|
||||
use s3s::dto::*;
|
||||
use s3s::region::Region;
|
||||
use s3s::{S3Error, S3ErrorCode, S3Request, S3Response, S3Result, s3_error};
|
||||
use std::collections::HashMap;
|
||||
use std::str::FromStr;
|
||||
@@ -58,95 +58,13 @@ use tokio::sync::RwLock;
|
||||
use tokio_util::io::StreamReader;
|
||||
use tracing::{info, instrument, warn};
|
||||
|
||||
pub type MultipartUsecaseResult<T> = Result<T, ApiError>;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct CreateMultipartUploadRequest {
|
||||
pub bucket: String,
|
||||
pub key: String,
|
||||
pub metadata: HashMap<String, String>,
|
||||
pub content_type: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct CreateMultipartUploadResponse {
|
||||
pub upload_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct UploadPartRequest {
|
||||
pub bucket: String,
|
||||
pub key: String,
|
||||
pub upload_id: String,
|
||||
pub part_number: i32,
|
||||
pub content_length: Option<i64>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct UploadPartResponse {
|
||||
pub etag: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct CompleteMultipartUploadPart {
|
||||
pub part_number: i32,
|
||||
pub etag: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct CompleteMultipartUploadRequest {
|
||||
pub bucket: String,
|
||||
pub key: String,
|
||||
pub upload_id: String,
|
||||
pub parts: Vec<CompleteMultipartUploadPart>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
||||
pub struct CompleteMultipartUploadResponse {
|
||||
pub etag: Option<String>,
|
||||
pub version_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct AbortMultipartUploadRequest {
|
||||
pub bucket: String,
|
||||
pub key: String,
|
||||
pub upload_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
||||
pub struct AbortMultipartUploadResponse;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
pub trait MultipartUsecase: Send + Sync {
|
||||
async fn create_multipart_upload(
|
||||
&self,
|
||||
req: CreateMultipartUploadRequest,
|
||||
) -> MultipartUsecaseResult<CreateMultipartUploadResponse>;
|
||||
|
||||
async fn upload_part(&self, req: UploadPartRequest) -> MultipartUsecaseResult<UploadPartResponse>;
|
||||
|
||||
async fn complete_multipart_upload(
|
||||
&self,
|
||||
req: CompleteMultipartUploadRequest,
|
||||
) -> MultipartUsecaseResult<CompleteMultipartUploadResponse>;
|
||||
|
||||
async fn abort_multipart_upload(
|
||||
&self,
|
||||
req: AbortMultipartUploadRequest,
|
||||
) -> MultipartUsecaseResult<AbortMultipartUploadResponse>;
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct DefaultMultipartUsecase {
|
||||
context: Option<Arc<AppContext>>,
|
||||
}
|
||||
|
||||
impl DefaultMultipartUsecase {
|
||||
pub fn new(context: Arc<AppContext>) -> Self {
|
||||
Self { context: Some(context) }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn without_context() -> Self {
|
||||
Self { context: None }
|
||||
}
|
||||
@@ -157,10 +75,6 @@ impl DefaultMultipartUsecase {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn context(&self) -> Option<Arc<AppContext>> {
|
||||
self.context.clone()
|
||||
}
|
||||
|
||||
fn bucket_metadata_sys(&self) -> Option<Arc<RwLock<metadata_sys::BucketMetadataSys>>> {
|
||||
self.context.as_ref().and_then(|context| context.bucket_metadata().handle())
|
||||
}
|
||||
@@ -423,7 +337,9 @@ impl DefaultMultipartUsecase {
|
||||
}
|
||||
}
|
||||
|
||||
let region = self.global_region().unwrap_or_else(|| RUSTFS_REGION.parse().unwrap());
|
||||
let region = self
|
||||
.global_region()
|
||||
.unwrap_or_else(|| Region::new(RUSTFS_REGION.into()).expect("RUSTFS_REGION constant must be a valid region"));
|
||||
let output = CompleteMultipartUploadOutput {
|
||||
bucket: Some(bucket.clone()),
|
||||
key: Some(key.clone()),
|
||||
@@ -1181,46 +1097,6 @@ impl DefaultMultipartUsecase {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl MultipartUsecase for DefaultMultipartUsecase {
|
||||
async fn create_multipart_upload(
|
||||
&self,
|
||||
req: CreateMultipartUploadRequest,
|
||||
) -> MultipartUsecaseResult<CreateMultipartUploadResponse> {
|
||||
let _ = req;
|
||||
Err(ApiError::from(StorageError::other(
|
||||
"DefaultMultipartUsecase::create_multipart_upload is not implemented yet",
|
||||
)))
|
||||
}
|
||||
|
||||
async fn upload_part(&self, req: UploadPartRequest) -> MultipartUsecaseResult<UploadPartResponse> {
|
||||
let _ = req;
|
||||
Err(ApiError::from(StorageError::other(
|
||||
"DefaultMultipartUsecase::upload_part is not implemented yet",
|
||||
)))
|
||||
}
|
||||
|
||||
async fn complete_multipart_upload(
|
||||
&self,
|
||||
req: CompleteMultipartUploadRequest,
|
||||
) -> MultipartUsecaseResult<CompleteMultipartUploadResponse> {
|
||||
let _ = req;
|
||||
Err(ApiError::from(StorageError::other(
|
||||
"DefaultMultipartUsecase::complete_multipart_upload is not implemented yet",
|
||||
)))
|
||||
}
|
||||
|
||||
async fn abort_multipart_upload(
|
||||
&self,
|
||||
req: AbortMultipartUploadRequest,
|
||||
) -> MultipartUsecaseResult<AbortMultipartUploadResponse> {
|
||||
let _ = req;
|
||||
Err(ApiError::from(StorageError::other(
|
||||
"DefaultMultipartUsecase::abort_multipart_upload is not implemented yet",
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
+129
-278
@@ -13,12 +13,11 @@
|
||||
// limitations under the License.
|
||||
|
||||
//! Object application use-case contracts.
|
||||
#![allow(dead_code)]
|
||||
|
||||
use crate::app::context::{AppContext, default_notify_interface, get_global_app_context};
|
||||
use crate::config::workload_profiles::RustFSBufferConfig;
|
||||
use crate::error::ApiError;
|
||||
use crate::storage::access::{ReqInfo, authorize_request, has_bypass_governance_header};
|
||||
use crate::storage::access::{ReqInfo, authorize_request, has_bypass_governance_header, req_info_mut};
|
||||
use crate::storage::concurrency::{
|
||||
CachedGetObject, ConcurrencyManager, GetObjectGuard, get_concurrency_aware_buffer_size, get_concurrency_manager,
|
||||
};
|
||||
@@ -38,7 +37,6 @@ use datafusion::arrow::{
|
||||
use futures::StreamExt;
|
||||
use http::{HeaderMap, HeaderValue, StatusCode};
|
||||
use metrics::{counter, histogram};
|
||||
use rustfs_ecstore::StorageAPI;
|
||||
use rustfs_ecstore::bucket::quota::checker::QuotaChecker;
|
||||
use rustfs_ecstore::bucket::{
|
||||
lifecycle::{
|
||||
@@ -65,7 +63,8 @@ use rustfs_ecstore::error::{StorageError, is_err_bucket_not_found, is_err_object
|
||||
use rustfs_ecstore::new_object_layer_fn;
|
||||
use rustfs_ecstore::set_disk::is_valid_storage_class;
|
||||
use rustfs_ecstore::store_api::{
|
||||
BucketOptions, HTTPRangeSpec, ObjectIO, ObjectInfo, ObjectOptions, ObjectToDelete, PutObjReader,
|
||||
BucketOperations, BucketOptions, HTTPRangeSpec, ObjectIO, ObjectInfo, ObjectOperations, ObjectOptions, ObjectToDelete,
|
||||
PutObjReader,
|
||||
};
|
||||
use rustfs_filemeta::{
|
||||
REPLICATE_INCOMING_DELETE, ReplicationStatusType, ReplicationType, RestoreStatusOps, VersionPurgeStatusType,
|
||||
@@ -112,7 +111,49 @@ use tokio_util::io::{ReaderStream, StreamReader};
|
||||
use tracing::{debug, error, info, instrument, warn};
|
||||
use uuid::Uuid;
|
||||
|
||||
pub type ObjectUsecaseResult<T> = Result<T, ApiError>;
|
||||
/// Extract trailing-header checksum values, overriding the corresponding input fields.
|
||||
fn apply_trailing_checksums(
|
||||
algorithm: Option<&str>,
|
||||
trailing_headers: &Option<s3s::TrailingHeaders>,
|
||||
checksums: &mut PutObjectChecksums,
|
||||
) {
|
||||
let Some(alg) = algorithm else { return };
|
||||
let Some(checksum_str) = trailing_headers.as_ref().and_then(|trailer| {
|
||||
let key = match alg {
|
||||
ChecksumAlgorithm::CRC32 => rustfs_rio::ChecksumType::CRC32.key(),
|
||||
ChecksumAlgorithm::CRC32C => rustfs_rio::ChecksumType::CRC32C.key(),
|
||||
ChecksumAlgorithm::SHA1 => rustfs_rio::ChecksumType::SHA1.key(),
|
||||
ChecksumAlgorithm::SHA256 => rustfs_rio::ChecksumType::SHA256.key(),
|
||||
ChecksumAlgorithm::CRC64NVME => rustfs_rio::ChecksumType::CRC64_NVME.key(),
|
||||
_ => return None,
|
||||
};
|
||||
trailer.read(|headers| {
|
||||
headers
|
||||
.get(key.unwrap_or_default())
|
||||
.and_then(|value| value.to_str().ok().map(|s| s.to_string()))
|
||||
})
|
||||
}) else {
|
||||
return;
|
||||
};
|
||||
|
||||
match alg {
|
||||
ChecksumAlgorithm::CRC32 => checksums.crc32 = checksum_str,
|
||||
ChecksumAlgorithm::CRC32C => checksums.crc32c = checksum_str,
|
||||
ChecksumAlgorithm::SHA1 => checksums.sha1 = checksum_str,
|
||||
ChecksumAlgorithm::SHA256 => checksums.sha256 = checksum_str,
|
||||
ChecksumAlgorithm::CRC64NVME => checksums.crc64nvme = checksum_str,
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct PutObjectChecksums {
|
||||
crc32: Option<String>,
|
||||
crc32c: Option<String>,
|
||||
sha1: Option<String>,
|
||||
sha256: Option<String>,
|
||||
crc64nvme: Option<String>,
|
||||
}
|
||||
|
||||
fn normalize_delete_objects_version_id(version_id: Option<String>) -> Result<(Option<String>, Option<Uuid>), String> {
|
||||
let version_id = version_id.map(|v| v.trim().to_string()).filter(|v| !v.is_empty());
|
||||
@@ -129,67 +170,13 @@ fn normalize_delete_objects_version_id(version_id: Option<String>) -> Result<(Op
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct PutObjectRequest {
|
||||
pub bucket: String,
|
||||
pub key: String,
|
||||
pub content_length: Option<i64>,
|
||||
pub content_type: Option<String>,
|
||||
pub version_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct PutObjectResponse {
|
||||
pub etag: String,
|
||||
pub version_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct GetObjectRequest {
|
||||
pub bucket: String,
|
||||
pub key: String,
|
||||
pub version_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
||||
pub struct GetObjectResponse {
|
||||
pub etag: Option<String>,
|
||||
pub content_length: i64,
|
||||
pub version_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct DeleteObjectRequest {
|
||||
pub bucket: String,
|
||||
pub key: String,
|
||||
pub version_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
||||
pub struct DeleteObjectResponse {
|
||||
pub delete_marker: Option<bool>,
|
||||
pub version_id: Option<String>,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
pub trait ObjectUsecase: Send + Sync {
|
||||
async fn put_object(&self, req: PutObjectRequest) -> ObjectUsecaseResult<PutObjectResponse>;
|
||||
|
||||
async fn get_object(&self, req: GetObjectRequest) -> ObjectUsecaseResult<GetObjectResponse>;
|
||||
|
||||
async fn delete_object(&self, req: DeleteObjectRequest) -> ObjectUsecaseResult<DeleteObjectResponse>;
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct DefaultObjectUsecase {
|
||||
context: Option<Arc<AppContext>>,
|
||||
}
|
||||
|
||||
impl DefaultObjectUsecase {
|
||||
pub fn new(context: Arc<AppContext>) -> Self {
|
||||
Self { context: Some(context) }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn without_context() -> Self {
|
||||
Self { context: None }
|
||||
}
|
||||
@@ -200,10 +187,6 @@ impl DefaultObjectUsecase {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn context(&self) -> Option<Arc<AppContext>> {
|
||||
self.context.clone()
|
||||
}
|
||||
|
||||
fn bucket_metadata_sys(&self) -> Option<Arc<RwLock<metadata_sys::BucketMetadataSys>>> {
|
||||
self.context.as_ref().and_then(|context| context.bucket_metadata().handle())
|
||||
}
|
||||
@@ -216,6 +199,35 @@ impl DefaultObjectUsecase {
|
||||
.unwrap_or_else(|| RustFSBufferConfig::default().base_config.default_unknown)
|
||||
}
|
||||
|
||||
async fn check_bucket_quota(&self, bucket: &str, op: QuotaOperation, size: u64) -> S3Result<()> {
|
||||
let Some(metadata_sys) = self.bucket_metadata_sys() else {
|
||||
return Ok(());
|
||||
};
|
||||
let quota_checker = QuotaChecker::new(metadata_sys);
|
||||
match quota_checker.check_quota(bucket, op, size).await {
|
||||
Ok(result) if !result.allowed => Err(S3Error::with_message(
|
||||
S3ErrorCode::InvalidRequest,
|
||||
format!(
|
||||
"Bucket quota exceeded. Current usage: {} bytes, limit: {} bytes",
|
||||
result.current_usage.unwrap_or(0),
|
||||
result.quota_limit.unwrap_or(0)
|
||||
),
|
||||
)),
|
||||
Err(e) => {
|
||||
warn!("Quota check failed for bucket {bucket}: {e}, allowing operation");
|
||||
Ok(())
|
||||
}
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
fn spawn_cache_invalidation(bucket: String, key: String, version_id: Option<String>) {
|
||||
let manager = get_concurrency_manager();
|
||||
tokio::spawn(async move {
|
||||
manager.invalidate_cache_versioned(&bucket, &key, version_id.as_deref()).await;
|
||||
});
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self, _fs, req))]
|
||||
pub async fn execute_put_object(&self, _fs: &FS, req: S3Request<PutObjectInput>) -> S3Result<S3Response<PutObjectOutput>> {
|
||||
if let Some(context) = &self.context {
|
||||
@@ -273,32 +285,9 @@ impl DefaultObjectUsecase {
|
||||
return Err(s3_error!(AccessDenied, "Access Denied"));
|
||||
}
|
||||
|
||||
// check quota for put operation
|
||||
if let Some(size) = content_length
|
||||
&& let Some(metadata_sys) = self.bucket_metadata_sys()
|
||||
{
|
||||
let quota_checker = QuotaChecker::new(metadata_sys);
|
||||
|
||||
match quota_checker
|
||||
.check_quota(&bucket, QuotaOperation::PutObject, size as u64)
|
||||
.await
|
||||
{
|
||||
Ok(check_result) => {
|
||||
if !check_result.allowed {
|
||||
return Err(S3Error::with_message(
|
||||
S3ErrorCode::InvalidRequest,
|
||||
format!(
|
||||
"Bucket quota exceeded. Current usage: {} bytes, limit: {} bytes",
|
||||
check_result.current_usage.unwrap_or(0),
|
||||
check_result.quota_limit.unwrap_or(0)
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Quota check failed for bucket {}: {}, allowing operation", bucket, e);
|
||||
}
|
||||
}
|
||||
if let Some(size) = content_length {
|
||||
self.check_bucket_quota(&bucket, QuotaOperation::PutObject, size as u64)
|
||||
.await?;
|
||||
}
|
||||
|
||||
let Some(body) = body else { return Err(s3_error!(IncompleteBody)) };
|
||||
@@ -330,10 +319,6 @@ impl DefaultObjectUsecase {
|
||||
StreamReader::new(body.map(|f| f.map_err(|e| std::io::Error::other(e.to_string())))),
|
||||
);
|
||||
|
||||
// let body = Box::new(StreamReader::new(body.map(|f| f.map_err(|e| std::io::Error::other(e.to_string())))));
|
||||
|
||||
// let mut reader = PutObjReader::new(body, content_length as usize);
|
||||
|
||||
let store = get_validated_store(&bucket).await?;
|
||||
|
||||
// TDD: Get bucket default encryption configuration
|
||||
@@ -514,10 +499,6 @@ impl DefaultObjectUsecase {
|
||||
// Fast in-memory update for immediate quota consistency
|
||||
rustfs_ecstore::data_usage::increment_bucket_usage_memory(&bucket, obj_info.size as u64).await;
|
||||
|
||||
// Invalidate cache for the written object to prevent stale data
|
||||
let manager = get_concurrency_manager();
|
||||
let put_bucket = bucket.clone();
|
||||
let put_key = key.clone();
|
||||
let put_version = obj_info.version_id.map(|v| v.to_string());
|
||||
|
||||
helper = helper.object(obj_info.clone());
|
||||
@@ -525,12 +506,7 @@ impl DefaultObjectUsecase {
|
||||
helper = helper.version_id(version_id.clone());
|
||||
}
|
||||
|
||||
let put_version_clone = put_version.clone();
|
||||
tokio::spawn(async move {
|
||||
manager
|
||||
.invalidate_cache_versioned(&put_bucket, &put_key, put_version_clone.as_deref())
|
||||
.await;
|
||||
});
|
||||
Self::spawn_cache_invalidation(bucket.clone(), key.clone(), put_version.clone());
|
||||
|
||||
let e_tag = obj_info.etag.clone().map(|etag| to_s3s_etag(&etag));
|
||||
|
||||
@@ -543,77 +519,35 @@ impl DefaultObjectUsecase {
|
||||
schedule_replication(obj_info, store, dsc, ReplicationType::Object).await;
|
||||
}
|
||||
|
||||
let mut checksum_crc32 = input.checksum_crc32;
|
||||
let mut checksum_crc32c = input.checksum_crc32c;
|
||||
let mut checksum_sha1 = input.checksum_sha1;
|
||||
let mut checksum_sha256 = input.checksum_sha256;
|
||||
let mut checksum_crc64nvme = input.checksum_crc64nvme;
|
||||
|
||||
if let Some(alg) = &input.checksum_algorithm
|
||||
&& let Some(Some(checksum_str)) = req.trailing_headers.as_ref().map(|trailer| {
|
||||
let key = match alg.as_str() {
|
||||
ChecksumAlgorithm::CRC32 => rustfs_rio::ChecksumType::CRC32.key(),
|
||||
ChecksumAlgorithm::CRC32C => rustfs_rio::ChecksumType::CRC32C.key(),
|
||||
ChecksumAlgorithm::SHA1 => rustfs_rio::ChecksumType::SHA1.key(),
|
||||
ChecksumAlgorithm::SHA256 => rustfs_rio::ChecksumType::SHA256.key(),
|
||||
ChecksumAlgorithm::CRC64NVME => rustfs_rio::ChecksumType::CRC64_NVME.key(),
|
||||
_ => return None,
|
||||
};
|
||||
trailer.read(|headers| {
|
||||
headers
|
||||
.get(key.unwrap_or_default())
|
||||
.and_then(|value| value.to_str().ok().map(|s| s.to_string()))
|
||||
})
|
||||
})
|
||||
{
|
||||
match alg.as_str() {
|
||||
ChecksumAlgorithm::CRC32 => checksum_crc32 = checksum_str,
|
||||
ChecksumAlgorithm::CRC32C => checksum_crc32c = checksum_str,
|
||||
ChecksumAlgorithm::SHA1 => checksum_sha1 = checksum_str,
|
||||
ChecksumAlgorithm::SHA256 => checksum_sha256 = checksum_str,
|
||||
ChecksumAlgorithm::CRC64NVME => checksum_crc64nvme = checksum_str,
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
let mut checksums = PutObjectChecksums {
|
||||
crc32: input.checksum_crc32,
|
||||
crc32c: input.checksum_crc32c,
|
||||
sha1: input.checksum_sha1,
|
||||
sha256: input.checksum_sha256,
|
||||
crc64nvme: input.checksum_crc64nvme,
|
||||
};
|
||||
apply_trailing_checksums(
|
||||
input.checksum_algorithm.as_ref().map(|a| a.as_str()),
|
||||
&req.trailing_headers,
|
||||
&mut checksums,
|
||||
);
|
||||
|
||||
let output = PutObjectOutput {
|
||||
e_tag,
|
||||
server_side_encryption: effective_sse, // TDD: Return effective encryption config
|
||||
server_side_encryption: effective_sse,
|
||||
sse_customer_algorithm: sse_customer_algorithm.clone(),
|
||||
sse_customer_key_md5: sse_customer_key_md5.clone(),
|
||||
ssekms_key_id: effective_kms_key_id, // TDD: Return effective KMS key ID
|
||||
checksum_crc32,
|
||||
checksum_crc32c,
|
||||
checksum_sha1,
|
||||
checksum_sha256,
|
||||
checksum_crc64nvme,
|
||||
ssekms_key_id: effective_kms_key_id,
|
||||
checksum_crc32: checksums.crc32,
|
||||
checksum_crc32c: checksums.crc32c,
|
||||
checksum_sha1: checksums.sha1,
|
||||
checksum_sha256: checksums.sha256,
|
||||
checksum_crc64nvme: checksums.crc64nvme,
|
||||
version_id: put_version,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
// TODO fix response for POST Policy (multipart/form-data) ,wait s3s crate update,fix issue #1564
|
||||
// // If it is a POST Policy(multipart/form-data) path, the PutObjectInput carries the success_action_* field
|
||||
// // Here, the response is uniformly rewritten, with the default being 204, redirect prioritizing 303, and status supporting 200/201/204
|
||||
// if input.success_action_status.is_some() || input.success_action_redirect.is_some() {
|
||||
// let mut form_fields = HashMap::<String, String>::new();
|
||||
// if let Some(v) = &input.success_action_status {
|
||||
// form_fields.insert("success_action_status".to_string(), v.to_string());
|
||||
// }
|
||||
// if let Some(v) = &input.success_action_redirect {
|
||||
// form_fields.insert("success_action_redirect".to_string(), v.to_string());
|
||||
// }
|
||||
//
|
||||
// // obj_info.etag has been converted to e_tag (s3s etag) above, so try to pass the original string here
|
||||
// let etag_str = e_tag.as_ref().map(|v| v.as_str());
|
||||
//
|
||||
// // Returns using POST semantics: 204/303/201/200
|
||||
// let resp = build_post_object_success_response(&form_fields, &bucket, &key, etag_str, None)?;
|
||||
//
|
||||
// // Keep helper event complete (note: (StatusCode, Body) is returned here instead of PutObjectOutput)
|
||||
// let result = Ok(resp);
|
||||
// let _ = helper.complete(&result);
|
||||
// return result;
|
||||
// }
|
||||
// TODO fix response for POST Policy (multipart/form-data), wait s3s crate update, fix issue #1564
|
||||
|
||||
let result = Ok(S3Response::new(output));
|
||||
let _ = helper.complete(&result);
|
||||
@@ -1200,7 +1134,10 @@ impl DefaultObjectUsecase {
|
||||
// based on the wait time. Longer wait times indicate higher system
|
||||
// load, which triggers more conservative I/O parameters.
|
||||
let permit_wait_start = std::time::Instant::now();
|
||||
let _disk_permit = manager.acquire_disk_read_permit().await;
|
||||
let _disk_permit = manager
|
||||
.acquire_disk_read_permit()
|
||||
.await
|
||||
.map_err(|_| s3_error!(InternalError, "disk read semaphore closed"))?;
|
||||
let permit_wait_duration = permit_wait_start.elapsed();
|
||||
|
||||
// Calculate adaptive I/O strategy from permit wait time
|
||||
@@ -2107,34 +2044,9 @@ impl DefaultObjectUsecase {
|
||||
src_info.user_defined.insert(k, v);
|
||||
}
|
||||
|
||||
// check quota for copy operation
|
||||
let has_bucket_metadata = if let Some(metadata_sys) = self.bucket_metadata_sys() {
|
||||
let quota_checker = QuotaChecker::new(metadata_sys);
|
||||
|
||||
match quota_checker
|
||||
.check_quota(&bucket, QuotaOperation::CopyObject, src_info.size as u64)
|
||||
.await
|
||||
{
|
||||
Ok(check_result) => {
|
||||
if !check_result.allowed {
|
||||
return Err(S3Error::with_message(
|
||||
S3ErrorCode::InvalidRequest,
|
||||
format!(
|
||||
"Bucket quota exceeded. Current usage: {} bytes, limit: {} bytes",
|
||||
check_result.current_usage.unwrap_or(0),
|
||||
check_result.quota_limit.unwrap_or(0)
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Quota check failed for bucket {}: {}, allowing operation", bucket, e);
|
||||
}
|
||||
}
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
self.check_bucket_quota(&bucket, QuotaOperation::CopyObject, src_info.size as u64)
|
||||
.await?;
|
||||
let has_bucket_metadata = self.bucket_metadata_sys().is_some();
|
||||
|
||||
let oi = store
|
||||
.copy_object(&src_bucket, &src_key, &bucket, &key, &mut src_info, &src_opts, &dst_opts)
|
||||
@@ -2146,17 +2058,8 @@ impl DefaultObjectUsecase {
|
||||
rustfs_ecstore::data_usage::increment_bucket_usage_memory(&bucket, oi.size as u64).await;
|
||||
}
|
||||
|
||||
// Invalidate cache for the destination object to prevent stale data
|
||||
let manager = get_concurrency_manager();
|
||||
let dest_bucket = bucket.clone();
|
||||
let dest_key = key.clone();
|
||||
let dest_version = oi.version_id.map(|v| v.to_string());
|
||||
let dest_version_clone = dest_version.clone();
|
||||
tokio::spawn(async move {
|
||||
manager
|
||||
.invalidate_cache_versioned(&dest_bucket, &dest_key, dest_version_clone.as_deref())
|
||||
.await;
|
||||
});
|
||||
Self::spawn_cache_invalidation(bucket.clone(), key.clone(), dest_version.clone());
|
||||
|
||||
// warn!("copy_object oi {:?}", &oi);
|
||||
let object_info = oi.clone();
|
||||
@@ -2254,7 +2157,7 @@ impl DefaultObjectUsecase {
|
||||
};
|
||||
|
||||
{
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = req_info_mut(&mut req)?;
|
||||
req_info.bucket = Some(bucket.clone());
|
||||
req_info.object = Some(obj_id.key.clone());
|
||||
req_info.version_id = version_id.clone();
|
||||
@@ -2606,16 +2509,7 @@ impl DefaultObjectUsecase {
|
||||
// Fast in-memory update for immediate quota consistency
|
||||
rustfs_ecstore::data_usage::decrement_bucket_usage_memory(&bucket, obj_info.size as u64).await;
|
||||
|
||||
// Invalidate cache for the deleted object
|
||||
let manager = get_concurrency_manager();
|
||||
let del_bucket = bucket.clone();
|
||||
let del_key = key.clone();
|
||||
let del_version = obj_info.version_id.map(|v| v.to_string());
|
||||
tokio::spawn(async move {
|
||||
manager
|
||||
.invalidate_cache_versioned(&del_bucket, &del_key, del_version.as_deref())
|
||||
.await;
|
||||
});
|
||||
Self::spawn_cache_invalidation(bucket.clone(), key.clone(), obj_info.version_id.map(|v| v.to_string()));
|
||||
|
||||
if obj_info.name.is_empty() {
|
||||
if replicate_force_delete {
|
||||
@@ -3489,49 +3383,30 @@ impl DefaultObjectUsecase {
|
||||
}
|
||||
}
|
||||
|
||||
let mut checksum_crc32 = input.checksum_crc32;
|
||||
let mut checksum_crc32c = input.checksum_crc32c;
|
||||
let mut checksum_sha1 = input.checksum_sha1;
|
||||
let mut checksum_sha256 = input.checksum_sha256;
|
||||
let mut checksum_crc64nvme = input.checksum_crc64nvme;
|
||||
|
||||
if let Some(alg) = &input.checksum_algorithm
|
||||
&& let Some(Some(checksum_str)) = req.trailing_headers.as_ref().map(|trailer| {
|
||||
let key = match alg.as_str() {
|
||||
ChecksumAlgorithm::CRC32 => rustfs_rio::ChecksumType::CRC32.key(),
|
||||
ChecksumAlgorithm::CRC32C => rustfs_rio::ChecksumType::CRC32C.key(),
|
||||
ChecksumAlgorithm::SHA1 => rustfs_rio::ChecksumType::SHA1.key(),
|
||||
ChecksumAlgorithm::SHA256 => rustfs_rio::ChecksumType::SHA256.key(),
|
||||
ChecksumAlgorithm::CRC64NVME => rustfs_rio::ChecksumType::CRC64_NVME.key(),
|
||||
_ => return None,
|
||||
};
|
||||
trailer.read(|headers| {
|
||||
headers
|
||||
.get(key.unwrap_or_default())
|
||||
.and_then(|value| value.to_str().ok().map(|s| s.to_string()))
|
||||
})
|
||||
})
|
||||
{
|
||||
match alg.as_str() {
|
||||
ChecksumAlgorithm::CRC32 => checksum_crc32 = checksum_str,
|
||||
ChecksumAlgorithm::CRC32C => checksum_crc32c = checksum_str,
|
||||
ChecksumAlgorithm::SHA1 => checksum_sha1 = checksum_str,
|
||||
ChecksumAlgorithm::SHA256 => checksum_sha256 = checksum_str,
|
||||
ChecksumAlgorithm::CRC64NVME => checksum_crc64nvme = checksum_str,
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
let mut checksums = PutObjectChecksums {
|
||||
crc32: input.checksum_crc32,
|
||||
crc32c: input.checksum_crc32c,
|
||||
sha1: input.checksum_sha1,
|
||||
sha256: input.checksum_sha256,
|
||||
crc64nvme: input.checksum_crc64nvme,
|
||||
};
|
||||
apply_trailing_checksums(
|
||||
input.checksum_algorithm.as_ref().map(|a| a.as_str()),
|
||||
&req.trailing_headers,
|
||||
&mut checksums,
|
||||
);
|
||||
|
||||
warn!(
|
||||
"put object extract checksum_crc32={checksum_crc32:?}, checksum_crc32c={checksum_crc32c:?}, checksum_sha1={checksum_sha1:?}, checksum_sha256={checksum_sha256:?}, checksum_crc64nvme={checksum_crc64nvme:?}",
|
||||
"put object extract checksum_crc32={:?}, checksum_crc32c={:?}, checksum_sha1={:?}, checksum_sha256={:?}, checksum_crc64nvme={:?}",
|
||||
checksums.crc32, checksums.crc32c, checksums.sha1, checksums.sha256, checksums.crc64nvme,
|
||||
);
|
||||
|
||||
let output = PutObjectOutput {
|
||||
checksum_crc32,
|
||||
checksum_crc32c,
|
||||
checksum_sha1,
|
||||
checksum_sha256,
|
||||
checksum_crc64nvme,
|
||||
checksum_crc32: checksums.crc32,
|
||||
checksum_crc32c: checksums.crc32c,
|
||||
checksum_sha1: checksums.sha1,
|
||||
checksum_sha256: checksums.sha256,
|
||||
checksum_crc64nvme: checksums.crc64nvme,
|
||||
..Default::default()
|
||||
};
|
||||
let result = Ok(S3Response::new(output));
|
||||
@@ -3540,30 +3415,6 @@ impl DefaultObjectUsecase {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl ObjectUsecase for DefaultObjectUsecase {
|
||||
async fn put_object(&self, req: PutObjectRequest) -> ObjectUsecaseResult<PutObjectResponse> {
|
||||
let _ = req;
|
||||
Err(ApiError::from(StorageError::other(
|
||||
"DefaultObjectUsecase::put_object DTO path is not implemented yet",
|
||||
)))
|
||||
}
|
||||
|
||||
async fn get_object(&self, req: GetObjectRequest) -> ObjectUsecaseResult<GetObjectResponse> {
|
||||
let _ = req;
|
||||
Err(ApiError::from(StorageError::other(
|
||||
"DefaultObjectUsecase::get_object DTO path is not implemented yet",
|
||||
)))
|
||||
}
|
||||
|
||||
async fn delete_object(&self, req: DeleteObjectRequest) -> ObjectUsecaseResult<DeleteObjectResponse> {
|
||||
let _ = req;
|
||||
Err(ApiError::from(StorageError::other(
|
||||
"DefaultObjectUsecase::delete_object DTO path is not implemented yet",
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -12,8 +12,6 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#![allow(dead_code)]
|
||||
|
||||
//! Adaptive buffer sizing optimization for different workload types.
|
||||
//!
|
||||
//! This module provides intelligent buffer size selection based on file size and workload profile
|
||||
@@ -86,6 +84,7 @@ pub enum WorkloadProfile {
|
||||
/// Secure storage: security first, memory constrained for compliance
|
||||
SecureStorage,
|
||||
/// Custom configuration for specialized requirements
|
||||
#[allow(dead_code)]
|
||||
Custom(BufferConfig),
|
||||
}
|
||||
|
||||
@@ -107,6 +106,7 @@ pub struct BufferConfig {
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RustFSBufferConfig {
|
||||
/// Selected workload profile
|
||||
#[allow(dead_code)]
|
||||
pub workload: WorkloadProfile,
|
||||
/// Computed buffer configuration (either from profile or custom)
|
||||
pub base_config: BufferConfig,
|
||||
@@ -309,6 +309,7 @@ impl BufferConfig {
|
||||
}
|
||||
|
||||
/// Validate the buffer configuration
|
||||
#[allow(dead_code)]
|
||||
pub fn validate(&self) -> Result<(), String> {
|
||||
if self.min_size == 0 {
|
||||
return Err("min_size must be greater than 0".to_string());
|
||||
|
||||
@@ -55,21 +55,6 @@ pub fn license_check() -> Result<()> {
|
||||
Ok(())
|
||||
});
|
||||
|
||||
// let invalid_license = config::get_config().license.as_ref().map(|license| {
|
||||
// if license.is_empty() {
|
||||
// error!("License is empty");
|
||||
// return Err(Error::other("Incorrect license, please contact RustFS.".to_string()));
|
||||
// }
|
||||
// let token = appauth::token::parse_license(license)?;
|
||||
// if token.expired < SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs() {
|
||||
// error!("License expired");
|
||||
// return Err(Error::other("Incorrect license, please contact RustFS.".to_string()));
|
||||
// }
|
||||
|
||||
// info!("License is valid ! expired at {}", token.expired);
|
||||
// Ok(())
|
||||
// });
|
||||
|
||||
if invalid_license.is_none() || invalid_license.is_some_and(|v| v.is_err()) {
|
||||
return Err(Error::other("Incorrect license, please contact RustFS."));
|
||||
}
|
||||
|
||||
+8
-11
@@ -46,7 +46,6 @@ use rustfs_common::{GlobalReadiness, SystemStage, set_global_addr};
|
||||
use rustfs_credentials::init_global_action_credentials;
|
||||
use rustfs_ecstore::store::init_lock_clients;
|
||||
use rustfs_ecstore::{
|
||||
StorageAPI,
|
||||
bucket::metadata_sys::init_bucket_metadata_sys,
|
||||
bucket::replication::{get_global_replication_pool, init_background_replication},
|
||||
config as ecconfig,
|
||||
@@ -56,6 +55,7 @@ use rustfs_ecstore::{
|
||||
set_global_endpoints,
|
||||
store::ECStore,
|
||||
store::init_local_disks,
|
||||
store_api::BucketOperations,
|
||||
store_api::BucketOptions,
|
||||
update_erasure_type,
|
||||
};
|
||||
@@ -94,8 +94,8 @@ fn main() {
|
||||
.expect("Failed to build Tokio runtime");
|
||||
let result = runtime.block_on(async_main());
|
||||
if let Err(ref e) = result {
|
||||
eprintln!("{} Server encountered an error and is shutting down: {}", jiff::Zoned::now(), e);
|
||||
error!("Server encountered an error and is shutting down: {}", e);
|
||||
// Use eprintln as tracing may not be initialized at this point
|
||||
eprintln!("[FATAL] Server encountered an error and is shutting down: {e}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
@@ -110,7 +110,8 @@ async fn async_main() -> Result<()> {
|
||||
let guard = match init_obs(Some(config.clone().obs_endpoint)).await {
|
||||
Ok(g) => g,
|
||||
Err(e) => {
|
||||
println!("Failed to initialize observability: {e}");
|
||||
// Use eprintln as tracing is not yet initialized
|
||||
eprintln!("[FATAL] Failed to initialize observability: {e}");
|
||||
return Err(Error::other(e));
|
||||
}
|
||||
};
|
||||
@@ -434,13 +435,13 @@ async fn run(config: config::Config) -> Result<()> {
|
||||
init_metrics_system(ctx.clone());
|
||||
}
|
||||
|
||||
println!(
|
||||
info!(
|
||||
target: "rustfs::main::run",
|
||||
"RustFS server version: {} started successfully at {}, current time: {}",
|
||||
version::get_version(),
|
||||
&server_address,
|
||||
jiff::Zoned::now()
|
||||
);
|
||||
info!(target: "rustfs::main::run","server started successfully at {}", &server_address);
|
||||
// 4. Mark as Full Ready now that critical components are warm
|
||||
readiness.mark_stage(SystemStage::FullReady);
|
||||
|
||||
@@ -580,9 +581,5 @@ async fn handle_shutdown(
|
||||
|
||||
// the last updated status is stopped
|
||||
state_manager.update(ServiceState::Stopped);
|
||||
info!(
|
||||
target: "rustfs::main::handle_shutdown",
|
||||
"Server stopped current "
|
||||
);
|
||||
println!("Server stopped successfully.");
|
||||
info!(target: "rustfs::main::handle_shutdown", "Server stopped successfully.");
|
||||
}
|
||||
|
||||
@@ -187,14 +187,9 @@ pub async fn start_http_server(
|
||||
"Console WebUI (localhost): {protocol}://127.0.0.1:{server_port}/rustfs/console/index.html",
|
||||
|
||||
);
|
||||
|
||||
println!("Console WebUI Start Time: {now_time}");
|
||||
println!("Console WebUI available at: {protocol}://{local_ip_str}:{server_port}/rustfs/console/index.html");
|
||||
println!("Console WebUI (localhost): {protocol}://127.0.0.1:{server_port}/rustfs/console/index.html");
|
||||
} else {
|
||||
info!(target: "rustfs::main::startup","RustFS API: {api_endpoints} {localhost_endpoint}");
|
||||
println!("RustFS Http API: {api_endpoints} {localhost_endpoint}");
|
||||
println!("RustFS Start Time: {now_time}");
|
||||
info!(target: "rustfs::main::startup", "RustFS API: {api_endpoints} {localhost_endpoint}");
|
||||
info!(target: "rustfs::main::startup", "RustFS Start Time: {now_time}");
|
||||
if rustfs_credentials::DEFAULT_ACCESS_KEY.eq(&config.access_key)
|
||||
&& rustfs_credentials::DEFAULT_SECRET_KEY.eq(&config.secret_key)
|
||||
{
|
||||
|
||||
@@ -129,29 +129,23 @@ pub(crate) fn get_tokio_runtime_builder() -> tokio::runtime::Builder {
|
||||
if print_tokio_thread_enable() {
|
||||
builder
|
||||
.on_thread_start(|| {
|
||||
let id = std::thread::current().id();
|
||||
println!(
|
||||
"RustFS Worker Thread running - initializing resources time: {:?}, thread id: {:?}",
|
||||
jiff::Zoned::now().to_string(),
|
||||
id
|
||||
);
|
||||
tracing::trace!(thread_id = ?std::thread::current().id(), "worker thread started");
|
||||
})
|
||||
.on_thread_stop(|| {
|
||||
let id = std::thread::current().id();
|
||||
println!(
|
||||
"RustFS Worker Thread stopping - cleaning up resources time: {:?}, thread id: {:?}",
|
||||
jiff::Zoned::now().to_string(),
|
||||
id
|
||||
)
|
||||
tracing::trace!(thread_id = ?std::thread::current().id(), "worker thread stopped");
|
||||
});
|
||||
}
|
||||
if !rustfs_obs::is_production_environment() {
|
||||
println!(
|
||||
"Starting Tokio runtime with configured parameters:\n\
|
||||
worker_threads: {worker_threads}, max_blocking_threads: {max_blocking_threads}, \
|
||||
thread_stack_size: {thread_stack_size}, thread_keep_alive: {thread_keep_alive}, \
|
||||
global_queue_interval: {global_queue_interval}, event_interval: {event_interval}, \
|
||||
max_io_events_per_tick: {max_io_events_per_tick}, thread_name: {thread_name}"
|
||||
tracing::debug!(
|
||||
worker_threads,
|
||||
max_blocking_threads,
|
||||
thread_stack_size,
|
||||
thread_keep_alive,
|
||||
global_queue_interval,
|
||||
event_interval,
|
||||
max_io_events_per_tick,
|
||||
thread_name,
|
||||
"Starting Tokio runtime with configured parameters"
|
||||
);
|
||||
}
|
||||
builder
|
||||
|
||||
@@ -24,11 +24,11 @@ use crate::error::ApiError;
|
||||
use crate::license::license_check;
|
||||
use crate::server::RemoteAddr;
|
||||
use metrics::counter;
|
||||
use rustfs_ecstore::StorageAPI;
|
||||
use rustfs_ecstore::bucket::metadata_sys;
|
||||
use rustfs_ecstore::bucket::policy_sys::PolicySys;
|
||||
use rustfs_ecstore::error::StorageError;
|
||||
use rustfs_ecstore::new_object_layer_fn;
|
||||
use rustfs_ecstore::store_api::ObjectOperations;
|
||||
use rustfs_iam::error::Error as IamError;
|
||||
use rustfs_policy::policy::action::{Action, S3Action};
|
||||
use rustfs_policy::policy::{Args, BucketPolicyArgs};
|
||||
@@ -37,7 +37,6 @@ use s3s::access::{S3Access, S3AccessContext};
|
||||
use s3s::{S3Error, S3ErrorCode, S3Request, S3Result, dto::*, s3_error};
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Default, Clone, Debug)]
|
||||
pub(crate) struct ReqInfo {
|
||||
pub cred: Option<rustfs_credentials::Credentials>,
|
||||
@@ -45,6 +44,7 @@ pub(crate) struct ReqInfo {
|
||||
pub bucket: Option<String>,
|
||||
pub object: Option<String>,
|
||||
pub version_id: Option<String>,
|
||||
#[allow(dead_code)]
|
||||
pub region: Option<s3s::region::Region>,
|
||||
}
|
||||
|
||||
@@ -201,6 +201,23 @@ async fn check_acl_access<T>(req: &S3Request<T>, req_info: &ReqInfo, action: &Ac
|
||||
Ok(acl_allows(&acl, user_id, is_authenticated, permission, ignore_public_acls))
|
||||
}
|
||||
|
||||
pub(crate) fn req_info_ref<T>(req: &S3Request<T>) -> S3Result<&ReqInfo> {
|
||||
req.extensions
|
||||
.get::<ReqInfo>()
|
||||
.ok_or_else(|| s3_error!(InternalError, "ReqInfo not found in request extensions"))
|
||||
}
|
||||
|
||||
pub(crate) fn req_info_mut<T>(req: &mut S3Request<T>) -> S3Result<&mut ReqInfo> {
|
||||
req.extensions
|
||||
.get_mut::<ReqInfo>()
|
||||
.ok_or_else(|| s3_error!(InternalError, "ReqInfo not found in request extensions"))
|
||||
}
|
||||
|
||||
fn ext_req_info_mut(ext: &mut http::Extensions) -> S3Result<&mut ReqInfo> {
|
||||
ext.get_mut::<ReqInfo>()
|
||||
.ok_or_else(|| s3_error!(InternalError, "ReqInfo not found in request extensions"))
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct ObjectTagConditions(pub HashMap<String, Vec<String>>);
|
||||
|
||||
@@ -241,7 +258,7 @@ pub async fn authorize_request<T>(req: &mut S3Request<T>, action: Action) -> S3R
|
||||
let remote_addr = req.extensions.get::<Option<RemoteAddr>>().and_then(|opt| opt.map(|a| a.0));
|
||||
let object_tag_conditions = req.extensions.get::<ObjectTagConditions>().cloned();
|
||||
|
||||
let req_info = req.extensions.get::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = req_info_ref(req)?;
|
||||
|
||||
if let Some(cred) = &req_info.cred {
|
||||
let Ok(iam_store) = rustfs_iam::get() else {
|
||||
@@ -550,7 +567,7 @@ impl S3Access for FS {
|
||||
async fn create_bucket(&self, req: &mut S3Request<CreateBucketInput>) -> S3Result<()> {
|
||||
license_check().map_err(|er| s3_error!(AccessDenied, "{:?}", er.to_string()))?;
|
||||
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
|
||||
authorize_request(req, Action::S3Action(S3Action::CreateBucketAction)).await?;
|
||||
@@ -588,7 +605,7 @@ impl S3Access for FS {
|
||||
}
|
||||
};
|
||||
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(src_bucket.clone());
|
||||
req_info.object = Some(src_key.clone());
|
||||
req_info.version_id = version_id.clone();
|
||||
@@ -601,7 +618,7 @@ impl S3Access for FS {
|
||||
authorize_request(req, Action::S3Action(S3Action::GetObjectAction)).await?;
|
||||
}
|
||||
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
req_info.object = Some(req.input.key.clone());
|
||||
@@ -622,7 +639,7 @@ impl S3Access for FS {
|
||||
///
|
||||
/// This method returns `Ok(())` by default.
|
||||
async fn delete_bucket(&self, req: &mut S3Request<DeleteBucketInput>) -> S3Result<()> {
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
|
||||
authorize_request(req, Action::S3Action(S3Action::DeleteBucketAction)).await?;
|
||||
@@ -647,7 +664,7 @@ impl S3Access for FS {
|
||||
///
|
||||
/// This method returns `Ok(())` by default.
|
||||
async fn delete_bucket_cors(&self, req: &mut S3Request<DeleteBucketCorsInput>) -> S3Result<()> {
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
|
||||
authorize_request(req, Action::S3Action(S3Action::DeleteBucketCorsAction)).await
|
||||
@@ -657,7 +674,7 @@ impl S3Access for FS {
|
||||
///
|
||||
/// This method returns `Ok(())` by default.
|
||||
async fn delete_bucket_encryption(&self, req: &mut S3Request<DeleteBucketEncryptionInput>) -> S3Result<()> {
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
|
||||
authorize_request(req, Action::S3Action(S3Action::PutBucketEncryptionAction)).await
|
||||
@@ -687,7 +704,7 @@ impl S3Access for FS {
|
||||
///
|
||||
/// This method returns `Ok(())` by default.
|
||||
async fn delete_bucket_lifecycle(&self, req: &mut S3Request<DeleteBucketLifecycleInput>) -> S3Result<()> {
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
|
||||
authorize_request(req, Action::S3Action(S3Action::PutBucketLifecycleAction)).await
|
||||
@@ -714,7 +731,7 @@ impl S3Access for FS {
|
||||
///
|
||||
/// This method returns `Ok(())` by default.
|
||||
async fn delete_bucket_policy(&self, req: &mut S3Request<DeleteBucketPolicyInput>) -> S3Result<()> {
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
|
||||
authorize_request(req, Action::S3Action(S3Action::DeleteBucketPolicyAction)).await
|
||||
@@ -724,7 +741,7 @@ impl S3Access for FS {
|
||||
///
|
||||
/// This method returns `Ok(())` by default.
|
||||
async fn delete_bucket_replication(&self, req: &mut S3Request<DeleteBucketReplicationInput>) -> S3Result<()> {
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
|
||||
authorize_request(req, Action::S3Action(S3Action::PutReplicationConfigurationAction)).await
|
||||
@@ -734,7 +751,7 @@ impl S3Access for FS {
|
||||
///
|
||||
/// This method returns `Ok(())` by default.
|
||||
async fn delete_bucket_tagging(&self, req: &mut S3Request<DeleteBucketTaggingInput>) -> S3Result<()> {
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
|
||||
authorize_request(req, Action::S3Action(S3Action::PutBucketTaggingAction)).await
|
||||
@@ -751,7 +768,7 @@ impl S3Access for FS {
|
||||
///
|
||||
/// This method returns `Ok(())` by default.
|
||||
async fn delete_object(&self, req: &mut S3Request<DeleteObjectInput>) -> S3Result<()> {
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
req_info.object = Some(req.input.key.clone());
|
||||
req_info.version_id = req.input.version_id.clone();
|
||||
@@ -775,7 +792,7 @@ impl S3Access for FS {
|
||||
///
|
||||
/// This method returns `Ok(())` by default.
|
||||
async fn delete_object_tagging(&self, req: &mut S3Request<DeleteObjectTaggingInput>) -> S3Result<()> {
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
req_info.object = Some(req.input.key.clone());
|
||||
req_info.version_id = req.input.version_id.clone();
|
||||
@@ -797,7 +814,7 @@ impl S3Access for FS {
|
||||
///
|
||||
/// This method returns `Ok(())` by default.
|
||||
async fn delete_objects(&self, req: &mut S3Request<DeleteObjectsInput>) -> S3Result<()> {
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
req_info.object = None;
|
||||
req_info.version_id = None;
|
||||
@@ -816,7 +833,7 @@ impl S3Access for FS {
|
||||
///
|
||||
/// This method returns `Ok(())` by default.
|
||||
async fn delete_public_access_block(&self, req: &mut S3Request<DeletePublicAccessBlockInput>) -> S3Result<()> {
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
|
||||
authorize_request(req, Action::S3Action(S3Action::DeleteBucketPublicAccessBlockAction)).await
|
||||
@@ -836,7 +853,7 @@ impl S3Access for FS {
|
||||
///
|
||||
/// This method returns `Ok(())` by default.
|
||||
async fn get_bucket_acl(&self, req: &mut S3Request<GetBucketAclInput>) -> S3Result<()> {
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
|
||||
authorize_request(req, Action::S3Action(S3Action::GetBucketAclAction)).await
|
||||
@@ -856,7 +873,7 @@ impl S3Access for FS {
|
||||
///
|
||||
/// This method returns `Ok(())` by default.
|
||||
async fn get_bucket_cors(&self, req: &mut S3Request<GetBucketCorsInput>) -> S3Result<()> {
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
|
||||
authorize_request(req, Action::S3Action(S3Action::GetBucketCorsAction)).await
|
||||
@@ -866,7 +883,7 @@ impl S3Access for FS {
|
||||
///
|
||||
/// This method returns `Ok(())` by default.
|
||||
async fn get_bucket_encryption(&self, req: &mut S3Request<GetBucketEncryptionInput>) -> S3Result<()> {
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
|
||||
authorize_request(req, Action::S3Action(S3Action::GetBucketEncryptionAction)).await
|
||||
@@ -899,7 +916,7 @@ impl S3Access for FS {
|
||||
&self,
|
||||
req: &mut S3Request<GetBucketLifecycleConfigurationInput>,
|
||||
) -> S3Result<()> {
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
|
||||
authorize_request(req, Action::S3Action(S3Action::GetBucketLifecycleAction)).await
|
||||
@@ -909,7 +926,7 @@ impl S3Access for FS {
|
||||
///
|
||||
/// This method returns `Ok(())` by default.
|
||||
async fn get_bucket_location(&self, req: &mut S3Request<GetBucketLocationInput>) -> S3Result<()> {
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
|
||||
authorize_request(req, Action::S3Action(S3Action::GetBucketLocationAction)).await
|
||||
@@ -936,7 +953,7 @@ impl S3Access for FS {
|
||||
&self,
|
||||
req: &mut S3Request<GetBucketNotificationConfigurationInput>,
|
||||
) -> S3Result<()> {
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
|
||||
authorize_request(req, Action::S3Action(S3Action::GetBucketNotificationAction)).await
|
||||
@@ -953,7 +970,7 @@ impl S3Access for FS {
|
||||
///
|
||||
/// This method returns `Ok(())` by default.
|
||||
async fn get_bucket_policy(&self, req: &mut S3Request<GetBucketPolicyInput>) -> S3Result<()> {
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
|
||||
authorize_request(req, get_bucket_policy_authorize_action()).await
|
||||
@@ -963,7 +980,7 @@ impl S3Access for FS {
|
||||
///
|
||||
/// This method returns `Ok(())` by default.
|
||||
async fn get_bucket_policy_status(&self, req: &mut S3Request<GetBucketPolicyStatusInput>) -> S3Result<()> {
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
|
||||
authorize_request(req, Action::S3Action(S3Action::GetBucketPolicyStatusAction)).await
|
||||
@@ -973,7 +990,7 @@ impl S3Access for FS {
|
||||
///
|
||||
/// This method returns `Ok(())` by default.
|
||||
async fn get_bucket_replication(&self, req: &mut S3Request<GetBucketReplicationInput>) -> S3Result<()> {
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
|
||||
authorize_request(req, Action::S3Action(S3Action::GetReplicationConfigurationAction)).await
|
||||
@@ -990,7 +1007,7 @@ impl S3Access for FS {
|
||||
///
|
||||
/// This method returns `Ok(())` by default.
|
||||
async fn get_bucket_tagging(&self, req: &mut S3Request<GetBucketTaggingInput>) -> S3Result<()> {
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
|
||||
authorize_request(req, Action::S3Action(S3Action::GetBucketTaggingAction)).await
|
||||
@@ -1000,7 +1017,7 @@ impl S3Access for FS {
|
||||
///
|
||||
/// This method returns `Ok(())` by default.
|
||||
async fn get_bucket_versioning(&self, req: &mut S3Request<GetBucketVersioningInput>) -> S3Result<()> {
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
|
||||
authorize_request(req, Action::S3Action(S3Action::GetBucketVersioningAction)).await
|
||||
@@ -1017,7 +1034,7 @@ impl S3Access for FS {
|
||||
///
|
||||
/// This method returns `Ok(())` by default.
|
||||
async fn get_object(&self, req: &mut S3Request<GetObjectInput>) -> S3Result<()> {
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
req_info.object = Some(req.input.key.clone());
|
||||
req_info.version_id = req.input.version_id.clone();
|
||||
@@ -1034,7 +1051,7 @@ impl S3Access for FS {
|
||||
///
|
||||
/// This method returns `Ok(())` by default.
|
||||
async fn get_object_acl(&self, req: &mut S3Request<GetObjectAclInput>) -> S3Result<()> {
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
req_info.object = Some(req.input.key.clone());
|
||||
req_info.version_id = req.input.version_id.clone();
|
||||
@@ -1051,7 +1068,7 @@ impl S3Access for FS {
|
||||
///
|
||||
/// This method returns `Ok(())` by default.
|
||||
async fn get_object_attributes(&self, req: &mut S3Request<GetObjectAttributesInput>) -> S3Result<()> {
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
req_info.object = Some(req.input.key.clone());
|
||||
req_info.version_id = req.input.version_id.clone();
|
||||
@@ -1081,7 +1098,7 @@ impl S3Access for FS {
|
||||
///
|
||||
/// This method returns `Ok(())` by default.
|
||||
async fn get_object_legal_hold(&self, req: &mut S3Request<GetObjectLegalHoldInput>) -> S3Result<()> {
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
req_info.object = Some(req.input.key.clone());
|
||||
req_info.version_id = req.input.version_id.clone();
|
||||
@@ -1093,7 +1110,7 @@ impl S3Access for FS {
|
||||
///
|
||||
/// This method returns `Ok(())` by default.
|
||||
async fn get_object_lock_configuration(&self, req: &mut S3Request<GetObjectLockConfigurationInput>) -> S3Result<()> {
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
|
||||
authorize_request(req, Action::S3Action(S3Action::GetBucketObjectLockConfigurationAction)).await
|
||||
@@ -1103,7 +1120,7 @@ impl S3Access for FS {
|
||||
///
|
||||
/// This method returns `Ok(())` by default.
|
||||
async fn get_object_retention(&self, req: &mut S3Request<GetObjectRetentionInput>) -> S3Result<()> {
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
req_info.object = Some(req.input.key.clone());
|
||||
req_info.version_id = req.input.version_id.clone();
|
||||
@@ -1115,7 +1132,7 @@ impl S3Access for FS {
|
||||
///
|
||||
/// This method returns `Ok(())` by default.
|
||||
async fn get_object_tagging(&self, req: &mut S3Request<GetObjectTaggingInput>) -> S3Result<()> {
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
req_info.object = Some(req.input.key.clone());
|
||||
req_info.version_id = req.input.version_id.clone();
|
||||
@@ -1139,7 +1156,7 @@ impl S3Access for FS {
|
||||
///
|
||||
/// This method returns `Ok(())` by default.
|
||||
async fn get_public_access_block(&self, req: &mut S3Request<GetPublicAccessBlockInput>) -> S3Result<()> {
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
|
||||
authorize_request(req, Action::S3Action(S3Action::GetBucketPublicAccessBlockAction)).await
|
||||
@@ -1149,7 +1166,7 @@ impl S3Access for FS {
|
||||
///
|
||||
/// This method returns `Ok(())` by default.
|
||||
async fn head_bucket(&self, req: &mut S3Request<HeadBucketInput>) -> S3Result<()> {
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
|
||||
authorize_request(req, Action::S3Action(S3Action::ListBucketAction)).await
|
||||
@@ -1159,7 +1176,7 @@ impl S3Access for FS {
|
||||
///
|
||||
/// This method returns `Ok(())` by default.
|
||||
async fn head_object(&self, req: &mut S3Request<HeadObjectInput>) -> S3Result<()> {
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
req_info.object = Some(req.input.key.clone());
|
||||
req_info.version_id = req.input.version_id.clone();
|
||||
@@ -1224,7 +1241,7 @@ impl S3Access for FS {
|
||||
///
|
||||
/// This method returns `Ok(())` by default.
|
||||
async fn list_multipart_uploads(&self, req: &mut S3Request<ListMultipartUploadsInput>) -> S3Result<()> {
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
|
||||
authorize_request(req, Action::S3Action(S3Action::ListBucketMultipartUploadsAction)).await
|
||||
@@ -1235,7 +1252,7 @@ impl S3Access for FS {
|
||||
/// Returns `Ok(())` if the request is allowed, or an error if access is denied or another
|
||||
/// authorization-related issue occurs.
|
||||
async fn list_object_versions(&self, req: &mut S3Request<ListObjectVersionsInput>) -> S3Result<()> {
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
authorize_request(req, Action::S3Action(S3Action::ListBucketVersionsAction)).await
|
||||
}
|
||||
@@ -1244,7 +1261,7 @@ impl S3Access for FS {
|
||||
///
|
||||
/// This method returns `Ok(())` by default.
|
||||
async fn list_objects(&self, req: &mut S3Request<ListObjectsInput>) -> S3Result<()> {
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
|
||||
authorize_request(req, Action::S3Action(S3Action::ListBucketAction)).await
|
||||
@@ -1254,7 +1271,7 @@ impl S3Access for FS {
|
||||
///
|
||||
/// This method returns `Ok(())` by default.
|
||||
async fn list_objects_v2(&self, req: &mut S3Request<ListObjectsV2Input>) -> S3Result<()> {
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
|
||||
authorize_request(req, Action::S3Action(S3Action::ListBucketAction)).await
|
||||
@@ -1281,7 +1298,7 @@ impl S3Access for FS {
|
||||
///
|
||||
/// This method returns `Ok(())` by default.
|
||||
async fn put_bucket_acl(&self, req: &mut S3Request<PutBucketAclInput>) -> S3Result<()> {
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
|
||||
authorize_request(req, Action::S3Action(S3Action::PutBucketAclAction)).await
|
||||
@@ -1301,7 +1318,7 @@ impl S3Access for FS {
|
||||
///
|
||||
/// This method returns `Ok(())` by default.
|
||||
async fn put_bucket_cors(&self, req: &mut S3Request<PutBucketCorsInput>) -> S3Result<()> {
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
|
||||
authorize_request(req, Action::S3Action(S3Action::PutBucketCorsAction)).await
|
||||
@@ -1311,7 +1328,7 @@ impl S3Access for FS {
|
||||
///
|
||||
/// This method returns `Ok(())` by default.
|
||||
async fn put_bucket_encryption(&self, req: &mut S3Request<PutBucketEncryptionInput>) -> S3Result<()> {
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
|
||||
authorize_request(req, Action::S3Action(S3Action::PutBucketEncryptionAction)).await
|
||||
@@ -1344,7 +1361,7 @@ impl S3Access for FS {
|
||||
&self,
|
||||
req: &mut S3Request<PutBucketLifecycleConfigurationInput>,
|
||||
) -> S3Result<()> {
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
|
||||
authorize_request(req, Action::S3Action(S3Action::PutBucketLifecycleAction)).await
|
||||
@@ -1371,7 +1388,7 @@ impl S3Access for FS {
|
||||
&self,
|
||||
req: &mut S3Request<PutBucketNotificationConfigurationInput>,
|
||||
) -> S3Result<()> {
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
|
||||
authorize_request(req, Action::S3Action(S3Action::PutBucketNotificationAction)).await
|
||||
@@ -1388,7 +1405,7 @@ impl S3Access for FS {
|
||||
///
|
||||
/// This method returns `Ok(())` by default.
|
||||
async fn put_bucket_policy(&self, req: &mut S3Request<PutBucketPolicyInput>) -> S3Result<()> {
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
|
||||
authorize_request(req, put_bucket_policy_authorize_action()).await
|
||||
@@ -1398,7 +1415,7 @@ impl S3Access for FS {
|
||||
///
|
||||
/// This method returns `Ok(())` by default.
|
||||
async fn put_bucket_replication(&self, req: &mut S3Request<PutBucketReplicationInput>) -> S3Result<()> {
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
|
||||
authorize_request(req, Action::S3Action(S3Action::PutReplicationConfigurationAction)).await
|
||||
@@ -1415,7 +1432,7 @@ impl S3Access for FS {
|
||||
///
|
||||
/// This method returns `Ok(())` by default.
|
||||
async fn put_bucket_tagging(&self, req: &mut S3Request<PutBucketTaggingInput>) -> S3Result<()> {
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
|
||||
authorize_request(req, Action::S3Action(S3Action::PutBucketTaggingAction)).await
|
||||
@@ -1425,7 +1442,7 @@ impl S3Access for FS {
|
||||
///
|
||||
/// This method returns `Ok(())` by default.
|
||||
async fn put_bucket_versioning(&self, req: &mut S3Request<PutBucketVersioningInput>) -> S3Result<()> {
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
|
||||
authorize_request(req, Action::S3Action(S3Action::PutBucketVersioningAction)).await
|
||||
@@ -1444,7 +1461,7 @@ impl S3Access for FS {
|
||||
async fn put_object(&self, req: &mut S3Request<PutObjectInput>) -> S3Result<()> {
|
||||
license_check().map_err(|er| s3_error!(AccessDenied, "{:?}", er.to_string()))?;
|
||||
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
req_info.object = Some(req.input.key.clone());
|
||||
req_info.version_id = req.input.version_id.clone();
|
||||
@@ -1456,7 +1473,7 @@ impl S3Access for FS {
|
||||
///
|
||||
/// This method returns `Ok(())` by default.
|
||||
async fn put_object_acl(&self, req: &mut S3Request<PutObjectAclInput>) -> S3Result<()> {
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
req_info.object = Some(req.input.key.clone());
|
||||
req_info.version_id = req.input.version_id.clone();
|
||||
@@ -1473,7 +1490,7 @@ impl S3Access for FS {
|
||||
///
|
||||
/// This method returns `Ok(())` by default.
|
||||
async fn put_object_legal_hold(&self, req: &mut S3Request<PutObjectLegalHoldInput>) -> S3Result<()> {
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
req_info.object = Some(req.input.key.clone());
|
||||
req_info.version_id = req.input.version_id.clone();
|
||||
@@ -1485,7 +1502,7 @@ impl S3Access for FS {
|
||||
///
|
||||
/// This method returns `Ok(())` by default.
|
||||
async fn put_object_lock_configuration(&self, req: &mut S3Request<PutObjectLockConfigurationInput>) -> S3Result<()> {
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
|
||||
authorize_request(req, Action::S3Action(S3Action::PutBucketObjectLockConfigurationAction)).await
|
||||
@@ -1493,7 +1510,7 @@ impl S3Access for FS {
|
||||
|
||||
/// Checks whether the PutObjectRetention request has accesses to the resources.
|
||||
async fn put_object_retention(&self, req: &mut S3Request<PutObjectRetentionInput>) -> S3Result<()> {
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
req_info.object = Some(req.input.key.clone());
|
||||
req_info.version_id = req.input.version_id.clone();
|
||||
@@ -1512,7 +1529,7 @@ impl S3Access for FS {
|
||||
///
|
||||
/// This method returns `Ok(())` by default.
|
||||
async fn put_object_tagging(&self, req: &mut S3Request<PutObjectTaggingInput>) -> S3Result<()> {
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
req_info.object = Some(req.input.key.clone());
|
||||
req_info.version_id = req.input.version_id.clone();
|
||||
@@ -1529,7 +1546,7 @@ impl S3Access for FS {
|
||||
///
|
||||
/// This method returns `Ok(())` by default.
|
||||
async fn put_public_access_block(&self, req: &mut S3Request<PutPublicAccessBlockInput>) -> S3Result<()> {
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
|
||||
authorize_request(req, Action::S3Action(S3Action::PutBucketPublicAccessBlockAction)).await
|
||||
@@ -1539,7 +1556,7 @@ impl S3Access for FS {
|
||||
///
|
||||
/// This method returns `Ok(())` by default.
|
||||
async fn restore_object(&self, req: &mut S3Request<RestoreObjectInput>) -> S3Result<()> {
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
req_info.object = Some(req.input.key.clone());
|
||||
req_info.version_id = req.input.version_id.clone();
|
||||
@@ -1551,7 +1568,7 @@ impl S3Access for FS {
|
||||
///
|
||||
/// This method returns `Ok(())` by default.
|
||||
async fn select_object_content(&self, req: &mut S3Request<SelectObjectContentInput>) -> S3Result<()> {
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
req_info.object = Some(req.input.key.clone());
|
||||
|
||||
@@ -1562,7 +1579,7 @@ impl S3Access for FS {
|
||||
///
|
||||
/// This method returns `Ok(())` by default.
|
||||
async fn upload_part(&self, req: &mut S3Request<UploadPartInput>) -> S3Result<()> {
|
||||
let req_info = req.extensions.get_mut::<ReqInfo>().expect("ReqInfo not found");
|
||||
let req_info = ext_req_info_mut(&mut req.extensions)?;
|
||||
req_info.bucket = Some(req.input.bucket.clone());
|
||||
req_info.object = Some(req.input.key.clone());
|
||||
|
||||
|
||||
@@ -134,7 +134,6 @@ impl IoLoadLevel {
|
||||
/// let enable_readahead = strategy.enable_readahead;
|
||||
/// let enable_cache_writeback = strategy.cache_writeback_enabled;
|
||||
/// ```
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct IoStrategy {
|
||||
/// Recommended buffer size for I/O operations (in bytes).
|
||||
@@ -164,6 +163,7 @@ pub struct IoStrategy {
|
||||
/// Whether to use tokio BufReader for improved async I/O.
|
||||
///
|
||||
/// Always enabled for better async performance.
|
||||
#[allow(dead_code)]
|
||||
pub use_buffered_io: bool,
|
||||
|
||||
/// The detected I/O load level.
|
||||
@@ -668,7 +668,6 @@ struct CachedObject {
|
||||
/// };
|
||||
/// manager.put_cached_object(cache_key, cached).await;
|
||||
/// ```
|
||||
#[allow(dead_code)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CachedGetObject {
|
||||
/// The object body data
|
||||
@@ -682,6 +681,7 @@ pub struct CachedGetObject {
|
||||
/// Last modified time as RFC3339 string (e.g., "2024-01-01T12:00:00Z")
|
||||
pub last_modified: Option<String>,
|
||||
/// Expiration time as RFC3339 string
|
||||
#[allow(dead_code)]
|
||||
pub expires: Option<String>,
|
||||
/// Cache-Control header value
|
||||
pub cache_control: Option<String>,
|
||||
@@ -700,10 +700,12 @@ pub struct CachedGetObject {
|
||||
/// Number of tags associated with the object
|
||||
pub tag_count: Option<i32>,
|
||||
/// Replication status
|
||||
#[allow(dead_code)]
|
||||
pub replication_status: Option<String>,
|
||||
/// User-defined metadata (x-amz-meta-*)
|
||||
pub user_metadata: std::collections::HashMap<String, String>,
|
||||
/// When this object was cached (for internal use, automatically set)
|
||||
#[allow(dead_code)]
|
||||
cached_at: Option<Instant>,
|
||||
/// Access count for hot key tracking (automatically managed)
|
||||
access_count: Arc<AtomicU64>,
|
||||
@@ -1320,11 +1322,8 @@ impl ConcurrencyManager {
|
||||
///
|
||||
/// This ensures we don't overwhelm the disk subsystem with too many
|
||||
/// concurrent reads, which can cause performance degradation.
|
||||
pub async fn acquire_disk_read_permit(&self) -> tokio::sync::SemaphorePermit<'_> {
|
||||
self.disk_read_semaphore
|
||||
.acquire()
|
||||
.await
|
||||
.expect("semaphore closed unexpectedly")
|
||||
pub async fn acquire_disk_read_permit(&self) -> Result<tokio::sync::SemaphorePermit<'_>, tokio::sync::AcquireError> {
|
||||
self.disk_read_semaphore.acquire().await
|
||||
}
|
||||
|
||||
// ============================================
|
||||
@@ -1791,10 +1790,10 @@ mod tests {
|
||||
async fn test_disk_read_permits() {
|
||||
let manager = ConcurrencyManager::new();
|
||||
|
||||
let permit1 = manager.acquire_disk_read_permit().await;
|
||||
let permit1 = manager.acquire_disk_read_permit().await.unwrap();
|
||||
assert_eq!(manager.disk_read_semaphore.available_permits(), 63);
|
||||
|
||||
let permit2 = manager.acquire_disk_read_permit().await;
|
||||
let permit2 = manager.acquire_disk_read_permit().await.unwrap();
|
||||
assert_eq!(manager.disk_read_semaphore.available_permits(), 62);
|
||||
|
||||
drop(permit1);
|
||||
|
||||
@@ -258,7 +258,7 @@ mod tests {
|
||||
.map(|_| {
|
||||
let mgr = Arc::new(manager.clone());
|
||||
tokio::spawn(async move {
|
||||
let _permit = mgr.acquire_disk_read_permit().await;
|
||||
let _permit = mgr.acquire_disk_read_permit().await.unwrap();
|
||||
sleep(Duration::from_millis(10)).await;
|
||||
})
|
||||
})
|
||||
|
||||
@@ -16,11 +16,10 @@ use crate::app::bucket_usecase::DefaultBucketUsecase;
|
||||
use crate::app::multipart_usecase::DefaultMultipartUsecase;
|
||||
use crate::app::object_usecase::DefaultObjectUsecase;
|
||||
use rustfs_ecstore::{
|
||||
StorageAPI,
|
||||
bucket::tagging::decode_tags_to_map,
|
||||
error::{is_err_object_not_found, is_err_version_not_found},
|
||||
new_object_layer_fn,
|
||||
store_api::ObjectOptions,
|
||||
store_api::{ObjectOperations, ObjectOptions},
|
||||
};
|
||||
use s3s::{S3, S3Error, S3ErrorCode, S3Request, S3Response, S3Result, dto::*, s3_error};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -25,8 +25,8 @@ use rustfs_ecstore::bucket::metadata_sys::get_replication_config;
|
||||
use rustfs_ecstore::bucket::object_lock::objectlock_sys;
|
||||
use rustfs_ecstore::bucket::replication::ReplicationConfigurationExt;
|
||||
use rustfs_ecstore::error::StorageError;
|
||||
use rustfs_ecstore::store_api::{BucketOptions, ObjectInfo, ObjectToDelete};
|
||||
use rustfs_ecstore::{StorageAPI, new_object_layer_fn};
|
||||
use rustfs_ecstore::new_object_layer_fn;
|
||||
use rustfs_ecstore::store_api::{BucketOperations, BucketOptions, ObjectInfo, ObjectToDelete};
|
||||
use rustfs_targets::EventName;
|
||||
use rustfs_targets::arn::{TargetID, TargetIDError};
|
||||
use rustfs_utils::http::{
|
||||
|
||||
@@ -12,13 +12,12 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#![allow(dead_code)]
|
||||
|
||||
use s3s::dto::{
|
||||
BucketKeyEnabled, BucketName, ChecksumCRC32, ChecksumCRC32C, ChecksumCRC64NVME, ChecksumSHA1, ChecksumSHA256, ChecksumType,
|
||||
ETag, Expiration, Location, ObjectKey, ObjectVersionId, RequestCharged, SSEKMSKeyId, ServerSideEncryption,
|
||||
};
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct CompleteMultipartUploadOutput {
|
||||
pub bucket: Option<BucketName>,
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use rustfs_ecstore::StorageAPI;
|
||||
use rustfs_ecstore::store::ECStore;
|
||||
use rustfs_ecstore::store_api::ListOperations;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Determines if the key "looks like a prefix" (ends with `/`).
|
||||
|
||||
@@ -633,39 +633,6 @@ impl NodeService {
|
||||
|
||||
pub(super) async fn handle_write(&self, _request: Request<WriteRequest>) -> Result<Response<WriteResponse>, Status> {
|
||||
unimplemented!("write");
|
||||
// let request = request.into_inner();
|
||||
// if let Some(disk) = self.find_disk(&request.disk).await {
|
||||
// let file_writer = if request.is_append {
|
||||
// disk.append_file(&request.volume, &request.path).await
|
||||
// } else {
|
||||
// disk.create_file("", &request.volume, &request.path, 0).await
|
||||
// };
|
||||
|
||||
// match file_writer {
|
||||
// Ok(mut file_writer) => match file_writer.write(&request.data).await {
|
||||
// Ok(_) => Ok(Response::new(WriteResponse {
|
||||
// success: true,
|
||||
// error: None,
|
||||
// })),
|
||||
// Err(err) => Ok(Response::new(WriteResponse {
|
||||
// success: false,
|
||||
// error: Some(err_to_proto_err(&err, &format!("write failed: {}", err))),
|
||||
// })),
|
||||
// },
|
||||
// Err(err) => Ok(Response::new(WriteResponse {
|
||||
// success: false,
|
||||
// error: Some(err_to_proto_err(&err, &format!("get writer failed: {}", err))),
|
||||
// })),
|
||||
// }
|
||||
// } else {
|
||||
// Ok(Response::new(WriteResponse {
|
||||
// success: false,
|
||||
// error: Some(err_to_proto_err(
|
||||
// &EcsError::new(StorageError::InvalidArgument(Default::default(), Default::default(), Default::default())),
|
||||
// "can not find disk",
|
||||
// )),
|
||||
// }))
|
||||
// }
|
||||
}
|
||||
|
||||
pub(super) async fn handle_rename_file(
|
||||
|
||||
@@ -63,26 +63,6 @@ mod lock;
|
||||
#[path = "metrics.rs"]
|
||||
mod metrics;
|
||||
|
||||
// fn match_for_io_error(err_status: &Status) -> Option<&std::io::Error> {
|
||||
// let mut err: &(dyn Error + 'static) = err_status;
|
||||
|
||||
// loop {
|
||||
// if let Some(io_err) = err.downcast_ref::<std::io::Error>() {
|
||||
// return Some(io_err);
|
||||
// }
|
||||
|
||||
// // h2::Error do not expose std::io::Error with `source()`
|
||||
// // https://github.com/hyperium/h2/pull/462
|
||||
// if let Some(h2_err) = err.downcast_ref::<h2::Error>() {
|
||||
// if let Some(io_err) = h2_err.get_io() {
|
||||
// return Some(io_err);
|
||||
// }
|
||||
// }
|
||||
|
||||
// err = err.source()?;
|
||||
// }
|
||||
// }
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct NodeService {
|
||||
local_peer: LocalPeerS3Client,
|
||||
@@ -198,200 +178,12 @@ impl Node for NodeService {
|
||||
let _ = request;
|
||||
|
||||
unimplemented!("write_stream");
|
||||
|
||||
// let mut in_stream = request.into_inner();
|
||||
// let (tx, rx) = mpsc::channel(128);
|
||||
|
||||
// tokio::spawn(async move {
|
||||
// let mut file_ref = None;
|
||||
// while let Some(result) = in_stream.next().await {
|
||||
// match result {
|
||||
// // Ok(v) => tx
|
||||
// // .send(Ok(EchoResponse { message: v.message }))
|
||||
// // .await
|
||||
// // .expect("working rx"),
|
||||
// Ok(v) => {
|
||||
// match file_ref.as_ref() {
|
||||
// Some(_) => (),
|
||||
// None => {
|
||||
// if let Some(disk) = find_local_disk(&v.disk).await {
|
||||
// let file_writer = if v.is_append {
|
||||
// disk.append_file(&v.volume, &v.path).await
|
||||
// } else {
|
||||
// disk.create_file("", &v.volume, &v.path, 0).await
|
||||
// };
|
||||
|
||||
// match file_writer {
|
||||
// Ok(file_writer) => file_ref = Some(file_writer),
|
||||
// Err(err) => {
|
||||
// tx.send(Ok(WriteResponse {
|
||||
// success: false,
|
||||
// error: Some(err_to_proto_err(
|
||||
// &err,
|
||||
// &format!("get file writer failed: {}", err),
|
||||
// )),
|
||||
// }))
|
||||
// .await
|
||||
// .expect("working rx");
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// } else {
|
||||
// tx.send(Ok(WriteResponse {
|
||||
// success: false,
|
||||
// error: Some(err_to_proto_err(
|
||||
// &EcsError::new(StorageError::InvalidArgument(
|
||||
// Default::default(),
|
||||
// Default::default(),
|
||||
// Default::default(),
|
||||
// )),
|
||||
// "can not find disk",
|
||||
// )),
|
||||
// }))
|
||||
// .await
|
||||
// .expect("working rx");
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
|
||||
// match file_ref.as_mut().unwrap().write(&v.data).await {
|
||||
// Ok(_) => tx.send(Ok(WriteResponse {
|
||||
// success: true,
|
||||
// error: None,
|
||||
// })),
|
||||
// Err(err) => tx.send(Ok(WriteResponse {
|
||||
// success: false,
|
||||
// error: Some(err_to_proto_err(&err, &format!("write failed: {}", err))),
|
||||
// })),
|
||||
// }
|
||||
// .await
|
||||
// .unwrap();
|
||||
// }
|
||||
// Err(err) => {
|
||||
// if let Some(io_err) = match_for_io_error(&err) {
|
||||
// if io_err.kind() == ErrorKind::BrokenPipe {
|
||||
// // here you can handle special case when client
|
||||
// // disconnected in unexpected way
|
||||
// eprintln!("\tclient disconnected: broken pipe");
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
|
||||
// match tx.send(Err(err)).await {
|
||||
// Ok(_) => (),
|
||||
// Err(_err) => break, // response was dropped
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// println!("\tstream ended");
|
||||
// });
|
||||
|
||||
// let out_stream = ReceiverStream::new(rx);
|
||||
|
||||
// Ok(Response::new(Box::pin(out_stream)))
|
||||
}
|
||||
|
||||
type ReadAtStream = ResponseStream<ReadAtResponse>;
|
||||
async fn read_at(&self, _request: Request<Streaming<ReadAtRequest>>) -> Result<Response<Self::ReadAtStream>, Status> {
|
||||
info!("read_at");
|
||||
unimplemented!("read_at");
|
||||
|
||||
// let mut in_stream = request.into_inner();
|
||||
// let (tx, rx) = mpsc::channel(128);
|
||||
|
||||
// tokio::spawn(async move {
|
||||
// let mut file_ref = None;
|
||||
// while let Some(result) = in_stream.next().await {
|
||||
// match result {
|
||||
// Ok(v) => {
|
||||
// match file_ref.as_ref() {
|
||||
// Some(_) => (),
|
||||
// None => {
|
||||
// if let Some(disk) = find_local_disk(&v.disk).await {
|
||||
// match disk.read_file(&v.volume, &v.path).await {
|
||||
// Ok(file_reader) => file_ref = Some(file_reader),
|
||||
// Err(err) => {
|
||||
// tx.send(Ok(ReadAtResponse {
|
||||
// success: false,
|
||||
// data: Vec::new(),
|
||||
// error: Some(err_to_proto_err(&err, &format!("read file failed: {}", err))),
|
||||
// read_size: -1,
|
||||
// }))
|
||||
// .await
|
||||
// .expect("working rx");
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// } else {
|
||||
// tx.send(Ok(ReadAtResponse {
|
||||
// success: false,
|
||||
// data: Vec::new(),
|
||||
// error: Some(err_to_proto_err(
|
||||
// &EcsError::new(StorageError::InvalidArgument(
|
||||
// Default::default(),
|
||||
// Default::default(),
|
||||
// Default::default(),
|
||||
// )),
|
||||
// "can not find disk",
|
||||
// )),
|
||||
// read_size: -1,
|
||||
// }))
|
||||
// .await
|
||||
// .expect("working rx");
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
|
||||
// let mut data = vec![0u8; v.length.try_into().unwrap()];
|
||||
|
||||
// match file_ref
|
||||
// .as_mut()
|
||||
// .unwrap()
|
||||
// .read_at(v.offset.try_into().unwrap(), &mut data)
|
||||
// .await
|
||||
// {
|
||||
// Ok(read_size) => tx.send(Ok(ReadAtResponse {
|
||||
// success: true,
|
||||
// data,
|
||||
// read_size: read_size.try_into().unwrap(),
|
||||
// error: None,
|
||||
// })),
|
||||
// Err(err) => tx.send(Ok(ReadAtResponse {
|
||||
// success: false,
|
||||
// data: Vec::new(),
|
||||
// error: Some(err_to_proto_err(&err, &format!("read at failed: {}", err))),
|
||||
// read_size: -1,
|
||||
// })),
|
||||
// }
|
||||
// .await
|
||||
// .unwrap();
|
||||
// }
|
||||
// Err(err) => {
|
||||
// if let Some(io_err) = match_for_io_error(&err) {
|
||||
// if io_err.kind() == ErrorKind::BrokenPipe {
|
||||
// // here you can handle special case when client
|
||||
// // disconnected in unexpected way
|
||||
// eprintln!("\tclient disconnected: broken pipe");
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
|
||||
// match tx.send(Err(err)).await {
|
||||
// Ok(_) => (),
|
||||
// Err(_err) => break, // response was dropped
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// println!("\tstream ended");
|
||||
// });
|
||||
|
||||
// let out_stream = ReceiverStream::new(rx);
|
||||
|
||||
// Ok(Response::new(Box::pin(out_stream)))
|
||||
}
|
||||
|
||||
async fn list_dir(&self, request: Request<ListDirRequest>) -> Result<Response<ListDirResponse>, Status> {
|
||||
|
||||
@@ -12,11 +12,12 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#![allow(dead_code)]
|
||||
|
||||
//! Facade modules for incremental S3 API extraction from `ecfs.rs`.
|
||||
//!
|
||||
//! This file intentionally starts as skeleton-only. Behavior remains in place
|
||||
//! until each helper is moved with dedicated small refactor steps.
|
||||
#![allow(dead_code)]
|
||||
|
||||
pub(crate) mod acl;
|
||||
pub(crate) mod bucket;
|
||||
|
||||
+15
-12
@@ -684,7 +684,7 @@ async fn apply_ssec_prepare_encryption_material(
|
||||
|
||||
Ok(EncryptionMaterial {
|
||||
sse_type: SSEType::SseC,
|
||||
server_side_encryption: ServerSideEncryption::AES256.parse().unwrap(),
|
||||
server_side_encryption: ServerSideEncryption::from_static(ServerSideEncryption::AES256),
|
||||
kms_key_id: None,
|
||||
algorithm,
|
||||
key_bytes: [0; 32],
|
||||
@@ -731,7 +731,7 @@ async fn apply_ssec_encryption_material(
|
||||
|
||||
Ok(EncryptionMaterial {
|
||||
sse_type: SSEType::SseC,
|
||||
server_side_encryption: ServerSideEncryption::AES256.parse().unwrap(),
|
||||
server_side_encryption: ServerSideEncryption::from_static(ServerSideEncryption::AES256),
|
||||
kms_key_id: None,
|
||||
algorithm: validated.algorithm,
|
||||
key_bytes: validated.key_bytes,
|
||||
@@ -776,7 +776,7 @@ async fn apply_ssec_decryption_material(
|
||||
|
||||
Ok(DecryptionMaterial {
|
||||
sse_type: SSEType::SseC,
|
||||
server_side_encryption: ServerSideEncryption::AES256.parse().unwrap(), // const
|
||||
server_side_encryption: ServerSideEncryption::from_static(ServerSideEncryption::AES256), // const
|
||||
kms_key_id: None,
|
||||
algorithm: SSECustomerAlgorithm::from(algorithm),
|
||||
|
||||
@@ -941,7 +941,8 @@ async fn apply_managed_decryption_material(
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let server_side_encryption = metadata.get("x-amz-server-side-encryption").unwrap().clone();
|
||||
// Safe: presence is guaranteed by the contains_key check above.
|
||||
let server_side_encryption = metadata.get("x-amz-server-side-encryption").cloned().unwrap_or_default();
|
||||
|
||||
// Parse metadata - try using service if available, otherwise parse manually
|
||||
let (encrypted_data_key, iv, algorithm) = if let Some(service) = get_global_encryption_service().await {
|
||||
@@ -1169,17 +1170,17 @@ impl TestSseDekProvider {
|
||||
let decoded_len = v.len();
|
||||
match v.try_into() {
|
||||
Ok(arr) => {
|
||||
println!("✓ Successfully loaded master key (32 bytes)");
|
||||
tracing::info!("Successfully loaded SSE master key (32 bytes)");
|
||||
arr
|
||||
}
|
||||
Err(_) => {
|
||||
eprintln!("✗ Failed to load master key: decoded key is not 32 bytes (got {} bytes)", decoded_len);
|
||||
tracing::error!("Failed to load master key: decoded key is not 32 bytes (got {decoded_len} bytes)");
|
||||
[0u8; 32]
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("✗ Failed to load master key: invalid base64 encoding: {}", e);
|
||||
tracing::error!("Failed to load master key: invalid base64 encoding: {e}");
|
||||
[0u8; 32]
|
||||
}
|
||||
}
|
||||
@@ -1188,8 +1189,9 @@ impl TestSseDekProvider {
|
||||
};
|
||||
|
||||
if master_key == [0u8; 32] {
|
||||
eprintln!("✗ Failed to load master key: no valid master key loaded! All encryption operations will fail.");
|
||||
eprintln!(" Set __RUSTFS_SSE_SIMPLE_CMK environment variable to a base64-encoded 32-byte key.");
|
||||
tracing::error!(
|
||||
"No valid SSE master key loaded. Set __RUSTFS_SSE_SIMPLE_CMK environment variable to a base64-encoded 32-byte key."
|
||||
);
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
@@ -1459,8 +1461,9 @@ pub fn validate_ssec_params(params: SsecParams) -> Result<ValidatedSsecParams, A
|
||||
return Err(ApiError::from(StorageError::other("SSE-C key MD5 mismatch")));
|
||||
}
|
||||
|
||||
// SAFETY: We validated the length is exactly 32 bytes above
|
||||
let key_array: [u8; 32] = key_bytes.try_into().expect("key length already validated to be 32 bytes");
|
||||
let key_array: [u8; 32] = key_bytes
|
||||
.try_into()
|
||||
.map_err(|_| ApiError::from(StorageError::other("SSE-C key must be exactly 32 bytes")))?;
|
||||
|
||||
Ok(ValidatedSsecParams {
|
||||
algorithm: params.algorithm,
|
||||
@@ -1706,7 +1709,7 @@ mod tests {
|
||||
.await
|
||||
.expect("Failed to generate DEK");
|
||||
|
||||
// 3. Prepare test data (明文)
|
||||
// 3. Prepare test data (plaintext)
|
||||
let plaintext = b"Hello, World! This is a test message for encryption and decryption.";
|
||||
println!("Original plaintext: {:?}", String::from_utf8_lossy(plaintext));
|
||||
println!("Plaintext length: {} bytes", plaintext.len());
|
||||
|
||||
Reference in New Issue
Block a user