11import { Readable } from 'node:stream' ;
22import type streamWeb from 'node:stream/web' ;
33import type {
4+ ALBEvent ,
5+ ALBResult ,
46 APIGatewayProxyEvent ,
57 APIGatewayProxyEventV2 ,
68 APIGatewayProxyResult ,
@@ -16,9 +18,10 @@ import type {
1618 V1Headers ,
1719 WebResponseToProxyResultOptions ,
1820} from '../types/rest.js' ;
19- import { HttpStatusCodes } from './constants.js' ;
21+ import { HttpStatusCodes , HttpStatusText } from './constants.js' ;
2022import { InvalidHttpMethodError } from './errors.js' ;
2123import {
24+ isALBEvent ,
2225 isAPIGatewayProxyEventV2 ,
2326 isBinaryResult ,
2427 isExtendedAPIGatewayProxyResult ,
@@ -47,11 +50,11 @@ const createBody = (body: string | null, isBase64Encoded: boolean) => {
4750 * Populates headers from single and multi-value header entries.
4851 *
4952 * @param headers - The Headers object to populate
50- * @param event - The API Gateway proxy event
53+ * @param event - The API Gateway proxy event or ALB event
5154 */
5255const populateV1Headers = (
5356 headers : Headers ,
54- event : APIGatewayProxyEvent
57+ event : APIGatewayProxyEvent | ALBEvent
5558) : void => {
5659 for ( const [ name , value ] of Object . entries ( event . headers ?? { } ) ) {
5760 if ( value !== undefined ) headers . set ( name , value ) ;
@@ -71,9 +74,12 @@ const populateV1Headers = (
7174 * Populates URL search parameters from single and multi-value query string parameters.
7275 *
7376 * @param url - The URL object to populate
74- * @param event - The API Gateway proxy event
77+ * @param event - The API Gateway proxy event or ALB event
7578 */
76- const populateV1QueryParams = ( url : URL , event : APIGatewayProxyEvent ) : void => {
79+ const populateV1QueryParams = (
80+ url : URL ,
81+ event : APIGatewayProxyEvent | ALBEvent
82+ ) : void => {
7783 for ( const [ name , value ] of Object . entries (
7884 event . queryStringParameters ?? { }
7985 ) ) {
@@ -154,14 +160,39 @@ const proxyEventV2ToWebRequest = (event: APIGatewayProxyEventV2): Request => {
154160} ;
155161
156162/**
157- * Converts an API Gateway proxy event (V1 or V2) to a Web API Request object.
163+ * Converts an ALB event to a Web API Request object.
164+ *
165+ * @param event - The ALB event
166+ * @returns A Web API Request object
167+ */
168+ const albEventToWebRequest = ( event : ALBEvent ) : Request => {
169+ const { httpMethod, path } = event ;
170+
171+ const headers = new Headers ( ) ;
172+ populateV1Headers ( headers , event ) ;
173+
174+ const hostname = headers . get ( 'Host' ) ?? 'localhost' ;
175+ const protocol = headers . get ( 'X-Forwarded-Proto' ) ?? 'https' ;
176+
177+ const url = new URL ( path , `${ protocol } ://${ hostname } /` ) ;
178+ populateV1QueryParams ( url , event ) ;
179+
180+ return new Request ( url . toString ( ) , {
181+ method : httpMethod ,
182+ headers,
183+ body : createBody ( event . body , event . isBase64Encoded ) ,
184+ } ) ;
185+ } ;
186+
187+ /**
188+ * Converts an API Gateway proxy event (V1 or V2) or ALB event to a Web API Request object.
158189 * Automatically detects the event version and calls the appropriate converter.
159190 *
160- * @param event - The API Gateway proxy event (V1 or V2)
191+ * @param event - The API Gateway proxy event (V1 or V2) or ALB event
161192 * @returns A Web API Request object
162193 */
163194const proxyEventToWebRequest = (
164- event : APIGatewayProxyEvent | APIGatewayProxyEventV2
195+ event : APIGatewayProxyEvent | APIGatewayProxyEventV2 | ALBEvent
165196) : Request => {
166197 if ( isAPIGatewayProxyEventV2 ( event ) ) {
167198 const method = event . requestContext . http . method . toUpperCase ( ) ;
@@ -170,10 +201,13 @@ const proxyEventToWebRequest = (
170201 }
171202 return proxyEventV2ToWebRequest ( event ) ;
172203 }
173- const method = event . requestContext . httpMethod . toUpperCase ( ) ;
204+ const method = event . httpMethod . toUpperCase ( ) ;
174205 if ( ! isHttpMethod ( method ) ) {
175206 throw new InvalidHttpMethodError ( method ) ;
176207 }
208+ if ( isALBEvent ( event ) ) {
209+ return albEventToWebRequest ( event ) ;
210+ }
177211 return proxyEventV1ToWebRequest ( event ) ;
178212} ;
179213
@@ -319,6 +353,42 @@ const webResponseToProxyResultV2 = async (
319353 return result ;
320354} ;
321355
356+ /**
357+ * Converts a Web API Response object to an ALB result.
358+ *
359+ * @param response - The Web API Response object
360+ * @param isBase64Encoded - Whether the response body should be base64 encoded (e.g., for binary or compressed content)
361+ * @returns An ALB result
362+ */
363+ const webResponseToALBResult = async (
364+ response : Response ,
365+ isBase64Encoded ?: boolean
366+ ) : Promise < ALBResult > => {
367+ const { headers, multiValueHeaders } = webHeadersToApiGatewayV1Headers (
368+ response . headers
369+ ) ;
370+
371+ const body = isBase64Encoded
372+ ? await responseBodyToBase64 ( response )
373+ : await response . text ( ) ;
374+
375+ const statusText = response . statusText || HttpStatusText [ response . status ] ;
376+
377+ const result : ALBResult = {
378+ statusCode : response . status ,
379+ statusDescription : `${ response . status } ${ statusText } ` ,
380+ headers,
381+ body,
382+ isBase64Encoded,
383+ } ;
384+
385+ if ( Object . keys ( multiValueHeaders ) . length > 0 ) {
386+ result . multiValueHeaders = multiValueHeaders ;
387+ }
388+
389+ return result ;
390+ } ;
391+
322392const webResponseToProxyResult = < T extends ResponseType > (
323393 response : Response ,
324394 responseType : T ,
@@ -330,6 +400,11 @@ const webResponseToProxyResult = <T extends ResponseType>(
330400 ResponseTypeMap [ T ]
331401 > ;
332402 }
403+ if ( responseType === 'ALB' ) {
404+ return webResponseToALBResult ( response , isBase64Encoded ) as Promise <
405+ ResponseTypeMap [ T ]
406+ > ;
407+ }
333408 return webResponseToProxyResultV2 ( response , isBase64Encoded ) as Promise <
334409 ResponseTypeMap [ T ]
335410 > ;
0 commit comments