be more lenient and fix review comments
All checks were successful
continuous-integration/drone/pr Build is passing

This commit is contained in:
Trinity Pointard 2021-12-06 11:29:59 +01:00
parent 41f83cff67
commit 3043cd1a86
3 changed files with 82 additions and 129 deletions

View file

@ -228,26 +228,33 @@ async fn handler_inner(garage: Arc<Garage>, req: Request<Body>) -> Result<Respon
list_type, list_type,
.. ..
} => { } => {
handle_list( if list_type == "2" {
garage, handle_list(
&ListObjectsQuery { garage,
is_v2: list_type == "v2", &ListObjectsQuery {
bucket, is_v2: true,
delimiter: delimiter.map(|d| d.to_string()), bucket,
max_keys: max_keys.unwrap_or(1000), delimiter: delimiter.map(|d| d.to_string()),
prefix: prefix.unwrap_or_default(), max_keys: max_keys.unwrap_or(1000),
marker: None, prefix: prefix.unwrap_or_default(),
continuation_token, marker: None,
start_after, continuation_token,
urlencode_resp: encoding_type.map(|e| e == "url").unwrap_or(false), start_after,
}, urlencode_resp: encoding_type.map(|e| e == "url").unwrap_or(false),
) },
.await )
.await
} else {
Err(Error::BadRequest(format!(
"Invalid endpoint: list-type={}",
list_type
)))
}
} }
Endpoint::DeleteObjects { bucket } => { Endpoint::DeleteObjects { bucket } => {
handle_delete_objects(garage, &bucket, req, content_sha256).await handle_delete_objects(garage, &bucket, req, content_sha256).await
} }
_ => Err(Error::BadRequest("Unsupported call".to_string())), endpoint => Err(Error::NotImplemented(endpoint.name().to_owned())),
} }
} }

View file

@ -65,6 +65,10 @@ pub enum Error {
/// The client sent an invalid request /// The client sent an invalid request
#[error(display = "Bad request: {}", _0)] #[error(display = "Bad request: {}", _0)]
BadRequest(String), BadRequest(String),
/// The client sent a request for an action not supported by garage
#[error(display = "Unimplemented action: {}", _0)]
NotImplemented(String),
} }
impl From<roxmltree::Error> for Error { impl From<roxmltree::Error> for Error {
@ -94,6 +98,7 @@ impl Error {
StatusCode::INTERNAL_SERVER_ERROR StatusCode::INTERNAL_SERVER_ERROR
} }
Error::InvalidRange(_) => StatusCode::RANGE_NOT_SATISFIABLE, Error::InvalidRange(_) => StatusCode::RANGE_NOT_SATISFIABLE,
Error::NotImplemented(_) => StatusCode::NOT_IMPLEMENTED,
_ => StatusCode::BAD_REQUEST, _ => StatusCode::BAD_REQUEST,
} }
} }

View file

@ -81,8 +81,54 @@ macro_rules! s3_match {
.parse() .parse()
.map_err(|_| Error::BadRequest("Failed to parse query parameter".to_owned()))? .map_err(|_| Error::BadRequest("Failed to parse query parameter".to_owned()))?
}}; }};
(@func
$(#[$doc:meta])*
pub enum Endpoint {
$(
$(#[$outer:meta])*
$variant:ident $({
bucket: String,
$($name:ident: $ty:ty,)*
})?,
)*
}) => {
$(#[$doc])*
pub enum Endpoint {
$(
$(#[$outer])*
$variant $({
bucket: String,
$($name: $ty, )*
})?,
)*
}
impl Endpoint {
pub fn name(&self) -> &'static str {
match self {
$(Endpoint::$variant $({ $($name: _,)* .. })? => stringify!($variant),)*
}
}
/// Get the bucket the request target. Returns None for requests not related to a bucket.
pub fn get_bucket(&self) -> Option<&str> {
match self {
$(
Endpoint::$variant $({ bucket, $($name: _,)* .. })? => s3_match!{@if ($(bucket $($name)*)?) then (Some(bucket)) else (None)},
)*
}
}
}
};
(@if ($($cond:tt)+) then ($($then:tt)*) else ($($else:tt)*)) => {
$($then)*
};
(@if () then ($($then:tt)*) else ($($else:tt)*)) => {
$($else)*
};
} }
s3_match! {@func
/// List of all S3 API endpoints. /// List of all S3 API endpoints.
/// ///
/// For each endpoint, it contains the parameters this endpoint receive by url (bucket, key and /// For each endpoint, it contains the parameters this endpoint receive by url (bucket, key and
@ -318,7 +364,7 @@ pub enum Endpoint {
}, },
ListObjectsV2 { ListObjectsV2 {
bucket: String, bucket: String,
/// This value should always be 2. It is not checked when constructing the struct // This value should always be 2. It is not checked when constructing the struct
list_type: String, list_type: String,
continuation_token: Option<String>, continuation_token: Option<String>,
delimiter: Option<char>, delimiter: Option<char>,
@ -440,7 +486,7 @@ pub enum Endpoint {
SelectObjectContent { SelectObjectContent {
bucket: String, bucket: String,
key: String, key: String,
/// This value should always be 2. It is not checked when constructing the struct // This value should always be 2. It is not checked when constructing the struct
select_type: String, select_type: String,
}, },
UploadPart { UploadPart {
@ -455,7 +501,7 @@ pub enum Endpoint {
part_number: u64, part_number: u64,
upload_id: String, upload_id: String,
}, },
} }}
impl Endpoint { impl Endpoint {
/// Determine which S3 endpoint a request is for using the request, and a bucket which was /// Determine which S3 endpoint a request is for using the request, and a bucket which was
@ -496,11 +542,9 @@ impl Endpoint {
}; };
if let Some(message) = query.nonempty_message() { if let Some(message) = query.nonempty_message() {
// maybe this should just be a warn! ? debug!("Unused query parameter: {}", message)
Err(Error::BadRequest(message.to_owned()))
} else {
Ok(res)
} }
Ok(res)
} }
/// Determine which endpoint a request is for, knowing it is a GET. /// Determine which endpoint a request is for, knowing it is a GET.
@ -683,107 +727,6 @@ impl Endpoint {
} }
} }
/// Get the bucket the request target. Returns None for requests not related to a bucket.
pub fn get_bucket(&self) -> Option<&str> {
s3_match! {
@extract
self,
bucket,
[
AbortMultipartUpload,
CompleteMultipartUpload,
CopyObject,
CreateBucket,
CreateMultipartUpload,
DeleteBucket,
DeleteBucketAnalyticsConfiguration,
DeleteBucketCors,
DeleteBucketEncryption,
DeleteBucketIntelligentTieringConfiguration,
DeleteBucketInventoryConfiguration,
DeleteBucketLifecycle,
DeleteBucketMetricsConfiguration,
DeleteBucketOwnershipControls,
DeleteBucketPolicy,
DeleteBucketReplication,
DeleteBucketTagging,
DeleteBucketWebsite,
DeleteObject,
DeleteObjects,
DeleteObjectTagging,
DeletePublicAccessBlock,
GetBucketAccelerateConfiguration,
GetBucketAcl,
GetBucketAnalyticsConfiguration,
GetBucketCors,
GetBucketEncryption,
GetBucketIntelligentTieringConfiguration,
GetBucketInventoryConfiguration,
GetBucketLifecycleConfiguration,
GetBucketLocation,
GetBucketLogging,
GetBucketMetricsConfiguration,
GetBucketNotificationConfiguration,
GetBucketOwnershipControls,
GetBucketPolicy,
GetBucketPolicyStatus,
GetBucketReplication,
GetBucketRequestPayment,
GetBucketTagging,
GetBucketVersioning,
GetBucketWebsite,
GetObject,
GetObjectAcl,
GetObjectLegalHold,
GetObjectLockConfiguration,
GetObjectRetention,
GetObjectTagging,
GetObjectTorrent,
GetPublicAccessBlock,
HeadBucket,
HeadObject,
ListBucketAnalyticsConfigurations,
ListBucketIntelligentTieringConfigurations,
ListBucketInventoryConfigurations,
ListBucketMetricsConfigurations,
ListMultipartUploads,
ListObjects,
ListObjectsV2,
ListObjectVersions,
ListParts,
PutBucketAccelerateConfiguration,
PutBucketAcl,
PutBucketAnalyticsConfiguration,
PutBucketCors,
PutBucketEncryption,
PutBucketIntelligentTieringConfiguration,
PutBucketInventoryConfiguration,
PutBucketLifecycleConfiguration,
PutBucketLogging,
PutBucketMetricsConfiguration,
PutBucketNotificationConfiguration,
PutBucketOwnershipControls,
PutBucketPolicy,
PutBucketReplication,
PutBucketRequestPayment,
PutBucketTagging,
PutBucketVersioning,
PutBucketWebsite,
PutObject,
PutObjectAcl,
PutObjectLegalHold,
PutObjectLockConfiguration,
PutObjectRetention,
PutObjectTagging,
PutPublicAccessBlock,
RestoreObject,
SelectObjectContent,
UploadPart,
UploadPartCopy,
]
}
}
/// Get the key the request target. Returns None for requests which don't use a key. /// Get the key the request target. Returns None for requests which don't use a key.
#[allow(dead_code)] #[allow(dead_code)]
pub fn get_key(&self) -> Option<&str> { pub fn get_key(&self) -> Option<&str> {
@ -929,10 +872,8 @@ macro_rules! generateQueryParameters {
} }
continue; continue;
} else { } else {
return Err(Error::BadRequest(format!( debug!("Received an unknown query parameter: '{}'", k);
"Unknown query parameter '{}'", false
k
)));
} }
} }
}; };
@ -953,7 +894,7 @@ macro_rules! generateQueryParameters {
Some("Keyword not used") Some("Keyword not used")
} $( } $(
else if self.$name.is_some() { else if self.$name.is_some() {
Some(concat!("Query parameter not needed: '", $rest, "'" )) Some(concat!("'", $rest, "'"))
} }
)* else { )* else {
None None