From 581dfb922a9e8909a4086f634e4c5ec727e60d35 Mon Sep 17 00:00:00 2001 From: smilkuri Date: Thu, 4 Sep 2025 17:02:53 +0000 Subject: [PATCH 1/5] fix(middleware-sdk-s3): handle cross-region redirects for HeadBucket with 400 status --- packages/middleware-sdk-s3/src/region-redirect-middleware.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/middleware-sdk-s3/src/region-redirect-middleware.ts b/packages/middleware-sdk-s3/src/region-redirect-middleware.ts index 7a9329573a75..86e739f41656 100644 --- a/packages/middleware-sdk-s3/src/region-redirect-middleware.ts +++ b/packages/middleware-sdk-s3/src/region-redirect-middleware.ts @@ -39,7 +39,10 @@ export function regionRedirectMiddleware(clientConfig: PreviouslyResolved): Init 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") + (err?.$metadata?.httpStatusCode === 400 && err?.name === "IllegalLocationConstraintException") || + (err?.$metadata?.httpStatusCode === 400 && + context.commandName === "HeadBucketCommand" && + err?.$response?.headers?.["x-amz-bucket-region"]) ) { try { const actualRegion = err.$response.headers["x-amz-bucket-region"]; From f977a7a6420aa5defc8e724c26d97a7756c2762b Mon Sep 17 00:00:00 2001 From: smilkuri Date: Thu, 4 Sep 2025 17:31:02 +0000 Subject: [PATCH 2/5] test(middleware-sdk-s3): add redirect test for 400 status without error name --- .../src/region-redirect-middleware.spec.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) 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; From 439788fbb704e48ac32ee5c2a877eb94d95d5e68 Mon Sep 17 00:00:00 2001 From: smilkuri Date: Thu, 4 Sep 2025 18:38:41 +0000 Subject: [PATCH 3/5] chore(middleware-sdk-s3): simplify the code --- .../src/region-redirect-middleware.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/middleware-sdk-s3/src/region-redirect-middleware.ts b/packages/middleware-sdk-s3/src/region-redirect-middleware.ts index 86e739f41656..74d6d9e10d38 100644 --- a/packages/middleware-sdk-s3/src/region-redirect-middleware.ts +++ b/packages/middleware-sdk-s3/src/region-redirect-middleware.ts @@ -36,13 +36,13 @@ export function regionRedirectMiddleware(clientConfig: PreviouslyResolved): Init return await next(args); } catch (err) { if (clientConfig.followRegionRedirects) { + const statusCode = err?.$metadata?.httpStatusCode; + const isHeadBucket = context.commandName === "HeadBucketCommand"; + const hasBucketRegionHeader = err?.$response?.headers?.["x-amz-bucket-region"]; 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") || - (err?.$metadata?.httpStatusCode === 400 && - context.commandName === "HeadBucketCommand" && - err?.$response?.headers?.["x-amz-bucket-region"]) + statusCode === 301 || + (statusCode === 400 && + (err?.name === "IllegalLocationConstraintException" || (isHeadBucket && hasBucketRegionHeader))) ) { try { const actualRegion = err.$response.headers["x-amz-bucket-region"]; From 73f68f113179ad45f0f9bdc0d8d2f1bbd44be1b8 Mon Sep 17 00:00:00 2001 From: smilkuri Date: Thu, 4 Sep 2025 19:21:12 +0000 Subject: [PATCH 4/5] chore(middleware-sdk-s3): rename and reuse the bucketRegionHeader --- .../middleware-sdk-s3/src/region-redirect-middleware.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/middleware-sdk-s3/src/region-redirect-middleware.ts b/packages/middleware-sdk-s3/src/region-redirect-middleware.ts index 74d6d9e10d38..939c1b21c5c1 100644 --- a/packages/middleware-sdk-s3/src/region-redirect-middleware.ts +++ b/packages/middleware-sdk-s3/src/region-redirect-middleware.ts @@ -38,14 +38,14 @@ export function regionRedirectMiddleware(clientConfig: PreviouslyResolved): Init if (clientConfig.followRegionRedirects) { const statusCode = err?.$metadata?.httpStatusCode; const isHeadBucket = context.commandName === "HeadBucketCommand"; - const hasBucketRegionHeader = err?.$response?.headers?.["x-amz-bucket-region"]; + const bucketRegionHeader = err?.$response?.headers?.["x-amz-bucket-region"]; if ( statusCode === 301 || (statusCode === 400 && - (err?.name === "IllegalLocationConstraintException" || (isHeadBucket && hasBucketRegionHeader))) + (err?.name === "IllegalLocationConstraintException" || (isHeadBucket && bucketRegionHeader))) ) { try { - const actualRegion = err.$response.headers["x-amz-bucket-region"]; + const actualRegion = bucketRegionHeader; context.logger?.debug(`Redirecting from ${await clientConfig.region()} to ${actualRegion}`); context.__s3RegionRedirect = actualRegion; } catch (e) { From 429c54e3b2a0902c2c4a174b4fda8c24435d4215 Mon Sep 17 00:00:00 2001 From: smilkuri Date: Thu, 4 Sep 2025 20:23:01 +0000 Subject: [PATCH 5/5] chore(region-redirect-middleware): make bucket region header a prerequisite --- .../src/region-redirect-middleware.ts | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/packages/middleware-sdk-s3/src/region-redirect-middleware.ts b/packages/middleware-sdk-s3/src/region-redirect-middleware.ts index 939c1b21c5c1..8a8425acaf31 100644 --- a/packages/middleware-sdk-s3/src/region-redirect-middleware.ts +++ b/packages/middleware-sdk-s3/src/region-redirect-middleware.ts @@ -39,19 +39,20 @@ export function regionRedirectMiddleware(clientConfig: PreviouslyResolved): Init const statusCode = err?.$metadata?.httpStatusCode; const isHeadBucket = context.commandName === "HeadBucketCommand"; const bucketRegionHeader = err?.$response?.headers?.["x-amz-bucket-region"]; - if ( - statusCode === 301 || - (statusCode === 400 && - (err?.name === "IllegalLocationConstraintException" || (isHeadBucket && bucketRegionHeader))) - ) { - 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); + 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;