Skip to content

fix(aws): make S3 emulator compatible with official AWS SDK wire format#65

Open
jlucaso1 wants to merge 1 commit intovercel-labs:mainfrom
jlucaso1:jlucaso1/fix-s3-sdk-compat
Open

fix(aws): make S3 emulator compatible with official AWS SDK wire format#65
jlucaso1 wants to merge 1 commit intovercel-labs:mainfrom
jlucaso1:jlucaso1/fix-s3-sdk-compat

Conversation

@jlucaso1
Copy link
Copy Markdown

@jlucaso1 jlucaso1 commented Apr 9, 2026

Summary

The S3 emulator was unreachable by any official AWS SDK because routes lived under a /s3/ prefix (GET /s3/, PUT /s3/:bucket, etc.), while every AWS SDK sends requests to root paths (GET /, PUT /:bucket). The inspector UI at GET / also collided with ListBuckets, and presigned POST uploads were missing entirely.

  • S3 routes moved to root paths matching the real S3 wire format, so @aws-sdk/client-s3 works with forcePathStyle: true. Legacy /s3/ prefixed paths are preserved as backward-compatible aliases (registered first so Hono's router resolves them before the wildcard /:bucket routes).
  • Presigned POST uploads (POST /:bucket) with policy validation: expiration checks, content-length-range enforcement, and starts-with condition matching on form fields.
  • ListObjectsV2 pagination via continuation-token and start-after query parameters with NextContinuationToken in truncated responses.
  • Inspector moved from GET / to GET /_inspector so the root path is free for ListBuckets.
  • Route registration reordered: inspector, SQS, and IAM routes register before S3 to prevent wildcard /:bucket from shadowing static paths.
  • All docs updated: README.md, packages/@emulators/aws/README.md, skills/aws/SKILL.md, apps/web/app/aws/page.mdx.

Test plan

  • All 45 AWS tests pass (was 36, +9 new)
  • Presigned POST: 204 default, 201 with success_action_status, policy enforcement (content-length-range, starts-with, expiration)
  • ListObjectsV2 pagination with max-keys, continuation-token, start-after
  • Backward-compat /s3/ aliases: list buckets, put/get objects via legacy paths
  • Inspector at /_inspector still renders S3/SQS/IAM tabs
  • GET / returns XML ListAllMyBucketsResult, not HTML inspector
  • Full monorepo CI: pnpm build && pnpm format:check && pnpm type-check && pnpm lint && pnpm test all pass
  • Manually tested with @aws-sdk/client-s3 ListBuckets, PutObject, GetObject, HeadObject, DeleteObject, CopyObject, ListObjectsV2
  • Manually tested with @aws-sdk/s3-presigned-post createPresignedPost flow

S3 routes now use root paths (GET /, PUT /:bucket, GET /:bucket/:key)
matching the real S3 wire protocol, so @aws-sdk/client-s3 works out of
the box with forcePathStyle. Legacy /s3/ prefixed paths are preserved as
backward-compatible aliases.

Also adds presigned POST uploads (POST /:bucket) with policy validation,
ListObjectsV2 pagination via continuation-token/start-after, and moves
the inspector UI to /_inspector so GET / is free for ListBuckets.
@vercel
Copy link
Copy Markdown
Contributor

vercel bot commented Apr 9, 2026

@jlucaso1 is attempting to deploy a commit to the Vercel Labs Team on Vercel.

A member of the Team first needs to authorize it.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 424f1d2ea6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +259 to +271
const successStatus = parseInt(body["success_action_status"] as string, 10);
if (successStatus === 201) {
const xml = `<?xml version="1.0" encoding="UTF-8"?>
<PostResponse>
<Location>${escapeXml(baseUrl)}/${escapeXml(bucketName)}/${escapeXml(key)}</Location>
<Bucket>${escapeXml(bucketName)}</Bucket>
<Key>${escapeXml(key)}</Key>
<ETag>"${etag}"</ETag>
</PostResponse>`;
return awsXmlResponse(c, xml, 201);
}

return c.body(null, 204);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Return 200 when success_action_status requests it

POST /:bucket only checks for success_action_status === 201 and otherwise always returns 204, so uploads that specify success_action_status=200 get the wrong status code even though the object is stored. S3 clients that rely on the requested success status can treat this as a failed upload flow; add an explicit 200 branch before the default 204 response.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant