celeritas
Version:
This is an API service framework which supports API requests over HTTP & WebSockets.
155 lines (124 loc) • 4.97 kB
JavaScript
;
const uuid = require("uuid");
const querystring = require('querystring');
const validator = require('validator');
const zlib = require('zlib');
const lodash = require('lodash');
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 the API is returning an error for this response, and the output data is an object, always use application/json for the return mime type.
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 it's a 300-399 status code, redirect.
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) {
if (emit.error)
emit = emit.error;
else
emit = emit.result;
}
switch(call.returnMimeType) {
case "application/json": emit = JSON.stringify(emit); break;
default: emit = emit;
}
if (typeof emit !== "string")
emit = emit.toString()
if (call.compression.output == "gzip") {
emit = await (() => {
return new Promise((resolve, reject) => {
zlib.gzip(emit, (err, buffer) => {
if (err)
reject(err);
resolve(buffer);
})
})
})();
}
//(call.compression.output == "gzip") ? call.response.pipe(zlib.createGunzip()).pipe(emit) : call.response.write(emit);
call.response.write(emit || "");
}
else
var emit = true;
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;