Open rcoh opened 3 years ago
Hey @rcoh,
Would love to work on this issue! I'm currently solving this by handling it with the following code
match request {
Ok(v) => Ok(v),
Err(SdkError::ServiceError(err)) => match err.err() {
GetObjectError::Unhandled(unhandled) => match unhandled.meta().code() {
Some("PermanentRedirect") => {
// Head s3 endpoint via another request to get 'x-amz-bucket-region' header
}
_ => Err(SdkError::ServiceError(err)),
},
&_ => Err(SdkError::ServiceError(err)),
},
Err(err) => Err(err),
}
This is not great though since i have to do a second request while the correct region is in the header. Would love some pointers on how to get started on implementing a fix for this. I was thinking it would be good to propagate it up to the
aws_sdk_s3::operation::get_object::GetObjectError
enum as
pub enum GetObjectError {
/// <p>Object is archived and inaccessible until restored.</p>
InvalidObjectState(crate::types::error::InvalidObjectState),
/// <p>The specified key does not exist.</p>
NoSuchKey(crate::types::error::NoSuchKey),
/// <p>The specified region is not correct.</p>
Redirect(Region),
/// An unexpected error occurred (e.g., invalid JSON returned by the service or an unknown error code).
Unhandled(::aws_smithy_types::error::Unhandled),
}
I've also built some code to go around this limitation of the client. Here it is if it may help others.
First, to conveniently get the region, if any:
pub trait RedirectableResult {
/// If the result is of a smithy error of code "PermanentRedirect" and
/// the region is provided as HTTP header, return this region
fn redirect_region(&self) -> Option<Region>;
}
impl<O, E> RedirectableResult for Result<O, SdkError<E, HttpResponse>>
where
E: ProvideErrorMetadata + std::fmt::Debug,
{
fn redirect_region(&self) -> Option<Region> {
if let Err(err) = self {
if err.code() == Some("PermanentRedirect") {
if let SdkError::ServiceError(err) = err {
let http_response = err.raw();
let region = http_response.headers().get("x-amz-bucket-region");
return region.map(|r| Region::new(r.to_string()));
}
}
}
None
}
}
Then to query, I do:
let mut policy = con.s3.get_bucket_policy().bucket(bucket_name).send().await;
if let Some(region) = policy.redirect_region() {
eprintln!("s3 client redirected, retrying with region {region:?}");
let config = aws_config::from_env().region(region).load().await;
let s3 = s3::Client::new(&config);
policy = s3.get_bucket_policy().bucket(bucket_name).send().await;
}
This automatic redirection should IMO be built in the standard Client.
Generally we shy away from doing automatic features like this. For example, there is actually a lot of inherent complexity here: For performance reasons, you don't want to force this redirect every time, you really need to cache it. But now you have the complexity of maintaining a cache of bucket-to-region that needs to be synchronized and shared across the client.
Generally, we attempt to have clients behave with behavior that is predictable as possible.
Community Note
Tell us about your request S3 redirect error doesn't show the correct region:
Tell us about the problem you're trying to solve. What are you trying to do, and why is it hard? What outcome are you trying to achieve, ultimately, and why is it hard/impossible to do right now? What is the impact of not having this problem solved? The more details you can provide, the better we'll be able to understand and solve the problem.
Are you currently working around this issue? How are you currently solving this problem? Using debug logging
Additional context Anything else we should know?
Attachments If you think you might have additional information that you'd like to include via an attachment, please do - we'll take a look. (Remember to remove any personally-identifiable information.)