This repository has been archived by the owner on Jan 28, 2025. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 459
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
use expires header rather than last-modified and a small code tidy
- Loading branch information
Showing
8 changed files
with
212 additions
and
82 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
52 changes: 52 additions & 0 deletions
52
packages/libs/lambda-at-edge/src/lib/getStaticRegenerationResponse.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import { OriginRequestDefaultHandlerManifest } from "../types"; | ||
|
||
interface StaticRegenerationResponseOptions { | ||
// URI of the origin object | ||
requestedOriginUri: string; | ||
// Header as set on the origin object | ||
expiresHeader: string; | ||
manifest: OriginRequestDefaultHandlerManifest; | ||
} | ||
|
||
interface StaticRegenerationResponseValue { | ||
// Cache-Control header | ||
cacheControl: string; | ||
secondsRemainingUntilRevalidation: number; | ||
} | ||
|
||
/** | ||
* Function called within an origin response as part of the Incremental Static | ||
* Regeneration logic. Returns required headers for the response, or false if | ||
* this response is not compatible with ISR. | ||
*/ | ||
const getStaticRegenerationResponse = ( | ||
options: StaticRegenerationResponseOptions | ||
): StaticRegenerationResponseValue | false => { | ||
const initialRevalidateSeconds = | ||
options.manifest.pages.ssg.nonDynamic?.[ | ||
options.requestedOriginUri.replace(".html", "") | ||
]?.initialRevalidateSeconds; | ||
|
||
// If this page did not write a revalidate value at build time it is not an | ||
// ISR page | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong. |
||
if (typeof initialRevalidateSeconds !== "number") { | ||
return false; | ||
} | ||
|
||
const expiresAt = new Date(options.expiresHeader); | ||
|
||
// isNaN will resolve true on initial load of this page (as the expiresHeader | ||
// won't be set), in which case we trigger a regeneration now | ||
const secondsRemainingUntilRevalidation = isNaN(expiresAt.getTime()) | ||
? 0 | ||
: // Never return a negative amount of seconds if revalidation could have | ||
// happened sooner | ||
Math.floor(Math.max(0, (expiresAt.getTime() - Date.now()) / 1000)); | ||
|
||
return { | ||
secondsRemainingUntilRevalidation, | ||
cacheControl: `public, max-age=0, s-maxage=${secondsRemainingUntilRevalidation}, must-revalidate` | ||
}; | ||
}; | ||
|
||
export { getStaticRegenerationResponse }; |
70 changes: 70 additions & 0 deletions
70
packages/libs/lambda-at-edge/src/lib/triggerStaticRegeneration.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import { s3BucketNameFromEventRequest } from "../s3/s3BucketNameFromEventRequest"; | ||
import { buildS3RetryStrategy } from "../s3/s3RetryStrategy"; | ||
import { OriginRequestDefaultHandlerManifest } from "../types"; | ||
|
||
interface TriggerStaticRegenerationOptions { | ||
request: AWSLambda.CloudFrontRequest; | ||
response: AWSLambda.CloudFrontResponse; | ||
manifest: OriginRequestDefaultHandlerManifest; | ||
basePath: string | undefined; | ||
} | ||
|
||
export const triggerStaticRegeneration = async ( | ||
options: TriggerStaticRegenerationOptions | ||
): Promise<void> => { | ||
const { region } = options.request.origin?.s3 || {}; | ||
const bucketName = s3BucketNameFromEventRequest(options.request); | ||
|
||
const { SQSClient, SendMessageCommand } = await import("@aws-sdk/client-sqs"); | ||
const sqs = new SQSClient({ | ||
region, | ||
maxAttempts: 3, | ||
retryStrategy: await buildS3RetryStrategy() | ||
}); | ||
|
||
const lastModifiedAt = new Date( | ||
options.response.headers["last-modified"]?.[0].value | ||
) | ||
.getTime() | ||
.toString(); | ||
|
||
await sqs.send( | ||
new SendMessageCommand({ | ||
QueueUrl: `https://sqs.${region}.amazonaws.com/${bucketName}.fifo`, | ||
MessageBody: options.request.uri, // This is not used, however it is a required property | ||
MessageAttributes: { | ||
BucketRegion: { | ||
DataType: "String", | ||
StringValue: region | ||
}, | ||
BucketName: { | ||
DataType: "String", | ||
StringValue: bucketName | ||
}, | ||
CloudFrontEventRequest: { | ||
DataType: "String", | ||
StringValue: JSON.stringify(options.request) | ||
}, | ||
Manifest: { | ||
DataType: "String", | ||
StringValue: JSON.stringify(options.manifest) | ||
}, | ||
...(options.basePath | ||
? { | ||
BasePath: { | ||
DataType: "String", | ||
StringValue: options.basePath | ||
} | ||
} | ||
: {}) | ||
}, | ||
// We only want to trigger the regeneration once for every previous | ||
// update. This will prevent the case where this page is being | ||
// requested again whilst its already started to regenerate. | ||
MessageDeduplicationId: lastModifiedAt, | ||
// Only deduplicate based on the object, i.e. we can generate | ||
// different pages in parallel, just not the same one | ||
MessageGroupId: options.request.uri | ||
}) | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -59,7 +59,9 @@ export const handler: AWSLambda.SQSHandler = async (event) => { | |
"passthrough" | ||
); | ||
|
||
const expires = new Date(Date.now() + renderOpts.revalidate * 1000); | ||
const revalidate = | ||
renderOpts.revalidate ?? ssgRoute.initialRevalidateSeconds; | ||
This comment has been minimized.
Sorry, something went wrong.
jvarho
Collaborator
|
||
const expires = new Date(Date.now() + revalidate * 1000); | ||
const s3BasePath = basePath ? `${basePath.replace(/^\//, "")}/` : ""; | ||
const s3JsonParams = { | ||
Bucket: bucketName, | ||
|
6 changes: 6 additions & 0 deletions
6
packages/libs/lambda-at-edge/src/s3/s3BucketNameFromEventRequest.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
export const s3BucketNameFromEventRequest = ( | ||
request: AWSLambda.CloudFrontRequest | ||
): string | undefined => { | ||
const { region, domainName } = request.origin?.s3 || {}; | ||
return domainName?.replace(`.s3.${region}.amazonaws.com`, ""); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -178,6 +178,46 @@ | |
fast-xml-parser "^3.16.0" | ||
tslib "^2.0.0" | ||
|
||
"@aws-sdk/[email protected]": | ||
version "1.0.0-rc.3" | ||
resolved "https://registry.yarnpkg.com/@aws-sdk/client-sqs/-/client-sqs-1.0.0-rc.3.tgz#aca468b52f77db00ffdf27d825022124d802da4d" | ||
integrity sha512-qEXJ++GJ46sPboyhRUJIv03buEvmXT5lLgjUdWjZKwzHaU34GPH0B7xxlLOUWmA+JvyPaK91ESjGqLc/82GLaA== | ||
dependencies: | ||
"@aws-crypto/sha256-browser" "^1.0.0" | ||
"@aws-crypto/sha256-js" "^1.0.0" | ||
"@aws-sdk/config-resolver" "1.0.0-rc.3" | ||
"@aws-sdk/credential-provider-node" "1.0.0-rc.3" | ||
"@aws-sdk/fetch-http-handler" "1.0.0-rc.3" | ||
"@aws-sdk/hash-node" "1.0.0-rc.3" | ||
"@aws-sdk/invalid-dependency" "1.0.0-rc.3" | ||
"@aws-sdk/md5-js" "1.0.0-rc.3" | ||
"@aws-sdk/middleware-content-length" "1.0.0-rc.3" | ||
"@aws-sdk/middleware-host-header" "1.0.0-rc.3" | ||
"@aws-sdk/middleware-logger" "1.0.0-rc.3" | ||
"@aws-sdk/middleware-retry" "1.0.0-rc.3" | ||
"@aws-sdk/middleware-sdk-sqs" "1.0.0-rc.3" | ||
"@aws-sdk/middleware-serde" "1.0.0-rc.3" | ||
"@aws-sdk/middleware-signing" "1.0.0-rc.3" | ||
"@aws-sdk/middleware-stack" "1.0.0-rc.3" | ||
"@aws-sdk/middleware-user-agent" "1.0.0-rc.3" | ||
"@aws-sdk/node-config-provider" "1.0.0-rc.3" | ||
"@aws-sdk/node-http-handler" "1.0.0-rc.3" | ||
"@aws-sdk/protocol-http" "1.0.0-rc.3" | ||
"@aws-sdk/smithy-client" "1.0.0-rc.3" | ||
"@aws-sdk/types" "1.0.0-rc.3" | ||
"@aws-sdk/url-parser-browser" "1.0.0-rc.3" | ||
"@aws-sdk/url-parser-node" "1.0.0-rc.3" | ||
"@aws-sdk/util-base64-browser" "1.0.0-rc.3" | ||
"@aws-sdk/util-base64-node" "1.0.0-rc.3" | ||
"@aws-sdk/util-body-length-browser" "1.0.0-rc.3" | ||
"@aws-sdk/util-body-length-node" "1.0.0-rc.3" | ||
"@aws-sdk/util-user-agent-browser" "1.0.0-rc.3" | ||
"@aws-sdk/util-user-agent-node" "1.0.0-rc.3" | ||
"@aws-sdk/util-utf8-browser" "1.0.0-rc.3" | ||
"@aws-sdk/util-utf8-node" "1.0.0-rc.3" | ||
fast-xml-parser "^3.16.0" | ||
tslib "^2.0.0" | ||
|
||
"@aws-sdk/[email protected]": | ||
version "1.0.0-rc.3" | ||
resolved "https://registry.yarnpkg.com/@aws-sdk/config-resolver/-/config-resolver-1.0.0-rc.3.tgz#0eb877cdabffb75ba3ed89f14e86301faeec12d2" | ||
|
@@ -441,6 +481,15 @@ | |
"@aws-sdk/util-arn-parser" "1.0.0-rc.3" | ||
tslib "^1.8.0" | ||
|
||
"@aws-sdk/[email protected]": | ||
version "1.0.0-rc.3" | ||
resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-sdk-sqs/-/middleware-sdk-sqs-1.0.0-rc.3.tgz#5f02a97b0f34a4848ef8769e1e21d09d178d3cd8" | ||
integrity sha512-d3kL0IDQtXf/kP3RXMH6+AsjYS69tPC+9r9O28ri/qPDQFUdeHVFxybneAA/5JWikDM6tZ4htgkm+Tm4PUm5hA== | ||
dependencies: | ||
"@aws-sdk/types" "1.0.0-rc.3" | ||
"@aws-sdk/util-hex-encoding" "1.0.0-rc.3" | ||
tslib "^1.8.0" | ||
|
||
"@aws-sdk/[email protected]": | ||
version "1.0.0-rc.3" | ||
resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-serde/-/middleware-serde-1.0.0-rc.3.tgz#81307310c51d50ec8425bee9fb08d35a7458dcfc" | ||
|
Like I wrote in elsewhere, dynamic pages do not have
initialRevalidateSeconds
in the manifest. The only way to know that they should be revalidated is to store that information in S3 – i.e. the Expires header you now use.