Last active
October 7, 2023 07:38
-
-
Save mccaffers/e89886c588e122d90ec9d853510a50da to your computer and use it in GitHub Desktop.
AWS Lambda Request Forwarder
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
'use strict' | |
import http from "https"; | |
import zlib from "zlib"; | |
/* global atob */ | |
function queryString (kvPairs) { | |
const result = [] | |
for (let key in kvPairs) { | |
let values = kvPairs[key] | |
for (let el in values) { | |
result.push(encodeURIComponent(key) + '=' + encodeURIComponent(values[el])) | |
} | |
} | |
if (result.length === 0) { | |
return '' | |
} | |
return '?' + result.join('&') | |
} | |
let request = async (event, httpOptions, data) => { | |
return new Promise((resolve, reject) => { | |
// Take the host endpoint (EC2 internal) from the Lambda Environment Variable | |
let queryString = (event.multiValueQueryStringParameters ? queryString(event.multiValueQueryStringParameters) : event.rawQueryString) | |
if(queryString === undefined) { | |
queryString = "" | |
} | |
let host = process.env.ENDPOINT + httpOptions.path + queryString; | |
const req = http.request(host, httpOptions, (res) => { | |
// Placeholder object for returning response | |
let hostResponse = { | |
body : [], | |
statusCode : res.statusCode, | |
headers: res.headers | |
} | |
res.on('data', (chunk) => { hostResponse.body.push(chunk); }) | |
res.on('end', () => { resolve(hostResponse) }) | |
}) | |
req.on('error', (e) => { | |
reject(e) | |
}) | |
// If we have a payload to deliver | |
if(data != null) { | |
let delivery = "" | |
try { | |
// The atob() function decodes a string of data which has been encoded using Base64 encoding. | |
// InvalidCharacterError | |
// Thrown if encodedData is not valid base64. | |
delivery = atob(data) | |
} catch(e) { | |
delivery = data | |
} | |
if(process.env.DEBUG === "TRUE"){ | |
console.log(delivery) | |
} | |
req.write(delivery) | |
} | |
req.end() | |
}) | |
} | |
export const handler = async (event, context) => { | |
// Placeholder response object | |
let response = {} | |
if(process.env.DEBUG === "TRUE"){ | |
console.log(JSON.stringify(event)) | |
} | |
try { | |
// Placeholder request object | |
let requestOptions = { | |
timeout: 10, | |
rejectUnauthorized: false, | |
headers: {} | |
} | |
// If the requested url has a path, pass it on to the EC2 instance | |
if(event.requestContext.path) { | |
requestOptions.path = event.requestContext.path; | |
} | |
// Update the placeholder with the method from the request | |
if(event.httpMethod) { | |
requestOptions.method = event.httpMethod; | |
} | |
// Pass through the headers | |
requestOptions.headers = event.headers; | |
// Make the request | |
let result = await request(event, requestOptions, event.body) | |
// Update the response to pass on the status code | |
response.statusCode = result.statusCode | |
response.headers = result.headers | |
// set-cookie returns as an array from Kibana | |
// a check is made to see whether this key exists "set-cookie" | |
// and whether it is an array | |
// if true, the array is merged together | |
if("set-cookie" in result.headers && Array.isArray(result.headers["set-cookie"])){ | |
response.headers["set-cookie"] = result.headers["set-cookie"].join(",") | |
} | |
// Concatenate the buffer from the response | |
var buffer = Buffer.concat(result.body); | |
// If debugging, print out all the contents | |
// Some of the contents may be compressed | |
// Checking the content-encoding and decompressing | |
if(process.env.DEBUG === "TRUE"){ | |
console.log(response.headers) | |
if (response.headers['content-encoding'] == "gzip"){ | |
const dezziped = zlib.gunzipSync(buffer); | |
console.log(dezziped.toString()) | |
} else if (response.headers['content-encoding'] == "br") { | |
const dezziped = zlib.brotliDecompressSync(buffer); | |
console.log(dezziped.toString()) | |
} else { | |
console.log(buffer.toString()) | |
} | |
} | |
// Convert binaries to base64 for API Gateway | |
var responseBody = buffer.toString('base64'); | |
response.body = responseBody | |
// Let the API Gateway know the content is encoded | |
response.isBase64Encoded = true; | |
// Apply a long content cache date for static files | |
// Reduces the load on Lambda and lets the browser pull the files | |
// from local cache | |
if(event.requestContext.path.includes(".js") | |
|| event.requestContext.path.includes(".woff2") | |
|| event.requestContext.path.includes(".css") | |
|| event.requestContext.path.includes(".png")){ | |
response.headers["cache-control"] = "public, max-age=31557600"; | |
} | |
return response | |
} catch (e) { | |
console.log(e) | |
response.body = `Internal server error: ${e.code ? e.code : "Unspecified"}` | |
return response | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment