Skip to content

Commit

Permalink
DO-1410: Lambda at edge handler lib
Browse files Browse the repository at this point in the history
  • Loading branch information
krishanthisera committed Nov 29, 2022
1 parent 1c777bc commit ed19a48
Show file tree
Hide file tree
Showing 9 changed files with 175 additions and 12 deletions.
2 changes: 1 addition & 1 deletion packages/lambda-at-edge-handlers/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,4 @@ Thumbs.db
cdk.out

*.d.ts
*.js
*.js
14 changes: 12 additions & 2 deletions packages/lambda-at-edge-handlers/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
import { prerenderHandler } from "./lib/prerender-handler";
import { handler as PrerenderHandler } from "./lib/prerender";
import { handler as PrerenderCacheControlHandler } from "./lib/cache-control";
import { handler as PrerenderErrorResponseHandler } from "./lib/error-response";
import { handler as PrerenderCheckHandler } from "./lib/prerender-check";
import { handler as GeoIpRedirectHandler } from "./lib/redirect";

export { prerenderHandler };
export {
PrerenderHandler,
PrerenderCacheControlHandler,
PrerenderErrorResponseHandler,
PrerenderCheckHandler,
GeoIpRedirectHandler,
};
21 changes: 21 additions & 0 deletions packages/lambda-at-edge-handlers/lib/cache-control.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import 'source-map-support/register';
import { CloudFrontResponseEvent, CloudFrontResponse } from 'aws-lambda';
/*
Prerender cache control function
Consider associate this function as *origin response* function
*/
export const handler = async (event: CloudFrontResponseEvent): Promise<CloudFrontResponse> => {
const cacheKey = process.env.PRERENDER_CACHE_KEY || 'x-prerender-requestid';
const cacheMaxAge = process.env.PRERENDER_CACHE_MAX_AGE || '0';
let response = event.Records[0].cf.response;

if (response.headers[`${cacheKey}`]) {
response.headers['Cache-Control'] = [
{
key: 'Cache-Control',
value: `max-age=${cacheMaxAge}`
}
]
}
return response;
}
61 changes: 61 additions & 0 deletions packages/lambda-at-edge-handlers/lib/error-response.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import "source-map-support/register";
import {
CloudFrontRequest,
CloudFrontResponseEvent,
CloudFrontResponse,
} from "aws-lambda";
import axios from "axios";
import * as https from "https";

const FRONTEND_HOST = process.env.FRONTEND_HOST;
const PATH_PREFIX = process.env.PATH_PREFIX;

// Create axios client outside of lambda function for re-use between calls
const instance = axios.create({
timeout: 1000,
// Don't follow redirects
maxRedirects: 0,
// Only valid response codes are 200
validateStatus: function (status) {
return status == 200;
},
// keep connection alive so we don't constantly do SSL negotiation
httpsAgent: new https.Agent({ keepAlive: true }),
});

export const handler = (
event: CloudFrontResponseEvent
): Promise<CloudFrontResponse | CloudFrontRequest> => {
let response = event.Records[0].cf.response;
let request = event.Records[0].cf.request;

if (
response.status != "200" &&
!request.headers["x-request-prerender"] &&
request.uri != `${PATH_PREFIX}/index.html`
) {
// Fetch default page and return body
return instance
.get(`https://${FRONTEND_HOST}${PATH_PREFIX}/index.html`)
.then((res) => {
response.body = res.data;

response.headers["content-type"] = [
{
key: "Content-Type",
value: "text/html",
},
];

// Remove content-length if set as this may be the value from the origin.
delete response.headers["content-length"];

return response;
})
.catch((err) => {
return response;
});
}

return Promise.resolve(response);
};
35 changes: 35 additions & 0 deletions packages/lambda-at-edge-handlers/lib/prerender-check.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import "source-map-support/register";
import { CloudFrontRequest, CloudFrontRequestEvent } from "aws-lambda";

const IS_BOT =
/googlebot|chrome-lighthouse|lighthouse|adsbot\-google|Feedfetcher\-Google|bingbot|yandex|baiduspider|Facebot|facebookexternalhit|twitterbot|rogerbot|linkedinbot|embedly|quora link preview|showyoubot|outbrain|pinterest|slackbot|vkShare|W3C_Validator/i;
const IS_FILE =
/\.(js|css|xml|less|png|jpg|jpeg|gif|pdf|doc|txt|ico|rss|zip|mp3|rar|exe|wmv|doc|avi|ppt|mpg|mpeg|tif|wav|mov|psd|ai|xls|mp4|m4a|swf|dat|dmg|iso|flv|m4v|torrent|ttf|woff|svg|eot)$/i;

export const handler = async (
event: CloudFrontRequestEvent
): Promise<CloudFrontRequest> => {
let request = event.Records[0].cf.request;

// If the request is from a bot, is not a file and is not from prerender
// then set the x-request-prerender header so the origin-request lambda function
// alters the origin to prerender.io
if (
!IS_FILE.test(request.uri) &&
IS_BOT.test(request.headers["user-agent"][0].value) &&
!request.headers["x-prerender"]
) {
request.headers["x-request-prerender"] = [
{
key: "x-request-prerender",
value: "true",
},
];

request.headers["x-prerender-host"] = [
{ key: "X-Prerender-Host", value: request.headers.host[0].value },
];
}

return request;
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@ import {
CloudFrontRequestEvent,
CloudFrontResponse,
} from "aws-lambda";

const PRERENDER_TOKEN = process.env.PRERENDER_TOKEN
? process.env.PRERENDER_TOKEN
: "undef";
: "undefined";
const PATH_PREFIX = process.env.PATH_PREFIX;
const PRERENDER_URL = process.env.PRERENDER_URL
? process.env.PRERENDER_URL
: "service.prerender.io";

export const prerenderHandler = async (
export const handler = async (
event: CloudFrontRequestEvent
): Promise<CloudFrontResponse | CloudFrontRequest> => {
let request = event.Records[0].cf.request;
Expand Down
34 changes: 34 additions & 0 deletions packages/lambda-at-edge-handlers/lib/redirect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import 'source-map-support/register';
import { CloudFrontRequestEvent, CloudFrontResponse, CloudFrontRequest } from 'aws-lambda';

const REDIRECT_HOST = process.env.REDIRECT_HOST;
const SUPPORTED_REGIONS = new RegExp(process.env.SUPPORTED_REGIONS);
const DEFAULT_REGION = process.env.DEFAULT_REGION;

export const handler = async (event: CloudFrontRequestEvent): Promise<CloudFrontResponse|CloudFrontRequest> => {
let request = event.Records[0].cf.request;

let redirectURL = `https://${REDIRECT_HOST}/`;
if (request.headers['cloudfront-viewer-country']) {
const countryCode = request.headers['cloudfront-viewer-country'][0].value;
if (SUPPORTED_REGIONS.test(countryCode)) {
redirectURL = `${redirectURL}${countryCode.toLowerCase()}${request.uri}`;
} else {
redirectURL = `${redirectURL}${DEFAULT_REGION.toLowerCase()}${request.uri}`;
}

return {
status: '302',
statusDescription: 'Found',
headers: {
location: [{
key: 'Location',
value: redirectURL,
}],
},
};
}

return request;

}
12 changes: 6 additions & 6 deletions packages/lambda-at-edge-handlers/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion packages/lambda-at-edge-handlers/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"dependencies": {
"@types/aws-lambda": "^8.10.77",
"esbuild": "0.12.15",
"source-map-support": "^0.5.16"
"source-map-support": "^0.5.16",
"axios": "^0.21.1"
}
}

0 comments on commit ed19a48

Please sign in to comment.