Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/do 1484 enable cache for redirects #1069

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 15 additions & 12 deletions packages/prerender-fargate/README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
# Prerender in Fargate
A construct to host [Prerender](https://github.com/prerender/prerender) in Fargate.

A construct to host [Prerender](https://github.com/prerender/prerender) in Fargate.

## Props
- `prerenderName`: Name of the Prerender service
- `domainName`: Domain name for Prerender
- `vpcId`: VPC to host Prerender in
- `bucketName`: Optional S3 bucket name
- `expirationDays`: Optional days until items expire in bucket (default to 7 days)
- `tokenList`: List of tokens to accept as authentication
- `certificateArn`: Certificate arn to match the domain
- `desiredInstanceCount`: Number of Prerender instances to run (default 1)
- `maxInstanceCount`: Maximum number of Prerender instances to run (default 2)
- `instanceCPU`: CPU to allocate to each instance (default 512)
- `instanceMemory`: Amount of memory to allocate to each instance (default 1024)

- `prerenderName`: Name of the Prerender service
- `domainName`: Domain name for Prerender
- `vpcId`: VPC to host Prerender in
- `bucketName`: Optional S3 bucket name
- `expirationDays`: Optional days until items expire in bucket (default to 7 days)
- `tokenList`: List of tokens to accept as authentication
- `certificateArn`: Certificate arn to match the domain
- `desiredInstanceCount`: Number of Prerender instances to run (default 1)
- `maxInstanceCount`: Maximum number of Prerender instances to run (default 2)
- `instanceCPU`: CPU to allocate to each instance (default 512)
- `instanceMemory`: Amount of memory to allocate to each instance (default 1024)
- `enableRedirectCache`: Cache 301 and 302 responses, too (default false)
2 changes: 2 additions & 0 deletions packages/prerender-fargate/lib/prerender-fargate.ts
Original file line number Diff line number Diff line change
@@ -22,6 +22,7 @@ export interface PrerenderOptions {
maxInstanceCount?: number,
instanceCPU?: number,
instanceMemory?: number
enableRedirectCache?: string
}

export class PrerenderFargate extends Construct {
@@ -80,6 +81,7 @@ export class PrerenderFargate extends Construct {
AWS_ACCESS_KEY_ID: accessKey.accessKeyId,
AWS_SECRET_ACCESS_KEY: accessKey.secretAccessKey.toString(),
AWS_REGION: Stack.of(this).region,
ENABLE_REDIRECT_CACHE: props.enableRedirectCache || "false",
TOKEN_LIST: props.tokenList.toString()
}
},
100 changes: 97 additions & 3 deletions packages/prerender-fargate/lib/prerender/server.js
Original file line number Diff line number Diff line change
@@ -36,8 +36,102 @@ server.use({
});

server.use(prerender.blacklist());
server.use(prerender.httpHeaders());
server.use(prerender.removeScriptTags());
server.use(s3Cache);

if (process.env.ENABLE_REDIRECT_CACHE.toLowerCase() === 'true'){
var he = require('he');
var s3 = new (require('aws-sdk')).S3({params:{Bucket: process.env.S3_BUCKET_NAME}});
server.use({
// The requestReceived and pageLoaded functions are a modified version of
// httpHeader plugin - https://github.com/prerender/prerender/blob/478fa6d0a5196ea29c88c69e64e72eb5507b6d2c/lib/plugins/httpHeaders.js combined with
// s3cache plugin - https://github.com/prerender/prerender-aws-s3-cache/blob/98707fa0f787de83aa41583682cd2c2d330a9cca/index.js
requestReceived: function(req, res, next) {
if(req.method !== 'GET' && req.method !== 'HEAD') {
return next();
}

var key = req.prerender.url;

if (process.env.S3_PREFIX_KEY) {
key = process.env.S3_PREFIX_KEY + '/' + key;
}

s3.getObject({
Key: key
}, function (err, result) {

if (!err && result) {
console.log("Found cached object: " + key);
if (result.Metadata.location){
res.setHeader('Location', result.Metadata.location);
}
// default 200 for legacy objects that do not have Metadata.httpreturncode defined
return res.send(result.Metadata.httpreturncode || 200, result.Body);
} else {
console.error(err);
}

next();
});
},

pageLoaded: function(req, res, next) {
const statusCodesToCache = ['200', '301', '302'];
var s3Metadata = {}

// Inspect prerender meta tags and update response accordingly
if (req.prerender.content && req.prerender.renderType == 'html') {
const statusMatchRegex = /<meta[^<>]*(?:name=['"]prerender-status-code['"][^<>]*content=['"]([0-9]{3})['"]|content=['"]([0-9]{3})['"][^<>]*name=['"]prerender-status-code['"])[^<>]*>/i;
const headerMatchRegex = /<meta[^<>]*(?:name=['"]prerender-header['"][^<>]*content=['"]([^'"]*?): ?([^'"]*?)['"]|content=['"]([^'"]*?): ?([^'"]*?)['"][^<>]*name=['"]prerender-header['"])[^<>]*>/gi
const head = req.prerender.content.toString().split('</head>', 1).pop()

const statusMatch = statusMatchRegex.exec(head)
if (statusMatch) {
req.prerender.statusCode = statusMatch[1] || statusMatch[2];
req.prerender.content = req.prerender.content.toString().replace(statusMatch[0], '');
}

let headerMatch = headerMatchRegex.exec(head)
while (headerMatch) {
s3Metadata.location = he.decode(headerMatch[2] || headerMatch[4]);
res.setHeader(headerMatch[1] || headerMatch[3], s3Metadata.location);
req.prerender.content = req.prerender.content.toString().replace(headerMatch[0], '');
headerMatch = headerMatchRegex.exec(head)
}

// Skip caching for the http response codes not in the list, such as 404
if ( ! statusCodesToCache.includes(req.prerender.statusCode.toString()) ) {
console.log(`StatusCode ${req.prerender.statusCode} for ${req.prerender.url} is not in the cachable code list. Returning without caching the result.`);
return res.send(req.prerender.statusCode, req.prerender.content);
}
}
s3Metadata.httpreturncode = req.prerender.statusCode.toString()

console.log(`Caching the object ${req.prerender.url} with statusCode ${req.prerender.statusCode}`);
var key = req.prerender.url;

if (process.env.S3_PREFIX_KEY) {
key = process.env.S3_PREFIX_KEY + '/' + key;
}

s3.putObject({
Key: key,
ContentType: 'text/html;charset=UTF-8',
StorageClass: 'REDUCED_REDUNDANCY',
Body: req.prerender.content,
Metadata: s3Metadata
}, function(err, result) {
console.log(result);
if (err) console.error(err);

next();
});
}
});
server.use(prerender.removeScriptTags());
} else {
server.use(prerender.httpHeaders());
TheOrangePuff marked this conversation as resolved.
Show resolved Hide resolved
server.use(prerender.removeScriptTags());
server.use(s3Cache);
}

server.start();
Loading