import type { IncomingMessage } from "http" ; import qs from "querystring" ; import { NetworkError , RestLessFieldNames } from "@fluidframework/server-services-client" ; import formidable from "formidable" ; export const decodeHeader = ( header ) : { name : string ; value : string ; } => { const [ name , value ] = header . split ( /: (.+)/ ) ; return { name , value } ; } ; type IncomingMessageEx = IncomingMessage & { body ? : any ; } ; /**
 * Server for communicating with a "RestLess" client.
 * Translates a "RestLess" HTTP request into a typical RESTful HTTP format
 */ export class RestLessServer { /**
     * If POST request has content-type application/x-www-urlencoded,
     * translates request from RestLess to standard REST in-place.
     */ public async translate ( request ) { if ( ! RestLessServer . isRestLess ( request ) ) { return request ; } if ( typeof request . body === "object" ) { this . translateRequestFields ( request , request . body ) ; this . parseRequestBody ( request ) ; } else if ( ! request . complete ) { await this . parseStreamRequestFormBody ( request ) ; } return request ; } private translateRequestFields ( request , fields ) { const methodOverride = fields [ RestLessFieldNames . Method ] as string ; request . method = methodOverride ; const headerField = fields [ RestLessFieldNames . Header ] ; let definedNewContentType = false ; const parseAndSetHeader = ( header ) => { const { name , value } = decodeHeader ( header ) ; if ( name . toLowerCase ( ) === "content-type" ) { definedNewContentType = true ; } request . headers [ name ] = value ; request . headers [ name . toLowerCase ( ) ] = value ; } ; if ( headerField instanceof Array ) { headerField . forEach ( parseAndSetHeader ) ; } else if ( typeof headerField === "string" ) { parseAndSetHeader ( headerField ) ; } if ( ! definedNewContentType ) { request . headers [ "content-type" ] = "application/json" ; } const bodyField = fields [ RestLessFieldNames . Body ] ; ( request as any ) . _body = true ; request . body = bodyField ; } private parseRequestBody ( request ) { if ( request . body ) { const contentType = request . headers [ "content-type" ] ?. toLowerCase ( ) ; if ( contentType . includes ( "application/json" ) ) { try { request . body = JSON . parse ( request . body ) ; } catch ( e ) { throw new NetworkError ( 400 , "Failed to parse json body" ) ; } } else if ( contentType . includes ( "application/x-www-form-urlencoded" ) ) { try { request . body = qs . parse ( request . body ) ; } catch ( e ) { throw new NetworkError ( 400 , "Failed to parse urlencoded body" ) ; } } } } private async parseStreamRequestFormBody ( request ) { return new Promise < void > ( ( resolve , reject ) => { const form = formidable ( { multiples : true } ) ; form . parse ( request , ( err , fields ) => { if ( err ) { reject ( err ) ; return ; } this . translateRequestFields ( request , fields ) ; this . parseRequestBody ( request ) ; resolve ( ) ; } ) ; } ) ; } private static isRestLess ( request ) { const isPost = request . method ?. toLowerCase ( ) === "post" ; const contentTypeContents = request . headers [ "content-type" ] ?. toLowerCase ( ) ?. split ( ";" ) ; const isForm = contentTypeContents ?. includes ( "application/x-www-form-urlencoded" ) ; const isRestLess = contentTypeContents ?. includes ( "restless" ) ; return isPost && isForm && isRestLess ; } }