diff --git a/.vscode/launch.json b/.vscode/launch.json
index 62da1e912..39f29cd96 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -99,7 +99,17 @@
"name": "Debug executable target/debug/rustfs",
"type": "lldb",
"request": "launch",
- "program": "${workspaceFolder}/target/debug/rustfs",
+ "cargo": {
+ "args": [
+ "run",
+ "--bin",
+ "rustfs",
+ "-j",
+ "1",
+ "--profile",
+ "dev"
+ ]
+ },
"args": [],
"cwd": "${workspaceFolder}",
//"stopAtEntry": false,
@@ -107,7 +117,7 @@
"env": {
"RUSTFS_ACCESS_KEY": "rustfsadmin",
"RUSTFS_SECRET_KEY": "rustfsadmin",
- "RUSTFS_VOLUMES": "./target/volume/test{1...4}",
+ //"RUSTFS_VOLUMES": "./target/volume/test{1...4}",
"RUSTFS_ADDRESS": ":9000",
"RUSTFS_CONSOLE_ENABLE": "true",
// "RUSTFS_OBS_TRACE_ENDPOINT": "http://127.0.0.1:4318/v1/traces", // jeager otlp http endpoint
@@ -116,11 +126,31 @@
// "RUSTFS_COMPRESS_ENABLE": "true",
"RUSTFS_CONSOLE_ADDRESS": "127.0.0.1:9001",
"RUSTFS_OBS_LOG_DIRECTORY": "./target/logs",
+ "RUST_LOG":"rustfs=debug,ecstore=debug,s3s=debug,iam=debug",
},
"sourceLanguages": [
"rust"
],
},
+ {
+ "type": "lldb",
+ "request": "launch",
+ "name": "Debug test_lifecycle_transition_basic",
+ "cargo": {
+ "args": [
+ "test",
+ "-p",
+ "rustfs-scanner",
+ "--test",
+ "lifecycle_integration_test",
+ "serial_tests::test_lifecycle_transition_basic",
+ "-j",
+ "1"
+ ]
+ },
+ "args": [],
+ "cwd": "${workspaceFolder}"
+ },
{
"name": "Debug executable target/debug/test",
"type": "lldb",
diff --git a/Cargo.lock b/Cargo.lock
index c8e124602..53619f2eb 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1225,6 +1225,15 @@ dependencies = [
"num-traits",
]
+[[package]]
+name = "bincode"
+version = "1.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
+dependencies = [
+ "serde",
+]
+
[[package]]
name = "bitflags"
version = "1.3.2"
@@ -1529,7 +1538,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6139a8597ed92cf816dfb33f5dd6cf0bb93a6adc938f11039f371bc5bcd26c3"
dependencies = [
"chrono",
- "phf",
+ "phf 0.12.1",
]
[[package]]
@@ -3196,6 +3205,15 @@ version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "780955b8b195a21ab8e4ac6b60dd1dbdcec1dc6c51c0617964b08c81785e12c9"
+[[package]]
+name = "doxygen-rs"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "415b6ec780d34dcf624666747194393603d0373b7141eef01d12ee58881507d9"
+dependencies = [
+ "phf 0.11.3",
+]
+
[[package]]
name = "dunce"
version = "1.0.5"
@@ -4283,6 +4301,44 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+[[package]]
+name = "heed"
+version = "0.22.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a56c94661ddfb51aa9cdfbf102cfcc340aa69267f95ebccc4af08d7c530d393"
+dependencies = [
+ "bitflags 2.10.0",
+ "byteorder",
+ "heed-traits",
+ "heed-types",
+ "libc",
+ "lmdb-master-sys",
+ "once_cell",
+ "page_size",
+ "serde",
+ "synchronoise",
+ "url",
+]
+
+[[package]]
+name = "heed-traits"
+version = "0.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eb3130048d404c57ce5a1ac61a903696e8fcde7e8c2991e9fcfc1f27c3ef74ff"
+
+[[package]]
+name = "heed-types"
+version = "0.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13c255bdf46e07fb840d120a36dcc81f385140d7191c76a7391672675c01a55d"
+dependencies = [
+ "bincode",
+ "byteorder",
+ "heed-traits",
+ "serde",
+ "serde_json",
+]
+
[[package]]
name = "hermit-abi"
version = "0.5.2"
@@ -5292,6 +5348,17 @@ version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77"
+[[package]]
+name = "lmdb-master-sys"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "864808e0b19fb6dd3b70ba94ee671b82fce17554cf80aeb0a155c65bb08027df"
+dependencies = [
+ "cc",
+ "doxygen-rs",
+ "libc",
+]
+
[[package]]
name = "local-ip-address"
version = "0.6.9"
@@ -6350,13 +6417,55 @@ dependencies = [
"subtle",
]
+[[package]]
+name = "phf"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078"
+dependencies = [
+ "phf_macros",
+ "phf_shared 0.11.3",
+]
+
[[package]]
name = "phf"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "913273894cec178f401a31ec4b656318d95473527be05c0752cc41cdc32be8b7"
dependencies = [
- "phf_shared",
+ "phf_shared 0.12.1",
+]
+
+[[package]]
+name = "phf_generator"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d"
+dependencies = [
+ "phf_shared 0.11.3",
+ "rand 0.8.5",
+]
+
+[[package]]
+name = "phf_macros"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216"
+dependencies = [
+ "phf_generator",
+ "phf_shared 0.11.3",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.114",
+]
+
+[[package]]
+name = "phf_shared"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5"
+dependencies = [
+ "siphasher",
]
[[package]]
@@ -7817,12 +7926,15 @@ dependencies = [
"faster-hex",
"flatbuffers",
"futures",
+ "futures-util",
"glob",
"google-cloud-auth",
"google-cloud-storage",
"hex-simd",
"hmac 0.13.0-rc.3",
"http 1.4.0",
+ "http-body 1.0.1",
+ "http-body-util",
"hyper",
"hyper-rustls",
"hyper-util",
@@ -8201,9 +8313,11 @@ dependencies = [
name = "rustfs-scanner"
version = "0.0.5"
dependencies = [
+ "anyhow",
"async-trait",
"chrono",
"futures",
+ "heed",
"http 1.4.0",
"path-clean",
"rand 0.10.0-rc.6",
@@ -8222,6 +8336,7 @@ dependencies = [
"tokio",
"tokio-util",
"tracing",
+ "uuid",
]
[[package]]
@@ -9556,6 +9671,15 @@ dependencies = [
"futures-core",
]
+[[package]]
+name = "synchronoise"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3dbc01390fc626ce8d1cffe3376ded2b72a11bb70e1c75f404a210e4daa4def2"
+dependencies = [
+ "crossbeam-queue",
+]
+
[[package]]
name = "synstructure"
version = "0.12.6"
diff --git a/crates/ecstore/Cargo.toml b/crates/ecstore/Cargo.toml
index 858f7f3c7..0325005cf 100644
--- a/crates/ecstore/Cargo.toml
+++ b/crates/ecstore/Cargo.toml
@@ -52,6 +52,7 @@ glob = { workspace = true }
thiserror.workspace = true
flatbuffers.workspace = true
futures.workspace = true
+futures-util.workspace = true
tracing.workspace = true
serde.workspace = true
time.workspace = true
@@ -60,6 +61,8 @@ serde_json.workspace = true
quick-xml = { workspace = true, features = ["serialize", "async-tokio"] }
s3s.workspace = true
http.workspace = true
+http-body = { workspace = true }
+http-body-util.workspace = true
url.workspace = true
uuid = { workspace = true, features = ["v4", "fast-rng", "serde"] }
reed-solomon-simd = { workspace = true }
diff --git a/crates/ecstore/src/bucket/lifecycle/lifecycle.rs b/crates/ecstore/src/bucket/lifecycle/lifecycle.rs
index b785d11b6..b82f125ee 100644
--- a/crates/ecstore/src/bucket/lifecycle/lifecycle.rs
+++ b/crates/ecstore/src/bucket/lifecycle/lifecycle.rs
@@ -18,12 +18,10 @@
#![allow(unused_must_use)]
#![allow(clippy::all)]
-use crate::bucket::lifecycle::rule::TransitionOps;
-use crate::store_api::ObjectInfo;
use rustfs_filemeta::{ReplicationStatusType, VersionPurgeStatusType};
use s3s::dto::{
BucketLifecycleConfiguration, ExpirationStatus, LifecycleExpiration, LifecycleRule, NoncurrentVersionTransition,
- ObjectLockConfiguration, ObjectLockEnabled, RestoreRequest, Transition,
+ ObjectLockConfiguration, ObjectLockEnabled, RestoreRequest, Transition, TransitionStorageClass,
};
use std::cmp::Ordering;
use std::collections::HashMap;
@@ -35,6 +33,9 @@ use time::{self, Duration, OffsetDateTime};
use tracing::info;
use uuid::Uuid;
+use crate::bucket::lifecycle::rule::TransitionOps;
+use crate::store_api::ObjectInfo;
+
pub const TRANSITION_COMPLETE: &str = "complete";
pub const TRANSITION_PENDING: &str = "pending";
const ERR_LIFECYCLE_NO_RULE: &str = "Lifecycle configuration should have at least one rule";
@@ -171,44 +172,51 @@ impl Lifecycle for BucketLifecycleConfiguration {
continue;
}
- let rule_prefix = rule.prefix.as_ref().expect("err!");
+ let rule_prefix = &rule.prefix.clone().unwrap_or_default();
if prefix.len() > 0 && rule_prefix.len() > 0 && !prefix.starts_with(rule_prefix) && !rule_prefix.starts_with(&prefix)
{
continue;
}
- let rule_noncurrent_version_expiration = rule.noncurrent_version_expiration.as_ref().expect("err!");
- if rule_noncurrent_version_expiration.noncurrent_days.expect("err!") > 0 {
+ if let Some(rule_noncurrent_version_expiration) = &rule.noncurrent_version_expiration {
+ if let Some(noncurrent_days) = rule_noncurrent_version_expiration.noncurrent_days {
+ if noncurrent_days > 0 {
+ return true;
+ }
+ }
+ if let Some(newer_noncurrent_versions) = rule_noncurrent_version_expiration.newer_noncurrent_versions {
+ if newer_noncurrent_versions > 0 {
+ return true;
+ }
+ }
+ }
+ if rule.noncurrent_version_transitions.is_some() {
return true;
}
- if rule_noncurrent_version_expiration.newer_noncurrent_versions.expect("err!") > 0 {
- return true;
+ if let Some(rule_expiration) = &rule.expiration {
+ if let Some(date1) = rule_expiration.date.clone() {
+ if OffsetDateTime::from(date1).unix_timestamp() < OffsetDateTime::now_utc().unix_timestamp() {
+ return true;
+ }
+ }
+ if rule_expiration.date.is_some() {
+ return true;
+ }
+ if let Some(expired_object_delete_marker) = rule_expiration.expired_object_delete_marker
+ && expired_object_delete_marker
+ {
+ return true;
+ }
}
- if !rule.noncurrent_version_transitions.is_none() {
- return true;
+ if let Some(rule_transitions) = &rule.transitions {
+ let rule_transitions_0 = rule_transitions[0].clone();
+ if let Some(date1) = rule_transitions_0.date {
+ if OffsetDateTime::from(date1).unix_timestamp() < OffsetDateTime::now_utc().unix_timestamp() {
+ return true;
+ }
+ }
}
- let rule_expiration = rule.expiration.as_ref().expect("err!");
- if !rule_expiration.date.is_none()
- && OffsetDateTime::from(rule_expiration.date.clone().expect("err!")).unix_timestamp()
- < OffsetDateTime::now_utc().unix_timestamp()
- {
- return true;
- }
- if !rule_expiration.date.is_none() {
- return true;
- }
- if rule_expiration.expired_object_delete_marker.expect("err!") {
- return true;
- }
- let rule_transitions: &[Transition] = &rule.transitions.as_ref().expect("err!");
- let rule_transitions_0 = rule_transitions[0].clone();
- if !rule_transitions_0.date.is_none()
- && OffsetDateTime::from(rule_transitions_0.date.expect("err!")).unix_timestamp()
- < OffsetDateTime::now_utc().unix_timestamp()
- {
- return true;
- }
- if !rule.transitions.is_none() {
+ if rule.transitions.is_some() {
return true;
}
}
@@ -232,7 +240,7 @@ impl Lifecycle for BucketLifecycleConfiguration {
return Err(std::io::Error::other(ERR_LIFECYCLE_BUCKET_LOCKED));
}
}
- }
+ }
}*/
}
for (i, _) in self.rules.iter().enumerate() {
@@ -325,7 +333,7 @@ impl Lifecycle for BucketLifecycleConfiguration {
if let Some(expired_object_delete_marker) = expiration.expired_object_delete_marker {
events.push(Event {
action: IlmAction::DeleteVersionAction,
- rule_id: rule.id.clone().expect("err!"),
+ rule_id: rule.id.clone().unwrap_or_default(),
due: Some(now),
noncurrent_days: 0,
newer_noncurrent_versions: 0,
@@ -339,7 +347,7 @@ impl Lifecycle for BucketLifecycleConfiguration {
if now.unix_timestamp() >= expected_expiry.unix_timestamp() {
events.push(Event {
action: IlmAction::DeleteVersionAction,
- rule_id: rule.id.clone().expect("err!"),
+ rule_id: rule.id.clone().unwrap_or_default(),
due: Some(expected_expiry),
noncurrent_days: 0,
newer_noncurrent_versions: 0,
@@ -360,7 +368,7 @@ impl Lifecycle for BucketLifecycleConfiguration {
if now.unix_timestamp() >= due.unix_timestamp() {
events.push(Event {
action: IlmAction::DelMarkerDeleteAllVersionsAction,
- rule_id: rule.id.clone().expect("err!"),
+ rule_id: rule.id.clone().unwrap_or_default(),
due: Some(due),
noncurrent_days: 0,
newer_noncurrent_versions: 0,
@@ -393,7 +401,7 @@ impl Lifecycle for BucketLifecycleConfiguration {
if now.unix_timestamp() >= expected_expiry.unix_timestamp() {
events.push(Event {
action: IlmAction::DeleteVersionAction,
- rule_id: rule.id.clone().expect("err!"),
+ rule_id: rule.id.clone().unwrap_or_default(),
due: Some(expected_expiry),
noncurrent_days: 0,
newer_noncurrent_versions: 0,
@@ -416,7 +424,7 @@ impl Lifecycle for BucketLifecycleConfiguration {
if now.unix_timestamp() == 0 || now.unix_timestamp() > due0.unix_timestamp() {
events.push(Event {
action: IlmAction::TransitionVersionAction,
- rule_id: rule.id.clone().expect("err!"),
+ rule_id: rule.id.clone().unwrap_or_default(),
due,
storage_class: rule.noncurrent_version_transitions.as_ref().unwrap()[0]
.storage_class
@@ -450,7 +458,7 @@ impl Lifecycle for BucketLifecycleConfiguration {
info!("eval_inner: expiration by date - date0={:?}", date0);
events.push(Event {
action: IlmAction::DeleteAction,
- rule_id: rule.id.clone().expect("err!"),
+ rule_id: rule.id.clone().unwrap_or_default(),
due: Some(date0),
noncurrent_days: 0,
newer_noncurrent_versions: 0,
@@ -471,7 +479,7 @@ impl Lifecycle for BucketLifecycleConfiguration {
info!("eval_inner: object should expire, adding DeleteAction");
let mut event = Event {
action: IlmAction::DeleteAction,
- rule_id: rule.id.clone().expect("err!"),
+ rule_id: rule.id.clone().unwrap_or_default(),
due: Some(expected_expiry),
noncurrent_days: 0,
newer_noncurrent_versions: 0,
@@ -496,9 +504,14 @@ impl Lifecycle for BucketLifecycleConfiguration {
if now.unix_timestamp() == 0 || now.unix_timestamp() > due0.unix_timestamp() {
events.push(Event {
action: IlmAction::TransitionAction,
- rule_id: rule.id.clone().expect("err!"),
+ rule_id: rule.id.clone().unwrap_or_default(),
due,
- storage_class: transitions[0].storage_class.clone().expect("err!").as_str().to_string(),
+ storage_class: transitions[0]
+ .storage_class
+ .clone()
+ .unwrap_or(TransitionStorageClass::from_static(""))
+ .as_str()
+ .to_string(),
noncurrent_days: 0,
newer_noncurrent_versions: 0,
});
@@ -512,9 +525,10 @@ impl Lifecycle for BucketLifecycleConfiguration {
if events.len() > 0 {
events.sort_by(|a, b| {
- if now.unix_timestamp() > a.due.expect("err!").unix_timestamp()
- && now.unix_timestamp() > b.due.expect("err").unix_timestamp()
- || a.due.expect("err").unix_timestamp() == b.due.expect("err").unix_timestamp()
+ if now.unix_timestamp() > a.due.unwrap_or_else(|| OffsetDateTime::UNIX_EPOCH).unix_timestamp()
+ && now.unix_timestamp() > b.due.unwrap_or_else(|| OffsetDateTime::UNIX_EPOCH).unix_timestamp()
+ || a.due.unwrap_or_else(|| OffsetDateTime::UNIX_EPOCH).unix_timestamp()
+ == b.due.unwrap_or_else(|| OffsetDateTime::UNIX_EPOCH).unix_timestamp()
{
match a.action {
IlmAction::DeleteAllVersionsAction
@@ -537,7 +551,9 @@ impl Lifecycle for BucketLifecycleConfiguration {
return Ordering::Less;
}
- if a.due.expect("err").unix_timestamp() < b.due.expect("err").unix_timestamp() {
+ if a.due.unwrap_or_else(|| OffsetDateTime::UNIX_EPOCH).unix_timestamp()
+ < b.due.unwrap_or_else(|| OffsetDateTime::UNIX_EPOCH).unix_timestamp()
+ {
return Ordering::Less;
}
return Ordering::Greater;
@@ -558,8 +574,8 @@ impl Lifecycle for BucketLifecycleConfiguration {
}
return Event {
action: IlmAction::DeleteVersionAction,
- rule_id: rule.id.clone().expect("err"),
- noncurrent_days: noncurrent_version_expiration.noncurrent_days.expect("noncurrent_days err.") as u32,
+ rule_id: rule.id.clone().unwrap_or_default(),
+ noncurrent_days: noncurrent_version_expiration.noncurrent_days.unwrap_or(0) as u32,
newer_noncurrent_versions: newer_noncurrent_versions as usize,
due: Some(OffsetDateTime::UNIX_EPOCH),
storage_class: "".into(),
@@ -567,8 +583,8 @@ impl Lifecycle for BucketLifecycleConfiguration {
} else {
return Event {
action: IlmAction::DeleteVersionAction,
- rule_id: rule.id.clone().expect("err"),
- noncurrent_days: noncurrent_version_expiration.noncurrent_days.expect("noncurrent_days err.") as u32,
+ rule_id: rule.id.clone().unwrap_or_default(),
+ noncurrent_days: noncurrent_version_expiration.noncurrent_days.unwrap_or(0) as u32,
newer_noncurrent_versions: 0,
due: Some(OffsetDateTime::UNIX_EPOCH),
storage_class: "".into(),
diff --git a/crates/ecstore/src/client/api_bucket_policy.rs b/crates/ecstore/src/client/api_bucket_policy.rs
index 9512e911b..c01039508 100644
--- a/crates/ecstore/src/client/api_bucket_policy.rs
+++ b/crates/ecstore/src/client/api_bucket_policy.rs
@@ -18,8 +18,10 @@
#![allow(unused_must_use)]
#![allow(clippy::all)]
-use bytes::Bytes;
use http::{HeaderMap, StatusCode};
+use http_body_util::BodyExt;
+use hyper::body::Body;
+use hyper::body::Bytes;
use std::collections::HashMap;
use crate::client::{
@@ -61,9 +63,19 @@ impl TransitionClient {
let resp = self.execute_method(http::Method::PUT, &mut req_metadata).await?;
//defer closeResponse(resp)
+
+ let resp_status = resp.status();
+ let h = resp.headers().clone();
+
//if resp != nil {
- if resp.status() != StatusCode::NO_CONTENT && resp.status() != StatusCode::OK {
- return Err(std::io::Error::other(http_resp_to_error_response(&resp, vec![], bucket_name, "")));
+ if resp_status != StatusCode::NO_CONTENT && resp.status() != StatusCode::OK {
+ return Err(std::io::Error::other(http_resp_to_error_response(
+ resp_status,
+ &h,
+ vec![],
+ bucket_name,
+ "",
+ )));
}
//}
Ok(())
@@ -97,8 +109,17 @@ impl TransitionClient {
.await?;
//defer closeResponse(resp)
- if resp.status() != StatusCode::NO_CONTENT {
- return Err(std::io::Error::other(http_resp_to_error_response(&resp, vec![], bucket_name, "")));
+ let resp_status = resp.status();
+ let h = resp.headers().clone();
+
+ if resp_status != StatusCode::NO_CONTENT {
+ return Err(std::io::Error::other(http_resp_to_error_response(
+ resp_status,
+ &h,
+ vec![],
+ bucket_name,
+ "",
+ )));
}
Ok(())
@@ -136,7 +157,15 @@ impl TransitionClient {
)
.await?;
- let policy = String::from_utf8_lossy(&resp.body().bytes().expect("err").to_vec()).to_string();
+ let mut body_vec = Vec::new();
+ let mut body = resp.into_body();
+ while let Some(frame) = body.frame().await {
+ let frame = frame.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?;
+ if let Some(data) = frame.data_ref() {
+ body_vec.extend_from_slice(data);
+ }
+ }
+ let policy = String::from_utf8_lossy(&body_vec).to_string();
Ok(policy)
}
}
diff --git a/crates/ecstore/src/client/api_error_response.rs b/crates/ecstore/src/client/api_error_response.rs
index 402c34df8..4900fb332 100644
--- a/crates/ecstore/src/client/api_error_response.rs
+++ b/crates/ecstore/src/client/api_error_response.rs
@@ -18,12 +18,11 @@
#![allow(unused_must_use)]
#![allow(clippy::all)]
-use http::StatusCode;
+use http::{HeaderMap, StatusCode};
use serde::{Deserialize, Serialize};
use serde::{de::Deserializer, ser::Serializer};
use std::fmt::Display;
-use s3s::Body;
use s3s::S3ErrorCode;
const _REPORT_ISSUE: &str = "Please report this issue at https://github.com/rustfs/rustfs/issues.";
@@ -95,20 +94,31 @@ pub fn to_error_response(err: &std::io::Error) -> ErrorResponse {
}
pub fn http_resp_to_error_response(
- resp: &http::Response
,
+ resp_status: StatusCode,
+ h: &HeaderMap,
b: Vec,
bucket_name: &str,
object_name: &str,
) -> ErrorResponse {
let err_body = String::from_utf8(b).unwrap();
+ if h.is_empty() || resp_status.is_client_error() || resp_status.is_server_error() {
+ return ErrorResponse {
+ status_code: resp_status,
+ code: S3ErrorCode::ResponseInterrupted,
+ message: "Invalid HTTP response.".to_string(),
+ bucket_name: bucket_name.to_string(),
+ key: object_name.to_string(),
+ ..Default::default()
+ };
+ }
let err_resp_ = quick_xml::de::from_str::(&err_body);
let mut err_resp = ErrorResponse::default();
if err_resp_.is_err() {
- match resp.status() {
+ match resp_status {
StatusCode::NOT_FOUND => {
if object_name == "" {
err_resp = ErrorResponse {
- status_code: resp.status(),
+ status_code: resp_status,
code: S3ErrorCode::NoSuchBucket,
message: "The specified bucket does not exist.".to_string(),
bucket_name: bucket_name.to_string(),
@@ -116,7 +126,7 @@ pub fn http_resp_to_error_response(
};
} else {
err_resp = ErrorResponse {
- status_code: resp.status(),
+ status_code: resp_status,
code: S3ErrorCode::NoSuchKey,
message: "The specified key does not exist.".to_string(),
bucket_name: bucket_name.to_string(),
@@ -127,7 +137,7 @@ pub fn http_resp_to_error_response(
}
StatusCode::FORBIDDEN => {
err_resp = ErrorResponse {
- status_code: resp.status(),
+ status_code: resp_status,
code: S3ErrorCode::AccessDenied,
message: "Access Denied.".to_string(),
bucket_name: bucket_name.to_string(),
@@ -137,7 +147,7 @@ pub fn http_resp_to_error_response(
}
StatusCode::CONFLICT => {
err_resp = ErrorResponse {
- status_code: resp.status(),
+ status_code: resp_status,
code: S3ErrorCode::BucketNotEmpty,
message: "Bucket not empty.".to_string(),
bucket_name: bucket_name.to_string(),
@@ -146,7 +156,7 @@ pub fn http_resp_to_error_response(
}
StatusCode::PRECONDITION_FAILED => {
err_resp = ErrorResponse {
- status_code: resp.status(),
+ status_code: resp_status,
code: S3ErrorCode::PreconditionFailed,
message: "Pre condition failed.".to_string(),
bucket_name: bucket_name.to_string(),
@@ -155,13 +165,13 @@ pub fn http_resp_to_error_response(
};
}
_ => {
- let mut msg = resp.status().to_string();
+ let mut msg = resp_status.to_string();
if err_body.len() > 0 {
msg = err_body;
}
err_resp = ErrorResponse {
- status_code: resp.status(),
- code: S3ErrorCode::Custom(resp.status().to_string().into()),
+ status_code: resp_status,
+ code: S3ErrorCode::Custom(resp_status.to_string().into()),
message: msg,
bucket_name: bucket_name.to_string(),
..Default::default()
@@ -171,32 +181,32 @@ pub fn http_resp_to_error_response(
} else {
err_resp = err_resp_.unwrap();
}
- err_resp.status_code = resp.status();
- if let Some(server_name) = resp.headers().get("Server") {
+ err_resp.status_code = resp_status;
+ if let Some(server_name) = h.get("Server") {
err_resp.server = server_name.to_str().expect("err").to_string();
}
- let code = resp.headers().get("x-minio-error-code");
+ let code = h.get("x-minio-error-code");
if code.is_some() {
err_resp.code = S3ErrorCode::Custom(code.expect("err").to_str().expect("err").into());
}
- let desc = resp.headers().get("x-minio-error-desc");
+ let desc = h.get("x-minio-error-desc");
if desc.is_some() {
err_resp.message = desc.expect("err").to_str().expect("err").trim_matches('"').to_string();
}
if err_resp.request_id == "" {
- if let Some(x_amz_request_id) = resp.headers().get("x-amz-request-id") {
+ if let Some(x_amz_request_id) = h.get("x-amz-request-id") {
err_resp.request_id = x_amz_request_id.to_str().expect("err").to_string();
}
}
if err_resp.host_id == "" {
- if let Some(x_amz_id_2) = resp.headers().get("x-amz-id-2") {
+ if let Some(x_amz_id_2) = h.get("x-amz-id-2") {
err_resp.host_id = x_amz_id_2.to_str().expect("err").to_string();
}
}
if err_resp.region == "" {
- if let Some(x_amz_bucket_region) = resp.headers().get("x-amz-bucket-region") {
+ if let Some(x_amz_bucket_region) = h.get("x-amz-bucket-region") {
err_resp.region = x_amz_bucket_region.to_str().expect("err").to_string();
}
}
diff --git a/crates/ecstore/src/client/api_get_object.rs b/crates/ecstore/src/client/api_get_object.rs
index 80759b2cc..2ac78aa1e 100644
--- a/crates/ecstore/src/client/api_get_object.rs
+++ b/crates/ecstore/src/client/api_get_object.rs
@@ -19,17 +19,25 @@
#![allow(unused_must_use)]
#![allow(clippy::all)]
-use bytes::Bytes;
+use futures_util::ready;
use http::HeaderMap;
-use std::io::Cursor;
+use std::io::{Cursor, Error as IoError, ErrorKind as IoErrorKind, Read};
+use std::pin::Pin;
+use std::task::{Context, Poll};
use tokio::io::BufReader;
+use tokio_util::io::StreamReader;
use crate::client::{
api_error_response::err_invalid_argument,
api_get_options::GetObjectOptions,
transition_api::{ObjectInfo, ReadCloser, ReaderImpl, RequestMetadata, TransitionClient, to_object_info},
};
+use futures_util::StreamExt;
+use http_body_util::BodyExt;
+use hyper::body::Body;
+use hyper::body::Bytes;
use rustfs_utils::hash::EMPTY_STRING_SHA256_HASH;
+use tokio_util::io::ReaderStream;
impl TransitionClient {
pub fn get_object(&self, bucket_name: &str, object_name: &str, opts: &GetObjectOptions) -> Result