From e50e297e796b3810fa2667a894c304c0ab6fe31f Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 29 Jul 2026 09:19:57 +0700 Subject: [PATCH] fix: add ETag and headers to 304 Not Modified responses AWS SDK requires ETag header in 304 responses. Without it, the SDK throws UnknownError despite receiving a valid 304 status code. Co-Authored-By: Claude Opus 5 (1M context) --- .../http/controllers/s3-controller.ts | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/interfaces/http/controllers/s3-controller.ts b/src/interfaces/http/controllers/s3-controller.ts index 7a99a83..b0aca88 100644 --- a/src/interfaces/http/controllers/s3-controller.ts +++ b/src/interfaces/http/controllers/s3-controller.ts @@ -489,7 +489,10 @@ const handleGetObject = async ( } const ifNoneMatch = headers['if-none-match']; if (ifNoneMatch && ifNoneMatch === etag) { - return new Response(null, { status: 304 }); + return new Response(null, { status: 304, headers: s3Headers(reqId, { + etag, 'content-type': file.mimeType, 'content-length': String(file.sizeBytes), + 'last-modified': lastModified.toUTCString(), 'x-amz-version-id': 'null', + }) }); } // H3: Conditional headers — If-Modified-Since / If-Unmodified-Since @@ -498,7 +501,10 @@ const handleGetObject = async ( if (ifModifiedSince) { const since = new Date(ifModifiedSince); if (!Number.isNaN(since.getTime()) && lastModified.getTime() <= since.getTime()) { - return new Response(null, { status: 304 }); + return new Response(null, { status: 304, headers: s3Headers(reqId, { + etag, 'content-type': file.mimeType, 'content-length': String(file.sizeBytes), + 'last-modified': lastModified.toUTCString(), 'x-amz-version-id': 'null', + }) }); } } const ifUnmodifiedSince = headers['if-unmodified-since']; @@ -739,7 +745,10 @@ const handleHeadObject = async ( } const ifNoneMatch = headers['if-none-match']; if (ifNoneMatch && ifNoneMatch === etag) { - return new Response(null, { status: 304 }); + return new Response(null, { status: 304, headers: s3Headers(reqId, { + etag, 'content-type': file.mimeType, 'content-length': String(file.sizeBytes), + 'last-modified': lastModified.toUTCString(), 'x-amz-version-id': 'null', + }) }); } // H3: Conditional headers for HEAD — If-Modified-Since / If-Unmodified-Since @@ -748,7 +757,10 @@ const handleHeadObject = async ( if (ifModifiedSince) { const since = new Date(ifModifiedSince); if (!Number.isNaN(since.getTime()) && lastModified.getTime() <= since.getTime()) { - return new Response(null, { status: 304 }); + return new Response(null, { status: 304, headers: s3Headers(reqId, { + etag, 'content-type': file.mimeType, 'content-length': String(file.sizeBytes), + 'last-modified': lastModified.toUTCString(), 'x-amz-version-id': 'null', + }) }); } } const ifUnmodifiedSince = headers['if-unmodified-since'];