|
3 | 3 | use aws_sdk_s3::error::ProvideErrorMetadata; |
4 | 4 | use aws_sdk_s3::error::SdkError; |
5 | 5 | use aws_sdk_s3::operation::get_object::GetObjectError; |
| 6 | +use aws_sdk_s3::operation::head_object::HeadObjectError; |
6 | 7 | use aws_smithy_types::byte_stream::error::Error as ByteStreamError; |
7 | 8 | use axum::{ |
8 | 9 | extract::rejection::JsonRejection, |
@@ -74,6 +75,14 @@ pub enum ActiveStorageError { |
74 | 75 | #[error("error retrieving object from S3 storage")] |
75 | 76 | S3GetObject(#[from] SdkError<GetObjectError>), |
76 | 77 |
|
| 78 | + /// Error while retrieving object head from S3 |
| 79 | + #[error("error retrieving object metadata from S3 storage")] |
| 80 | + S3HeadObject(#[from] SdkError<HeadObjectError>), |
| 81 | + |
| 82 | + /// HTTP 403 from object store |
| 83 | + #[error("error receiving object metadata from S3 storage")] |
| 84 | + Forbidden, |
| 85 | + |
77 | 86 | /// Error acquiring a semaphore |
78 | 87 | #[error("error acquiring resources")] |
79 | 88 | SemaphoreAcquireError(#[from] AcquireError), |
@@ -225,7 +234,11 @@ impl From<ActiveStorageError> for ErrorResponse { |
225 | 234 | | ActiveStorageError::ShapeInvalid(_) => Self::bad_request(&error), |
226 | 235 |
|
227 | 236 | // Not found |
228 | | - ActiveStorageError::UnsupportedOperation { operation: _ } => Self::not_found(&error), |
| 237 | + ActiveStorageError::UnsupportedOperation { operation: _ } |
| 238 | + // If we receive a forbidden from object store, return a |
| 239 | + // not found to client to avoid leaking information about |
| 240 | + // bucket contents. |
| 241 | + | ActiveStorageError::Forbidden => Self::not_found(&error), |
229 | 242 |
|
230 | 243 | // Internal server error |
231 | 244 | ActiveStorageError::FromBytes { type_name: _ } |
@@ -277,6 +290,31 @@ impl From<ActiveStorageError> for ErrorResponse { |
277 | 290 | _ => Self::internal_server_error(&error), |
278 | 291 | } |
279 | 292 | } |
| 293 | + |
| 294 | + ActiveStorageError::S3HeadObject(sdk_error) => { |
| 295 | + // Tailor the response based on the specific SdkError variant. |
| 296 | + match &sdk_error { |
| 297 | + // These are generic SdkError variants. |
| 298 | + // Internal server error |
| 299 | + SdkError::ConstructionFailure(_) |
| 300 | + | SdkError::DispatchFailure(_) |
| 301 | + | SdkError::ResponseError(_) |
| 302 | + | SdkError::TimeoutError(_) => Self::internal_server_error(&error), |
| 303 | + |
| 304 | + // This is a more specific ServiceError variant, |
| 305 | + // with HeadObjectError as the inner error. |
| 306 | + SdkError::ServiceError(head_obj_error) => { |
| 307 | + let head_obj_error = head_obj_error.err(); |
| 308 | + match head_obj_error { |
| 309 | + HeadObjectError::NotFound(_) => Self::bad_request(&error), |
| 310 | + // Enum is marked as non-exhaustive |
| 311 | + _ => Self::internal_server_error(&error), |
| 312 | + } |
| 313 | + } |
| 314 | + // Enum is marked as non-exhaustive |
| 315 | + _ => Self::internal_server_error(&error), |
| 316 | + } |
| 317 | + } |
280 | 318 | }; |
281 | 319 |
|
282 | 320 | // Log server errors. |
|
0 commit comments