import { ServerResponse , IncomingMessage } from 'http' ; import { decode } from 'ufo' ; import getEtag from 'etag' ; import xss from 'xss' ; import { IPX } from './ipx' ; import { createError } from './utils' ; export interface IPXHRequest { url : string ; headers ? : Record < string , string > ; options ? : any ; } export interface IPXHResponse { statusCode : number ; statusMessage : string ; headers : Record < string , string > ; body : any ; } async function _handleRequest ( req , ipx ) { const res = { statusCode : 200 , statusMessage : '' , headers : { } , body : '' } ; const [ modifiersStr = '' , ... idSegments ] = req . url . substr ( 1 ) . split ( '/' ) ; const id = decode ( idSegments . join ( '/' ) ) ; if ( ! modifiersStr ) { throw createError ( 'Modifiers is missing in path: ' + req . url , 400 ) ; } if ( ! id || id === '/' ) { throw createError ( 'Resource id is missing: ' + req . url , 400 ) ; } const modifiers = Object . create ( null ) ; if ( modifiersStr !== '_' ) { for ( const p of modifiersStr . split ( ',' ) ) { const [ key , value = '' ] = p . split ( '_' ) ; modifiers [ key ] = decode ( value ) ; } } const img = ipx ( id , modifiers , req . options ) ; const src = await img . src ( ) ; if ( src . mtime ) { if ( req . headers [ 'if-modified-since' ] ) { if ( new Date ( req . headers [ 'if-modified-since' ] ) >= src . mtime ) { res . statusCode = 304 ; return res ; } } res . headers [ 'Last-Modified' ] = ( + src . mtime ) + '' ; } if ( src . maxAge !== undefined ) { res . headers [ 'Cache-Control' ] = `max-age=${ + src . maxAge }, public, s-maxage=${ + src . maxAge }` ; } const { data , format } = await img . data ( ) ; const etag = getEtag ( data ) ; res . headers . ETag = etag ; if ( etag && req . headers [ 'if-none-match' ] === etag ) { res . statusCode = 304 ; return res ; } if ( format ) { res . headers [ 'Content-Type' ] = `image/${ format }` ; } res . body = data ; return res ; } export function handleRequest ( req , ipx ) { return _handleRequest ( req , ipx ) . catch ( ( err ) => { const statusCode = parseInt ( err . statusCode ) || 500 ; const statusMessage = err . statusMessage ? xss ( err . statusMessage ) : `IPX Error (${ statusCode })` ; if ( process . env . NODE_ENV !== 'production' && statusCode === 500 ) { console . error ( err ) ; } return { statusCode , statusMessage , body : statusMessage , headers : { } } ; } ) ; } export function createIPXMiddleware ( ipx ) { return function IPXMiddleware ( req , res ) { handleRequest ( { url : req . url , headers : req . headers as any } , ipx ) . then ( ( _res ) => { res . statusCode = _res . statusCode ; res . statusMessage = _res . statusMessage ; for ( const name in _res . headers ) { res . setHeader ( name , _res . headers [ name ] ) ; } res . end ( _res . body ) ; } ) ; } ; }