Skip to content

Commit 7667f34

Browse files
committed
replace matching S3 error types with a list of error codes to check
1 parent 8e23b99 commit 7667f34

File tree

1 file changed

+55
-42
lines changed

1 file changed

+55
-42
lines changed

src/storage/s3.rs

Lines changed: 55 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ use aws_config::BehaviorVersion;
55
use aws_sdk_s3::{
66
config::{retry::RetryConfig, Region},
77
error::{ProvideErrorMetadata, SdkError},
8-
operation::{get_object::GetObjectError, head_object::HeadObjectError},
98
types::{Delete, ObjectIdentifier, Tag, Tagging},
109
Client,
1110
};
@@ -21,16 +20,50 @@ use tracing::{error, warn};
2120
const PUBLIC_ACCESS_TAG: &str = "static-cloudfront-access";
2221
const PUBLIC_ACCESS_VALUE: &str = "allow";
2322

24-
fn err_is_not_found<E>(err: &SdkError<E>) -> bool
23+
// error codes to check for when trying to determaine if an error is
24+
// a "NOT FOUND" error.
25+
// Definition taken from the S3 rust SDK,
26+
// and validated by manually testing with actual S3.
27+
static NOT_FOUND_ERROR_CODES: [&str; 5] = [
28+
// from sentry errors
29+
"KeyTooLongError",
30+
// https://github.com/awslabs/aws-sdk-rust/blob/6155192dbd003af7bc5d4c1419bf17794302f8c3/sdk/s3/src/protocol_serde/shape_get_object.rs#L201-L251
31+
"NoSuchKey",
32+
// https://github.com/awslabs/aws-sdk-rust/blob/6155192dbd003af7bc5d4c1419bf17794302f8c3/sdk/s3/src/protocol_serde/shape_head_object.rs#L1-L39"
33+
"NotFound",
34+
// https://github.com/awslabs/aws-sdk-rust/blob/6155192dbd003af7bc5d4c1419bf17794302f8c3/sdk/mediastoredata/src/protocol_serde/shape_get_object.rs#L47-L128
35+
"ObjectNotFoundException",
36+
// from testing long keys with minio
37+
"XMinioInvalidObjectName",
38+
];
39+
40+
trait S3ResultExt<T> {
41+
fn convert_errors(self) -> anyhow::Result<T>;
42+
}
43+
44+
impl<T, E> S3ResultExt<T> for Result<T, SdkError<E>>
2545
where
26-
E: ProvideErrorMetadata,
46+
E: ProvideErrorMetadata + std::error::Error + Send + Sync + 'static,
2747
{
28-
if err.code() == Some("KeyTooLongError") || err.code() == Some("XMinioInvalidObjectName") {
29-
true
30-
} else if let SdkError::ServiceError(err) = err {
31-
err.raw().status().as_u16() == http::StatusCode::NOT_FOUND.as_u16()
32-
} else {
33-
false
48+
fn convert_errors(self) -> anyhow::Result<T> {
49+
match self {
50+
Ok(result) => Ok(result),
51+
Err(err) => {
52+
if let Some(err_code) = err.code() {
53+
if NOT_FOUND_ERROR_CODES.iter().any(|&code| err_code == code) {
54+
return Err(super::PathNotFoundError.into());
55+
}
56+
}
57+
58+
if let SdkError::ServiceError(err) = &err {
59+
if err.raw().status().as_u16() == http::StatusCode::NOT_FOUND.as_u16() {
60+
return Err(super::PathNotFoundError.into());
61+
}
62+
}
63+
64+
Err(err.into())
65+
}
66+
}
3467
}
3568
}
3669

@@ -88,40 +121,31 @@ impl S3Backend {
88121
.key(path)
89122
.send()
90123
.await
124+
.convert_errors()
91125
{
92126
Ok(_) => Ok(true),
93-
Err(SdkError::ServiceError(err))
94-
if matches!(err.err(), HeadObjectError::NotFound(_)) =>
95-
{
96-
Ok(false)
97-
}
98-
Err(err) if err_is_not_found(&err) => Ok(false),
99-
Err(other) => Err(other.into()),
127+
Err(err) if err.is::<super::PathNotFoundError>() => Ok(false),
128+
Err(other) => Err(other),
100129
}
101130
}
102131

103132
pub(super) async fn get_public_access(&self, path: &str) -> Result<bool, Error> {
104-
match self
133+
Ok(self
105134
.client
106135
.get_object_tagging()
107136
.bucket(&self.bucket)
108137
.key(path)
109138
.send()
110139
.await
111-
{
112-
Ok(tags) => Ok(tags
113-
.tag_set()
114-
.iter()
115-
.filter(|tag| tag.key() == PUBLIC_ACCESS_TAG)
116-
.any(|tag| tag.value() == PUBLIC_ACCESS_VALUE)),
117-
Err(err) if err_is_not_found(&err) => Err(super::PathNotFoundError.into()),
118-
Err(other) => Err(other.into()),
119-
}
140+
.convert_errors()?
141+
.tag_set()
142+
.iter()
143+
.filter(|tag| tag.key() == PUBLIC_ACCESS_TAG)
144+
.any(|tag| tag.value() == PUBLIC_ACCESS_VALUE))
120145
}
121146

122147
pub(super) async fn set_public_access(&self, path: &str, public: bool) -> Result<(), Error> {
123-
match self
124-
.client
148+
self.client
125149
.put_object_tagging()
126150
.bucket(&self.bucket)
127151
.key(path)
@@ -144,11 +168,8 @@ impl S3Backend {
144168
})
145169
.send()
146170
.await
147-
{
148-
Ok(_) => Ok(()),
149-
Err(err) if err_is_not_found(&err) => Err(super::PathNotFoundError.into()),
150-
Err(other) => Err(other.into()),
151-
}
171+
.convert_errors()
172+
.map(|_| ())
152173
}
153174

154175
pub(super) async fn get(
@@ -165,15 +186,7 @@ impl S3Backend {
165186
.set_range(range.map(|r| format!("bytes={}-{}", r.start(), r.end())))
166187
.send()
167188
.await
168-
.map_err(|err| match err {
169-
SdkError::ServiceError(err)
170-
if matches!(err.err(), GetObjectError::NoSuchKey(_)) =>
171-
{
172-
super::PathNotFoundError.into()
173-
}
174-
err if err_is_not_found(&err) => super::PathNotFoundError.into(),
175-
err => Error::from(err),
176-
})?;
189+
.convert_errors()?;
177190

178191
let mut content = crate::utils::sized_buffer::SizedBuffer::new(max_size);
179192
content.reserve(

0 commit comments

Comments
 (0)