@cloudbase/node-sdk
Version:
tencent cloud base server sdk for node.js
133 lines (117 loc) • 3.65 kB
text/typescript
import http from 'http'
import { IReqOpts } from '../../types/internal'
import { withRetry, IRetryOptions } from './retry'
import { RequestTimgingsMeasurer } from './request-timings-measurer'
import { request } from './request-core'
const SAFE_RETRY_CODE_SET = new Set([
'ENOTFOUND',
'ENETDOWN',
'EHOSTDOWN',
'ENETUNREACH',
'EHOSTUNREACH',
'ECONNREFUSED'
])
// const RETRY_CODE_SET = new Set(['ECONNRESET', 'ESOCKETTIMEDOUT'])
const RETRY_STATUS_CODE_SET = new Set([])
/* istanbul ignore next */
function shouldRetry(e: any, result: any, operation: any) {
// 重试的错误码
if (e && SAFE_RETRY_CODE_SET.has(e.code)) {
return {
retryAble: true,
message: e.message
}
}
// 连接超时
if (e && e.code === 'ETIMEDOUT' && e.connecting === true) {
return {
retryAble: true,
message: e.message
}
}
// 重试的状态码
if (result && RETRY_STATUS_CODE_SET.has(result.statusCode)) {
return {
retryAble: true,
message: `${result.request.method} ${result.request.href} ${result.statusCode} ${http.STATUS_CODES[result.statusCode]
}`
}
}
return {
retryAble: false,
message: ''
}
}
interface ITimingsMeasurerOptions {
waitingTime?: number
interval?: number
enable?: boolean
}
interface IExtraRequestOptions {
debug?: boolean
op?: string
seqId?: string
attempts?: number
timingsMeasurerOptions?: ITimingsMeasurerOptions
retryOptions?: IRetryOptions
}
interface IResponse {
statusCode: number
headers: http.IncomingHttpHeaders
body: string | Buffer | http.IncomingMessage
}
/* istanbul ignore next */
export async function requestWithTimingsMeasure(opts: IReqOpts, extraOptions?: IExtraRequestOptions): Promise<IResponse> {
return await new Promise((resolve, reject) => {
const timingsMeasurerOptions: ITimingsMeasurerOptions
= extraOptions.timingsMeasurerOptions || {}
const {
waitingTime = 1000,
interval = 200,
enable = !!extraOptions.debug
} = timingsMeasurerOptions
const timingsMeasurer = RequestTimgingsMeasurer.new({
waitingTime,
interval,
enable
})
timingsMeasurer.on('progress', (timings: any, reason = '') => {
const timingsLine = `s:${timings.socket || '-'}|l:${timings.lookup
|| '-'}|c:${timings.connect || '-'}|r:${timings.ready || '-'}|w:${timings.waiting
|| '-'}|d:${timings.download || '-'}|e:${timings.end || '-'}|E:${timings.error || '-'}`
console.warn(
`[TCB][RequestTimgings][${extraOptions.op || ''}] spent ${Date.now()
- timings.start}ms(${timingsLine}) [${extraOptions.seqId
}][${extraOptions.attempts || 1}][${reason}]`
)
})
; (function r() {
const cRequest = request(opts, (err: Error, res: http.IncomingMessage, body) => {
if (err) {
reject(err)
} else {
resolve({
statusCode: res.statusCode,
headers: res.headers,
body
})
}
})
if (cRequest instanceof http.ClientRequest) {
timingsMeasurer.measure(cRequest)
}
}())
})
}
export async function extraRequest(opts: IReqOpts, extraOptions?: IExtraRequestOptions) {
if (extraOptions && extraOptions.retryOptions) {
return await withRetry(
async attempts => {
return await requestWithTimingsMeasure(opts, { ...extraOptions, attempts })
},
{ shouldRetry, ...extraOptions.retryOptions }
)
} else {
return await requestWithTimingsMeasure(opts, { ...extraOptions, attempts: 1 })
}
}