diff --git a/packages/middleware-sdk-s3/src/region-redirect-middleware.spec.ts b/packages/middleware-sdk-s3/src/region-redirect-middleware.spec.ts index 6fd175e18c93..b984db23627c 100644 --- a/packages/middleware-sdk-s3/src/region-redirect-middleware.spec.ts +++ b/packages/middleware-sdk-s3/src/region-redirect-middleware.spec.ts @@ -31,6 +31,17 @@ describe(regionRedirectMiddleware.name, () => { return null as any; }; + const nextHeadBucket400 = (arg: any) => { + if (call === 0) { + call++; + throw Object.assign(new Error(), { + $metadata: { httpStatusCode: 400 }, + $response: { headers: { "x-amz-bucket-region": redirectRegion } }, + }); + } + return null as any; + }; + beforeEach(() => { call = 0; }); @@ -51,6 +62,14 @@ describe(regionRedirectMiddleware.name, () => { expect(context.__s3RegionRedirect).toEqual(redirectRegion); }); + it("set S3 region redirect on context for HeadBucket with 400 status and x-amz-bucket-region header", async () => { + const middleware = regionRedirectMiddleware({ region, followRegionRedirects: true }); + const context = { commandName: "HeadBucketCommand" } as HandlerExecutionContext; + const handler = middleware(nextHeadBucket400, context); + await handler({ input: null }); + expect(context.__s3RegionRedirect).toEqual(redirectRegion); + }); + it("does not follow the redirect when followRegionRedirects is false", async () => { const middleware = regionRedirectMiddleware({ region, followRegionRedirects: false }); const context = {} as HandlerExecutionContext; diff --git a/packages/middleware-sdk-s3/src/region-redirect-middleware.ts b/packages/middleware-sdk-s3/src/region-redirect-middleware.ts index 7a9329573a75..8a8425acaf31 100644 --- a/packages/middleware-sdk-s3/src/region-redirect-middleware.ts +++ b/packages/middleware-sdk-s3/src/region-redirect-middleware.ts @@ -36,19 +36,23 @@ export function regionRedirectMiddleware(clientConfig: PreviouslyResolved): Init return await next(args); } catch (err) { if (clientConfig.followRegionRedirects) { - if ( - err?.$metadata?.httpStatusCode === 301 || - // err.name === "PermanentRedirect" && --> removing the error name check, as that allows for HEAD operations (which have the 301 status code, but not the same error name) to be covered for region redirection as well - (err?.$metadata?.httpStatusCode === 400 && err?.name === "IllegalLocationConstraintException") - ) { - try { - const actualRegion = err.$response.headers["x-amz-bucket-region"]; - context.logger?.debug(`Redirecting from ${await clientConfig.region()} to ${actualRegion}`); - context.__s3RegionRedirect = actualRegion; - } catch (e) { - throw new Error("Region redirect failed: " + e); + const statusCode = err?.$metadata?.httpStatusCode; + const isHeadBucket = context.commandName === "HeadBucketCommand"; + const bucketRegionHeader = err?.$response?.headers?.["x-amz-bucket-region"]; + if (bucketRegionHeader) { + if ( + statusCode === 301 || + (statusCode === 400 && (err?.name === "IllegalLocationConstraintException" || isHeadBucket)) + ) { + try { + const actualRegion = bucketRegionHeader; + context.logger?.debug(`Redirecting from ${await clientConfig.region()} to ${actualRegion}`); + context.__s3RegionRedirect = actualRegion; + } catch (e) { + throw new Error("Region redirect failed: " + e); + } + return next(args); } - return next(args); } } throw err;