(params: HTTPRequestParams, onResponse: (r: http.IncomingMessage) => void, onError: (error: Error) => void)
| 46 | export const NET_DEFAULT_TIMEOUT = 30_000; |
| 47 | |
| 48 | export function httpRequest(params: HTTPRequestParams, onResponse: (r: http.IncomingMessage) => void, onError: (error: Error) => void): { cancel(error: Error | undefined): void } { |
| 49 | let url = new URL(params.url); |
| 50 | const options: https.RequestOptions = { |
| 51 | method: params.method || 'GET', |
| 52 | headers: params.headers, |
| 53 | }; |
| 54 | if (params.rejectUnauthorized !== undefined) |
| 55 | options.rejectUnauthorized = params.rejectUnauthorized; |
| 56 | |
| 57 | const proxyURL = getProxyForUrl(params.url); |
| 58 | if (proxyURL) { |
| 59 | const parsedProxyURL = normalizeProxyURL(proxyURL); |
| 60 | if (params.url.startsWith('http:')) { |
| 61 | options.path = url.toString(); |
| 62 | const headers = options.headers || {}; |
| 63 | if (!Object.keys(headers).some(header => header.toLowerCase() === 'host')) |
| 64 | headers.host = url.host; |
| 65 | options.headers = headers; |
| 66 | url = parsedProxyURL; |
| 67 | } else { |
| 68 | options.agent = new HttpsProxyAgent(parsedProxyURL); |
| 69 | } |
| 70 | } |
| 71 | |
| 72 | options.agent ??= url.protocol === 'https:' ? httpsHappyEyeballsAgent : httpHappyEyeballsAgent; |
| 73 | |
| 74 | let cancelRequest: (e: Error | undefined) => void; |
| 75 | const requestCallback = (res: http.IncomingMessage) => { |
| 76 | const statusCode = res.statusCode || 0; |
| 77 | if (statusCode >= 300 && statusCode < 400 && res.headers.location) { |
| 78 | // Close the original socket before following the redirect. Otherwise |
| 79 | // it may stay idle and cause a timeout error. |
| 80 | request.destroy(); |
| 81 | cancelRequest = httpRequest({ ...params, url: new URL(res.headers.location, params.url).toString() }, onResponse, onError).cancel; |
| 82 | } else { |
| 83 | onResponse(res); |
| 84 | } |
| 85 | }; |
| 86 | const request = url.protocol === 'https:' ? |
| 87 | https.request(url, options, requestCallback) : |
| 88 | http.request(url, options, requestCallback); |
| 89 | request.on('error', onError); |
| 90 | if (params.socketTimeout !== undefined) { |
| 91 | request.setTimeout(params.socketTimeout, () => { |
| 92 | onError(new Error(`Request to ${params.url} timed out after ${params.socketTimeout}ms`)); |
| 93 | request.abort(); |
| 94 | }); |
| 95 | } |
| 96 | cancelRequest = e => { |
| 97 | try { |
| 98 | request.destroy(e); |
| 99 | } catch { |
| 100 | } |
| 101 | }; |
| 102 | request.end(params.data); |
| 103 | return { cancel: e => cancelRequest(e) }; |
| 104 | } |
| 105 |
no test coverage detected
searching dependent graphs…