celeritas
Version:
This is an API service framework which supports API requests over HTTP & WebSockets.
139 lines (110 loc) • 4.55 kB
JavaScript
;
const uuid = require("uuid");
const querystring = require('querystring');
const validator = require('validator');
const zlib = require('zlib');
const { promisify } = require('util');
const lodash = require('lodash');
const gzipAsync = promisify(zlib.gzip);
String.prototype.capitalize = function() {
return this.replace(/(?:^|\s)\S/g, function(a) { return a.toUpperCase(); });
};
class HttpCall {
constructor (app, request, response, util) {
var ipAddress = request.headers['x-forwarded-for'] || request.connection.remoteAddress;
if (ipAddress.indexOf(",") !== -1)
ipAddress = ipAddress.split(",")[0];
var queryData = (request.url.indexOf("?") !== -1) ? querystring.parse(request.url.substring(request.url.indexOf("?")+1)) : {};
if (typeof queryData.ipAddress == "undefined")
queryData.ipAddress = ipAddress;
for (let param in queryData) {
try {
queryData[param] = JSON.parse(queryData[param]);
}
catch (err) {}
}
Object.assign(this, {
id: uuid().replace(/-/g, ""),
status: util.PENDING,
method: request.method.toUpperCase(),
hostname: request.headers.host,
url: request.headers.host + request.url,
route: request.url.split("?").shift().substring(1).toLowerCase(),
post: null,
get: queryData,
param: request.url.split("?").shift().substring(1).split("/").pop(),
timestamp: new Date().toISOString(),
ipAddress: ipAddress,
returnMimeType: "application/json",
returnInput: (typeof request.headers['x-return-input'] !== "undefined" && JSON.parse(request.headers['x-return-input']) == true) ? true : false,
compression: {
input: (typeof request.headers['content-encoding'] !== "undefined" && (request.headers['content-encoding'].indexOf("gzip") > -1)) ? "gzip" : null,
output: (typeof request.headers['accept-encoding'] !== "undefined" && (request.headers['accept-encoding'].indexOf("gzip") > -1)) ? "gzip" : null
},
cache: util.assembleCache(request.headers['cache-control'] || ""),
paging: util.assemblePaging(queryData),
sort: util.assembleSort(queryData),
auth: util.assembleAuth(request.headers.authorization),
output: {
code: null,
data: null
}
});
this._rawGet = lodash.cloneDeep(this.get);
if (validator.isMongoId(this.param))
this.route = (this.route.substr(0, this.route.length - this.param.length)) + "*";
else
this.param = null;
request.setTimeout((typeof request.headers['x-timeout'] !== "undefined") ? parseInt(request.headers['x-timeout']) : app.api.timeout);
this.request = request;
this.response = response;
}
static async emit (call) {
var pkge = require("./../package.json");
if (call.output.code >= 400 && typeof call.output.data == "object")
call.returnMimeType = "application/json";
var headers = {
"Access-Control-Expose-Headers": call.app.accessControl.exposeHeaders,
"Access-Control-Allow-Headers": call.app.accessControl.allowHeaders,
"Access-Control-Allow-Origin": call.app.accessControl.allowOrigin,
"Access-Control-Allow-Methods": call.app.accessControl.allowMethods,
"Access-Control-Allow-Credentials": true,
"X-Request-Id": call.id,
"X-Powered-By": pkge.name + "@" + pkge.version,
"Content-Type": call.returnMimeType,
"Authorization": (call.auth !== null && (call.auth.authenticated == true || call.output.code != 401)) ? call.auth.type.capitalize() + " " + call.auth.raw : null
};
if (call.output.code >= 300 && call.output.code < 400 && call.output.data.result.redirect)
headers.Location = call.output.data.result.redirect;
if (call.compression.output && call.compression.output.indexOf("gzip") >= 0) {
headers['Content-Encoding'] = "gzip";
headers['Accept-Encoding'] = "gzip";
}
call.response.writeHead(call.output.code, headers);
if (call.output.code != 204) {
var emit = call.output.data;
if (call.app.api.metaData !== true)
emit = emit.error ? emit.error : emit.result;
switch (call.returnMimeType) {
case "application/json": emit = JSON.stringify(emit); break;
default: emit = typeof emit !== "string" ? emit.toString() : emit;
}
if (call.compression.output === "gzip") {
const gzip = zlib.createGzip();
gzip.pipe(call.response);
gzip.end(emit);
} else {
call.response.end(emit || "");
}
} else {
call.response.end();
}
if (call.output.code < 300)
call.status = call.util.COMPLETE;
else
call.status = call.util.ERROR;
delete call.app._calls[call.id];
return Promise.resolve(call);
}
}
module.exports = HttpCall;