1
+ import { createHash } from "node:crypto" ;
1
2
import { IncomingMessage , ServerResponse } from "node:http" ;
2
3
import https from "node:https" ;
3
4
import path from "node:path" ;
@@ -20,7 +21,7 @@ import {
20
21
// @ts -ignore
21
22
import type { NextUrlWithParsedQuery } from "next/dist/server/request-meta" ;
22
23
23
- import { loadConfig } from "./config/util.js" ;
24
+ import { loadBuildId , loadConfig } from "./config/util.js" ;
24
25
import { awsLogger , debug , error } from "./logger.js" ;
25
26
import { optimizeImage } from "./plugins/image-optimization.js" ;
26
27
import { setNodeEnv } from "./util.js" ;
@@ -33,6 +34,7 @@ const s3Client = new S3Client({ logger: awsLogger });
33
34
setNodeEnv ( ) ;
34
35
const nextDir = path . join ( __dirname , ".next" ) ;
35
36
const config = loadConfig ( nextDir ) ;
37
+ const buildId = loadBuildId ( nextDir ) ;
36
38
const nextConfig = {
37
39
...defaultConfig ,
38
40
images : {
@@ -64,14 +66,24 @@ export async function handler(
64
66
headers ,
65
67
queryString === null ? undefined : queryString ,
66
68
) ;
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
+ }
67
79
const result = await optimizeImage (
68
80
headers ,
69
81
imageParams ,
70
82
nextConfig ,
71
83
downloadHandler ,
72
84
) ;
73
85
74
- return buildSuccessResponse ( result ) ;
86
+ return buildSuccessResponse ( result , etag ) ;
75
87
} catch ( e : any ) {
76
88
return buildFailureResponse ( e ) ;
77
89
}
@@ -115,16 +127,38 @@ function validateImageParams(
115
127
return imageParams ;
116
128
}
117
129
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
+
119
157
return {
120
158
statusCode : 200 ,
121
159
body : result . buffer . toString ( "base64" ) ,
122
160
isBase64Encoded : true ,
123
- headers : {
124
- Vary : "Accept" ,
125
- "Cache-Control" : `public,max-age=${ result . maxAge } ,immutable` ,
126
- "Content-Type" : result . contentType ,
127
- } ,
161
+ headers,
128
162
} ;
129
163
}
130
164
0 commit comments