diff --git a/entrypoint.js b/entrypoint.js index 42b5b6a..9097dd2 100644 --- a/entrypoint.js +++ b/entrypoint.js @@ -5,6 +5,7 @@ import stream from 'node:stream' const kChunks = Symbol('chunks') const kCallback = Symbol('callback') +const kFromLoadBalancer = Symbol('loadBalancer') function shouldBase64Encode (headers) { /* Every other content-encoding than "identity" is higlhy likely to be binary */ @@ -25,6 +26,20 @@ function shouldBase64Encode (headers) { return true } +function createMultipleHeaderValues (headers) { + const result = Object.create(null) + + for (const key in headers) { + if (headers[key] instanceof Array) { + result[key] = headers[key] + } else { + result[key] = [headers[key]] + } + } + + return result +} + function extractMultipleHeaderValues (headers) { const result = Object.create(null) @@ -42,7 +57,9 @@ class LambdaSocket extends stream.Duplex { constructor (event) { super() - const sourceIp = event.requestContext.identity.sourceIp + this[kFromLoadBalancer] = (event.requestContext.elb != null) + + const sourceIp = event.requestContext.identity?.sourceIp ?? '127.0.0.1' const sourceFamily = net.isIPv6(sourceIp) ? 'IPv6' : 'IPv4' this.bufferSize = 0 @@ -139,14 +156,29 @@ class LambdaResponse extends http.ServerResponse { const headers = this.getHeaders() const base64Encode = shouldBase64Encode(headers) - const multiValueHeaders = extractMultipleHeaderValues(headers) - - const result = { - isBase64Encoded: base64Encode, - statusCode: this.statusCode, - headers, - multiValueHeaders, - body: Buffer.concat(this[kChunks]).toString(base64Encode ? 'base64' : 'utf8') + const body = Buffer.concat(this[kChunks]).toString(base64Encode ? 'base64' : 'utf8') + + let result + if (this.socket[kFromLoadBalancer]) { + const multiValueHeaders = createMultipleHeaderValues(headers) + + result = { + isBase64Encoded: base64Encode, + statusCode: this.statusCode, + statusDescription: `${this.statusCode} ${this.statusMessage}`, + multiValueHeaders, + body + } + } else { + const multiValueHeaders = extractMultipleHeaderValues(headers) + + result = { + isBase64Encoded: base64Encode, + statusCode: this.statusCode, + headers, + multiValueHeaders, + body + } } this.emit('finish') diff --git a/readme.md b/readme.md index c961c2d..dc899b9 100644 --- a/readme.md +++ b/readme.md @@ -81,6 +81,12 @@ scandium update --name=my-awesome-api By default, Scandium will set up an API Gateway that simply forwards all requests to the Lambda function. If you want to utilise the benefits of API Gateway fully, you can provide a Swagger file describing your API endpoints. Pass the `--swagger=my-api-definition.yml` to either the `create` or `update` command and Scandium will configure the API Gateway for you. +## Application Load Balancer + +The deployed entrypoint has support for being called from an application load balancer, as well as API Gateway. Note that "Multi value headers" must be turned on in the load balancer target group for this to work. + +There is currently no support in the Scandium CLI to automatically setup an ALB for you. + ## `prepare`/`build`-scripts Scandium has support for `prepare`/`build` scripts, if the script is present in the `package.json` it will make sure that the script is being run with full `devDependencies` installed. The final package being uploaded to Lambda will still only contain the production `dependencies`.