in src/package-sources/npmjs/couch-changes.lambda-shared.ts [79:150]
private https(method: string, url: URL, body?: { [key: string]: unknown }, attempt = 1): Promise<{ [key: string]: unknown }> {
return new Promise((ok, ko) => {
const retry = () => setTimeout(
() => {
console.log(`Retrying ${method.toUpperCase()} ${url}`);
this.https(method, url, body, attempt + 1).then(ok, ko);
},
Math.min(500 * attempt, 5_000),
);
const headers: OutgoingHttpHeaders = {
'Accept': 'application/json',
'Accept-Encoding': 'gzip',
};
if (body) {
headers['Content-Type'] = 'application/json';
}
console.log(`Request: ${method.toUpperCase()} ${url}`);
const req = request(
url,
{
agent: this.agent,
headers,
method,
port: 443,
servername: url.hostname,
},
(res) => {
if (res.statusCode == null) {
const error = new Error(`[FATAL] Request failed: ${method.toUpperCase()} ${url}`);
Error.captureStackTrace(error);
return ko(error);
}
console.log(`Response: ${method.toUpperCase()} ${url} => HTTP ${res.statusCode} (${res.statusMessage})`);
// Transient (server) errors:
if (res.statusCode >= 500 && res.statusCode < 600) {
console.error(`[RETRYABLE] HTTP ${res.statusCode} (${res.statusMessage}) - ${method.toUpperCase()} ${url}`);
// Call again after a short back-off
return retry();
}
// Permanent (client) errors:
if (res.statusCode >= 400 && res.statusCode < 500) {
const error = new Error(`[FATAL] HTTP ${res.statusCode} (${res.statusMessage}) - ${method.toUpperCase()} ${url}`);
Error.captureStackTrace(error);
return ko(error);
}
const onError = (err: Error & { code?: string }) => {
if (err.code === 'ECONNRESET') {
// Transient networking problem?
console.error(`[RETRYABLE] ${err.code} - ${method.toUpperCase()} ${url}`);
retry();
} else {
ko(err);
}
};
res.once('error', onError);
const json = JSONStream.parse(true);
json.once('data', ok);
json.once('error', onError);
const plainPayload = res.headers['content-encoding'] === 'gzip' ? gunzip(res) : res;
plainPayload.pipe(json, { end: true });
},
);
req.end(body && JSON.stringify(body, null, 2));
});
}