"use strict"; var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); Object.defineProperty(exports, "__esModule", { value: true }); var ws_node_1 = require("./../ws-node"); var getBody = require("raw-body"); var typer = require("content-type"); var mediaTyper = require("media-typer"); var cors = require("cors"); var bodyParser = require("body-parser"); var cookieParser = require("cookie-parser"); var onHeaders = require('on-headers'); var isUtf8 = require('is-utf8'); module.exports = function (RED) { var WsBeginNode = /** @class */ (function (_super) { __extends(WsBeginNode, _super); function WsBeginNode(config) { var _this = //Init the base class. _super.call(this) || this; //Create the node RED.nodes.createNode(_this, config); if (!RED.settings.httpNodeRoot) { _this.warn("Cannot create WsBeginNode node when httpNodeRoot set to false"); return _this; } //Create a url based on the id. var url = "/startflow/" + _this.z; if (!url) { _this.warn("Missing url"); return _this; } //Create an empty corsHandler var corsHandler = function (req, res, next) { next(); }; //Fill the corshanler if we have a httpNodeCors in the settings if (RED.settings.httpNodeCors) { corsHandler = cors(RED.settings.httpNodeCors); RED.httpNode.options("*", corsHandler); } //Create an empty httpMiddleware var httpMiddleware = function (req, res, next) { next(); }; //Fill the httpMiddleware if we have a httpNodeMiddleware in the settings if (RED.settings.httpNodeMiddleware) { if (typeof RED.settings.httpNodeMiddleware === "function") { httpMiddleware = RED.settings.httpNodeMiddleware; } } var maxApiRequestSize = RED.settings.apiMaxLength || '5mb'; var jsonParser = bodyParser.json({ limit: maxApiRequestSize }); var urlencParser = bodyParser.urlencoded({ limit: maxApiRequestSize, extended: true }); //Create an empty metricsHandler var metricsHandler = function (req, res, next) { next(); }; //Fill the metricsHandler if the metric collection is enabled if (_this.metric()) { metricsHandler = _this.metricsHandler; } //Start listening to the url with all the handlers/callbacks RED.httpNode.post(url, cookieParser(), httpMiddleware, corsHandler, metricsHandler, jsonParser, urlencParser, _this.multipartParser, _this.rawBodyParser, function (req, res) { _this.callback(req, res); }, function (err, req, res, next) { _this.errorHandler(err, req, res, next); }); //Remove the routes _this.on("close", function () { RED.httpNode._router.stack.forEach(function (route, i, routes) { if (route.route && route.route.path === url && route.route.methods["POST"]) { routes.splice(i, 1); } }); }); return _this; } /** * Body parser that is used to parse the request and send it in the output */ WsBeginNode.prototype.rawBodyParser = function (req, res, next) { if (req.skipRawBodyParser) { next(); } // don't parse this if told to skip if (req._body) { return next(); } req.body = ""; req._body = true; var isText = true; var checkUTF = false; if (req.headers['content-type']) { var contentType = typer.parse(req.headers['content-type']); if (contentType.type) { var parsedType = mediaTyper.parse(contentType.type); if (parsedType.type === "text") { isText = true; } else if (parsedType.subtype === "xml" || parsedType.suffix === "xml") { isText = true; } else if (parsedType.type !== "application") { isText = false; } else if ((parsedType.subtype !== "octet-stream") && (parsedType.subtype !== "cbor")) { checkUTF = true; } else { // application/octet-stream or application/cbor isText = false; } } } getBody(req, { length: req.headers['content-length'], encoding: isText ? "utf8" : null }, function (err, buf) { if (err) { return next(err); } if (!isText && checkUTF && isUtf8(buf)) { req.body = buf.toString(); } else { req.body = buf; } next(); }); }; /** * Standard errorhandler that will log the error and send a 500 error back in the request */ WsBeginNode.prototype.errorHandler = function (err, req, res, next) { this.warn(err); res.sendStatus(500); }; ; /** * This function will be called when a succesfull call is made. It will send the data to the next node and call the sendResponse function */ WsBeginNode.prototype.callback = function (req, res) { var msgid = RED.util.generateId(); //Let the client know the request arrived succesfully this.sendResponse(this.createResponseWrapper(this, res), req.body); var message = { _msgid: msgid, payload: { request: req } }; this.send(message); }; ; /** * Empty code for the multipart parser. This code can be extended if needed. */ WsBeginNode.prototype.multipartParser = function (req, res, next) { next(); }; ; /** * Metric handler that will get the headers from the response and call the default metric function. */ WsBeginNode.prototype.metricsHandler = function (req, res, next) { var _this = this; var startAt = process.hrtime(); onHeaders(res, function () { if (res._msgid) { var diff = process.hrtime(startAt); var ms = diff[0] * 1e3 + diff[1] * 1e-6; var metricResponseTime = ms.toFixed(3); var metricContentLength = res.getHeader("content-length"); //assuming that _id has been set for res._metrics in HttpOut node! _this.metric("response.time.millis", { _msgid: res._msgid }, metricResponseTime); _this.metric("response.content-length.bytes", { _msgid: res._msgid }, metricContentLength); } }); next(); }; ; /** * Send a 200 ok back to the client */ WsBeginNode.prototype.sendResponse = function (res, payload) { if (res) { var statusCode = 200; if (typeof payload == "object" && !Buffer.isBuffer(payload)) { res._res.status(statusCode).jsonp(payload); } else { if (res._res.get('content-length') == null) { var len = void 0; if (payload == null) { len = 0; } else if (Buffer.isBuffer(payload)) { len = payload.length; } else if (typeof payload == "number") { len = Buffer.byteLength("" + payload); } else { len = Buffer.byteLength(payload); } res._res.set('content-length', len); } if (typeof payload === "number") { payload = "" + payload; } res._res.status(statusCode).send(payload); } } else { this.warn("No response object"); } }; /** * Creates a response wrapper based on the */ WsBeginNode.prototype.createResponseWrapper = function (node, res) { var wrapper = { _res: res }; var toWrap = ["append", "attachment", "cookie", "clearCookie", "download", "end", "format", "get", "json", "jsonp", "links", "location", "redirect", "render", "send", "sendfile", "sendFile", "sendStatus", "set", "status", "type", "consty"]; toWrap.forEach(function (f) { //@ts-ignore Element implicitly has an 'any' type because expression of type 'any' can't be used to index type wrapper[f] = function () { this.warn("Deprecated call to msg.res." + f); var result = res[f].apply(res, arguments); if (result === res) { return wrapper; } else { return result; } }; }); return wrapper; }; return WsBeginNode; }(ws_node_1.WsNode)); /** Register the node */ RED.nodes.registerType("ws-begin-node", WsBeginNode); };