Skip to content

Commit 2118ba2

Browse files
authored
Feat add a static etag for Image (#398)
* feat add a static etag * Create hot-toys-push.md
1 parent 6a3c69a commit 2118ba2

File tree

3 files changed

+52
-9
lines changed

3 files changed

+52
-9
lines changed

.changeset/hot-toys-push.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"open-next": patch
3+
---
4+
5+
Feat add a static etag for Image Optimization

packages/open-next/src/adapters/image-optimization-adapter.ts

+42-8
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { createHash } from "node:crypto";
12
import { IncomingMessage, ServerResponse } from "node:http";
23
import https from "node:https";
34
import path from "node:path";
@@ -20,7 +21,7 @@ import {
2021
// @ts-ignore
2122
import type { NextUrlWithParsedQuery } from "next/dist/server/request-meta";
2223

23-
import { loadConfig } from "./config/util.js";
24+
import { loadBuildId, loadConfig } from "./config/util.js";
2425
import { awsLogger, debug, error } from "./logger.js";
2526
import { optimizeImage } from "./plugins/image-optimization.js";
2627
import { setNodeEnv } from "./util.js";
@@ -33,6 +34,7 @@ const s3Client = new S3Client({ logger: awsLogger });
3334
setNodeEnv();
3435
const nextDir = path.join(__dirname, ".next");
3536
const config = loadConfig(nextDir);
37+
const buildId = loadBuildId(nextDir);
3638
const nextConfig = {
3739
...defaultConfig,
3840
images: {
@@ -64,14 +66,24 @@ export async function handler(
6466
headers,
6567
queryString === null ? undefined : queryString,
6668
);
69+
let etag: string | undefined;
70+
// We don't cache any images, so in order to be able to return 304 responses, we compute an ETag from what is assumed to be static
71+
if (process.env.OPENNEXT_STATIC_ETAG) {
72+
etag = computeEtag(imageParams);
73+
}
74+
if (etag && headers["if-none-match"] === etag) {
75+
return {
76+
statusCode: 304,
77+
};
78+
}
6779
const result = await optimizeImage(
6880
headers,
6981
imageParams,
7082
nextConfig,
7183
downloadHandler,
7284
);
7385

74-
return buildSuccessResponse(result);
86+
return buildSuccessResponse(result, etag);
7587
} catch (e: any) {
7688
return buildFailureResponse(e);
7789
}
@@ -115,16 +127,38 @@ function validateImageParams(
115127
return imageParams;
116128
}
117129

118-
function buildSuccessResponse(result: any) {
130+
function computeEtag(imageParams: {
131+
href: string;
132+
width: number;
133+
quality: number;
134+
}) {
135+
return createHash("sha1")
136+
.update(
137+
JSON.stringify({
138+
href: imageParams.href,
139+
width: imageParams.width,
140+
quality: imageParams.quality,
141+
buildId,
142+
}),
143+
)
144+
.digest("base64");
145+
}
146+
147+
function buildSuccessResponse(result: any, etag?: string) {
148+
const headers: Record<string, string> = {
149+
Vary: "Accept",
150+
"Content-Type": result.contentType,
151+
"Cache-Control": `public,max-age=${result.maxAge},immutable`,
152+
};
153+
if (etag) {
154+
headers["ETag"] = etag;
155+
}
156+
119157
return {
120158
statusCode: 200,
121159
body: result.buffer.toString("base64"),
122160
isBase64Encoded: true,
123-
headers: {
124-
Vary: "Accept",
125-
"Cache-Control": `public,max-age=${result.maxAge},immutable`,
126-
"Content-Type": result.contentType,
127-
},
161+
headers,
128162
};
129163
}
130164

packages/open-next/src/build.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -375,12 +375,16 @@ async function createImageOptimizationBundle() {
375375
},
376376
});
377377

378-
// Copy over .next/required-server-files.json file
378+
// Copy over .next/required-server-files.json file and BUILD_ID
379379
fs.mkdirSync(path.join(outputPath, ".next"));
380380
fs.copyFileSync(
381381
path.join(appBuildOutputPath, ".next/required-server-files.json"),
382382
path.join(outputPath, ".next/required-server-files.json"),
383383
);
384+
fs.copyFileSync(
385+
path.join(appBuildOutputPath, ".next/BUILD_ID"),
386+
path.join(outputPath, ".next/BUILD_ID"),
387+
);
384388

385389
// Sharp provides pre-build binaries for all platforms. https://github.com/lovell/sharp/blob/main/docs/install.md#cross-platform
386390
// Target should be same as used by Lambda, see https://github.com/sst/sst/blob/ca6f763fdfddd099ce2260202d0ce48c72e211ea/packages/sst/src/constructs/NextjsSite.ts#L114

0 commit comments

Comments
 (0)