Skip to content

Commit 7f3f52c

Browse files
committed
supporting http-lambda-proxy
1 parent ebc20c0 commit 7f3f52c

File tree

5 files changed

+104
-90
lines changed

5 files changed

+104
-90
lines changed

README.md

Lines changed: 18 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ service.start(3000)
4242
// If omitted, restana is used as default HTTP framework
4343
server,
4444
// Optional restana library configuration (https://www.npmjs.com/package/restana#configuration)
45-
//
4645
// Please note that if "server" is provided, this settings are ignored.
4746
restana: {},
4847
// Optional global middlewares in the format: (req, res, next) => next()
@@ -51,23 +50,36 @@ service.start(3000)
5150
// Optional global value for routes "pathRegex". Default value: '/*'
5251
pathRegex: '/*',
5352
// Optional global requests timeout value (given in milliseconds). Default value: '0' (DISABLED)
53+
// Ignored if proxyType = 'lambda'
5454
timeout: 0,
5555
// Optional "target" value that overrides the routes "target" config value. Feature intended for testing purposes.
5656
targetOverride: "https://yourdev.api-gateway.com",
5757

5858
// HTTP proxy
5959
routes: [{
60+
// Optional proxy type definition. Supported values: http, lambda
61+
// Default value: http
62+
proxyType: 'http'
6063
// Optional `fast-proxy` library configuration (https://www.npmjs.com/package/fast-proxy#options)
6164
// base parameter defined as the route target. Default value: {}
65+
// This settings apply only when proxyType = 'http'
6266
fastProxy: {},
67+
// Optional `http-lambda-proxy` library configuration (https://www.npmjs.com/package/http-lambda-proxy#options)
68+
// The 'target' parameter is extracted from route.target, default region = 'eu-central-1'
69+
// This settings apply only when proxyType = 'lambda'
70+
lambdaProxy: {
71+
region: 'eu-central-1'
72+
},
6373
// Optional proxy handler function. Default value: (req, res, url, proxy, proxyOpts) => proxy(req, res, url, proxyOpts)
6474
proxyHandler: () => {},
6575
// Optional flag to indicate if target uses the HTTP2 protocol. Default value: false
76+
// This setting apply only when proxyType = 'http'
6677
http2: false,
6778
// Optional path matching regex. Default value: '/*'
6879
// In order to disable the 'pathRegex' at all, you can use an empty string: ''
6980
pathRegex: '/*',
7081
// Optional service requests timeout value (given in milliseconds). Default value: '0' (DISABLED)
82+
// This setting apply only when proxyType = 'http'
7183
timeout: 0,
7284
// route prefix
7385
prefix: '/public',
@@ -79,7 +91,8 @@ service.start(3000)
7991
},
8092
// Optional "prefix rewrite" before request is forwarded. Default value: ''
8193
prefixRewrite: '',
82-
// Remote HTTP server URL to forward the request
94+
// Remote HTTP server URL to forward the request.
95+
// If proxyType = 'lambda', the value is the name of the Lambda function, version, or alias.
8396
target: 'http://localhost:3000',
8497
// Optional HTTP methods to limit the requests proxy to certain verbs only
8598
// Supported HTTP methods: ['GET', 'DELETE', 'PATCH', 'POST', 'PUT', 'HEAD', 'OPTIONS', 'TRACE']
@@ -99,54 +112,14 @@ service.start(3000)
99112
// ...
100113
}
101114

102-
// other options allowed https://www.npmjs.com/package/fast-proxy#opts
115+
// if proxyType= 'http', other options allowed https://www.npmjs.com/package/fast-proxy#opts
103116
}
104117
}]
105118
}
106119
```
107-
### onResponse Hook default implementation
108-
For developers reference, next we describe how the default `onResponse` hook looks like:
109-
```js
110-
const pump = require('pump')
111-
const toArray = require('stream-to-array')
112-
const TRANSFER_ENCODING_HEADER_NAME = 'transfer-encoding'
113-
114-
const onResponse = async (req, res, stream) => {
115-
const chunked = stream.headers[TRANSFER_ENCODING_HEADER_NAME]
116-
? stream.headers[TRANSFER_ENCODING_HEADER_NAME].endsWith('chunked')
117-
: false
118-
119-
if (req.headers.connection === 'close' && chunked) {
120-
try {
121-
// remove transfer-encoding header
122-
const transferEncoding = stream.headers[TRANSFER_ENCODING_HEADER_NAME].replace(/(,( )?)?chunked/, '')
123-
if (transferEncoding) {
124-
res.setHeader(TRANSFER_ENCODING_HEADER_NAME, transferEncoding)
125-
} else {
126-
res.removeHeader(TRANSFER_ENCODING_HEADER_NAME)
127-
}
128-
129-
if (!stream.headers['content-length']) {
130-
// pack all pieces into 1 buffer to calculate content length
131-
const resBuffer = Buffer.concat(await toArray(stream))
132-
133-
// add content-length header and send the merged response buffer
134-
res.setHeader('content-length', '' + Buffer.byteLength(resBuffer))
135-
res.statusCode = stream.statusCode
136-
res.end(resBuffer)
120+
### onResponse hooks default implementation
121+
For developers reference, default hooks implementation are located in `lib/default-hooks.js` file.
137122

138-
return
139-
}
140-
} catch (err) {
141-
res.statusCode = 500
142-
res.end(err.message)
143-
}
144-
}
145-
146-
res.statusCode = stream.statusCode
147-
pump(stream, res)
148-
}
149-
```
150123
## The "*GET /services.json*" endpoint
151124
Since version `1.3.5` the gateway exposes minimal documentation about registered services at: `GET /services.json`
152125

index.js

Lines changed: 25 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,10 @@
22

33
const fastProxy = require('fast-proxy')
44
const restana = require('restana')
5-
const pump = require('pump')
6-
const toArray = require('stream-to-array')
75
const defaultProxyHandler = (req, res, url, proxy, proxyOpts) => proxy(req, res, url, proxyOpts)
86
const DEFAULT_METHODS = require('restana/libs/methods').filter(method => method !== 'all')
97
const send = require('@polka/send-type')
10-
const TRANSFER_ENCODING_HEADER_NAME = 'transfer-encoding'
8+
const PROXY_TYPES = ['http', 'lambda']
119

1210
const gateway = (opts) => {
1311
opts = Object.assign({
@@ -37,6 +35,15 @@ const gateway = (opts) => {
3735
route.prefixRewrite = ''
3836
}
3937

38+
// retrieve proxy type
39+
const { proxyType = 'http' } = route
40+
if (!PROXY_TYPES.includes(proxyType)) {
41+
throw new Error('Unsupported proxy type, expecting one of ' + PROXY_TYPES.toString())
42+
}
43+
44+
// retrieve default hooks for proxy
45+
const { onRequestNoOp, onResponse } = require('./lib/default-hooks')[proxyType]
46+
4047
// populating required NOOPS
4148
route.hooks = route.hooks || {}
4249
route.hooks.onRequest = route.hooks.onRequest || onRequestNoOp
@@ -49,11 +56,21 @@ const gateway = (opts) => {
4956
route.pathRegex = undefined === route.pathRegex ? opts.pathRegex : String(route.pathRegex)
5057

5158
// instantiate route proxy
52-
const { proxy } = fastProxy({
53-
base: opts.targetOverride || route.target,
54-
http2: !!route.http2,
55-
...(opts.fastProxy)
56-
})
59+
let proxy
60+
if (proxyType === 'http') {
61+
proxy = fastProxy({
62+
base: opts.targetOverride || route.target,
63+
http2: !!route.http2,
64+
...(opts.fastProxy)
65+
}).proxy
66+
} else if (proxyType === 'lambda') {
67+
proxy = require('http-lambda-proxy')({
68+
target: opts.targetOverride || route.target,
69+
...(route.lambdaProxy || {
70+
region: 'eu-central-1'
71+
})
72+
})
73+
}
5774

5875
// route proxy handler function
5976
const proxyHandler = route.proxyHandler || defaultProxyHandler
@@ -102,41 +119,4 @@ const handler = (route, proxy, proxyHandler) => async (req, res, next) => {
102119
}
103120
}
104121

105-
const onRequestNoOp = (req, res) => { }
106-
const onResponse = async (req, res, stream) => {
107-
const chunked = stream.headers[TRANSFER_ENCODING_HEADER_NAME]
108-
? stream.headers[TRANSFER_ENCODING_HEADER_NAME].endsWith('chunked')
109-
: false
110-
111-
if (req.headers.connection === 'close' && chunked) {
112-
try {
113-
// remove transfer-encoding header
114-
const transferEncoding = stream.headers[TRANSFER_ENCODING_HEADER_NAME].replace(/(,( )?)?chunked/, '')
115-
if (transferEncoding) {
116-
res.setHeader(TRANSFER_ENCODING_HEADER_NAME, transferEncoding)
117-
} else {
118-
res.removeHeader(TRANSFER_ENCODING_HEADER_NAME)
119-
}
120-
121-
if (!stream.headers['content-length']) {
122-
// pack all pieces into 1 buffer to calculate content length
123-
const resBuffer = Buffer.concat(await toArray(stream))
124-
125-
// add content-length header and send the merged response buffer
126-
res.setHeader('content-length', '' + Buffer.byteLength(resBuffer))
127-
res.statusCode = stream.statusCode
128-
res.end(resBuffer)
129-
130-
return
131-
}
132-
} catch (err) {
133-
res.statusCode = 500
134-
res.end(err.message)
135-
}
136-
}
137-
138-
res.statusCode = stream.statusCode
139-
pump(stream, res)
140-
}
141-
142122
module.exports = gateway

lib/default-hooks.js

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
'use strict'
2+
3+
const pump = require('pump')
4+
const toArray = require('stream-to-array')
5+
const TRANSFER_ENCODING_HEADER_NAME = 'transfer-encoding'
6+
7+
module.exports = {
8+
lambda: {
9+
onRequestNoOp (req, res) { },
10+
onResponse (req, res, response) {
11+
const { statusCode, body } = JSON.parse(response.Payload)
12+
13+
res.statusCode = statusCode
14+
res.end(body)
15+
}
16+
},
17+
http: {
18+
onRequestNoOp (req, res) { },
19+
async onResponse (req, res, stream) {
20+
const chunked = stream.headers[TRANSFER_ENCODING_HEADER_NAME]
21+
? stream.headers[TRANSFER_ENCODING_HEADER_NAME].endsWith('chunked')
22+
: false
23+
24+
if (req.headers.connection === 'close' && chunked) {
25+
try {
26+
// remove transfer-encoding header
27+
const transferEncoding = stream.headers[TRANSFER_ENCODING_HEADER_NAME].replace(/(,( )?)?chunked/, '')
28+
if (transferEncoding) {
29+
res.setHeader(TRANSFER_ENCODING_HEADER_NAME, transferEncoding)
30+
} else {
31+
res.removeHeader(TRANSFER_ENCODING_HEADER_NAME)
32+
}
33+
34+
if (!stream.headers['content-length']) {
35+
// pack all pieces into 1 buffer to calculate content length
36+
const resBuffer = Buffer.concat(await toArray(stream))
37+
38+
// add content-length header and send the merged response buffer
39+
res.setHeader('content-length', '' + Buffer.byteLength(resBuffer))
40+
res.statusCode = stream.statusCode
41+
res.end(resBuffer)
42+
43+
return
44+
}
45+
} catch (err) {
46+
res.statusCode = 500
47+
res.end(err.message)
48+
}
49+
}
50+
51+
res.statusCode = stream.statusCode
52+
pump(stream, res)
53+
}
54+
}
55+
}

lib/proxy-factory.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
'use strict'
2+
3+
module.exports = ({ proxyType, opts, route }) => {
4+
5+
}

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
"express-jwt": "^5.3.1",
4141
"express-rate-limit": "^5.1.1",
4242
"helmet": "^3.21.3",
43+
"http-lambda-proxy": "^1.0.1",
4344
"mocha": "^7.1.0",
4445
"nyc": "^15.0.0",
4546
"opossum": "^4.2.4",

0 commit comments

Comments
 (0)