diff --git a/src/adapter/aws-lambda/handler.ts b/src/adapter/aws-lambda/handler.ts index c3cb275b4..3e2e1c1c2 100644 --- a/src/adapter/aws-lambda/handler.ts +++ b/src/adapter/aws-lambda/handler.ts @@ -112,8 +112,9 @@ export const streamHandle = < ) => { return awslambda.streamifyResponse( async (event: LambdaEvent, responseStream: NodeJS.WritableStream, context: LambdaContext) => { + const processor = getProcessor(event) try { - const req = createRequest(event) + const req = processor.createRequest(event) const requestContext = getRequestContext(event) const res = await app.fetch(req, { @@ -156,7 +157,9 @@ export const handle = => { - const req = createRequest(event) + const processor = getProcessor(event) + + const req = processor.createRequest(event) const requestContext = getRequestContext(event) const res = await app.fetch(req, { @@ -165,105 +168,162 @@ export const handle = => { - const contentType = res.headers.get('content-type') - let isBase64Encoded = contentType && isContentTypeBinary(contentType) ? true : false +abstract class EventProcessor { + protected abstract getPath(event: E): string - if (!isBase64Encoded) { - const contentEncoding = res.headers.get('content-encoding') - isBase64Encoded = isContentEncodingBinary(contentEncoding) - } + protected abstract getMethod(event: E): string - const body = isBase64Encoded ? encodeBase64(await res.arrayBuffer()) : await res.text() + protected abstract getQueryString(event: E): string - const result: APIGatewayProxyResult = { - body: body, - headers: {}, - statusCode: res.status, - isBase64Encoded, - } + protected abstract getCookies(event: E, headers: Headers): void - setCookies(event, res, result) - res.headers.forEach((value, key) => { - result.headers[key] = value - }) + protected abstract setCookiesToResult(result: APIGatewayProxyResult, cookies: string[]): void - return result -} + createRequest(event: E): Request { + const queryString = this.getQueryString(event) + const domainName = + event.requestContext && 'domainName' in event.requestContext + ? event.requestContext.domainName + : event.headers?.['host'] ?? event.multiValueHeaders?.['host']?.[0] + const path = this.getPath(event) + const urlPath = `https://${domainName}${path}` + const url = queryString ? `${urlPath}?${queryString}` : urlPath -const createRequest = (event: LambdaEvent) => { - const queryString = extractQueryString(event) - const domainName = - event.requestContext && 'domainName' in event.requestContext - ? event.requestContext.domainName - : event.headers?.['host'] ?? event.multiValueHeaders?.['host']?.[0] - const path = isProxyEventV2(event) ? event.rawPath : event.path - const urlPath = `https://${domainName}${path}` - const url = queryString ? `${urlPath}?${queryString}` : urlPath - - const headers = new Headers() - getCookies(event, headers) - if (event.headers) { - for (const [k, v] of Object.entries(event.headers)) { - if (v) { - headers.set(k, v) + const headers = new Headers() + this.getCookies(event, headers) + if (event.headers) { + for (const [k, v] of Object.entries(event.headers)) { + if (v) { + headers.set(k, v) + } } } + if (event.multiValueHeaders) { + for (const [k, values] of Object.entries(event.multiValueHeaders)) { + if (values) { + values.forEach((v) => headers.append(k, v)) + } + } + } + + const method = this.getMethod(event) + const requestInit: RequestInit = { + headers, + method, + } + + if (event.body) { + requestInit.body = event.isBase64Encoded ? Buffer.from(event.body, 'base64') : event.body + } + + return new Request(url, requestInit) } - if (event.multiValueHeaders) { - for (const [k, values] of Object.entries(event.multiValueHeaders)) { - if (values) { - values.forEach((v) => headers.append(k, v)) + + async createResult(event: E, res: Response): Promise { + const contentType = res.headers.get('content-type') + let isBase64Encoded = contentType && isContentTypeBinary(contentType) ? true : false + + if (!isBase64Encoded) { + const contentEncoding = res.headers.get('content-encoding') + isBase64Encoded = isContentEncodingBinary(contentEncoding) + } + + const body = isBase64Encoded ? encodeBase64(await res.arrayBuffer()) : await res.text() + + const result: APIGatewayProxyResult = { + body: body, + headers: {}, + statusCode: res.status, + isBase64Encoded, + } + + this.setCookies(event, res, result) + res.headers.forEach((value, key) => { + result.headers[key] = value + }) + + return result + } + + setCookies = (event: LambdaEvent, res: Response, result: APIGatewayProxyResult) => { + if (res.headers.has('set-cookie')) { + const cookies = res.headers.get('set-cookie')?.split(', ') + if (Array.isArray(cookies)) { + this.setCookiesToResult(result, cookies) + res.headers.delete('set-cookie') } } } +} - const method = isProxyEventV2(event) ? event.requestContext.http.method : event.httpMethod - const requestInit: RequestInit = { - headers, - method, +const v2Processor = new (class EventV2Processor extends EventProcessor { + protected getPath(event: APIGatewayProxyEventV2): string { + return event.rawPath } - if (event.body) { - requestInit.body = event.isBase64Encoded ? Buffer.from(event.body, 'base64') : event.body + protected getMethod(event: APIGatewayProxyEventV2): string { + return event.requestContext.http.method } - return new Request(url, requestInit) -} + protected getQueryString(event: APIGatewayProxyEventV2): string { + return event.rawQueryString + } -const extractQueryString = (event: LambdaEvent) => { - return isProxyEventV2(event) - ? event.rawQueryString - : Object.entries(event.queryStringParameters || {}) - .filter(([, value]) => value) - .map(([key, value]) => `${key}=${value}`) - .join('&') -} + protected getCookies(event: APIGatewayProxyEventV2, headers: Headers): void { + if (Array.isArray(event.cookies)) { + headers.set('Cookie', event.cookies.join('; ')) + } + } -const getCookies = (event: LambdaEvent, headers: Headers) => { - if (isProxyEventV2(event) && Array.isArray(event.cookies)) { - headers.set('Cookie', event.cookies.join('; ')) + protected setCookiesToResult(result: APIGatewayProxyResult, cookies: string[]): void { + result.cookies = cookies } -} +})() -const setCookies = (event: LambdaEvent, res: Response, result: APIGatewayProxyResult) => { - if (res.headers.has('set-cookie')) { - const cookies = res.headers.get('set-cookie')?.split(', ') - if (Array.isArray(cookies)) { - if (isProxyEventV2(event)) { - result.cookies = cookies - } else { - result.multiValueHeaders = { - 'set-cookie': cookies, - } - } - res.headers.delete('set-cookie') +const v1Processor = new (class EventV1Processor extends EventProcessor< + Exclude +> { + protected getPath(event: Exclude): string { + return event.path + } + + protected getMethod(event: Exclude): string { + return event.httpMethod + } + + protected getQueryString(event: Exclude): string { + return Object.entries(event.queryStringParameters || {}) + .filter(([, value]) => value) + .map(([key, value]) => `${key}=${value}`) + .join('&') + } + + protected getCookies( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + event: Exclude, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + headers: Headers + ): void { + // nop + } + + protected setCookiesToResult(result: APIGatewayProxyResult, cookies: string[]): void { + result.multiValueHeaders = { + 'set-cookie': cookies, } } +})() + +const getProcessor = (event: LambdaEvent): EventProcessor => { + if (isProxyEventV2(event)) { + return v2Processor + } else { + return v1Processor + } } const isProxyEventV2 = (event: LambdaEvent): event is APIGatewayProxyEventV2 => {