@@ -169,8 +169,83 @@ const requestPrototype: Record<string, any> = {
169169 } ,
170170}
171171
172+ const handleError = ( e : unknown ) : Response =>
173+ new Response ( null , {
174+ status :
175+ e instanceof Error && ( e . name === 'TimeoutError' || e . constructor . name === 'TimeoutError' )
176+ ? 504 // timeout error emits 504 timeout
177+ : 500 ,
178+ } )
179+
180+ const responseViaResponseObject = async (
181+ res : Response | Promise < Response > ,
182+ outgoing : ServerResponse | Http2ServerResponse
183+ ) => {
184+ try {
185+ res = await res
186+ } catch ( e : unknown ) {
187+ res = handleError ( e )
188+ }
189+
190+ const resHeaderRecord : OutgoingHttpHeaders = { }
191+ const cookies = [ ]
192+ for ( const [ k , v ] of res . headers ) {
193+ if ( k === 'set-cookie' ) {
194+ cookies . push ( v )
195+ } else {
196+ resHeaderRecord [ k ] = v
197+ }
198+ }
199+ if ( cookies . length > 0 ) {
200+ resHeaderRecord [ 'set-cookie' ] = cookies
201+ }
202+
203+ if ( res . body ) {
204+ try {
205+ /**
206+ * If content-encoding is set, we assume that the response should be not decoded.
207+ * Else if transfer-encoding is set, we assume that the response should be streamed.
208+ * Else if content-length is set, we assume that the response content has been taken care of.
209+ * Else if x-accel-buffering is set to no, we assume that the response should be streamed.
210+ * Else if content-type is not application/json nor text/* but can be text/event-stream,
211+ * we assume that the response should be streamed.
212+ */
213+ if (
214+ resHeaderRecord [ 'transfer-encoding' ] ||
215+ resHeaderRecord [ 'content-encoding' ] ||
216+ resHeaderRecord [ 'content-length' ] ||
217+ // nginx buffering variant
218+ ( resHeaderRecord [ 'x-accel-buffering' ] &&
219+ regBuffer . test ( resHeaderRecord [ 'x-accel-buffering' ] as string ) ) ||
220+ ! regContentType . test ( resHeaderRecord [ 'content-type' ] as string )
221+ ) {
222+ outgoing . writeHead ( res . status , resHeaderRecord )
223+ await writeFromReadableStream ( res . body , outgoing )
224+ } else {
225+ const buffer = await res . arrayBuffer ( )
226+ resHeaderRecord [ 'content-length' ] = buffer . byteLength
227+ outgoing . writeHead ( res . status , resHeaderRecord )
228+ outgoing . end ( new Uint8Array ( buffer ) )
229+ }
230+ } catch ( e : unknown ) {
231+ const err = ( e instanceof Error ? e : new Error ( 'unknown error' , { cause : e } ) ) as Error & {
232+ code : string
233+ }
234+ if ( err . code === 'ERR_STREAM_PREMATURE_CLOSE' ) {
235+ console . info ( 'The user aborted a request.' )
236+ } else {
237+ console . error ( e )
238+ outgoing . destroy ( err )
239+ }
240+ }
241+ } else {
242+ outgoing . writeHead ( res . status , resHeaderRecord )
243+ outgoing . end ( )
244+ }
245+ }
246+
172247export const getRequestListener = ( fetchCallback : FetchCallback ) => {
173- return async (
248+ return (
174249 incoming : IncomingMessage | Http2ServerRequest ,
175250 outgoing : ServerResponse | Http2ServerResponse
176251 ) => {
@@ -182,79 +257,19 @@ export const getRequestListener = (fetchCallback: FetchCallback) => {
182257 } as unknown as Request
183258 Object . setPrototypeOf ( req , requestPrototype )
184259 try {
185- res = ( await fetchCallback ( req ) ) as Response
186- } catch ( e : unknown ) {
187- res = new Response ( null , { status : 500 } )
188- if ( e instanceof Error ) {
189- // timeout error emits 504 timeout
190- if ( e . name === 'TimeoutError' || e . constructor . name === 'TimeoutError' ) {
191- res = new Response ( null , { status : 504 } )
192- }
193- }
194- }
195-
196- if ( ( res as any ) . __cache ) {
197- const [ body , header ] = ( res as any ) . __cache
198- header [ 'content-length' ] ||= '' + Buffer . byteLength ( body )
199- outgoing . writeHead ( res . status , header )
200- outgoing . end ( body )
201- return
202- }
203-
204- const resHeaderRecord : OutgoingHttpHeaders = { }
205- const cookies = [ ]
206- for ( const [ k , v ] of res . headers ) {
207- if ( k === 'set-cookie' ) {
208- cookies . push ( v )
209- } else {
210- resHeaderRecord [ k ] = v
260+ res = fetchCallback ( req ) as Response | Promise < Response >
261+ if ( "__cache" in res ) {
262+ // response via cache
263+ const [ body , header ] = ( res as any ) . __cache
264+ header [ 'content-length' ] ||= '' + Buffer . byteLength ( body )
265+ outgoing . writeHead ( ( res as Response ) . status , header )
266+ outgoing . end ( body )
267+ return
211268 }
212- }
213- if ( cookies . length > 0 ) {
214- resHeaderRecord [ 'set-cookie' ] = cookies
269+ } catch ( e : unknown ) {
270+ res = handleError ( e )
215271 }
216272
217- if ( res . body ) {
218- try {
219- /**
220- * If content-encoding is set, we assume that the response should be not decoded.
221- * Else if transfer-encoding is set, we assume that the response should be streamed.
222- * Else if content-length is set, we assume that the response content has been taken care of.
223- * Else if x-accel-buffering is set to no, we assume that the response should be streamed.
224- * Else if content-type is not application/json nor text/* but can be text/event-stream,
225- * we assume that the response should be streamed.
226- */
227- if (
228- resHeaderRecord [ 'transfer-encoding' ] ||
229- resHeaderRecord [ 'content-encoding' ] ||
230- resHeaderRecord [ 'content-length' ] ||
231- // nginx buffering variant
232- ( resHeaderRecord [ 'x-accel-buffering' ] &&
233- regBuffer . test ( resHeaderRecord [ 'x-accel-buffering' ] as string ) ) ||
234- ! regContentType . test ( resHeaderRecord [ 'content-type' ] as string )
235- ) {
236- outgoing . writeHead ( res . status , resHeaderRecord )
237- await writeFromReadableStream ( res . body , outgoing )
238- } else {
239- const buffer = await res . arrayBuffer ( )
240- resHeaderRecord [ 'content-length' ] = buffer . byteLength
241- outgoing . writeHead ( res . status , resHeaderRecord )
242- outgoing . end ( new Uint8Array ( buffer ) )
243- }
244- } catch ( e : unknown ) {
245- const err = ( e instanceof Error ? e : new Error ( 'unknown error' , { cause : e } ) ) as Error & {
246- code : string
247- }
248- if ( err . code === 'ERR_STREAM_PREMATURE_CLOSE' ) {
249- console . info ( 'The user aborted a request.' )
250- } else {
251- console . error ( e )
252- outgoing . destroy ( err )
253- }
254- }
255- } else {
256- outgoing . writeHead ( res . status , resHeaderRecord )
257- outgoing . end ( )
258- }
273+ return responseViaResponseObject ( res , outgoing )
259274 }
260275}
0 commit comments