import { TemplatedApp , HttpResponse , HttpRequest , RecognizedString } from "@trufflesuite/uws-js-unofficial" ; import ContentTypes from "./utils/content-types" ; import HttpResponseCodes from "./utils/http-response-codes" ; import { Connector } from "@ganache/flavors" ; import { InternalOptions } from "../options" ; import { types } from "util" ; type HttpMethods = "GET" | "OPTIONS" | "POST" ; const noop = ( ) => { } ; param method param request function prepareCORSResponseHeaders ( method , request ) { const origin = request . getHeader ( "origin" ) ; const acrh = request . getHeader ( "access-control-request-headers" ) ; return ( response ) => { const isCORSRequest = origin !== "" ; if ( isCORSRequest ) { if ( method === "OPTIONS" ) { response . writeHeader ( "Access-Control-Allow-Methods" , "POST" ) ; if ( acrh !== "" ) { response . writeHeader ( "Access-Control-Allow-Headers" , acrh ) ; } response . writeHeader ( "Access-Control-Max-Age" , "600" ) ; } response . writeHeader ( "Access-Control-Allow-Credentials" , "true" ) ; response . writeHeader ( "Access-Control-Allow-Origin" , origin ) ; } } ; } function sendResponse ( response , statusCode , contentType , data , writeHeaders = noop ) { response . cork ( ( ) => { response . writeStatus ( statusCode ) ; writeHeaders ( response ) ; if ( contentType != null ) { response . writeHeader ( "Content-Type" , contentType ) ; } if ( data != null ) { response . end ( data ) ; } else { response . end ( ) ; } } ) ; } export type HttpServerOptions = Pick < InternalOptions [ "server" ] , "rpcEndpoint" > ; export default class HttpServer { #connector : Connector ; constructor ( app , connector , options ) { this . #connector = connector ; app . post ( options . rpcEndpoint , this . #handlePost ) . options ( options . rpcEndpoint , this . #handleOptions ) ; app . get ( "/418" , response => { sendResponse ( response , HttpResponseCodes . IM_A_TEAPOT , ContentTypes . PLAIN , "418 I'm a teapot" ) ; } ) ; app . any ( "/*" , ( response , request ) => { const connectionHeader = request . getHeader ( "connection" ) ; if ( connectionHeader && connectionHeader . toLowerCase ( ) === "upgrade" ) { sendResponse ( response , HttpResponseCodes . BAD_REQUEST , ContentTypes . PLAIN , "400 Bad Request" ) ; } else { sendResponse ( response , HttpResponseCodes . NOT_FOUND , ContentTypes . PLAIN , "404 Not Found" ) ; } } ) ; } #handlePost = ( response , request ) => { const writeHeaders = prepareCORSResponseHeaders ( "POST" , request ) ; let buffer ; let aborted = false ; response . onAborted ( ( ) => { aborted = true ; } ) ; response . onData ( ( message , isLast ) => { const chunk = Buffer . from ( message ) ; if ( isLast ) { const connector = this . #connector as any ; let payload ; try { const message = buffer ? Buffer . concat ( [ buffer , chunk ] , buffer . length + chunk . length ) : chunk ; payload = connector . parse ( message ) ; } catch ( e ) { sendResponse ( response , HttpResponseCodes . BAD_REQUEST , ContentTypes . PLAIN , "400 Bad Request: " + e . message , writeHeaders ) ; return ; } connector . handle ( payload , request ) . then ( ( { value } ) => value ) . then ( result => { if ( aborted ) { return ; } const data = connector . format ( result , payload ) ; if ( types . isGeneratorObject ( data ) ) { response . cork ( ( ) => { response . writeStatus ( HttpResponseCodes . OK ) ; writeHeaders ( response ) ; response . writeHeader ( "Content-Type" , ContentTypes . JSON ) ; for ( const datum of data ) { response . write ( datum as RecognizedString ) ; } response . end ( ) ; } ) ; } else { sendResponse ( response , HttpResponseCodes . OK , ContentTypes . JSON , data , writeHeaders ) ; } } ) . catch ( error => { if ( aborted ) { return ; } const data = connector . formatError ( error , payload ) ; sendResponse ( response , HttpResponseCodes . OK , ContentTypes . JSON , data , writeHeaders ) ; } ) ; } else { if ( buffer ) { buffer = Buffer . concat ( [ buffer , chunk ] , buffer . length + chunk . length ) ; } else { buffer = Buffer . concat ( [ chunk ] , chunk . length ) ; } } } ) ; } ; #handleOptions = ( response , request ) => { const writeHeaders = prepareCORSResponseHeaders ( "OPTIONS" , request ) ; sendResponse ( response , HttpResponseCodes . NO_CONTENT , null , null , writeHeaders ) ; } ; public close ( ) { } }