UNPKG

@nodefony/http-bundle

Version:

Nodefony Framework Bundle HTTP

515 lines (481 loc) 15 kB
const QS = require("qs"); const {syncBuiltinESMExports} = require("module"); syncBuiltinESMExports(); let formidable = null; import("formidable").then((esmFS) => { formidable = esmFS.default; }); module.exports = nodefony.register("Request", () => { // ARRAY PHP LIKE const reg = /(.*)[\[][\]]$/; const settingsXml = {}; class Parser { constructor (request) { this.request = request; this.chunks = []; this.request.request.on("data", (data) => { try { this.write(data); } catch (e) { throw e; } }); } write (buffer) { this.chunks.push(buffer); return buffer; } async parse () { this.request.data = Buffer.concat(this.chunks); return this; } } class ParserQs extends Parser { constructor (request) { super(request); this.queryStringParser = this.request.context.queryStringParser || {}; } async parse () { try { await super.parse(); this.request.queryPost = QS.parse(this.request.data.toString(this.charset), this.queryStringParser); this.request.query = nodefony.extend({}, this.request.query, this.request.queryPost); this.request.context.requestEnded = true; return this; } catch (err) { throw err; } } } class ParserXml extends Parser { constructor (request) { super(request); this.xmlParser = new xmlParser(settingsXml); } async parse () { await super.parse(); return new Promise((resolve, reject) => { this.xmlParser.parseString(this.request.data.toString(this.charset), (err, result) => { if (err) { return reject(err); } this.request.queryPost = result; this.request.context.requestEnded = true; return resolve(this); }); }); } } const acceptParser = function (acc) { if (!acc) { return [{ type: new RegExp(".*"), subtype: new RegExp(".*") }]; } const obj = {}; try { const types = acc.split(","); for (let i = 0; i < types.length; i++) { const type = types[i].split(";"); const mine = type.shift(); const dec = mine.split("/"); const ele1 = dec.shift(); const ele2 = dec.shift(); obj[mine] = { type: new RegExp(ele1 === "*" ? ".*" : ele1), subtype: new RegExp(ele2 === "*" ? ".*" : ele2) }; for (let j = 0; j < type.length; j++) { const params = type[j].split("="); const name = params.shift(); obj[mine][name] = params.shift(); } } // sort const tab = []; const qvalue = []; for (const ele in obj) { const line = obj[ele]; if (line.q) { qvalue.push(obj[ele]); } else { tab.push(obj[ele]); } } if (qvalue.length) { return tab.concat(qvalue.sort((a, b) => { if (a.q > b.q) { return -1; } if (a.q < b.q) { return 1; } return 0; })); } return tab; } catch (e) { throw e; } }; const parse = { POST: true, PUT: true, DELETE: true }; const Request = class httpRequest { constructor (request, context) { this.context = context; this.request = request; this.request.body = null; this.headers = request.headers; this.method = this.getMethod(); this.host = this.getHost(); this.hostname = this.getHostName(this.host); this.sUrl = this.getFullUrl(request); this.url = this.getUrl(this.sUrl); if (this.url.search) { this.url.query = QS.parse(this.url.search, this.context.queryStringParser || {}); } else { this.url.query = {}; } this.parser = null; this.queryPost = {}; this.queryFile = []; // this.context.setParameters("query.files", this.queryFile); this.queryGet = this.url.query; // this.context.setParameters("query.get", this.queryGet); this.query = this.url.query; this.origin = this.headers.origin; try { this.accept = acceptParser(this.headers.accept); this.acceptHtml = this.accepts("html"); } catch (e) { this.log(e, "WARNING"); } this.rawContentType = {}; this.contentType = this.getContentType(this.request); this.charset = this.getCharset(this.request); this.domain = this.getDomain(); this.remoteAddress = this.getRemoteAddress(); this.dataSize = 0; this.data = Buffer.from([], this.charset); this.request.on("data", (data) => { this.dataSize += data.length; }); this.context.once("onRequestEnd", () => { this.request.body = this.query; }); this.initialize(); } initialize () { return this.parseRequest() .then((parser) => { switch (true) { case parser instanceof Parser: case parser instanceof ParserQs: case parser instanceof ParserXml: this.request.once("end", () => { try { if (this.context.finished) { return; } parser.parse(); return this.context.fireAsync("onRequestEnd", this); } catch (error) { return this.context.kernelHttp.onError(this.context.container, error); } }); break; default: if (!parser) { this.request.once("end", () => { try { if (this.context.finished) { return; } this.context.requestEnded = true; return this.context.fireAsync("onRequestEnd", this); } catch (error) { return this.context.kernelHttp.onError(this.context.container, error); } }); } } return parser; }) .catch((error) => { if (this.context.requestEnded) { return this.context.kernelHttp.onError(this.context.container, error); } this.request.once("end", () => { this.context.requestEnded = true; return this.context.kernelHttp.onError(this.context.container, error); }); }); } parseRequest () { return new Promise((resolve, reject) => { if (this.method in parse) { switch (this.contentType) { case "application/xml": case "text/xml": this.parser = new ParserXml(this); return resolve(this.parser); case "application/x-www-form-urlencoded": try { this.parser = new ParserQs(this); } catch (e) { return reject(e); } return resolve(this.parser); default: const parserInst = new Parser(this); const opt = nodefony.extend(this.context.requestSettings, { encoding: this.charset === "utf8" ? "utf-8" : this.charset }); this.parser = formidable(opt); this.parser.parse(this.request, async (err, fields, files) => { if (err) { this.log("err.message use Simple parser", "WARNING"); switch (err.code) { case 1003: case 1011: try { this.parser = parserInst; } catch (e) { return reject(e); } return resolve(await this.parser.parse()); break; default: console.error(err); err.code = err.httpCode; return reject(err); } } try { await parserInst.parse(); this.queryPost = fields; this.query = nodefony.extend({}, this.query, this.queryPost); if (Object.keys(files).length) { for (const file in files) { try { if (reg.exec(file)) { if (nodefony.isArray(files[file])) { for (const multifiles in files[file]) { this.createFileUpload(multifiles, files[file][multifiles], opt.maxFileSize); } } else if (files[file].filepath) { this.createFileUpload(file, files[file], opt.maxFileSize); } } else if (nodefony.isArray(files[file])) { for (const multifiles in files[file]) { this.createFileUpload(null, files[file][multifiles], opt.maxFileSize); } } else { this.createFileUpload(file, files[file], opt.maxFileSize); } } catch (err) { return reject(err); } } } } catch (err) { return reject(err); } this.context.requestEnded = true; this.context.fireAsync("onRequestEnd", this); return resolve(this.parser); }); } } else { return resolve(); } }); } createFileUpload (name, file, maxSize) { if (file.size > maxSize) { throw new Error(`maxFileSize exceeded, received ${file.size} bytes of file data for : ${file.originalFilename}` || name || file.newFilename); } const fileUpload = this.context.uploadService.createUploadFile(file, name); const index = this.queryFile.push(fileUpload); this.queryFile[fileUpload.filename] = this.queryFile[index - 1]; return fileUpload; } accepts (Type) { let parse = null; let subtype = "*"; let type = "*"; try { if (Type) { parse = Type.split("/"); } if (parse) { switch (parse.length) { case 1: subtype = parse.shift(); break; case 2: type = parse.shift(); subtype = parse.shift(); break; default: throw new Error("request accepts method bad type format"); } } for (let i = 0; i < this.accept.length; i++) { const line = this.accept[i]; if ((type === "*" || line.type.test(type)) && (subtype === "*" || line.subtype.test(subtype))) { return true; } continue; } return false; } catch (e) { throw e; } } getHost () { return this.request.headers.host; } getHostName (host) { if (this.url && this.url.hostname) { return this.url.hostname; } if (host) { return host.split(":")[0]; } if (host = this.getHost()) { return host.split(":")[0]; } return ""; } getUserAgent () { return this.request.headers["user-agent"]; } getMethod () { return this.request.method; } clean () { this.data = null; delete this.data; this.body = null; delete this.body; if (this.parser && this.parser.chunks) { this.parser.chunks = null; delete this.parser.chunks; } this.parser = null; delete this.parser; this.queryPost = null; delete this.queryPost; this.queryFile = null; delete this.queryFile; this.queryGet = null; delete this.queryGet; this.query = null; delete this.query; this.request = null; delete this.request; this.accept = null; delete this.accept; // this.container = null ; // delete this.container ; // super.clean(); } log (pci, severity, msgid, msg) { if (!msgid) { msgid = `${this.context.type} REQUEST `; } return this.context.log(pci, severity, msgid, msg); } getContentType (request) { if (request.headers["content-type"]) { const tab = request.headers["content-type"].split(";"); if (tab.length > 1) { for (let i = 1; i < tab.length; i++) { if (typeof tab[i] === "string") { const ele = tab[i].split("="); const key = ele[0].replace(" ", "").toLowerCase(); this.rawContentType[key] = ele[1]; } else { continue; } } } this.extentionContentType = request.headers["content-type"]; return tab[0]; } return null; } getCharset () { if (this.rawContentType.charset) { return this.rawContentType.charset; } return "utf8"; } getDomain () { return this.getHostName(); // return this.host.split(":")[0]; } getRemoteAddress () { // proxy mode if (this.headers && this.headers["x-forwarded-for"]) { return this.headers["x-forwarded-for"]; } if (this.request.connection && this.request.connection.remoteAddress) { return this.request.connection.remoteAddress; } if (this.request.socket && this.request.socket.remoteAddress) { return this.request.socket.remoteAddress; } if (this.request.connection && this.request.connection.socket && this.request.connection.socket.remoteAddress) { return this.request.connection.socket.remoteAddress; } return null; } setUrl (Url) { this.url = this.getUrl(Url); } getUrl (sUrl, query) { return url.parse(sUrl, query); } /* cleanUrl(Url){ let parse = url.parse(Url) let myurl = null if( parse.pathname ){ let pathname = parse.pathname.substring(0, parse.pathname.lastIndexOf('/')) ; if( pathname){ parse.pathname = pathname } } return url.format(parse) }*/ getFullUrl (request) { const myurl = `://${this.host}${request.url}`; // proxy mode if (this.headers && this.headers["x-forwarded-for"]) { return `${this.headers["x-forwarded-proto"]}${myurl}`; } if (request.connection.encrypted) { return `https${myurl}`; } return `http${myurl}`; } getHeader (name) { if (name in this.headers) { return this.headers[name]; } return null; } isAjax () { if (this.headers["x-requested-with"]) { return this.headers["x-requested-with"].toLowerCase() === "xmlhttprequest"; } return false; } }; return Request; });