| const getEventBody = (event) => Buffer.from(event.body, event.isBase64Encoded ? 'base64' : 'utf8'); | ||
| module.exports = getEventBody; |
| const getSocketPath = () => { | ||
| const socketPathSuffix = Math.random().toString(36).substring(2, 15); | ||
| return `/tmp/server-${socketPathSuffix}.sock`; | ||
| }; | ||
| module.exports = getSocketPath; |
| const handleError = (error, statusCode = 500) => | ||
| console.error(error) || { | ||
| body: error.message, | ||
| headers: {}, | ||
| isBase64Encoded: false, | ||
| statusCode, | ||
| }; | ||
| module.exports = handleError; |
| const binarycase = require('binary-case'); | ||
| const handleHeaders = (originalHeaders, bodyBuffer) => { | ||
| // HACK: modifies header casing to get around API Gateway's limitation of not allowing multiple | ||
| // headers with the same name, as discussed on the AWS Forum https://forums.aws.amazon.com/message.jspa?messageID=725953#725953 | ||
| delete originalHeaders.connection; | ||
| return { | ||
| 'content-length': String(bodyBuffer.length), | ||
| ...Object.keys(originalHeaders).reduce((result, header) => { | ||
| const value = originalHeaders[header]; | ||
| if (header.toLowerCase() === 'set-cookie' && Array.isArray(value)) { | ||
| return { | ||
| ...result, | ||
| ...value.reduce( | ||
| (headers, cookie, index) => ({ ...headers, [binarycase(header, index + 1)]: cookie }), | ||
| {}, | ||
| ), | ||
| }; | ||
| } | ||
| return { | ||
| ...result, | ||
| [header.toLowerCase()]: Array.isArray(value) ? value.join(',') : value, | ||
| }; | ||
| }, {}), | ||
| }; | ||
| }; | ||
| module.exports = handleHeaders; |
| const handleHeaders = require('./handleHeaders'); | ||
| const handleRequestResponse = (response) => | ||
| new Promise((resolve, reject) => { | ||
| const respBodyChunks = []; | ||
| response.on('data', (chunk) => { | ||
| respBodyChunks.push(Buffer.from(chunk)); | ||
| }); | ||
| response.on('error', reject); | ||
| response.on('end', () => { | ||
| const bodyBuffer = Buffer.concat(respBodyChunks); | ||
| resolve({ | ||
| body: bodyBuffer.toString('base64'), | ||
| headers: handleHeaders(response.headers, bodyBuffer), | ||
| isBase64Encoded: true, | ||
| statusCode: response.statusCode || 200, | ||
| }); | ||
| }); | ||
| }); | ||
| module.exports = handleRequestResponse; |
| /* | ||
| This creates an http server (for use with express, koa, etc) and serve it through lambda | ||
| References: | ||
| https://github.com/zeit/now-builders/blob/fa411a5e4cb10de2ceafa30d335652a16d1963f4/packages/now-node-bridge/src/bridge.ts | ||
| https://github.com/awslabs/aws-serverless-express/blob/master/src/index.js | ||
| */ | ||
| const proxyEvent = require('./proxyEvent'); | ||
| const getSocketPath = require('./getSocketPath'); | ||
| const http = require('http'); | ||
| process.on('unhandledRejection', (err) => { | ||
| console.error('Unhandled rejection:', err); | ||
| process.exit(1); // eslint-disable-line no-process-exit | ||
| }); | ||
| const server = (requestListener) => { | ||
| const server = http.createServer(requestListener); | ||
| const socketPath = getSocketPath(); | ||
| let serverIsListenting = false; | ||
| server.on('listening', () => { | ||
| serverIsListenting = true; | ||
| }); | ||
| server.on('close', () => { | ||
| serverIsListenting = false; | ||
| }); | ||
| return async (event) => { | ||
| if (!serverIsListenting) { | ||
| await new Promise((resolve) => server.listen(socketPath).on('listening', resolve)); | ||
| } | ||
| const response = await proxyEvent(event, socketPath); | ||
| server.close(); | ||
| return response; | ||
| }; | ||
| }; | ||
| module.exports = server; |
| const getEventBody = require('./getEventBody'); | ||
| const handleError = require('./handleError'); | ||
| const handleRequestResponse = require('./handleRequestResponse'); | ||
| const http = require('http'); | ||
| const url = require('url'); | ||
| const proxyEvent = (event, socketPath) => | ||
| new Promise((resolve) => { | ||
| const requestOptions = { | ||
| headers: event.headers, | ||
| method: event.httpMethod, | ||
| path: url.format({ pathname: event.path, query: event.queryStringParameters }), | ||
| socketPath, | ||
| }; | ||
| const req = http.request(requestOptions, async (res) => { | ||
| try { | ||
| const response = await handleRequestResponse(res); | ||
| resolve(response); | ||
| } catch (error) { | ||
| resolve(handleError(error)); | ||
| } | ||
| }); | ||
| if (event.body) { | ||
| req.write(getEventBody(event)); | ||
| } | ||
| req.on('error', (error) => resolve(handleError(error, 502))); | ||
| req.end(); | ||
| }); | ||
| module.exports = proxyEvent; |
+11
-8
@@ -8,2 +8,3 @@ { | ||
| "dependencies": { | ||
| "binary-case": "^1.1.4", | ||
| "cookie": "^0.4.0", | ||
@@ -15,8 +16,9 @@ "path-to-regexp": "^6.1.0" | ||
| "eslint": "6.8.0", | ||
| "eslint-config-prettier": "6.9.0", | ||
| "eslint-plugin-jest": "23.6.0", | ||
| "husky": "4.2.1", | ||
| "jest": "25.1.0", | ||
| "lint-staged": "10.0.3", | ||
| "prettier": "1.19.1" | ||
| "eslint-config-prettier": "6.10.1", | ||
| "eslint-plugin-jest": "23.8.2", | ||
| "express": "4.17.1", | ||
| "husky": "4.2.3", | ||
| "jest": "25.2.7", | ||
| "lint-staged": "10.1.2", | ||
| "prettier": "2.0.2" | ||
| }, | ||
@@ -29,3 +31,4 @@ "engines": { | ||
| "helpers/", | ||
| "router/" | ||
| "router/", | ||
| "server/" | ||
| ], | ||
@@ -55,3 +58,3 @@ "homepage": "https://github.com/Prefinem/lambdify", | ||
| }, | ||
| "version": "4.3.0" | ||
| "version": "4.3.1" | ||
| } |
+46
-0
@@ -325,1 +325,47 @@ # lambdify | ||
| - `overrides` is an object that will override anything else in the original event | ||
| # Lambda Web Server / Express | ||
| Run a webserver on your lambda instance! Express, Koa, etc should now run without a hitch. | ||
| This is very similar to [aws-serverless-express](https://github.com/awslabs/aws-serverless-express). The main point being, it's a little more clean and simple to understand | ||
| ## Example | ||
| Your Lambda Handler (usually `index.js`) | ||
| ```js | ||
| const lambdaServer = require('lambdify/server'); | ||
| const app = require('./app'); | ||
| exports.handler = lambdaServer(app); | ||
| ``` | ||
| `app.js` | ||
| ```js | ||
| const express = require('express'); | ||
| const app = express(); | ||
| app.get('/', (req, res) => res.send('Hello World!')); | ||
| module.export = app; | ||
| ``` | ||
| And for testing locally (I like to name it `server.js`) | ||
| ```js | ||
| const app = require('./app.js'); | ||
| app.listen(3000, () => console.log('Now Listening')); | ||
| ``` | ||
| ## Why not use `aws-serverless-express` | ||
| This focuses on simplicity and standard use cases. It also doesn't worry about legacy implementations of lambda callback / context use and is focused on Node 10 / 12 support only with an emphasis on async / await code | ||
| ## Other inspiration | ||
| This was originally developed to handle next.js SSR on AWS Lambda. Officially there is no support and although packages like [serverless-nextjs-plugin](https://www.npmjs.com/package/serverless-nextjs-plugin) exist, they require more packages and the serverless deployment system. | ||
| Example of Next.js package soon to come |
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
29853
22.66%54
14.89%504
31.59%371
14.15%3
50%8
14.29%2
Infinity%+ Added
+ Added