From bf103b0dcf1082f3a4fb4d342174b24d52567af3 Mon Sep 17 00:00:00 2001 From: Steven Date: Wed, 30 Jun 2021 12:17:40 -0400 Subject: [PATCH 1/4] Add upstream `max-age` to optimized image --- packages/next/server/image-optimizer.ts | 85 +++++++++++++------ .../image-optimizer/test/index.test.js | 84 +++++++++--------- 2 files changed, 105 insertions(+), 64 deletions(-) diff --git a/packages/next/server/image-optimizer.ts b/packages/next/server/image-optimizer.ts index 5def834f5993e..283ab506d1ed3 100644 --- a/packages/next/server/image-optimizer.ts +++ b/packages/next/server/image-optimizer.ts @@ -22,7 +22,7 @@ const PNG = 'image/png' const JPEG = 'image/jpeg' const GIF = 'image/gif' const SVG = 'image/svg+xml' -const CACHE_VERSION = 2 +const CACHE_VERSION = 3 const MODERN_TYPES = [/* AVIF, */ WEBP] const ANIMATABLE_TYPES = [WEBP, PNG, GIF] const VECTOR_TYPES = [SVG] @@ -158,24 +158,23 @@ export async function imageOptimizer( if (await fileExists(hashDir, 'directory')) { const files = await promises.readdir(hashDir) for (let file of files) { - const [prefix, etag, extension] = file.split('.') - const expireAt = Number(prefix) + const [maxAgeStr, expireAtSt, etag, extension] = file.split('.') + const maxAge = Number(maxAgeStr) + const expireAt = Number(expireAtSt) const contentType = getContentType(extension) const fsPath = join(hashDir, file) if (now < expireAt) { - res.setHeader( - 'Cache-Control', + const result = setResponseHeaders( + req, + res, + etag, + maxAge, + contentType, isStatic - ? 'public, max-age=315360000, immutable' - : 'public, max-age=0, must-revalidate' ) - if (sendEtagResponse(req, res, etag)) { - return { finished: true } + if (!result.finished) { + createReadStream(fsPath).pipe(res) } - if (contentType) { - res.setHeader('Content-Type', contentType) - } - createReadStream(fsPath).pipe(res) return { finished: true } } else { await promises.unlink(fsPath) @@ -271,8 +270,14 @@ export async function imageOptimizer( const animate = ANIMATABLE_TYPES.includes(upstreamType) && isAnimated(upstreamBuffer) if (vector || animate) { - await writeToCacheDir(hashDir, upstreamType, expireAt, upstreamBuffer) - sendResponse(req, res, upstreamType, upstreamBuffer, isStatic) + await writeToCacheDir( + hashDir, + upstreamType, + maxAge, + expireAt, + upstreamBuffer + ) + sendResponse(req, res, maxAge, upstreamType, upstreamBuffer, isStatic) return { finished: true } } @@ -342,13 +347,19 @@ export async function imageOptimizer( } if (optimizedBuffer) { - await writeToCacheDir(hashDir, contentType, expireAt, optimizedBuffer) - sendResponse(req, res, contentType, optimizedBuffer, isStatic) + await writeToCacheDir( + hashDir, + contentType, + maxAge, + expireAt, + optimizedBuffer + ) + sendResponse(req, res, maxAge, contentType, optimizedBuffer, isStatic) } else { throw new Error('Unable to optimize buffer') } } catch (error) { - sendResponse(req, res, upstreamType, upstreamBuffer, isStatic) + sendResponse(req, res, maxAge, upstreamType, upstreamBuffer, isStatic) } return { finished: true } @@ -362,37 +373,61 @@ export async function imageOptimizer( async function writeToCacheDir( dir: string, contentType: string, + maxAge: number, expireAt: number, buffer: Buffer ) { await promises.mkdir(dir, { recursive: true }) const extension = getExtension(contentType) const etag = getHash([buffer]) - const filename = join(dir, `${expireAt}.${etag}.${extension}`) + const filename = join(dir, `${maxAge}.${expireAt}.${etag}.${extension}`) await promises.writeFile(filename, buffer) } -function sendResponse( +function setResponseHeaders( req: IncomingMessage, res: ServerResponse, + etag: string, + maxAge: number, contentType: string | null, - buffer: Buffer, isStatic: boolean ) { - const etag = getHash([buffer]) res.setHeader( 'Cache-Control', isStatic ? 'public, max-age=315360000, immutable' - : 'public, max-age=0, must-revalidate' + : `public, max-age=${maxAge}, must-revalidate` ) if (sendEtagResponse(req, res, etag)) { - return + // already called res.end() so we're finished + return { finished: true } } if (contentType) { res.setHeader('Content-Type', contentType) } - res.end(buffer) + return { finished: false } +} + +function sendResponse( + req: IncomingMessage, + res: ServerResponse, + maxAge: number, + contentType: string | null, + buffer: Buffer, + isStatic: boolean +) { + const etag = getHash([buffer]) + const result = setResponseHeaders( + req, + res, + etag, + maxAge, + contentType, + isStatic + ) + if (!result.finished) { + res.end(buffer) + } } function getSupportedMimeType(options: string[], accept = ''): string { diff --git a/test/integration/image-optimizer/test/index.test.js b/test/integration/image-optimizer/test/index.test.js index e553e0a31dc19..3efb3f6d8da9c 100644 --- a/test/integration/image-optimizer/test/index.test.js +++ b/test/integration/image-optimizer/test/index.test.js @@ -56,8 +56,8 @@ function runTests({ w, isDev, domains }) { const res = await fetchViaHTTP(appPort, '/_next/image', query, {}) expect(res.status).toBe(200) expect(res.headers.get('content-type')).toContain('image/gif') - expect(res.headers.get('cache-control')).toBe( - 'public, max-age=0, must-revalidate' + expect(res.headers.get('Cache-Control')).toBe( + 'public, max-age=60, must-revalidate' ) expect(res.headers.get('etag')).toBeTruthy() expect(isAnimated(await res.buffer())).toBe(true) @@ -68,8 +68,8 @@ function runTests({ w, isDev, domains }) { const res = await fetchViaHTTP(appPort, '/_next/image', query, {}) expect(res.status).toBe(200) expect(res.headers.get('content-type')).toContain('image/png') - expect(res.headers.get('cache-control')).toBe( - 'public, max-age=0, must-revalidate' + expect(res.headers.get('Cache-Control')).toBe( + 'public, max-age=60, must-revalidate' ) expect(res.headers.get('etag')).toBeTruthy() expect(isAnimated(await res.buffer())).toBe(true) @@ -80,8 +80,8 @@ function runTests({ w, isDev, domains }) { const res = await fetchViaHTTP(appPort, '/_next/image', query, {}) expect(res.status).toBe(200) expect(res.headers.get('content-type')).toContain('image/webp') - expect(res.headers.get('cache-control')).toBe( - 'public, max-age=0, must-revalidate' + expect(res.headers.get('Cache-Control')).toBe( + 'public, max-age=60, must-revalidate' ) expect(res.headers.get('etag')).toBeTruthy() expect(isAnimated(await res.buffer())).toBe(true) @@ -93,8 +93,8 @@ function runTests({ w, isDev, domains }) { const res = await fetchViaHTTP(appPort, '/_next/image', query, opts) expect(res.status).toBe(200) expect(res.headers.get('Content-Type')).toContain('image/svg+xml') - expect(res.headers.get('cache-control')).toBe( - 'public, max-age=0, must-revalidate' + expect(res.headers.get('Cache-Control')).toBe( + 'public, max-age=60, must-revalidate' ) expect(res.headers.get('etag')).toBeTruthy() const actual = await res.text() @@ -111,8 +111,8 @@ function runTests({ w, isDev, domains }) { const res = await fetchViaHTTP(appPort, '/_next/image', query, opts) expect(res.status).toBe(200) expect(res.headers.get('Content-Type')).toContain('image/x-icon') - expect(res.headers.get('cache-control')).toBe( - 'public, max-age=0, must-revalidate' + expect(res.headers.get('Cache-Control')).toBe( + 'public, max-age=60, must-revalidate' ) expect(res.headers.get('etag')).toBeTruthy() const actual = await res.text() @@ -131,8 +131,8 @@ function runTests({ w, isDev, domains }) { const res = await fetchViaHTTP(appPort, '/_next/image', query, opts) expect(res.status).toBe(200) expect(res.headers.get('Content-Type')).toContain('image/jpeg') - expect(res.headers.get('cache-control')).toBe( - 'public, max-age=0, must-revalidate' + expect(res.headers.get('Cache-Control')).toBe( + 'public, max-age=60, must-revalidate' ) expect(res.headers.get('etag')).toBeTruthy() }) @@ -145,8 +145,8 @@ function runTests({ w, isDev, domains }) { const res = await fetchViaHTTP(appPort, '/_next/image', query, opts) expect(res.status).toBe(200) expect(res.headers.get('Content-Type')).toContain('image/png') - expect(res.headers.get('cache-control')).toBe( - 'public, max-age=0, must-revalidate' + expect(res.headers.get('Cache-Control')).toBe( + 'public, max-age=60, must-revalidate' ) expect(res.headers.get('etag')).toBeTruthy() }) @@ -242,8 +242,8 @@ function runTests({ w, isDev, domains }) { const res = await fetchViaHTTP(appPort, '/_next/image', query, opts) expect(res.status).toBe(200) expect(res.headers.get('Content-Type')).toBe('image/webp') - expect(res.headers.get('cache-control')).toBe( - 'public, max-age=0, must-revalidate' + expect(res.headers.get('Cache-Control')).toBe( + 'public, max-age=60, must-revalidate' ) expect(res.headers.get('etag')).toBeTruthy() await expectWidth(res, w) @@ -255,8 +255,8 @@ function runTests({ w, isDev, domains }) { const res = await fetchViaHTTP(appPort, '/_next/image', query, opts) expect(res.status).toBe(200) expect(res.headers.get('Content-Type')).toBe('image/png') - expect(res.headers.get('cache-control')).toBe( - 'public, max-age=0, must-revalidate' + expect(res.headers.get('Cache-Control')).toBe( + 'public, max-age=60, must-revalidate' ) expect(res.headers.get('etag')).toBeTruthy() await expectWidth(res, w) @@ -268,8 +268,8 @@ function runTests({ w, isDev, domains }) { const res = await fetchViaHTTP(appPort, '/_next/image', query, opts) expect(res.status).toBe(200) expect(res.headers.get('Content-Type')).toBe('image/png') - expect(res.headers.get('cache-control')).toBe( - 'public, max-age=0, must-revalidate' + expect(res.headers.get('Cache-Control')).toBe( + 'public, max-age=60, must-revalidate' ) expect(res.headers.get('etag')).toBeTruthy() await expectWidth(res, w) @@ -281,8 +281,8 @@ function runTests({ w, isDev, domains }) { const res = await fetchViaHTTP(appPort, '/_next/image', query, opts) expect(res.status).toBe(200) expect(res.headers.get('Content-Type')).toBe('image/gif') - expect(res.headers.get('cache-control')).toBe( - 'public, max-age=0, must-revalidate' + expect(res.headers.get('Cache-Control')).toBe( + 'public, max-age=60, must-revalidate' ) expect(res.headers.get('etag')).toBeTruthy() // FIXME: await expectWidth(res, w) @@ -294,8 +294,8 @@ function runTests({ w, isDev, domains }) { const res = await fetchViaHTTP(appPort, '/_next/image', query, opts) expect(res.status).toBe(200) expect(res.headers.get('Content-Type')).toBe('image/tiff') - expect(res.headers.get('cache-control')).toBe( - 'public, max-age=0, must-revalidate' + expect(res.headers.get('Cache-Control')).toBe( + 'public, max-age=60, must-revalidate' ) expect(res.headers.get('etag')).toBeTruthy() // FIXME: await expectWidth(res, w) @@ -309,8 +309,8 @@ function runTests({ w, isDev, domains }) { const res = await fetchViaHTTP(appPort, '/_next/image', query, opts) expect(res.status).toBe(200) expect(res.headers.get('Content-Type')).toBe('image/webp') - expect(res.headers.get('cache-control')).toBe( - 'public, max-age=0, must-revalidate' + expect(res.headers.get('Cache-Control')).toBe( + 'public, max-age=60, must-revalidate' ) expect(res.headers.get('etag')).toBeTruthy() await expectWidth(res, w) @@ -324,8 +324,8 @@ function runTests({ w, isDev, domains }) { const res = await fetchViaHTTP(appPort, '/_next/image', query, opts) expect(res.status).toBe(200) expect(res.headers.get('Content-Type')).toBe('image/webp') - expect(res.headers.get('cache-control')).toBe( - 'public, max-age=0, must-revalidate' + expect(res.headers.get('Cache-Control')).toBe( + 'public, max-age=60, must-revalidate' ) expect(res.headers.get('etag')).toBeTruthy() await expectWidth(res, w) @@ -344,8 +344,8 @@ function runTests({ w, isDev, domains }) { const res = await fetchViaHTTP(appPort, '/_next/image', query, opts) expect(res.status).toBe(200) expect(res.headers.get('Content-Type')).toBe('image/webp') - expect(res.headers.get('cache-control')).toBe( - 'public, max-age=0, must-revalidate' + expect(res.headers.get('Cache-Control')).toBe( + 'public, max-age=60, must-revalidate' ) expect(res.headers.get('etag')).toBeTruthy() await expectWidth(res, w) @@ -447,7 +447,7 @@ function runTests({ w, isDev, domains }) { expect(res1.status).toBe(200) expect(res1.headers.get('Content-Type')).toBe('image/webp') expect(res1.headers.get('Cache-Control')).toBe( - 'public, max-age=0, must-revalidate' + 'public, max-age=60, must-revalidate' ) const etag = res1.headers.get('Etag') expect(etag).toBeTruthy() @@ -458,6 +458,9 @@ function runTests({ w, isDev, domains }) { expect(res2.status).toBe(304) expect(res2.headers.get('Content-Type')).toBeFalsy() expect(res2.headers.get('Etag')).toBe(etag) + expect(res2.headers.get('Cache-Control')).toBe( + 'public, max-age=60, must-revalidate' + ) expect((await res2.buffer()).length).toBe(0) const query3 = { url: '/test.jpg', w, q: 25 } @@ -465,7 +468,7 @@ function runTests({ w, isDev, domains }) { expect(res3.status).toBe(200) expect(res3.headers.get('Content-Type')).toBe('image/webp') expect(res3.headers.get('Cache-Control')).toBe( - 'public, max-age=0, must-revalidate' + 'public, max-age=60, must-revalidate' ) expect(res3.headers.get('Etag')).toBeTruthy() expect(res3.headers.get('Etag')).not.toBe(etag) @@ -481,8 +484,8 @@ function runTests({ w, isDev, domains }) { const res = await fetchViaHTTP(appPort, '/_next/image', query, opts) expect(res.status).toBe(200) expect(res.headers.get('Content-Type')).toBe('image/bmp') - expect(res.headers.get('cache-control')).toBe( - 'public, max-age=0, must-revalidate' + expect(res.headers.get('Cache-Control')).toBe( + 'public, max-age=60, must-revalidate' ) expect(res.headers.get('etag')).toBeTruthy() @@ -496,8 +499,8 @@ function runTests({ w, isDev, domains }) { const res = await fetchViaHTTP(appPort, '/_next/image', query, opts) expect(res.status).toBe(200) expect(res.headers.get('Content-Type')).toBe('image/webp') - expect(res.headers.get('cache-control')).toBe( - 'public, max-age=0, must-revalidate' + expect(res.headers.get('Cache-Control')).toBe( + 'public, max-age=60, must-revalidate' ) expect(res.headers.get('etag')).toBeTruthy() await expectWidth(res, 400) @@ -511,8 +514,8 @@ function runTests({ w, isDev, domains }) { const res = await fetchViaHTTP(appPort, '/_next/image', query, opts) expect(res.status).toBe(200) expect(res.headers.get('Content-Type')).toBe('image/png') - expect(res.headers.get('cache-control')).toBe( - 'public, max-age=0, must-revalidate' + expect(res.headers.get('Cache-Control')).toBe( + 'public, max-age=60, must-revalidate' ) const png = await res.buffer() @@ -573,7 +576,7 @@ function runTests({ w, isDev, domains }) { await expectWidth(res2, w) // There should be only one image created in the cache directory. - const hashItems = [2, '/test.png', w, 80, 'image/webp'] + const hashItems = [3, '/test.png', w, 80, 'image/webp'] const hash = createHash('sha256') for (let item of hashItems) { if (typeof item === 'number') hash.update(String(item)) @@ -877,6 +880,9 @@ describe('Image Optimizer', () => { const res = await fetchViaHTTP(appPort, '/_next/image', query, opts) expect(res.status).toBe(200) expect(res.headers.get('Content-Type')).toBe('image/webp') + expect(res.headers.get('Cache-Control')).toBe( + 'public, max-age=31536000, must-revalidate' + ) await expectWidth(res, 64) }) }) From 481da13b10f8b62b9b8691ba6390c1b13c8bfd30 Mon Sep 17 00:00:00 2001 From: Steven Date: Wed, 30 Jun 2021 12:21:26 -0400 Subject: [PATCH 2/4] Fix test to use existing fsToJson() --- .../image-optimizer/test/index.test.js | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/test/integration/image-optimizer/test/index.test.js b/test/integration/image-optimizer/test/index.test.js index 3efb3f6d8da9c..efdd1dffd71f2 100644 --- a/test/integration/image-optimizer/test/index.test.js +++ b/test/integration/image-optimizer/test/index.test.js @@ -562,6 +562,7 @@ function runTests({ w, isDev, domains }) { }) it('should handle concurrent requests', async () => { + await fs.remove(imagesDir) const query = { url: '/test.png', w, q: 80 } const opts = { headers: { accept: 'image/webp,*/*' } } const [res1, res2] = await Promise.all([ @@ -575,19 +576,8 @@ function runTests({ w, isDev, domains }) { await expectWidth(res1, w) await expectWidth(res2, w) - // There should be only one image created in the cache directory. - const hashItems = [3, '/test.png', w, 80, 'image/webp'] - const hash = createHash('sha256') - for (let item of hashItems) { - if (typeof item === 'number') hash.update(String(item)) - else { - hash.update(item) - } - } - const hashDir = hash.digest('base64').replace(/\//g, '-') - const dir = join(imagesDir, hashDir) - const files = await fs.readdir(dir) - expect(files.length).toBe(1) + const json1 = await fsToJson(imagesDir) + expect(Object.keys(json1).length).toBe(1) }) } From f571ae6ab963077020b67cc2dccc151d07fe4b16 Mon Sep 17 00:00:00 2001 From: Steven Date: Wed, 30 Jun 2021 13:25:45 -0400 Subject: [PATCH 3/4] Change to max-age=0 for next dev --- packages/next/server/image-optimizer.ts | 47 +++++++++++++---- packages/next/server/next-server.ts | 3 +- .../image-optimizer/test/index.test.js | 50 +++++++++---------- 3 files changed, 65 insertions(+), 35 deletions(-) diff --git a/packages/next/server/image-optimizer.ts b/packages/next/server/image-optimizer.ts index 283ab506d1ed3..30a175203e7c2 100644 --- a/packages/next/server/image-optimizer.ts +++ b/packages/next/server/image-optimizer.ts @@ -35,7 +35,8 @@ export async function imageOptimizer( res: ServerResponse, parsedUrl: UrlWithParsedQuery, nextConfig: NextConfig, - distDir: string + distDir: string, + isDev = false ) { const imageData: ImageConfig = nextConfig.images || imageConfigDefault const { deviceSizes = [], imageSizes = [], domains = [], loader } = imageData @@ -170,7 +171,8 @@ export async function imageOptimizer( etag, maxAge, contentType, - isStatic + isStatic, + isDev ) if (!result.finished) { createReadStream(fsPath).pipe(res) @@ -277,7 +279,15 @@ export async function imageOptimizer( expireAt, upstreamBuffer ) - sendResponse(req, res, maxAge, upstreamType, upstreamBuffer, isStatic) + sendResponse( + req, + res, + maxAge, + upstreamType, + upstreamBuffer, + isStatic, + isDev + ) return { finished: true } } @@ -354,12 +364,28 @@ export async function imageOptimizer( expireAt, optimizedBuffer ) - sendResponse(req, res, maxAge, contentType, optimizedBuffer, isStatic) + sendResponse( + req, + res, + maxAge, + contentType, + optimizedBuffer, + isStatic, + isDev + ) } else { throw new Error('Unable to optimize buffer') } } catch (error) { - sendResponse(req, res, maxAge, upstreamType, upstreamBuffer, isStatic) + sendResponse( + req, + res, + maxAge, + upstreamType, + upstreamBuffer, + isStatic, + isDev + ) } return { finished: true } @@ -390,13 +416,14 @@ function setResponseHeaders( etag: string, maxAge: number, contentType: string | null, - isStatic: boolean + isStatic: boolean, + isDev: boolean ) { res.setHeader( 'Cache-Control', isStatic ? 'public, max-age=315360000, immutable' - : `public, max-age=${maxAge}, must-revalidate` + : `public, max-age=${isDev ? 0 : maxAge}, must-revalidate` ) if (sendEtagResponse(req, res, etag)) { // already called res.end() so we're finished @@ -414,7 +441,8 @@ function sendResponse( maxAge: number, contentType: string | null, buffer: Buffer, - isStatic: boolean + isStatic: boolean, + isDev: boolean ) { const etag = getHash([buffer]) const result = setResponseHeaders( @@ -423,7 +451,8 @@ function sendResponse( etag, maxAge, contentType, - isStatic + isStatic, + isDev ) if (!result.finished) { res.end(buffer) diff --git a/packages/next/server/next-server.ts b/packages/next/server/next-server.ts index 32ef98432e60c..4485694598587 100644 --- a/packages/next/server/next-server.ts +++ b/packages/next/server/next-server.ts @@ -789,7 +789,8 @@ export default class Server { res, parsedUrl, server.nextConfig, - server.distDir + server.distDir, + this.renderOpts.dev ), }, { diff --git a/test/integration/image-optimizer/test/index.test.js b/test/integration/image-optimizer/test/index.test.js index efdd1dffd71f2..680089b63199d 100644 --- a/test/integration/image-optimizer/test/index.test.js +++ b/test/integration/image-optimizer/test/index.test.js @@ -13,7 +13,6 @@ import { } from 'next-test-utils' import isAnimated from 'next/dist/compiled/is-animated' import { join } from 'path' -import { createHash } from 'crypto' jest.setTimeout(1000 * 60 * 2) @@ -57,7 +56,7 @@ function runTests({ w, isDev, domains }) { expect(res.status).toBe(200) expect(res.headers.get('content-type')).toContain('image/gif') expect(res.headers.get('Cache-Control')).toBe( - 'public, max-age=60, must-revalidate' + `public, max-age=${isDev ? 0 : 60}, must-revalidate` ) expect(res.headers.get('etag')).toBeTruthy() expect(isAnimated(await res.buffer())).toBe(true) @@ -69,7 +68,7 @@ function runTests({ w, isDev, domains }) { expect(res.status).toBe(200) expect(res.headers.get('content-type')).toContain('image/png') expect(res.headers.get('Cache-Control')).toBe( - 'public, max-age=60, must-revalidate' + `public, max-age=${isDev ? 0 : 60}, must-revalidate` ) expect(res.headers.get('etag')).toBeTruthy() expect(isAnimated(await res.buffer())).toBe(true) @@ -81,7 +80,7 @@ function runTests({ w, isDev, domains }) { expect(res.status).toBe(200) expect(res.headers.get('content-type')).toContain('image/webp') expect(res.headers.get('Cache-Control')).toBe( - 'public, max-age=60, must-revalidate' + `public, max-age=${isDev ? 0 : 60}, must-revalidate` ) expect(res.headers.get('etag')).toBeTruthy() expect(isAnimated(await res.buffer())).toBe(true) @@ -94,7 +93,7 @@ function runTests({ w, isDev, domains }) { expect(res.status).toBe(200) expect(res.headers.get('Content-Type')).toContain('image/svg+xml') expect(res.headers.get('Cache-Control')).toBe( - 'public, max-age=60, must-revalidate' + `public, max-age=${isDev ? 0 : 60}, must-revalidate` ) expect(res.headers.get('etag')).toBeTruthy() const actual = await res.text() @@ -112,7 +111,7 @@ function runTests({ w, isDev, domains }) { expect(res.status).toBe(200) expect(res.headers.get('Content-Type')).toContain('image/x-icon') expect(res.headers.get('Cache-Control')).toBe( - 'public, max-age=60, must-revalidate' + `public, max-age=${isDev ? 0 : 60}, must-revalidate` ) expect(res.headers.get('etag')).toBeTruthy() const actual = await res.text() @@ -132,7 +131,7 @@ function runTests({ w, isDev, domains }) { expect(res.status).toBe(200) expect(res.headers.get('Content-Type')).toContain('image/jpeg') expect(res.headers.get('Cache-Control')).toBe( - 'public, max-age=60, must-revalidate' + `public, max-age=${isDev ? 0 : 60}, must-revalidate` ) expect(res.headers.get('etag')).toBeTruthy() }) @@ -146,7 +145,7 @@ function runTests({ w, isDev, domains }) { expect(res.status).toBe(200) expect(res.headers.get('Content-Type')).toContain('image/png') expect(res.headers.get('Cache-Control')).toBe( - 'public, max-age=60, must-revalidate' + `public, max-age=${isDev ? 0 : 60}, must-revalidate` ) expect(res.headers.get('etag')).toBeTruthy() }) @@ -243,7 +242,7 @@ function runTests({ w, isDev, domains }) { expect(res.status).toBe(200) expect(res.headers.get('Content-Type')).toBe('image/webp') expect(res.headers.get('Cache-Control')).toBe( - 'public, max-age=60, must-revalidate' + `public, max-age=${isDev ? 0 : 60}, must-revalidate` ) expect(res.headers.get('etag')).toBeTruthy() await expectWidth(res, w) @@ -256,7 +255,7 @@ function runTests({ w, isDev, domains }) { expect(res.status).toBe(200) expect(res.headers.get('Content-Type')).toBe('image/png') expect(res.headers.get('Cache-Control')).toBe( - 'public, max-age=60, must-revalidate' + `public, max-age=${isDev ? 0 : 60}, must-revalidate` ) expect(res.headers.get('etag')).toBeTruthy() await expectWidth(res, w) @@ -269,7 +268,7 @@ function runTests({ w, isDev, domains }) { expect(res.status).toBe(200) expect(res.headers.get('Content-Type')).toBe('image/png') expect(res.headers.get('Cache-Control')).toBe( - 'public, max-age=60, must-revalidate' + `public, max-age=${isDev ? 0 : 60}, must-revalidate` ) expect(res.headers.get('etag')).toBeTruthy() await expectWidth(res, w) @@ -282,7 +281,7 @@ function runTests({ w, isDev, domains }) { expect(res.status).toBe(200) expect(res.headers.get('Content-Type')).toBe('image/gif') expect(res.headers.get('Cache-Control')).toBe( - 'public, max-age=60, must-revalidate' + `public, max-age=${isDev ? 0 : 60}, must-revalidate` ) expect(res.headers.get('etag')).toBeTruthy() // FIXME: await expectWidth(res, w) @@ -295,7 +294,7 @@ function runTests({ w, isDev, domains }) { expect(res.status).toBe(200) expect(res.headers.get('Content-Type')).toBe('image/tiff') expect(res.headers.get('Cache-Control')).toBe( - 'public, max-age=60, must-revalidate' + `public, max-age=${isDev ? 0 : 60}, must-revalidate` ) expect(res.headers.get('etag')).toBeTruthy() // FIXME: await expectWidth(res, w) @@ -310,7 +309,7 @@ function runTests({ w, isDev, domains }) { expect(res.status).toBe(200) expect(res.headers.get('Content-Type')).toBe('image/webp') expect(res.headers.get('Cache-Control')).toBe( - 'public, max-age=60, must-revalidate' + `public, max-age=${isDev ? 0 : 60}, must-revalidate` ) expect(res.headers.get('etag')).toBeTruthy() await expectWidth(res, w) @@ -325,7 +324,7 @@ function runTests({ w, isDev, domains }) { expect(res.status).toBe(200) expect(res.headers.get('Content-Type')).toBe('image/webp') expect(res.headers.get('Cache-Control')).toBe( - 'public, max-age=60, must-revalidate' + `public, max-age=${isDev ? 0 : 60}, must-revalidate` ) expect(res.headers.get('etag')).toBeTruthy() await expectWidth(res, w) @@ -345,7 +344,7 @@ function runTests({ w, isDev, domains }) { expect(res.status).toBe(200) expect(res.headers.get('Content-Type')).toBe('image/webp') expect(res.headers.get('Cache-Control')).toBe( - 'public, max-age=60, must-revalidate' + `public, max-age=${isDev ? 0 : 60}, must-revalidate` ) expect(res.headers.get('etag')).toBeTruthy() await expectWidth(res, w) @@ -447,7 +446,7 @@ function runTests({ w, isDev, domains }) { expect(res1.status).toBe(200) expect(res1.headers.get('Content-Type')).toBe('image/webp') expect(res1.headers.get('Cache-Control')).toBe( - 'public, max-age=60, must-revalidate' + `public, max-age=${isDev ? 0 : 60}, must-revalidate` ) const etag = res1.headers.get('Etag') expect(etag).toBeTruthy() @@ -459,7 +458,7 @@ function runTests({ w, isDev, domains }) { expect(res2.headers.get('Content-Type')).toBeFalsy() expect(res2.headers.get('Etag')).toBe(etag) expect(res2.headers.get('Cache-Control')).toBe( - 'public, max-age=60, must-revalidate' + `public, max-age=${isDev ? 0 : 60}, must-revalidate` ) expect((await res2.buffer()).length).toBe(0) @@ -468,7 +467,7 @@ function runTests({ w, isDev, domains }) { expect(res3.status).toBe(200) expect(res3.headers.get('Content-Type')).toBe('image/webp') expect(res3.headers.get('Cache-Control')).toBe( - 'public, max-age=60, must-revalidate' + `public, max-age=${isDev ? 0 : 60}, must-revalidate` ) expect(res3.headers.get('Etag')).toBeTruthy() expect(res3.headers.get('Etag')).not.toBe(etag) @@ -485,7 +484,7 @@ function runTests({ w, isDev, domains }) { expect(res.status).toBe(200) expect(res.headers.get('Content-Type')).toBe('image/bmp') expect(res.headers.get('Cache-Control')).toBe( - 'public, max-age=60, must-revalidate' + `public, max-age=${isDev ? 0 : 60}, must-revalidate` ) expect(res.headers.get('etag')).toBeTruthy() @@ -500,7 +499,7 @@ function runTests({ w, isDev, domains }) { expect(res.status).toBe(200) expect(res.headers.get('Content-Type')).toBe('image/webp') expect(res.headers.get('Cache-Control')).toBe( - 'public, max-age=60, must-revalidate' + `public, max-age=${isDev ? 0 : 60}, must-revalidate` ) expect(res.headers.get('etag')).toBeTruthy() await expectWidth(res, 400) @@ -515,7 +514,7 @@ function runTests({ w, isDev, domains }) { expect(res.status).toBe(200) expect(res.headers.get('Content-Type')).toBe('image/png') expect(res.headers.get('Cache-Control')).toBe( - 'public, max-age=60, must-revalidate' + `public, max-age=${isDev ? 0 : 60}, must-revalidate` ) const png = await res.buffer() @@ -538,7 +537,7 @@ function runTests({ w, isDev, domains }) { const res1 = await fetchViaHTTP(appPort, '/_next/image', query, opts) expect(res1.status).toBe(200) - expect(res1.headers.get('cache-control')).toBe( + expect(res1.headers.get('Cache-Control')).toBe( 'public, max-age=315360000, immutable' ) await expectWidth(res1, w) @@ -546,7 +545,7 @@ function runTests({ w, isDev, domains }) { // Ensure subsequent request also has immutable header const res2 = await fetchViaHTTP(appPort, '/_next/image', query, opts) expect(res2.status).toBe(200) - expect(res2.headers.get('cache-control')).toBe( + expect(res2.headers.get('Cache-Control')).toBe( 'public, max-age=315360000, immutable' ) await expectWidth(res2, w) @@ -713,6 +712,7 @@ describe('Image Optimizer', () => { const domains = [ 'localhost', 'example.com', + 'assets.vercel.com', 'image-optimization-test.vercel.app', ] @@ -871,7 +871,7 @@ describe('Image Optimizer', () => { expect(res.status).toBe(200) expect(res.headers.get('Content-Type')).toBe('image/webp') expect(res.headers.get('Cache-Control')).toBe( - 'public, max-age=31536000, must-revalidate' + `public, max-age=31536000, must-revalidate` ) await expectWidth(res, 64) }) From 0ee0362bb11649e0af937ed6c6818ddd481d9de9 Mon Sep 17 00:00:00 2001 From: Steven Date: Wed, 30 Jun 2021 13:30:19 -0400 Subject: [PATCH 4/4] Switch test from next dev to next start --- test/integration/image-optimizer/test/index.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/integration/image-optimizer/test/index.test.js b/test/integration/image-optimizer/test/index.test.js index 680089b63199d..161301d3d28b3 100644 --- a/test/integration/image-optimizer/test/index.test.js +++ b/test/integration/image-optimizer/test/index.test.js @@ -854,10 +854,10 @@ describe('Image Optimizer', () => { }, }` nextConfig.replace('{ /* replaceme */ }', newConfig) + await nextBuild(appDir) appPort = await findPort() - app = await launchApp(appDir, appPort) + app = await nextStart(appDir, appPort) }) - afterAll(async () => { await killApp(app) nextConfig.restore()