Add documentation and few enhancements (#45)

Signed-off-by: Bala.FA <bala@minio.io>
This commit is contained in:
Bala FA 2023-09-26 05:16:11 +05:30 committed by GitHub
parent aee702f337
commit 28e7fee8fa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 941 additions and 7 deletions

View File

@ -24,6 +24,7 @@ jobs:
- name: Run tests
run: |
truncate --size=6M asiaphotos-2015.zip
./tests/start-server.sh
export SERVER_ENDPOINT=localhost:9000
export ACCESS_KEY=minioadmin

File diff suppressed because it is too large Load Diff

View File

@ -13,6 +13,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//! S3 client to perform bucket and object operations
use crate::s3::args::*;
use crate::s3::creds::Provider;
use crate::s3::error::{Error, ErrorResponse};
@ -201,6 +203,10 @@ fn parse_list_objects_common_prefixes(
}
#[derive(Clone, Debug, Default)]
/// Simple Storage Service (aka S3) client to perform bucket and object operations.
///
/// If credential provider is passed, all S3 operation requests are signed using AWS Signature
/// Version 4; else they are performed anonymously.
pub struct Client<'a> {
client: reqwest::Client,
base_url: BaseUrl,
@ -209,6 +215,22 @@ pub struct Client<'a> {
}
impl<'a> Client<'a> {
/// Returns a S3 client with given base URL.
///
/// # Examples
///
/// ```
/// use minio::s3::client::Client;
/// use minio::s3::creds::StaticProvider;
/// use minio::s3::http::BaseUrl;
/// let mut base_url = BaseUrl::from_string("play.min.io".to_string()).unwrap();
/// let static_provider = StaticProvider::new(
/// "Q3AM3UQ867SPQQA43P2F",
/// "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG",
/// None,
/// );
/// let client = Client::new(base_url.clone(), Some(&static_provider), None, None).unwrap();
/// ```
pub fn new(
base_url: BaseUrl,
provider: Option<&(dyn Provider + Send + Sync)>,
@ -626,6 +648,7 @@ impl<'a> Client<'a> {
Ok(location)
}
/// Executes [AbortMultipartUpload](https://docs.aws.amazon.com/AmazonS3/latest/API/API_AbortMultipartUpload.html) S3 API
pub async fn abort_multipart_upload(
&self,
args: &AbortMultipartUploadArgs<'_>,
@ -712,6 +735,7 @@ impl<'a> Client<'a> {
}
}
/// Executes [CompleteMultipartUpload](https://docs.aws.amazon.com/AmazonS3/latest/API/API_CompleteMultipartUpload.html) S3 API
pub async fn complete_multipart_upload(
&self,
args: &CompleteMultipartUploadArgs<'_>,
@ -1131,6 +1155,7 @@ impl<'a> Client<'a> {
})
}
/// Executes [CreateMultipartUpload](https://docs.aws.amazon.com/AmazonS3/latest/API/API_CreateMultipartUpload.html) S3 API
pub async fn create_multipart_upload(
&self,
args: &CreateMultipartUploadArgs<'_>,
@ -2434,6 +2459,7 @@ impl<'a> Client<'a> {
))
}
/// Executes [ListObjects](https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListObjects.html) S3 API
pub async fn list_objects_v1(
&self,
args: &ListObjectsV1Args<'_>,
@ -2500,6 +2526,7 @@ impl<'a> Client<'a> {
})
}
/// Executes [ListObjectsV2](https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListObjectsV2.html) S3 API
pub async fn list_objects_v2(
&self,
args: &ListObjectsV2Args<'_>,
@ -2584,6 +2611,7 @@ impl<'a> Client<'a> {
})
}
/// Executes [ListObjectVersions](https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListObjectVersions.html) S3 API
pub async fn list_object_versions(
&self,
args: &ListObjectVersionsArgs<'_>,
@ -2980,6 +3008,7 @@ impl<'a> Client<'a> {
res
}
/// Executes [PutObject](https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html) S3 API
pub async fn put_object_api(
&self,
args: &PutObjectApiArgs<'_>,
@ -3099,6 +3128,7 @@ impl<'a> Client<'a> {
})
}
/// Executes [DeleteObjects](https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteObjects.html) S3 API
pub async fn remove_objects_api(
&self,
args: &RemoveObjectsApiArgs<'_>,
@ -3779,6 +3809,7 @@ impl<'a> Client<'a> {
.await
}
/// Executes [UploadPart](https://docs.aws.amazon.com/AmazonS3/latest/API/API_UploadPart.html) S3 API
pub async fn upload_part(
&self,
args: &UploadPartArgs<'_>,
@ -3803,6 +3834,7 @@ impl<'a> Client<'a> {
self.put_object_api(&poa_args).await
}
/// Executes [UploadPartCopy](https://docs.aws.amazon.com/AmazonS3/latest/API/API_UploadPartCopy.html) S3 API
pub async fn upload_part_copy(
&self,
args: &UploadPartCopyArgs<'_>,

View File

@ -13,23 +13,36 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//! Credential providers
#[derive(Clone, Debug, Default)]
/// Credentials contain access key, secret key and session token optionally
pub struct Credentials {
pub access_key: String,
pub secret_key: String,
pub session_token: Option<String>,
}
/// Provider trait to fetch credentials
pub trait Provider: std::fmt::Debug {
fn fetch(&self) -> Credentials;
}
#[derive(Clone, Debug)]
/// Static credential provider
pub struct StaticProvider {
creds: Credentials,
}
impl StaticProvider {
/// Returns a static provider with given access key, secret key and optional session token
///
/// # Examples
///
/// ```
/// use minio::s3::creds::StaticProvider;
/// let provider = StaticProvider::new("minioadmin", "minio123", None);
/// ```
pub fn new(access_key: &str, secret_key: &str, session_token: Option<&str>) -> StaticProvider {
StaticProvider {
creds: Credentials {

View File

@ -13,6 +13,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//! Error definitions for S3 operations
extern crate alloc;
use crate::s3::utils::get_default_text;
use bytes::{Buf, Bytes};
@ -20,6 +22,7 @@ use std::fmt;
use xmltree::Element;
#[derive(Clone, Debug, Default)]
/// Error response for S3 operations
pub struct ErrorResponse {
pub code: String,
pub message: String,
@ -50,6 +53,7 @@ impl ErrorResponse {
}
#[derive(Debug)]
/// Error definitions
pub enum Error {
TimeParseError(chrono::ParseError),
InvalidUrl(http::uri::InvalidUri),
@ -74,6 +78,7 @@ pub enum Error {
InvalidPartNumber(String),
EmptyParts(String),
InvalidRetentionMode(String),
InvalidRetentionConfig(String),
InvalidMinPartSize(usize),
InvalidMaxPartSize(usize),
InvalidObjectSize(usize),
@ -128,6 +133,7 @@ impl fmt::Display for Error {
Error::InvalidPartNumber(m) => write!(f, "{}", m),
Error::EmptyParts(m) => write!(f, "{}", m),
Error::InvalidRetentionMode(m) => write!(f, "invalid retention mode {}", m),
Error::InvalidRetentionConfig(m) => write!(f, "invalid retention configuration; {}", m),
Error::InvalidMinPartSize(s) => write!(f, "part size {} is not supported; minimum allowed 5MiB", s),
Error::InvalidMaxPartSize(s) => write!(f, "part size {} is not supported; maximum allowed 5GiB", s),
Error::InvalidObjectSize(s) => write!(f, "object size {} is not supported; maximum allowed 5TiB", s),

View File

@ -13,6 +13,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//! HTTP URL definitions
use crate::s3::error::Error;
use crate::s3::utils::{to_query_string, Multimap};
use derivative::Derivative;
@ -22,6 +24,7 @@ use std::fmt;
#[derive(Derivative)]
#[derivative(Clone, Debug, Default)]
/// Represents HTTP URL
pub struct Url {
#[derivative(Default(value = "true"))]
pub https: bool,
@ -90,6 +93,7 @@ fn extract_region(host: &str) -> String {
#[derive(Derivative)]
#[derivative(Clone, Debug, Default)]
/// Represents Base URL of S3 endpoint
pub struct BaseUrl {
#[derivative(Default(value = "true"))]
pub https: bool,
@ -103,6 +107,7 @@ pub struct BaseUrl {
}
impl BaseUrl {
/// Builds URL from base URL for given parameters for S3 operation
pub fn build_url(
&self,
method: &Method,
@ -188,6 +193,21 @@ impl BaseUrl {
Ok(url)
}
/// Returns a base URL from given host string
///
/// # Examples
///
/// ```
/// use minio::s3::http::BaseUrl;
/// // Get base URL from host name
/// let base_url = BaseUrl::from_string("play.min.io".to_string()).unwrap();
/// // Get base URL from host:port
/// let base_url = BaseUrl::from_string("play.minio.io:9000".to_string()).unwrap();
/// // Get base URL from IPv4 address
/// let base_url = BaseUrl::from_string("http://192.168.124.63:9000".to_string()).unwrap();
/// // Get base URL from IPv6 address
/// let base_url = BaseUrl::from_string("[0:0:0:0:0:ffff:c0a8:7c3f]:9000".to_string()).unwrap();
/// ```
pub fn from_string(s: String) -> Result<BaseUrl, Error> {
let url = s.parse::<Uri>()?;

View File

@ -13,6 +13,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//! Implementation of Simple Storage Service (aka S3) client
pub mod args;
pub mod client;
pub mod creds;

View File

@ -13,6 +13,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//! Responses for [minio::s3::client::Client](crate::s3::client::Client) APIs
use crate::s3::error::Error;
use crate::s3::types::{
parse_legal_hold, Bucket, Item, LifecycleConfig, NotificationConfig, ObjectLockConfig,
@ -28,23 +30,28 @@ use std::io::BufReader;
use xmltree::Element;
#[derive(Debug)]
/// Response of [list_buckets()](crate::s3::client::Client::list_buckets) API
pub struct ListBucketsResponse {
pub headers: HeaderMap,
pub buckets: Vec<Bucket>,
}
#[derive(Debug)]
/// Base response for bucket operation
pub struct BucketResponse {
pub headers: HeaderMap,
pub region: String,
pub bucket_name: String,
}
/// Response of [make_bucket()](crate::s3::client::Client::make_bucket) API
pub type MakeBucketResponse = BucketResponse;
/// Response of [remove_bucket()](crate::s3::client::Client::remove_bucket) API
pub type RemoveBucketResponse = BucketResponse;
#[derive(Debug)]
/// Base response for object operation
pub struct ObjectResponse {
pub headers: HeaderMap,
pub region: String,
@ -53,9 +60,11 @@ pub struct ObjectResponse {
pub version_id: Option<String>,
}
/// Response of [remove_object()](crate::s3::client::Client::remove_object) API
pub type RemoveObjectResponse = ObjectResponse;
#[derive(Debug)]
/// Base Upload ID response
pub struct UploadIdResponse {
pub headers: HeaderMap,
pub region: String,
@ -64,11 +73,14 @@ pub struct UploadIdResponse {
pub upload_id: String,
}
/// Response of [abort_multipart_upload()](crate::s3::client::Client::abort_multipart_upload) API
pub type AbortMultipartUploadResponse = UploadIdResponse;
/// Response of [create_multipart_upload()](crate::s3::client::Client::create_multipart_upload) API
pub type CreateMultipartUploadResponse = UploadIdResponse;
#[derive(Debug)]
/// Base response for put object
pub struct PutObjectBaseResponse {
pub headers: HeaderMap,
pub bucket_name: String,
@ -78,23 +90,32 @@ pub struct PutObjectBaseResponse {
pub version_id: Option<String>,
}
/// Response of [complete_multipart_upload()](crate::s3::client::Client::complete_multipart_upload) API
pub type CompleteMultipartUploadResponse = PutObjectBaseResponse;
/// Response of [put_object_api()](crate::s3::client::Client::put_object_api) S3 API
pub type PutObjectApiResponse = PutObjectBaseResponse;
/// Response of [upload_part()](crate::s3::client::Client::upload_part) S3 API
pub type UploadPartResponse = PutObjectApiResponse;
/// Response of [put_object()](crate::s3::client::Client::put_object) API
pub type PutObjectResponse = PutObjectApiResponse;
/// Response of [upload_part_copy()](crate::s3::client::Client::upload_part_copy) S3 API
pub type UploadPartCopyResponse = PutObjectApiResponse;
/// Response of [copy_object()](crate::s3::client::Client::copy_object) API
pub type CopyObjectResponse = PutObjectApiResponse;
/// Response of [compose_object()](crate::s3::client::Client::compose_object) API
pub type ComposeObjectResponse = PutObjectApiResponse;
/// Response of [upload_object()](crate::s3::client::Client::upload_object) API
pub type UploadObjectResponse = PutObjectApiResponse;
#[derive(Debug)]
/// Response of [stat_object()](crate::s3::client::Client::stat_object) API
pub struct StatObjectResponse {
pub headers: HeaderMap,
pub region: String,
@ -184,6 +205,7 @@ impl StatObjectResponse {
}
#[derive(Clone, Debug)]
/// Error defintion of [remove_objects_api()](crate::s3::client::Client::remove_objects_api) S3 API
pub struct DeleteError {
pub code: String,
pub message: String,
@ -192,6 +214,7 @@ pub struct DeleteError {
}
#[derive(Clone, Debug)]
/// Deleted object defintion of [remove_objects_api()](crate::s3::client::Client::remove_objects_api) S3 API
pub struct DeletedObject {
pub name: String,
pub version_id: Option<String>,
@ -200,6 +223,7 @@ pub struct DeletedObject {
}
#[derive(Clone, Debug)]
/// Response of [remove_objects_api()](crate::s3::client::Client::remove_objects_api) S3 API
pub struct RemoveObjectsApiResponse {
pub headers: HeaderMap,
pub region: String,
@ -208,9 +232,11 @@ pub struct RemoveObjectsApiResponse {
pub errors: Vec<DeleteError>,
}
/// Response of [remove_objects()](crate::s3::client::Client::remove_objects) API
pub type RemoveObjectsResponse = RemoveObjectsApiResponse;
#[derive(Clone, Debug)]
/// Response of [list_objects_v1()](crate::s3::client::Client::list_objects_v1) S3 API
pub struct ListObjectsV1Response {
pub headers: HeaderMap,
pub name: String,
@ -225,6 +251,7 @@ pub struct ListObjectsV1Response {
}
#[derive(Clone, Debug)]
/// Response of [list_objects_v2()](crate::s3::client::Client::list_objects_v2) S3 API
pub struct ListObjectsV2Response {
pub headers: HeaderMap,
pub name: String,
@ -241,6 +268,7 @@ pub struct ListObjectsV2Response {
}
#[derive(Clone, Debug)]
/// Response of [list_object_versions()](crate::s3::client::Client::list_object_versions) S3 API
pub struct ListObjectVersionsResponse {
pub headers: HeaderMap,
pub name: String,
@ -257,6 +285,7 @@ pub struct ListObjectVersionsResponse {
}
#[derive(Clone, Debug)]
/// Response of [list_objects()](crate::s3::client::Client::list_objects) API
pub struct ListObjectsResponse {
pub headers: HeaderMap,
pub name: String,
@ -284,6 +313,7 @@ pub struct ListObjectsResponse {
pub next_version_id_marker: String,
}
/// Response of [select_object_content()](crate::s3::client::Client::select_object_content) API
pub struct SelectObjectContentResponse {
pub headers: HeaderMap,
pub region: String,
@ -607,6 +637,7 @@ impl SelectObjectContentResponse {
}
#[derive(Clone, Debug)]
/// Response of [listen_bucket_notification()](crate::s3::client::Client::listen_bucket_notification) API
pub struct ListenBucketNotificationResponse {
pub headers: HeaderMap,
pub region: String,
@ -627,9 +658,11 @@ impl ListenBucketNotificationResponse {
}
}
/// Response of [delete_bucket_encryption()](crate::s3::client::Client::delete_bucket_encryption) API
pub type DeleteBucketEncryptionResponse = BucketResponse;
#[derive(Clone, Debug)]
/// Response of [get_bucket_encryption()](crate::s3::client::Client::get_bucket_encryption) API
pub struct GetBucketEncryptionResponse {
pub headers: HeaderMap,
pub region: String,
@ -637,13 +670,17 @@ pub struct GetBucketEncryptionResponse {
pub config: SseConfig,
}
/// Response of [set_bucket_encryption()](crate::s3::client::Client::set_bucket_encryption) API
pub type SetBucketEncryptionResponse = BucketResponse;
/// Response of [enable_object_legal_hold()](crate::s3::client::Client::enable_object_legal_hold) API
pub type EnableObjectLegalHoldResponse = ObjectResponse;
/// Response of [disable_object_legal_hold()](crate::s3::client::Client::disable_object_legal_hold) API
pub type DisableObjectLegalHoldResponse = ObjectResponse;
#[derive(Clone, Debug)]
/// Response of [is_object_legal_hold_enabled()](crate::s3::client::Client::is_object_legal_hold_enabled) API
pub struct IsObjectLegalHoldEnabledResponse {
pub headers: HeaderMap,
pub region: String,
@ -653,9 +690,11 @@ pub struct IsObjectLegalHoldEnabledResponse {
pub enabled: bool,
}
/// Response of [delete_bucket_lifecycle()](crate::s3::client::Client::delete_bucket_lifecycle) API
pub type DeleteBucketLifecycleResponse = BucketResponse;
#[derive(Clone, Debug)]
/// Response of [get_bucket_lifecycle()](crate::s3::client::Client::get_bucket_lifecycle) API
pub struct GetBucketLifecycleResponse {
pub headers: HeaderMap,
pub region: String,
@ -663,11 +702,14 @@ pub struct GetBucketLifecycleResponse {
pub config: LifecycleConfig,
}
/// Response of [set_bucket_lifecycle()](crate::s3::client::Client::set_bucket_lifecycle) API
pub type SetBucketLifecycleResponse = BucketResponse;
/// Response of [delete_bucket_notification()](crate::s3::client::Client::delete_bucket_notification) API
pub type DeleteBucketNotificationResponse = BucketResponse;
#[derive(Clone, Debug)]
/// Response of [get_bucket_notification()](crate::s3::client::Client::get_bucket_notification) API
pub struct GetBucketNotificationResponse {
pub headers: HeaderMap,
pub region: String,
@ -675,11 +717,14 @@ pub struct GetBucketNotificationResponse {
pub config: NotificationConfig,
}
/// Response of [set_bucket_notification()](crate::s3::client::Client::set_bucket_notification) API
pub type SetBucketNotificationResponse = BucketResponse;
/// Response of [delete_bucket_policy()](crate::s3::client::Client::delete_bucket_policy) API
pub type DeleteBucketPolicyResponse = BucketResponse;
#[derive(Clone, Debug)]
/// Response of [get_bucket_policy()](crate::s3::client::Client::get_bucket_policy) API
pub struct GetBucketPolicyResponse {
pub headers: HeaderMap,
pub region: String,
@ -687,11 +732,14 @@ pub struct GetBucketPolicyResponse {
pub config: String,
}
/// Response of [set_bucket_policy()](crate::s3::client::Client::set_bucket_policy) API
pub type SetBucketPolicyResponse = BucketResponse;
/// Response of [delete_bucket_replication()](crate::s3::client::Client::delete_bucket_replication) API
pub type DeleteBucketReplicationResponse = BucketResponse;
#[derive(Clone, Debug)]
/// Response of [get_bucket_replication()](crate::s3::client::Client::get_bucket_replication) API
pub struct GetBucketReplicationResponse {
pub headers: HeaderMap,
pub region: String,
@ -699,11 +747,14 @@ pub struct GetBucketReplicationResponse {
pub config: ReplicationConfig,
}
/// Response of [set_bucket_replication()](crate::s3::client::Client::set_bucket_replication) API
pub type SetBucketReplicationResponse = BucketResponse;
/// Response of [delete_bucket_tags()](crate::s3::client::Client::delete_bucket_tags) API
pub type DeleteBucketTagsResponse = BucketResponse;
#[derive(Clone, Debug)]
/// Response of [get_bucket_tags()](crate::s3::client::Client::get_bucket_tags) API
pub struct GetBucketTagsResponse {
pub headers: HeaderMap,
pub region: String,
@ -711,9 +762,11 @@ pub struct GetBucketTagsResponse {
pub tags: std::collections::HashMap<String, String>,
}
/// Response of [set_bucket_tags()](crate::s3::client::Client::set_bucket_tags) API
pub type SetBucketTagsResponse = BucketResponse;
#[derive(Clone, Debug)]
/// Response of [get_bucket_versioning()](crate::s3::client::Client::get_bucket_versioning) API
pub struct GetBucketVersioningResponse {
pub headers: HeaderMap,
pub region: String,
@ -722,11 +775,14 @@ pub struct GetBucketVersioningResponse {
pub mfa_delete: Option<bool>,
}
/// Response of [set_bucket_versioning()](crate::s3::client::Client::set_bucket_versioning) API
pub type SetBucketVersioningResponse = BucketResponse;
/// Response of [delete_object_lock_config()](crate::s3::client::Client::delete_object_lock_config) API
pub type DeleteObjectLockConfigResponse = BucketResponse;
#[derive(Clone, Debug)]
/// Response of [get_object_lock_config()](crate::s3::client::Client::get_object_lock_config) API
pub struct GetObjectLockConfigResponse {
pub headers: HeaderMap,
pub region: String,
@ -734,9 +790,11 @@ pub struct GetObjectLockConfigResponse {
pub config: ObjectLockConfig,
}
/// Response of [set_object_lock_config()](crate::s3::client::Client::set_object_lock_config) API
pub type SetObjectLockConfigResponse = BucketResponse;
#[derive(Clone, Debug)]
/// Response of [get_object_retention()](crate::s3::client::Client::get_object_retention) API
pub struct GetObjectRetentionResponse {
pub headers: HeaderMap,
pub region: String,
@ -747,11 +805,14 @@ pub struct GetObjectRetentionResponse {
pub retain_until_date: Option<UtcTime>,
}
/// Response of [set_object_retention()](crate::s3::client::Client::set_object_retention) API
pub type SetObjectRetentionResponse = ObjectResponse;
/// Response of [delete_object_tags()](crate::s3::client::Client::delete_object_tags) API
pub type DeleteObjectTagsResponse = ObjectResponse;
#[derive(Clone, Debug)]
/// Response of [get_object_tags()](crate::s3::client::Client::get_object_tags) API
pub struct GetObjectTagsResponse {
pub headers: HeaderMap,
pub region: String,
@ -761,9 +822,11 @@ pub struct GetObjectTagsResponse {
pub tags: std::collections::HashMap<String, String>,
}
/// Response of [set_object_tags()](crate::s3::client::Client::set_object_tags) API
pub type SetObjectTagsResponse = ObjectResponse;
#[derive(Clone, Debug)]
/// Response of [get_presigned_object_url()](crate::s3::client::Client::get_presigned_object_url) API
pub struct GetPresignedObjectUrlResponse {
pub region: String,
pub bucket_name: String,
@ -773,6 +836,7 @@ pub struct GetPresignedObjectUrlResponse {
}
#[derive(Clone, Debug)]
/// Response of [download_object()](crate::s3::client::Client::download_object) API
pub struct DownloadObjectResponse {
pub headers: HeaderMap,
pub region: String,

View File

@ -13,6 +13,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//! Signature V4 for S3 API
use crate::s3::utils::{
get_canonical_headers, get_canonical_query_string, sha256_hash, to_amz_date, to_signer_date,
Multimap, UtcTime,
@ -22,16 +24,19 @@ use hmac::{Hmac, Mac};
use hyper::http::Method;
use sha2::Sha256;
/// Returns HMAC hash for given key and data
pub fn hmac_hash(key: &[u8], data: &[u8]) -> Vec<u8> {
let mut hasher = Hmac::<Sha256>::new_from_slice(key).expect("HMAC can take key of any size");
hasher.update(data);
hasher.finalize().into_bytes().to_vec()
}
/// Returns hex encoded HMAC hash for given key and data
pub fn hmac_hash_hex(key: &[u8], data: &[u8]) -> String {
hexencode(hmac_hash(key, data))
}
/// Returns scope value of given date, region and service name
pub fn get_scope(date: UtcTime, region: &str, service_name: &str) -> String {
format!(
"{}/{}/{}/aws4_request",
@ -41,6 +46,7 @@ pub fn get_scope(date: UtcTime, region: &str, service_name: &str) -> String {
)
}
/// Returns hex encoded SHA256 hash of canonical request
pub fn get_canonical_request_hash(
method: &Method,
uri: &str,
@ -63,6 +69,7 @@ pub fn get_canonical_request_hash(
return sha256_hash(canonical_request.as_bytes());
}
/// Returns string-to-sign value of given date, scope and canonical request hash
pub fn get_string_to_sign(date: UtcTime, scope: &str, canonical_request_hash: &str) -> String {
format!(
"AWS4-HMAC-SHA256\n{}\n{}\n{}",
@ -72,6 +79,7 @@ pub fn get_string_to_sign(date: UtcTime, scope: &str, canonical_request_hash: &s
)
}
/// Returns signing key of given secret key, date, region and service name
pub fn get_signing_key(
secret_key: &str,
date: UtcTime,
@ -87,10 +95,12 @@ pub fn get_signing_key(
return hmac_hash(date_region_service_key.as_slice(), b"aws4_request");
}
/// Returns signature value for given signing key and string-to-sign
pub fn get_signature(signing_key: &[u8], string_to_sign: &[u8]) -> String {
hmac_hash_hex(signing_key, string_to_sign)
}
/// Returns authorization value for given access key, scope, signed headers and signature
pub fn get_authorization(
access_key: &str,
scope: &str,
@ -103,6 +113,7 @@ pub fn get_authorization(
)
}
/// Signs and updates headers for given parameters
pub fn sign_v4(
service_name: &str,
method: &Method,
@ -134,6 +145,7 @@ pub fn sign_v4(
headers.insert("Authorization".to_string(), authorization);
}
/// Signs and updates headers for given parameters for S3 request
pub fn sign_v4_s3(
method: &Method,
uri: &str,
@ -159,6 +171,7 @@ pub fn sign_v4_s3(
)
}
/// Signs and updates headers for given parameters for STS request
pub fn sign_v4_sts(
method: &Method,
uri: &str,
@ -184,6 +197,7 @@ pub fn sign_v4_sts(
)
}
/// Signs and updates headers for given parameters for pre-sign request
pub fn presign_v4(
method: &Method,
host: &str,
@ -230,6 +244,7 @@ pub fn presign_v4(
query_params.insert("X-Amz-Signature".to_string(), signature);
}
/// Signs and updates headers for given parameters for pre-sign POST request
pub fn post_presign_v4(
string_to_sign: &str,
secret_key: &str,

View File

@ -13,9 +13,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//! Server side encryption definitions
use crate::s3::utils;
use std::any::Any;
/// Base server side encryption
pub trait Sse: std::fmt::Debug {
fn headers(&self) -> utils::Multimap;
fn copy_headers(&self) -> utils::Multimap;
@ -24,6 +27,7 @@ pub trait Sse: std::fmt::Debug {
}
#[derive(Clone, Debug)]
/// Server side encryption customer key type
pub struct SseCustomerKey {
headers: utils::Multimap,
copy_headers: utils::Multimap,
@ -88,6 +92,7 @@ impl Sse for SseCustomerKey {
}
#[derive(Clone, Debug)]
/// Server side encryption KMS type
pub struct SseKms {
headers: utils::Multimap,
}
@ -133,6 +138,7 @@ impl Sse for SseKms {
}
#[derive(Clone, Debug)]
/// Server side encryption S3 type
pub struct SseS3 {
headers: utils::Multimap,
}

View File

@ -13,6 +13,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//! Various types for S3 API requests and responses
use crate::s3::error::Error;
use crate::s3::utils::{
from_iso8601utc, get_default_text, get_option_text, get_text, to_iso8601utc, UtcTime,
@ -23,6 +25,7 @@ use std::fmt;
use xmltree::Element;
#[derive(Clone, Debug, Default)]
/// Contains information of an item of [list_objects()](crate::s3::client::Client::list_objects) API
pub struct Item {
pub name: String,
pub last_modified: Option<UtcTime>,
@ -40,18 +43,21 @@ pub struct Item {
}
#[derive(Clone, Debug)]
/// Contains bucket name and creation date
pub struct Bucket {
pub name: String,
pub creation_date: UtcTime,
}
#[derive(Clone, Debug)]
/// Contains part number and etag of multipart upload
pub struct Part {
pub number: u16,
pub etag: String,
}
#[derive(Clone, Debug)]
/// Contains retention mode information
pub enum RetentionMode {
GOVERNANCE,
COMPLIANCE,
@ -77,11 +83,13 @@ impl fmt::Display for RetentionMode {
}
#[derive(Clone, Debug)]
/// Contains retention mode and retain until date
pub struct Retention {
pub mode: RetentionMode,
pub retain_until_date: UtcTime,
}
/// Parses legal hold string value
pub fn parse_legal_hold(s: &str) -> Result<bool, Error> {
match s {
"ON" => Ok(true),
@ -91,12 +99,14 @@ pub fn parse_legal_hold(s: &str) -> Result<bool, Error> {
}
#[derive(Clone, Debug, Copy)]
/// Contains delete object name and optional version ID
pub struct DeleteObject<'a> {
pub name: &'a str,
pub version_id: Option<&'a str>,
}
#[derive(Clone, Debug)]
/// Compression types
pub enum CompressionType {
NONE,
GZIP,
@ -114,6 +124,7 @@ impl fmt::Display for CompressionType {
}
#[derive(Clone, Debug)]
/// File header information types
pub enum FileHeaderInfo {
USE,
IGNORE,
@ -131,6 +142,7 @@ impl fmt::Display for FileHeaderInfo {
}
#[derive(Clone, Debug)]
/// JSON document types
pub enum JsonType {
DOCUMENT,
LINES,
@ -146,6 +158,7 @@ impl fmt::Display for JsonType {
}
#[derive(Clone, Debug)]
/// Quote fields types
pub enum QuoteFields {
ALWAYS,
ASNEEDED,
@ -161,6 +174,7 @@ impl fmt::Display for QuoteFields {
}
#[derive(Clone, Debug, Default)]
/// CSV input serialization definitions
pub struct CsvInputSerialization {
pub compression_type: Option<CompressionType>,
pub allow_quoted_record_delimiter: bool,
@ -173,15 +187,18 @@ pub struct CsvInputSerialization {
}
#[derive(Clone, Debug, Default)]
/// JSON input serialization definitions
pub struct JsonInputSerialization {
pub compression_type: Option<CompressionType>,
pub json_type: Option<JsonType>,
}
#[derive(Clone, Debug, Default)]
/// Parque input serialization definitions
pub struct ParquetInputSerialization;
#[derive(Clone, Debug, Default)]
/// CSV output serialization definitions
pub struct CsvOutputSerialization {
pub field_delimiter: Option<char>,
pub quote_character: Option<char>,
@ -191,11 +208,13 @@ pub struct CsvOutputSerialization {
}
#[derive(Clone, Debug, Default)]
/// JSON output serialization definitions
pub struct JsonOutputSerialization {
pub record_delimiter: Option<char>,
}
#[derive(Clone, Debug, Default)]
/// Select request for [select_object_content()](crate::s3::client::Client::select_object_content) API
pub struct SelectRequest<'a> {
pub expr: &'a str,
pub csv_input: Option<CsvInputSerialization>,
@ -456,6 +475,7 @@ impl<'a> SelectRequest<'a> {
}
#[derive(Clone, Debug)]
/// Progress information of [select_object_content()](crate::s3::client::Client::select_object_content) API
pub struct SelectProgress {
pub bytes_scanned: usize,
pub bytes_progressed: usize,
@ -463,14 +483,17 @@ pub struct SelectProgress {
}
#[derive(Debug, Deserialize, Serialize)]
/// User identity contains principal ID
pub struct UserIdentity {
#[serde(alias = "principalId")]
pub principal_id: Option<String>,
}
/// Owner identity contains principal ID
pub type OwnerIdentity = UserIdentity;
#[derive(Debug, Deserialize, Serialize)]
/// Request parameters contain principal ID, region and source IP address
pub struct RequestParameters {
#[serde(alias = "principalId")]
pub principal_id: Option<String>,
@ -481,6 +504,7 @@ pub struct RequestParameters {
}
#[derive(Debug, Deserialize, Serialize)]
/// Response elements information
pub struct ResponseElements {
#[serde(alias = "content-length")]
pub content_length: Option<String>,
@ -493,6 +517,7 @@ pub struct ResponseElements {
}
#[derive(Debug, Deserialize, Serialize)]
/// S3 bucket information
pub struct S3Bucket {
#[serde(alias = "name")]
pub name: Option<String>,
@ -503,6 +528,7 @@ pub struct S3Bucket {
}
#[derive(Debug, Deserialize, Serialize)]
/// S3 object information
pub struct S3Object {
#[serde(alias = "key")]
pub key: Option<String>,
@ -519,6 +545,7 @@ pub struct S3Object {
}
#[derive(Debug, Deserialize, Serialize)]
/// S3 definitions for NotificationRecord
pub struct S3 {
#[serde(alias = "s3SchemaVersion")]
pub s3_schema_version: Option<String>,
@ -531,6 +558,7 @@ pub struct S3 {
}
#[derive(Debug, Deserialize, Serialize)]
/// Source information
pub struct Source {
#[serde(alias = "host")]
pub host: Option<String>,
@ -541,6 +569,7 @@ pub struct Source {
}
#[derive(Debug, Deserialize, Serialize)]
/// Notification record information
pub struct NotificationRecord {
#[serde(alias = "eventVersion")]
pub event_version: Option<String>,
@ -565,12 +594,14 @@ pub struct NotificationRecord {
}
#[derive(Debug, Deserialize, Serialize)]
/// Contains notification records
pub struct NotificationRecords {
#[serde(alias = "Records")]
pub records: Vec<NotificationRecord>,
}
#[derive(Clone, Debug)]
/// Directive types
pub enum Directive {
Copy,
Replace,
@ -596,6 +627,7 @@ impl fmt::Display for Directive {
}
#[derive(Clone, Debug)]
/// Server-side information configuration
pub struct SseConfig {
pub sse_algorithm: String,
pub kms_master_key_id: Option<String>,
@ -636,18 +668,21 @@ impl SseConfig {
}
#[derive(Clone, Debug)]
/// Contains key and value
pub struct Tag {
pub key: String,
pub value: String,
}
#[derive(Clone, Debug)]
/// And operator contains prefix and tags
pub struct AndOperator {
pub prefix: Option<String>,
pub tags: Option<HashMap<String, String>>,
}
#[derive(Clone, Debug)]
/// Filter information
pub struct Filter {
pub and_operator: Option<AndOperator>,
pub prefix: Option<String>,
@ -763,6 +798,7 @@ impl Filter {
}
#[derive(Clone, Debug)]
/// Lifecycle rule information
pub struct LifecycleRule {
pub abort_incomplete_multipart_upload_days_after_initiation: Option<usize>,
pub expiration_date: Option<UtcTime>,
@ -900,6 +936,7 @@ impl LifecycleRule {
}
#[derive(Clone, Debug)]
/// Lifecycle configuration
pub struct LifecycleConfig {
pub rules: Vec<LifecycleRule>,
}
@ -1155,6 +1192,7 @@ fn to_xml_common_notification_config(
}
#[derive(Clone, Debug)]
/// Prefix filter rule
pub struct PrefixFilterRule {
pub value: String,
}
@ -1164,6 +1202,7 @@ impl PrefixFilterRule {
}
#[derive(Clone, Debug)]
/// Suffix filter rule
pub struct SuffixFilterRule {
pub value: String,
}
@ -1173,6 +1212,7 @@ impl SuffixFilterRule {
}
#[derive(Clone, Debug)]
/// Cloud function configuration information
pub struct CloudFuncConfig {
pub events: Vec<String>,
pub id: Option<String>,
@ -1223,6 +1263,7 @@ impl CloudFuncConfig {
}
#[derive(Clone, Debug)]
/// Queue configuration information
pub struct QueueConfig {
pub events: Vec<String>,
pub id: Option<String>,
@ -1273,6 +1314,7 @@ impl QueueConfig {
}
#[derive(Clone, Debug)]
/// Topic configuration information
pub struct TopicConfig {
pub events: Vec<String>,
pub id: Option<String>,
@ -1323,6 +1365,7 @@ impl TopicConfig {
}
#[derive(Clone, Debug)]
/// Notification configuration information
pub struct NotificationConfig {
pub cloud_func_config_list: Option<Vec<CloudFuncConfig>>,
pub queue_config_list: Option<Vec<QueueConfig>>,
@ -1413,6 +1456,7 @@ impl NotificationConfig {
}
#[derive(Clone, Debug)]
/// Access control translation information
pub struct AccessControlTranslation {
pub owner: String,
}
@ -1432,11 +1476,13 @@ impl Default for AccessControlTranslation {
}
#[derive(Clone, Debug)]
/// Encryption configuration information
pub struct EncryptionConfig {
pub replica_kms_key_id: Option<String>,
}
#[derive(Clone, Debug)]
/// Metrics information
pub struct Metrics {
pub event_threshold_minutes: Option<i32>,
pub status: bool,
@ -1452,6 +1498,7 @@ impl Metrics {
}
#[derive(Clone, Debug)]
/// Replication time information
pub struct ReplicationTime {
pub time_minutes: Option<i32>,
pub status: bool,
@ -1467,6 +1514,7 @@ impl ReplicationTime {
}
#[derive(Clone, Debug)]
/// Destination information
pub struct Destination {
pub bucket_arn: String,
pub access_control_translation: Option<AccessControlTranslation>,
@ -1601,11 +1649,13 @@ impl Destination {
}
#[derive(Clone, Debug)]
/// Source selection criteria information
pub struct SourceSelectionCriteria {
pub sse_kms_encrypted_objects_status: Option<bool>,
}
#[derive(Clone, Debug)]
/// Replication rule information
pub struct ReplicationRule {
pub destination: Destination,
pub delete_marker_replication_status: Option<bool>,
@ -1750,6 +1800,7 @@ impl ReplicationRule {
}
#[derive(Clone, Debug)]
/// Replication configuration information
pub struct ReplicationConfig {
pub role: Option<String>,
pub rules: Vec<ReplicationRule>,
@ -1794,6 +1845,7 @@ impl ReplicationConfig {
}
#[derive(Clone, Debug)]
/// Object lock configuration information
pub struct ObjectLockConfig {
pub retention_mode: Option<RetentionMode>,
pub retention_duration_days: Option<i32>,

View File

@ -13,6 +13,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//! Various utility and helper functions
use crate::s3::error::Error;
use base64::engine::general_purpose::STANDARD as BASE64;
use base64::engine::Engine as _;
@ -29,14 +31,18 @@ pub use urlencoding::decode as urldecode;
pub use urlencoding::encode as urlencode;
use xmltree::Element;
/// Date and time with UTC timezone
pub type UtcTime = DateTime<Utc>;
/// Multimap for string key and string value
pub type Multimap = MultiMap<String, String>;
/// Encodes data using base64 algorithm
pub fn b64encode<T: AsRef<[u8]>>(input: T) -> String {
BASE64.encode(input)
}
/// Merges two multimaps.
pub fn merge(m1: &mut Multimap, m2: &Multimap) {
for (key, values) in m2.iter_all() {
for value in values {
@ -45,36 +51,44 @@ pub fn merge(m1: &mut Multimap, m2: &Multimap) {
}
}
/// Computes CRC32 of given data.
pub fn crc32(data: &[u8]) -> u32 {
Crc::<u32>::new(&CRC_32_ISO_HDLC).checksum(data)
}
/// Converts data array into 32 bit unsigned int
pub fn uint32(mut data: &[u8]) -> Result<u32, std::io::Error> {
data.read_u32::<BigEndian>()
}
/// Gets hex encoded SHA256 hash of given data
pub fn sha256_hash(data: &[u8]) -> String {
let mut hasher = Sha256::new();
hasher.update(data);
format!("{:x}", hasher.finalize())
}
/// Gets bas64 encoded MD5 hash of given data
pub fn md5sum_hash(data: &[u8]) -> String {
b64encode(md5compute(data).as_slice())
}
/// Gets current UTC time
pub fn utc_now() -> UtcTime {
chrono::offset::Utc::now()
}
/// Gets signer date value of given time
pub fn to_signer_date(time: UtcTime) -> String {
time.format("%Y%m%d").to_string()
}
/// Gets AMZ date value of given time
pub fn to_amz_date(time: UtcTime) -> String {
time.format("%Y%m%dT%H%M%SZ").to_string()
}
/// Gets HTTP header value of given time
pub fn to_http_header_value(time: UtcTime) -> String {
format!(
"{}, {} {} {} GMT",
@ -99,10 +113,12 @@ pub fn to_http_header_value(time: UtcTime) -> String {
)
}
/// Gets ISO8601 UTC formatted value of given time
pub fn to_iso8601utc(time: UtcTime) -> String {
time.format("%Y-%m-%dT%H:%M:%S.%3fZ").to_string()
}
/// Parses ISO8601 UTC formatted value to time
pub fn from_iso8601utc(s: &str) -> Result<UtcTime, ParseError> {
Ok(DateTime::<Utc>::from_naive_utc_and_offset(
match NaiveDateTime::parse_from_str(s, "%Y-%m-%dT%H:%M:%S.%3fZ") {
@ -113,6 +129,7 @@ pub fn from_iso8601utc(s: &str) -> Result<UtcTime, ParseError> {
))
}
/// Parses HTTP header value to time
pub fn from_http_header_value(s: &str) -> Result<UtcTime, ParseError> {
Ok(DateTime::<Utc>::from_naive_utc_and_offset(
NaiveDateTime::parse_from_str(s, "%a, %d %b %Y %H:%M:%S GMT")?,
@ -120,6 +137,7 @@ pub fn from_http_header_value(s: &str) -> Result<UtcTime, ParseError> {
))
}
/// Converts multimap to HTTP headers
pub fn to_http_headers(map: &Multimap) -> Vec<String> {
let mut headers: Vec<String> = Vec::new();
for (key, values) in map.iter_all() {
@ -134,6 +152,7 @@ pub fn to_http_headers(map: &Multimap) -> Vec<String> {
headers
}
/// Converts multimap to HTTP query string
pub fn to_query_string(map: &Multimap) -> String {
let mut query = String::new();
for (key, values) in map.iter_all() {
@ -149,6 +168,7 @@ pub fn to_query_string(map: &Multimap) -> String {
query
}
/// Converts multimap to canonical query string
pub fn get_canonical_query_string(map: &Multimap) -> String {
let mut keys: Vec<String> = Vec::new();
for (key, _) in map.iter() {
@ -176,6 +196,7 @@ pub fn get_canonical_query_string(map: &Multimap) -> String {
query
}
/// Converts multimap to signed headers and canonical headers
pub fn get_canonical_headers(map: &Multimap) -> (String, String) {
lazy_static! {
static ref MULTI_SPACE_REGEX: Regex = Regex::new("( +)").unwrap();
@ -223,6 +244,7 @@ pub fn get_canonical_headers(map: &Multimap) -> (String, String) {
(signed_headers, canonical_headers)
}
/// Validates given bucket name
pub fn check_bucket_name(bucket_name: &str, strict: bool) -> Result<(), Error> {
if bucket_name.trim().is_empty() {
return Err(Error::InvalidBucketName(String::from(
@ -277,6 +299,7 @@ pub fn check_bucket_name(bucket_name: &str, strict: bool) -> Result<(), Error> {
Ok(())
}
/// Gets text value of given XML element for given tag.
pub fn get_text(element: &Element, tag: &str) -> Result<String, Error> {
Ok(element
.get_child(tag)
@ -286,6 +309,7 @@ pub fn get_text(element: &Element, tag: &str) -> Result<String, Error> {
.to_string())
}
/// Gets optional text value of given XML element for given tag.
pub fn get_option_text(element: &Element, tag: &str) -> Option<String> {
if let Some(v) = element.get_child(tag) {
return Some(v.get_text().unwrap_or_default().to_string());
@ -294,12 +318,14 @@ pub fn get_option_text(element: &Element, tag: &str) -> Option<String> {
None
}
/// Gets default text value of given XML element for given tag.
pub fn get_default_text(element: &Element, tag: &str) -> String {
element.get_child(tag).map_or(String::new(), |v| {
v.get_text().unwrap_or_default().to_string()
})
}
/// Copies source byte slice into destination byte slice
pub fn copy_slice(dst: &mut [u8], src: &[u8]) -> usize {
let mut c = 0;
for (d, s) in dst.iter_mut().zip(src.iter()) {

View File

@ -567,7 +567,7 @@ impl<'a> ClientTest<'_> {
};
let spawned_task = task::spawn(listen_task());
task::sleep(std::time::Duration::from_millis(100)).await;
task::sleep(std::time::Duration::from_millis(200)).await;
let size = 16_usize;
self.client
@ -1059,10 +1059,14 @@ impl<'a> ClientTest<'_> {
.await
.unwrap();
let mut args = SetObjectRetentionArgs::new(&bucket_name, &object_name).unwrap();
args.retention_mode = Some(RetentionMode::GOVERNANCE);
let retain_until_date = utc_now() + Duration::days(1);
args.retain_until_date = Some(retain_until_date);
let args = SetObjectRetentionArgs::new(
&bucket_name,
&object_name,
Some(RetentionMode::GOVERNANCE),
Some(retain_until_date),
)
.unwrap();
self.client.set_object_retention(&args).await.unwrap();
@ -1080,7 +1084,7 @@ impl<'a> ClientTest<'_> {
_ => false,
},);
let mut args = SetObjectRetentionArgs::new(&bucket_name, &object_name).unwrap();
let mut args = SetObjectRetentionArgs::new(&bucket_name, &object_name, None, None).unwrap();
args.bypass_governance_mode = true;
self.client.set_object_retention(&args).await.unwrap();