Skip to content

Instantly share code, notes, and snippets.

@mccaffers
Last active October 7, 2023 07:38
Show Gist options
  • Save mccaffers/e89886c588e122d90ec9d853510a50da to your computer and use it in GitHub Desktop.
Save mccaffers/e89886c588e122d90ec9d853510a50da to your computer and use it in GitHub Desktop.
AWS Lambda Request Forwarder
'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