mirror of
https://github.com/rustfs/rustfs.git
synced 2026-07-27 16:48:58 +00:00
273 lines
9.3 KiB
Rust
273 lines
9.3 KiB
Rust
#![allow(clippy::map_entry)]
|
||
#![allow(unused_imports)]
|
||
#![allow(unused_variables)]
|
||
#![allow(unused_mut)]
|
||
#![allow(unused_assignments)]
|
||
#![allow(unused_must_use)]
|
||
#![allow(clippy::all)]
|
||
|
||
use http::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.";
|
||
|
||
#[derive(Serialize, Deserialize, Debug, Clone, thiserror::Error, PartialEq, Eq)]
|
||
#[serde(default, rename_all = "PascalCase")]
|
||
pub struct ErrorResponse {
|
||
#[serde(serialize_with = "serialize_code", deserialize_with = "deserialize_code")]
|
||
pub code: S3ErrorCode,
|
||
pub message: String,
|
||
pub bucket_name: String,
|
||
pub key: String,
|
||
pub resource: String,
|
||
pub request_id: String,
|
||
pub host_id: String,
|
||
pub region: String,
|
||
pub server: String,
|
||
#[serde(skip)]
|
||
pub status_code: StatusCode,
|
||
}
|
||
|
||
fn serialize_code<S>(_data: &S3ErrorCode, s: S) -> Result<S::Ok, S::Error>
|
||
where
|
||
S: Serializer,
|
||
{
|
||
s.serialize_str("")
|
||
}
|
||
|
||
fn deserialize_code<'de, D>(d: D) -> Result<S3ErrorCode, D::Error>
|
||
where
|
||
D: Deserializer<'de>,
|
||
{
|
||
Ok(S3ErrorCode::from_bytes(String::deserialize(d)?.as_bytes()).unwrap_or(S3ErrorCode::Custom("".into())))
|
||
}
|
||
|
||
impl Default for ErrorResponse {
|
||
fn default() -> Self {
|
||
ErrorResponse {
|
||
code: S3ErrorCode::Custom("".into()),
|
||
message: Default::default(),
|
||
bucket_name: Default::default(),
|
||
key: Default::default(),
|
||
resource: Default::default(),
|
||
request_id: Default::default(),
|
||
host_id: Default::default(),
|
||
region: Default::default(),
|
||
server: Default::default(),
|
||
status_code: Default::default(),
|
||
}
|
||
}
|
||
}
|
||
|
||
impl Display for ErrorResponse {
|
||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||
write!(f, "{}", self.message)
|
||
}
|
||
}
|
||
|
||
pub fn to_error_response(err: &std::io::Error) -> ErrorResponse {
|
||
if let Some(err) = err.get_ref() {
|
||
if err.is::<ErrorResponse>() {
|
||
err.downcast_ref::<ErrorResponse>().expect("err!").clone()
|
||
} else {
|
||
ErrorResponse::default()
|
||
}
|
||
} else {
|
||
ErrorResponse::default()
|
||
}
|
||
}
|
||
|
||
pub fn http_resp_to_error_response(
|
||
resp: http::Response<Body>,
|
||
b: Vec<u8>,
|
||
bucket_name: &str,
|
||
object_name: &str,
|
||
) -> ErrorResponse {
|
||
let err_body = String::from_utf8(b).unwrap();
|
||
let err_resp_ = serde_xml_rs::from_str::<ErrorResponse>(&err_body);
|
||
let mut err_resp = ErrorResponse::default();
|
||
if err_resp_.is_err() {
|
||
match resp.status() {
|
||
StatusCode::NOT_FOUND => {
|
||
if object_name == "" {
|
||
err_resp = ErrorResponse {
|
||
status_code: resp.status(),
|
||
code: S3ErrorCode::NoSuchBucket,
|
||
message: "The specified bucket does not exist.".to_string(),
|
||
bucket_name: bucket_name.to_string(),
|
||
..Default::default()
|
||
};
|
||
} else {
|
||
err_resp = ErrorResponse {
|
||
status_code: resp.status(),
|
||
code: S3ErrorCode::NoSuchKey,
|
||
message: "The specified key does not exist.".to_string(),
|
||
bucket_name: bucket_name.to_string(),
|
||
key: object_name.to_string(),
|
||
..Default::default()
|
||
};
|
||
}
|
||
}
|
||
StatusCode::FORBIDDEN => {
|
||
err_resp = ErrorResponse {
|
||
status_code: resp.status(),
|
||
code: S3ErrorCode::AccessDenied,
|
||
message: "Access Denied.".to_string(),
|
||
bucket_name: bucket_name.to_string(),
|
||
key: object_name.to_string(),
|
||
..Default::default()
|
||
};
|
||
}
|
||
StatusCode::CONFLICT => {
|
||
err_resp = ErrorResponse {
|
||
status_code: resp.status(),
|
||
code: S3ErrorCode::BucketNotEmpty,
|
||
message: "Bucket not empty.".to_string(),
|
||
bucket_name: bucket_name.to_string(),
|
||
..Default::default()
|
||
};
|
||
}
|
||
StatusCode::PRECONDITION_FAILED => {
|
||
err_resp = ErrorResponse {
|
||
status_code: resp.status(),
|
||
code: S3ErrorCode::PreconditionFailed,
|
||
message: "Pre condition failed.".to_string(),
|
||
bucket_name: bucket_name.to_string(),
|
||
key: object_name.to_string(),
|
||
..Default::default()
|
||
};
|
||
}
|
||
_ => {
|
||
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()),
|
||
message: msg,
|
||
bucket_name: bucket_name.to_string(),
|
||
..Default::default()
|
||
};
|
||
}
|
||
}
|
||
} else {
|
||
err_resp = err_resp_.unwrap();
|
||
}
|
||
err_resp.status_code = resp.status();
|
||
if let Some(server_name) = resp.headers().get("Server") {
|
||
err_resp.server = server_name.to_str().expect("err").to_string();
|
||
}
|
||
|
||
let code = resp.headers().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");
|
||
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") {
|
||
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") {
|
||
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") {
|
||
err_resp.region = x_amz_bucket_region.to_str().expect("err").to_string();
|
||
}
|
||
}
|
||
if err_resp.code == S3ErrorCode::InvalidLocationConstraint/*InvalidRegion*/ && err_resp.region != "" {
|
||
err_resp.message = format!("Region does not match, expecting region ‘{}’.", err_resp.region);
|
||
}
|
||
|
||
err_resp
|
||
}
|
||
|
||
pub fn err_transfer_acceleration_bucket(bucket_name: &str) -> ErrorResponse {
|
||
ErrorResponse {
|
||
status_code: StatusCode::BAD_REQUEST,
|
||
code: S3ErrorCode::InvalidArgument,
|
||
message: "The name of the bucket used for Transfer Acceleration must be DNS-compliant and must not contain periods ‘.’."
|
||
.to_string(),
|
||
bucket_name: bucket_name.to_string(),
|
||
..Default::default()
|
||
}
|
||
}
|
||
|
||
pub fn err_entity_too_large(total_size: i64, max_object_size: i64, bucket_name: &str, object_name: &str) -> ErrorResponse {
|
||
let msg = format!(
|
||
"Your proposed upload size ‘{}’ exceeds the maximum allowed object size ‘{}’ for single PUT operation.",
|
||
total_size, max_object_size
|
||
);
|
||
ErrorResponse {
|
||
status_code: StatusCode::BAD_REQUEST,
|
||
code: S3ErrorCode::EntityTooLarge,
|
||
message: msg,
|
||
bucket_name: bucket_name.to_string(),
|
||
key: object_name.to_string(),
|
||
..Default::default()
|
||
}
|
||
}
|
||
|
||
pub fn err_entity_too_small(total_size: i64, bucket_name: &str, object_name: &str) -> ErrorResponse {
|
||
let msg = format!(
|
||
"Your proposed upload size ‘{}’ is below the minimum allowed object size ‘0B’ for single PUT operation.",
|
||
total_size
|
||
);
|
||
ErrorResponse {
|
||
status_code: StatusCode::BAD_REQUEST,
|
||
code: S3ErrorCode::EntityTooSmall,
|
||
message: msg,
|
||
bucket_name: bucket_name.to_string(),
|
||
key: object_name.to_string(),
|
||
..Default::default()
|
||
}
|
||
}
|
||
|
||
pub fn err_unexpected_eof(total_read: i64, total_size: i64, bucket_name: &str, object_name: &str) -> ErrorResponse {
|
||
let msg = format!(
|
||
"Data read ‘{}’ is not equal to the size ‘{}’ of the input Reader.",
|
||
total_read, total_size
|
||
);
|
||
ErrorResponse {
|
||
status_code: StatusCode::BAD_REQUEST,
|
||
code: S3ErrorCode::Custom("UnexpectedEOF".into()),
|
||
message: msg,
|
||
bucket_name: bucket_name.to_string(),
|
||
key: object_name.to_string(),
|
||
..Default::default()
|
||
}
|
||
}
|
||
|
||
pub fn err_invalid_argument(message: &str) -> ErrorResponse {
|
||
ErrorResponse {
|
||
status_code: StatusCode::BAD_REQUEST,
|
||
code: S3ErrorCode::InvalidArgument,
|
||
message: message.to_string(),
|
||
request_id: "rustfs".to_string(),
|
||
..Default::default()
|
||
}
|
||
}
|
||
|
||
pub fn err_api_not_supported(message: &str) -> ErrorResponse {
|
||
ErrorResponse {
|
||
status_code: StatusCode::NOT_IMPLEMENTED,
|
||
code: S3ErrorCode::Custom("APINotSupported".into()),
|
||
message: message.to_string(),
|
||
request_id: "rustfs".to_string(),
|
||
..Default::default()
|
||
}
|
||
}
|