UNPKG

celeritas

Version:

This is an API service framework which supports API requests over HTTP & WebSockets.

155 lines (124 loc) 4.97 kB
'use strict'; 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;