post-get-service
Version:
simple way to create node http in memory server
199 lines (166 loc) • 4.62 kB
JavaScript
/* eslint-disable unicorn/no-object-as-default-parameter */
// @ts-check
const { isPrimitive, isArray, isString, safeHasOwnPropery, safeJSONstringify, isObject } = require('sat-utils');
const http = require('http');
const https = require('https');
const MAX_AVAILABLE_RETRIES = 2;
const default_headers = { 'Content-Type': 'application/json' };
function can_be_stringified(item) {
try {
JSON.stringify(item);
return true;
} catch {
return false;
}
}
function get_stringify_only_allowed(data) {
if (isObject(data)) {
const copied = {};
for (const key of Object.keys(data)) {
if (can_be_stringified(data[key])) {
copied[key] = data[key];
}
}
return copied;
}
if (isArray(data)) {
return data.map(data_item => get_stringify_only_allowed(data_item));
}
if (isPrimitive(data) && can_be_stringified(data)) {
return data;
}
return '';
}
function is_retryable_network_error(err) {
if (err && err.code) {
return (
err.code === 'ECONNABORTED' ||
err.code === 'ECONNRESET' ||
err.code === 'ECONNREFUSED' ||
err.code === 'EADDRINUSE' ||
err.code === 'EPIPE' ||
err.code === 'ETIMEDOUT'
);
}
return false;
}
function should_reexecute_request(retries, err) {
return retries < MAX_AVAILABLE_RETRIES && is_retryable_network_error(err);
}
/**
*
* @param {*} options
* @param {*} handle_result
* @param {*} request_data
* @param {number} retry_call_number
*/
function send_request(options, handle_result, request_data, retry_call_number = 0) {
const req = options.protocol === 'https:' ? https.request : http.request;
const called_request = req(options, function (response) {
const body = [];
response.on('data', body.push.bind(body));
response.on('end', function () {
const resp = Buffer.concat(body).toString('utf8').replace(/\0/g, '');
handle_result({ req_result: resp });
});
});
called_request.on('error', function (error) {
if (should_reexecute_request(retry_call_number, error)) {
retry_call_number += 1;
setTimeout(function () {
send_request(options, handle_result, request_data, retry_call_number);
}, 25);
} else {
handle_result({ req_error: error });
}
});
if (request_data) {
called_request.write(
isString(request_data) ? request_data : JSON.stringify(get_stringify_only_allowed(request_data)),
);
}
called_request.end();
}
/**
*
* @param {string} url
* @param {'POST'|'GET'} method
* @param {*} headers
* @param {number} [timeout] timeout
* @returns {{
* host,
* port,
* path,
* protocol,
* href,
* headers,
* method,
* timeout,
* }}
*/
function prepare_request_opts(url, method, headers = {}, timeout) {
const { host, port, pathname, protocol, href } = new URL(url);
const hostwithoutPort = host.endsWith(`:${port}`) ? host.replace(`:${port}`, '') : host;
return {
host: hostwithoutPort,
port,
path: pathname,
protocol,
href,
headers,
method,
timeout,
};
}
/**
*
* @param {*} opts
* @param {*} [data]
* @returns {Promise<any>}
*/
async function handle_request(opts, data) {
return new Promise(resolve => {
if (opts.method === 'GET') {
data = undefined;
}
send_request(opts, resolve, data);
});
}
function try_parse_json(item) {
try {
return JSON.parse(item);
} catch {
return item;
}
}
/**
* @param {string} url request url
* @param {!{[k: string]: string}} [headers] request headers
* @param {number} [timeout] timeout
*/
async function get(url, headers = { 'Content-Type': 'application/json' }, timeout) {
const request_opts = prepare_request_opts(url, 'GET', headers, timeout);
const result = await handle_request(request_opts);
if (safeHasOwnPropery(result, 'req_result')) {
return isString(result.req_result) ? try_parse_json(result.req_result) : result.req_result;
}
return result.req_error;
}
/**
* @param {string} url request url
* @param {!{[k: string]: string}} [headers] request headers
* @param {number} [timeout] timeout
*/
async function post(url, data, headers = { 'Content-Type': 'application/json' }, timeout) {
const request_opts = prepare_request_opts(url, 'POST', headers, timeout);
const result = await handle_request(request_opts, data);
if (safeHasOwnPropery(result, 'req_result')) {
return isString(result.req_result) ? try_parse_json(result.req_result) : result.req_result;
}
return safeJSONstringify(result.req_error);
}
module.exports = {
get,
post,
default_headers,
};