Skip to content

Commit

Permalink
Detect content type of upstream image
Browse files Browse the repository at this point in the history
  • Loading branch information
styfle committed Jun 28, 2021
1 parent d11589d commit 6a5279a
Showing 1 changed file with 37 additions and 3 deletions.
40 changes: 37 additions & 3 deletions packages/next/next-server/server/image-optimizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,9 @@ export async function imageOptimizer(

res.statusCode = upstreamRes.status
upstreamBuffer = Buffer.from(await upstreamRes.arrayBuffer())
upstreamType = upstreamRes.headers.get('Content-Type')
upstreamType =
detectContentType(upstreamBuffer) ||
upstreamRes.headers.get('Content-Type')
maxAge = getMaxAge(upstreamRes.headers.get('Cache-Control'))
} else {
try {
Expand Down Expand Up @@ -252,7 +254,8 @@ export async function imageOptimizer(
res.statusCode = mockRes.statusCode

upstreamBuffer = Buffer.concat(resBuffers)
upstreamType = mockRes.getHeader('Content-Type')
upstreamType =
detectContentType(upstreamBuffer) || mockRes.getHeader('Content-Type')
maxAge = getMaxAge(mockRes.getHeader('Cache-Control'))
} catch (err) {
res.statusCode = 500
Expand All @@ -273,7 +276,6 @@ export async function imageOptimizer(
return { finished: true }
}

// If upstream type is not a valid image type, return 400 error.
if (!upstreamType.startsWith('image/')) {
res.statusCode = 400
res.end("The requested resource isn't a valid image.")
Expand Down Expand Up @@ -426,6 +428,38 @@ function parseCacheControl(str: string | null): Map<string, string> {
return map
}

/**
* Inspects the first few bytes of a buffer to determine if
* it matches the "magic number" of known file signatures.
* https://en.wikipedia.org/wiki/List_of_file_signatures
*/
function detectContentType(buffer: Buffer) {
if ([0xff, 0xd8, 0xff].every((i, b) => buffer[i] === b)) {
return JPEG
}
if (
[0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a].every(
(i, b) => buffer[i] === b
)
) {
return PNG
}
if ([0x47, 0x49, 0x46, 0x38].every((i, b) => buffer[i] === b)) {
return GIF
}
if (
[0x52, 0x49, 0x46, 0x46, 0, 0, 0, 0, 0x57, 0x45, 0x42, 0x50].every(
(i, b) => buffer[i] === b
)
) {
return WEBP
}
if ([0x3c, 0x3f, 0x78, 0x6d, 0x6c].every((i, b) => buffer[i] === b)) {
return SVG
}
return null
}

export function getMaxAge(str: string | null): number {
const minimum = 60
const map = parseCacheControl(str)
Expand Down

0 comments on commit 6a5279a

Please sign in to comment.