import { Options , Payload , Response , Status , Transport , TransportOptions } from '@amplitude/types' ; import { AsyncQueue , mapJSONToResponse , mapHttpMessageToResponse } from '@amplitude/utils' ; import * as http from 'http' ; import * as https from 'https' ; import { REQUEST_TIMEOUT_MILLIS_DEFAULT } from '../constants' ; import * as url from 'url' ; hidden export interface HTTPRequest { param options param callback request ( options , callback ? ) : http . ClientRequest ; } /** Base Transport class implementation */ export class HTTPTransport implements Transport { /** The Agent used for corresponding transport */ public module : HTTPRequest ; protected _uploadInProgress = false ; protected _requestQueue : AsyncQueue = new AsyncQueue ( ) ; /** Create instance and set this.dsn */ public constructor ( public options ) { this . options = options ; if ( options . serverUrl . startsWith ( 'http://' ) ) { this . module = http ; } else if ( options . serverUrl . startsWith ( 'https://' ) ) { this . module = https ; } else { throw new Error ( 'Invalid server url' ) ; } } inheritDoc public async sendPayload ( payload , limitInMs ? ) { const timeoutMS = limitInMs ?? this . options . requestTimeoutMillis ?? REQUEST_TIMEOUT_MILLIS_DEFAULT ; const call = async ( ) : Promise < Response > => await this . _sendWithModule ( payload , timeoutMS ) ; return await this . _requestQueue . addToQueue ( call ) ; } /** Returns a build request option object used by request */ protected _getRequestOptions ( ) { const headers = { ... this . options . headers , } ; const url = new URL ( this . options . serverUrl ) ; const options = { headers , method : 'POST' , hostname : url . hostname , path : url . pathname , } ; return options ; } /** JSDoc */ protected async _sendWithModule ( payload , limitInMs ) { return await new Promise < Response > ( ( resolve , reject ) => { let timeoutId ; const req = this . module . request ( this . _getRequestOptions ( ) , ( res ) => { res . setEncoding ( 'utf8' ) ; let rawData = '' ; res . on ( 'data' , ( chunk ) => { rawData += chunk ; } ) ; res . on ( 'end' , ( ) => { if ( timeoutId !== undefined ) { clearTimeout ( timeoutId ) ; } if ( res . complete && rawData . length > 0 ) { try { const responseWithBody = mapJSONToResponse ( JSON . parse ( rawData ) ) ; if ( responseWithBody !== null ) { return resolve ( responseWithBody ) ; } } catch { } } resolve ( mapHttpMessageToResponse ( res ) ) ; } ) ; } ) ; if ( limitInMs > 0 ) { timeoutId = setTimeout ( ( ) => { req . destroy ( ) ; resolve ( { status : Status . Timeout , statusCode : 0 } ) ; } , limitInMs ) ; } req . on ( 'error' , reject ) ; req . end ( JSON . stringify ( payload ) ) ; } ) ; } } export const setupDefaultTransport = ( options ) : Transport => { const transportOptions = { serverUrl : options . serverUrl , headers : { 'Content-Type' : 'application/json' , } , requestTimeoutMillis : options . requestTimeoutMillis , } ; return new HTTPTransport ( transportOptions ) ; } ;