UNPKG

@h4ad/serverless-adapter

Version:

Run REST APIs and other web applications using your existing Node.js application framework (NestJS, Express, Koa, Hapi, Fastify and many others), on top of AWS, Azure, Digital Ocean and many other clouds.

1 lines 7.65 kB
{"version":3,"sources":["../../../src/frameworks/cors/cors.framework.ts"],"sourcesContent":["//#region Imports\n\nimport type { IncomingMessage, ServerResponse } from 'http';\nimport cors, { type CorsOptions } from 'cors';\nimport type { FrameworkContract } from '../../contracts';\nimport { getDefaultIfUndefined } from '../../core';\n\n//#endregion\n\n/**\n * The options to customize {@link CorsFramework}\n *\n * @breadcrumb Frameworks / CorsFramework\n * @public\n */\nexport type CorsFrameworkOptions = CorsOptions & {\n /**\n * Send error 403 when cors is invalid. From what I read in `cors`, `fastify/cors` and [this problem](https://stackoverflow.com/questions/57212248/why-is-http-request-been-processed-in-action-even-when-cors-is-not-enabled)\n * it is normal to process the request even if the origin is invalid.\n * So this option will respond with error if this method was called from an invalid origin (or not allowed method) like [access control lib](https://github.com/primus/access-control/blob/master/index.js#L95-L115) .\n *\n * @defaultValue true\n */\n forbiddenOnInvalidOriginOrMethod?: boolean;\n};\n\n/**\n * The framework that handles cors for your api without relying on internals of the framework\n *\n * @example\n * ```typescript\n * import express from 'express';\n * import { ServerlessAdapter } from '@h4ad/serverless-adapter';\n * import { ExpressFramework } from '@h4ad/serverless-adapter/lib/frameworks/express';\n * import { CorsFramework } from '@h4ad/serverless-adapter/lib/frameworks/cors';\n *\n * const expressFramework = new ExpressFramework();\n * const options: CorsOptions = {}; // customize the options\n * const framework = new CorsFramework(expressFramework, options);\n *\n * export const handler = ServerlessAdapter.new(null)\n * .setFramework(framework)\n * // set other configurations and then build\n * .build();\n * ```\n *\n * @breadcrumb Frameworks / CorsFramework\n * @public\n */\nexport class CorsFramework<TApp> implements FrameworkContract<TApp> {\n //#region Constructor\n\n /**\n * Default Constructor\n */\n constructor(\n protected readonly framework: FrameworkContract<TApp>,\n protected readonly options?: CorsFrameworkOptions,\n ) {\n this.cachedCorsInstance = cors(this.options);\n }\n\n //#endregion\n\n /**\n * All cors headers that can be added by cors package\n */\n protected readonly corsHeaders: string[] = [\n 'Access-Control-Max-Age',\n 'Access-Control-Expose-Headers',\n 'Access-Control-Allow-Headers',\n 'Access-Control-Request-Headers',\n 'Access-Control-Allow-Credentials',\n 'Access-Control-Allow-Methods',\n 'Access-Control-Allow-Origin',\n ];\n\n /**\n * The cached instance of cors\n */\n protected readonly cachedCorsInstance: ReturnType<typeof cors>;\n\n //#region Public Methods\n\n /**\n * {@inheritDoc}\n */\n public sendRequest(\n app: TApp,\n request: IncomingMessage,\n response: ServerResponse,\n ): void {\n this.cachedCorsInstance(\n request,\n response,\n this.onCorsNext(app, request, response),\n );\n }\n\n //#endregion\n\n //#region Protected Methods\n\n /**\n * Handle next execution called by the cors package\n */\n protected onCorsNext(\n app: TApp,\n request: IncomingMessage,\n response: ServerResponse,\n ): () => void {\n return () => {\n this.formatHeaderValuesAddedByCorsPackage(response);\n\n const errorOnInvalidOrigin = getDefaultIfUndefined(\n this.options?.forbiddenOnInvalidOriginOrMethod,\n true,\n );\n\n if (errorOnInvalidOrigin) {\n const allowedOrigin = response.getHeader('access-control-allow-origin');\n const isInvalidOrigin = this.isInvalidOriginOrMethodIsNotAllowed(\n request,\n allowedOrigin,\n );\n\n if (isInvalidOrigin) {\n response.statusCode = 403;\n response.setHeader('Content-Type', 'text/plain');\n response.end(\n [\n 'Invalid HTTP Access Control (CORS) request:',\n ` Origin: ${request.headers.origin}`,\n ` Method: ${request.method}`,\n ].join('\\n'),\n );\n\n return;\n }\n }\n\n this.framework.sendRequest(app, request, response);\n };\n }\n\n /**\n * Format the headers to be standardized with the rest of the library, such as ApiGatewayV2.\n * Also, some frameworks don't support headers as an array, so we need to format the values.\n */\n protected formatHeaderValuesAddedByCorsPackage(\n response: ServerResponse,\n ): void {\n for (const corsHeader of this.corsHeaders) {\n const value = response.getHeader(corsHeader);\n\n if (value === undefined) continue;\n\n response.removeHeader(corsHeader);\n response.setHeader(\n corsHeader.toLowerCase(),\n Array.isArray(value) ? value.join(',') : value,\n );\n }\n }\n\n /**\n * Check if the origin is invalid or if the method is not allowed.\n * Highly inspired by [access-control](https://github.com/primus/access-control/blob/master/index.js#L95-L115)\n */\n protected isInvalidOriginOrMethodIsNotAllowed(\n request: IncomingMessage,\n allowedOrigin: number | string | string[] | undefined,\n ): boolean {\n if (!allowedOrigin) return true;\n\n if (\n !!request.headers.origin &&\n allowedOrigin !== '*' &&\n request.headers.origin !== allowedOrigin\n )\n return true;\n\n const notPermitedInMethods =\n this.options &&\n Array.isArray(this.options.methods) &&\n this.options.methods.every(\n m => m.toLowerCase() !== request.method?.toLowerCase(),\n );\n const differentMethod =\n this.options &&\n typeof this.options.methods === 'string' &&\n this.options.methods\n .split(',')\n .every(m => m.trim().toLowerCase() !== request.method?.toLowerCase());\n\n if (this.options?.methods && (notPermitedInMethods || differentMethod))\n return true;\n\n return false;\n }\n\n //#endregion\n}\n"],"mappings":"0FAGA,OAAOA,MAAgC,OA8ChC,IAAMC,EAAN,KAA6D,CAMlE,YACqBC,EACAC,EACnB,CAFmB,eAAAD,EACA,aAAAC,EAEnB,KAAK,mBAAqBC,EAAK,KAAK,OAAO,CAC7C,CA5DF,MAiDoE,CAAAC,EAAA,sBAkB/C,YAAwB,CACzC,yBACA,gCACA,+BACA,iCACA,mCACA,+BACA,6BACF,EAKmB,mBAOZ,YACLC,EACAC,EACAC,EACM,CACN,KAAK,mBACHD,EACAC,EACA,KAAK,WAAWF,EAAKC,EAASC,CAAQ,CACxC,CACF,CASU,WACRF,EACAC,EACAC,EACY,CACZ,MAAO,IAAM,CAQX,GAPA,KAAK,qCAAqCA,CAAQ,EAErBC,EAC3B,KAAK,SAAS,iCACd,EACF,EAE0B,CACxB,IAAMC,EAAgBF,EAAS,UAAU,6BAA6B,EAMtE,GALwB,KAAK,oCAC3BD,EACAG,CACF,EAEqB,CACnBF,EAAS,WAAa,IACtBA,EAAS,UAAU,eAAgB,YAAY,EAC/CA,EAAS,IACP,CACE,8CACA,aAAaD,EAAQ,QAAQ,MAAM,GACnC,aAAaA,EAAQ,MAAM,EAC7B,EAAE,KAAK;AAAA,CAAI,CACb,EAEA,MACF,CACF,CAEA,KAAK,UAAU,YAAYD,EAAKC,EAASC,CAAQ,CACnD,CACF,CAMU,qCACRA,EACM,CACN,QAAWG,KAAc,KAAK,YAAa,CACzC,IAAMC,EAAQJ,EAAS,UAAUG,CAAU,EAEvCC,IAAU,SAEdJ,EAAS,aAAaG,CAAU,EAChCH,EAAS,UACPG,EAAW,YAAY,EACvB,MAAM,QAAQC,CAAK,EAAIA,EAAM,KAAK,GAAG,EAAIA,CAC3C,EACF,CACF,CAMU,oCACRL,EACAG,EACS,CAGT,GAFI,CAACA,GAGDH,EAAQ,QAAQ,QAClBG,IAAkB,KAClBH,EAAQ,QAAQ,SAAWG,EAE3B,MAAO,GAET,IAAMG,EACJ,KAAK,SACL,MAAM,QAAQ,KAAK,QAAQ,OAAO,GAClC,KAAK,QAAQ,QAAQ,MACnBC,GAAKA,EAAE,YAAY,IAAMP,EAAQ,QAAQ,YAAY,CACvD,EACIQ,EACJ,KAAK,SACL,OAAO,KAAK,QAAQ,SAAY,UAChC,KAAK,QAAQ,QACV,MAAM,GAAG,EACT,MAAMD,GAAKA,EAAE,KAAK,EAAE,YAAY,IAAMP,EAAQ,QAAQ,YAAY,CAAC,EAExE,MAAI,QAAK,SAAS,UAAYM,GAAwBE,GAIxD,CAGF","names":["cors","CorsFramework","framework","options","cors","__name","app","request","response","getDefaultIfUndefined","allowedOrigin","corsHeader","value","notPermitedInMethods","m","differentMethod"]}