Skip to content

Commit

Permalink
test(lambda-edge): added tests to lambda edge adapter
Browse files Browse the repository at this point in the history
  • Loading branch information
H4ad committed Feb 27, 2022
1 parent 86f790f commit 247630e
Show file tree
Hide file tree
Showing 5 changed files with 775 additions and 22 deletions.
72 changes: 52 additions & 20 deletions src/v2/adapters/aws/lambda-edge.adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ import {

//#endregion

/**
* The type alias to indicate where we get the default value of query string to create the request.
*/
export type DefaultQueryString =
CloudFrontRequestEvent['Records'][number]['cf']['request']['querystring'];

/**
* The type alias to indicate where we get the default value of path to create the request.
*/
Expand Down Expand Up @@ -68,6 +74,24 @@ export const DEFAULT_LAMBDA_EDGE_DISALLOWED_HEADERS: (string | RegExp)[] = [
'X-Real-IP',
];

/**
* The default max response size in bytes of viewer request and viewer response.
*
* @default 1024 * 40 = 40960 = 40KB
*
* {@link https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/cloudfront-limits.html Reference}
*/
export const DEFAULT_VIEWER_MAX_RESPONSE_SIZE_IN_BYTES = 1024 * 40;

/**
* The default max response size in bytes of origin request and origin response.
*
* @default 1024 * 1024 = 1048576 = 1MB
*
* {@link https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/cloudfront-limits.html Reference}
*/
export const DEFAULT_ORIGIN_MAX_RESPONSE_SIZE_IN_BYTES = 1024 * 1024;

/**
* The options to customize the {@link LambdaEdgeAdapter}.
*/
Expand All @@ -77,7 +101,7 @@ export interface LambdaEdgeAdapterOptions {
*
* {@link https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/cloudfront-limits.html Reference}
*
* @default 1024 * 40 = 1048576 = 1MB
* @default {@link DEFAULT_VIEWER_MAX_RESPONSE_SIZE_IN_BYTES}
*/
viewerMaxResponseSizeInBytes?: number;

Expand All @@ -86,7 +110,7 @@ export interface LambdaEdgeAdapterOptions {
*
* {@link https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/cloudfront-limits.html Reference}
*
* @default 1024 * 1024 = 40960 = 40KB
* @default {@link DEFAULT_ORIGIN_MAX_RESPONSE_SIZE_IN_BYTES}
*/
originMaxResponseSizeInBytes?: number;

Expand All @@ -103,6 +127,8 @@ export interface LambdaEdgeAdapterOptions {
/**
* Return the path to be used to create a request to the framework
*
* @note You MUST append the query params from {@link DefaultQueryString}, you can use the helper {@link getPathWithQueryStringParams}.
*
* @param event The event sent by the serverless
* @default The value from {@link DefaultForwardPath}
*/
Expand Down Expand Up @@ -142,6 +168,7 @@ export interface LambdaEdgeAdapterOptions {
*
* This adapter is not fully compatible with Lambda@edge supported by @vendia/serverless-express, the request body was modified to return {@link NewLambdaEdgeBody} instead {@link OldLambdaEdgeBody}.
* Also, the response has been modified to return entire body sent by the framework, in this form you MUST return the body from the framework in the format of {@link CloudFrontRequestResult}.
* And when we get an error during the forwarding to the framework, we call `resolver.fail` instead of trying to return status 500 like the old implementation was.
*
* {@link https://docs.aws.amazon.com/lambda/latest/dg/lambda-edge.html Lambda edge docs}
* {@link https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-event-structure.html Event Reference}
Expand Down Expand Up @@ -271,23 +298,23 @@ export class LambdaEdgeAdapter
JSON.stringify(response),
).length;

const isOrigiRequestOrResponse = this.isEventTypeOrigin(
props.event?.Records[0]?.cf.config,
const isOriginRequestOrResponse = this.isEventTypeOrigin(
props.event.Records[0].cf.config,
);
const maxSizeInBytes = isOrigiRequestOrResponse
const maxSizeInBytes = isOriginRequestOrResponse
? getDefaultIfUndefined(
this.options?.originMaxResponseSizeInBytes,
1024 * 1024,
DEFAULT_ORIGIN_MAX_RESPONSE_SIZE_IN_BYTES,
)
: getDefaultIfUndefined(
this.options?.viewerMaxResponseSizeInBytes,
1024 * 40,
DEFAULT_VIEWER_MAX_RESPONSE_SIZE_IN_BYTES,
);

if (responseToServiceBytes <= maxSizeInBytes) return response;

if (this.options?.onResponseSizeExceedLimit)
this.options?.onResponseSizeExceedLimit(response);
this.options.onResponseSizeExceedLimit(response);
else {
props.log.error(
`SERVERLESS_ADAPTER:LAMBDA_EDGE_ADAPTER: Max response size exceeded: ${responseToServiceBytes} of the max of ${maxSizeInBytes}.`,
Expand All @@ -303,19 +330,8 @@ export class LambdaEdgeAdapter
public onErrorWhileForwarding({
error,
resolver,
respondWithErrors,
}: OnErrorProps<CloudFrontRequestEvent, CloudFrontRequestResult>): void {
const body = respondWithErrors ? error.stack : undefined;

const errorResponse: CloudFrontResultResponse = {
status: '500',
body: JSON.stringify(body),
headers: {},
bodyEncoding: 'text',
statusDescription: 'Internal Server Error',
};

resolver.succeed(errorResponse);
resolver.fail(error);
}

//#endregion
Expand Down Expand Up @@ -357,6 +373,19 @@ export class LambdaEdgeAdapter
const parsedBody: CloudFrontResultResponse | CloudFrontRequest =
JSON.parse(body);

if (parsedBody.headers) {
parsedBody.headers = Object.keys(parsedBody.headers).reduce(
(acc, header) => {
if (this.shouldStripHeader(header)) return acc;

acc[header] = parsedBody.headers![header];

return acc;
},
{} as CloudFrontHeaders,
);
}

if (!shouldUseHeadersFromFramework) return parsedBody;

parsedBody.headers = this.getHeadersForCloudfrontResponse(frameworkHeaders);
Expand Down Expand Up @@ -406,6 +435,9 @@ export class LambdaEdgeAdapter
* @param headerKey The header that will be tested
*/
protected shouldStripHeader(headerKey: string): boolean {
if (this.options?.shouldStripHeader)
return this.options.shouldStripHeader(headerKey);

const headerKeyLowerCase = headerKey.toLowerCase();

for (const stripHeaderIf of this.cachedDisallowedHeaders) {
Expand Down
4 changes: 2 additions & 2 deletions src/v2/contracts/adapter.contract.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { SingleValueHeaders } from '../@types';
import { BothValueHeaders, SingleValueHeaders } from '../@types';
import { ILogger } from '../core';
import { ServerlessResponse } from '../network';
import { Resolver } from './resolver.contract';
Expand Down Expand Up @@ -92,7 +92,7 @@ export interface GetResponseAdapterProps<TEvent> {
/**
* The framework response headers
*/
headers: Record<string, string | string[]>;
headers: BothValueHeaders;

/**
* Indicates whether the response is base64 encoded or not
Expand Down
2 changes: 2 additions & 0 deletions src/v2/core/path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ export function getPathWithQueryStringParams(
| undefined
| null,
): string {
if (String(queryParams || '').length === 0) return path;

if (typeof queryParams === 'string') return `${path}?${queryParams}`;

const queryParamsString = getQueryParamsStringFromRecord(queryParams);
Expand Down
Loading

0 comments on commit 247630e

Please sign in to comment.