/**
 * Copyright 2020 Opstrace, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */ import { strict as assert } from "assert" ; import got , { Got , RetryObject , Response as GotResponse } from "got" ; import { log } from "./log" ; const httpTimeoutSettings = { connect : 10000 , request : 30000 } ; export function customGotRetryfunc ( ro ) { if ( ro . computedValue === 0 ) { return 0 ; } const req = ro . error . request ; if ( req === undefined ) { log . debug ( "retryfunc() called w/o request context. attempt; %s, error: %s" , ro . attemptCount , ro . error ) ; return 1000 ; } let waitSec = 1.6 ** ro . attemptCount ; if ( waitSec > 15 ) { waitSec = 15 ; } const waitMilliSec = waitSec * 1000 ; let msg = "" ; msg += `${ req . options . method } HTTP request to ${ req . requestUrl } failed. ` ; msg += `Attempt: ${ ro . attemptCount }. ` ; msg += `Error: ${ ro . error . code }, ${ ro . error . message }. ` ; msg += `Retry in ${ waitSec . toFixed ( 1 ) } s. ` ; const resp = ro . error . response ; if ( resp === undefined ) { log . info ( msg ) ; return waitMilliSec ; } msg += `Response code: ${ resp . statusCode }. ` ; msg += `Response body[:500]: ${ getBodyPrefix ( resp ) }.` ; log . info ( msg ) ; return waitMilliSec ; } function getBodyPrefix ( resp ) { let bodyPrefix = "no body" ; if ( resp . body !== undefined ) { if ( Buffer . isBuffer ( resp . body ) ) { bodyPrefix = resp . body . slice ( 0 , 500 ) . toString ( "utf-8" ) ; } else { assert ( typeof resp . body === "string" || resp . body instanceof String ) ; bodyPrefix = resp . body . slice ( 0 , 500 ) ; } } return bodyPrefix ; } export const httpcl = got . extend ( { retry : { limit : 10 , calculateDelay : customGotRetryfunc , statusCodes : [ 408 , 413 , 500 , 502 , 503 , 504 , 521 , 522 , 524 ] , maxRetryAfter : 5 * 60 * 1000 } , throwHttpErrors : true , timeout : httpTimeoutSettings } ) ; export function debugLogHTTPResponse ( resp ) { if ( resp === undefined ) { log . debug ( "debugLogHTTPResponse: response not defined" ) ; return ; } const ts = resp . timings ; log . debug ( `HTTP resp to ${ resp . request . options . method }(${ resp . requestUrl }):
    status: ${ resp . statusCode }
    body[:500]: ${ getBodyPrefix ( resp ) }
    headers: ${ JSON . stringify ( resp . headers ) }
    totalTime: ${ ( ts . phases . total ? ts . phases . total : - 1000 ) / 1000.0 } s
    dnsDone->TCPconnectDone: ${ ( ts . phases . tcp ? ts . phases . tcp : - 1000 ) / 1000.0 } s
    connectDone->reqSent ${ ( ts . phases . request ? ts . phases . request : - 1000 ) / 1000.0 } s
    reqSent->firstResponseByte: ${ ( ts . phases . firstByte ? ts . phases . firstByte : - 1000 ) / 1000.0 } s
    ` ) ; } export function debugLogHTTPResponseLight ( resp ) { const t1 = ( resp . timings . phases . total ? resp . timings . phases . total : - 1000 ) / 1000.0 ; const t2 = ( resp . timings . phases . firstByte ? resp . timings . phases . firstByte : - 1000 ) / 1000.0 ; log . debug ( `resp to ${ resp . request . options . method }(${ resp . requestUrl }) -> ${ resp . statusCode }. total time: ${ t1 . toFixed ( 2 ) } s. resp time: ${ t2 . toFixed ( 2 ) } s.` ) ; }