/**
 * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * This module defines the Runtime client which is responsible for all HTTP
 * interactions with the Runtime layer.
 */ "use strict" ; import { Agent , RequestOptions , IncomingMessage , ClientRequest , OutgoingHttpHeaders , } from "http" ; import { URL } from "url" ; import { InvocationResponse , NativeClient } from "../Common" ; import * as Errors from "../Errors" ; import * as XRayError from "../Errors/XRayError" ; const ERROR_TYPE_HEADER = "Lambda-Runtime-Function-Error-Type" ; interface HttpModule { Agent : typeof Agent ; request ( options , callback ? ) : ClientRequest ; } export interface IRuntimeClient { nextInvocation : ( ) => Promise < InvocationResponse > ; postInvocationError : ( error : unknown , id : string , callback : ( ) => void ) => void ; postInvocationResponse : ( response : unknown , id : string , callback : ( ) => void ) => void ; } function userAgent ( ) { const version = require ( "../../package.json" ) . version ; return `aws-lambda-nodejs/${ process . version }-${ version }` ; } /**
 * Objects of this class are responsible for all interactions with the Runtime
 * API.
 */ export default class RuntimeClient implements IRuntimeClient { agent : Agent ; http : HttpModule ; nativeClient : NativeClient ; userAgent : string ; useAlternativeClient : boolean ; hostname : string ; port : number ; constructor ( hostnamePort , httpClient ? , nativeClient ? ) { this . http = httpClient || require ( "http" ) ; this . nativeClient = nativeClient || require ( "../../build/Release/runtime-client.node" ) ; this . userAgent = userAgent ( ) ; this . nativeClient . initializeClient ( this . userAgent ) ; this . useAlternativeClient = process . env [ "AWS_LAMBDA_NODEJS_USE_ALTERNATIVE_CLIENT_1" ] === "true" ; const [ hostname , port ] = hostnamePort . split ( ":" ) ; this . hostname = hostname ; this . port = parseInt ( port , 10 ) ; this . agent = new this . http . Agent ( { keepAlive : true , maxSockets : 1 , } ) ; } param { Object } response param { String } id param { function ( ) } callback postInvocationResponse ( response , id , callback ) { const bodyString = _trySerializeResponse ( response ) ; this . nativeClient . done ( id , bodyString ) ; callback ( ) ; } param { Error } error param { function ( ) } callback postInitError ( error , callback ) { const response = Errors . toRuntimeResponse ( error ) ; this . _post ( `/2018-06-01/runtime/init/error` , response , { [ ERROR_TYPE_HEADER ] : response . errorType } , callback ) ; } param { Error } error param { String } id param { function ( ) } callback postInvocationError ( error , id , callback ) { const response = Errors . toRuntimeResponse ( error ) ; const bodyString = _trySerializeResponse ( response ) ; const xrayString = XRayError . toFormatted ( error ) ; this . nativeClient . error ( id , bodyString , xrayString ) ; callback ( ) ; } return { PromiseLike . < Object > } async nextInvocation ( ) { if ( this . useAlternativeClient ) { const options = { hostname : this . hostname , port : this . port , path : "/2018-06-01/runtime/invocation/next" , method : "GET" , agent : this . agent , headers : { "User-Agent" : this . userAgent , } , } ; return new Promise ( ( resolve , reject ) => { const request = this . http . request ( options , ( response ) => { let data = "" ; response . setEncoding ( "utf-8" ) . on ( "data" , ( chunk ) => { data += chunk ; } ) . on ( "end" , ( ) => { resolve ( { bodyJson : data , headers : response . headers , } ) ; } ) ; } ) ; request . on ( "error" , ( e ) => { reject ( e ) ; } ) . end ( ) ; } ) ; } return this . nativeClient . next ( ) ; } param { String } path param { Object } body param { Object } headers param { function ( ) } callback _post ( path , body , headers , callback ) { const bodyString = _trySerializeResponse ( body ) ; const options = { hostname : this . hostname , port : this . port , path : path , method : "POST" , headers : Object . assign ( { "Content-Type" : "application/json" , "Content-Length" : Buffer . from ( bodyString ) . length , } , headers || { } ) , agent : this . agent , } ; const request = this . http . request ( options , ( response ) => { response . on ( "end" , ( ) => { callback ( ) ; } ) . on ( "error" , ( e ) => { throw e ; } ) . on ( "data" , ( ) => { } ) ; } ) ; request . on ( "error" , ( e ) => { throw e ; } ) . end ( bodyString , "utf-8" ) ; } } /**
 * Attempt to serialize an object as json. Capture the failure if it occurs and
 * throw one that's known to be serializable.
 */ function _trySerializeResponse ( body ) { try { return JSON . stringify ( body === undefined ? null : body ) ; } catch ( err ) { throw new Error ( "Unable to stringify response body" ) ; } }