import * as querystring from "querystring" ; import { Socket } from "net" ; export class WebhookRequest { public rawContents : Buffer ; public method : string ; public uri : string ; public body : string ; public rawBody : Buffer ; public queryParameters : { [ id ] : string | string [ ] ; } = { } ; public headers : { [ id ] : string ; } ; private requestID : number ; public constructor ( public sourceSocket ? ) { this . rawContents = Buffer . from ( "" ) ; this . rawBody = Buffer . from ( "" ) ; this . body = "" ; this . requestID = new Date ( ) . getTime ( ) ; if ( this . sourceSocket === undefined ) { this . sourceSocket = null ; } } public static fromBuffer ( sourceSocket , payload , id ? ) { const webhookRequest = new WebhookRequest ( sourceSocket ) ; webhookRequest . append ( payload ) ; webhookRequest . requestID = id ; return webhookRequest ; } public append ( data ) { this . rawContents = Buffer . concat ( [ this . rawContents , data ] ) ; if ( this . headers == null ) { this . headers = { } ; const endIndex = this . rawContents . indexOf ( "\r\n\r\n" ) ; if ( endIndex !== - 1 ) { this . parseHeaders ( this . rawContents . slice ( 0 , endIndex ) . toString ( ) ) ; if ( endIndex + 4 < this . rawContents . length ) { const bodyPart = this . rawContents . slice ( ( endIndex + 4 ) ) ; this . appendBody ( bodyPart ) ; } } } else { this . appendBody ( data ) ; } } public appendBody ( bodyPart ) { this . rawBody = Buffer . concat ( [ this . rawBody , bodyPart ] ) ; this . body += bodyPart . toString ( ) ; } public done ( ) { if ( this . method === "GET" ) { return true ; } return ( this . rawBody . length === this . contentLength ( ) ) ; } public contentLength ( ) { let contentLength = - 1 ; if ( this . headers != null ) { let contentLengthString = this . headers [ "content-length" ] ; contentLength = parseInt ( contentLengthString ) ; } return contentLength ; } public isPing ( ) { return ( this . uri . indexOf ( "/ping" ) !== - 1 ) ; } private parseHeaders ( headersString ) { let lines = headersString . split ( "\n" ) ; let requestLine = lines [ 0 ] ; let requestLineParts = requestLine . split ( " " ) ; this . method = requestLineParts [ 0 ] ; this . uri = requestLineParts [ 1 ] ; if ( this . uri . indexOf ( "?" ) >= 0 ) { const qs = this . uri . replace ( /^.*\?/ , "" ) ; this . queryParameters = querystring . parse ( qs ) ; } for ( let i = 1 ; i < lines . length ; i ++ ) { let headerLine = lines [ i ] ; let headerParts = headerLine . split ( ":" ) ; let key = headerParts [ 0 ] . toLowerCase ( ) ; this . headers [ key ] = headerParts [ 1 ] . trim ( ) ; } } public nodeID ( ) { let nodeValue = null ; if ( "node-id" in this . queryParameters ) { if ( typeof this . queryParameters [ "node-id" ] === "string" ) { nodeValue = this . queryParameters [ "node-id" ] as string ; } else { throw new Error ( "Only one node-id should be present in the query" ) ; } } return nodeValue ; } private removeBespokenQueries ( httpLine ) { return httpLine . replace ( "&node-id=" + this . nodeID ( ) , "" ) . replace ( "node-id=" + this . nodeID ( ) , "" ) . replace ( "&bespoken-key=" + this . nodeID ( ) , "" ) . replace ( "bespoken-key=" + this . nodeID ( ) , "" ) . replace ( "? HTTP" , " HTTP" ) ; } public requestWithoutBespokenData ( ) { const firstLineBreak = this . rawContents . indexOf ( "\n" ) ; const httpLine = this . rawContents . slice ( 0 , firstLineBreak ) . toString ( ) ; return Buffer . concat ( [ Buffer . from ( this . removeBespokenQueries ( httpLine ) ) , this . rawContents . slice ( firstLineBreak ) ] ) ; } public isJSON ( ) { try { JSON . parse ( this . body ) ; return true ; } catch ( error ) { return false ; } } public toString ( ) { return this . method + " " + this . uri ; } public id ( ) { return this . requestID ; } }