const Component = require("./_component"); const utils = require("../utils"); const https = require("https"); const http = require("http"); const express = require("express"); const bodyParser = require("body-parser"); const bodyHand = require('body'); const url = require('url'); const path = require('path'); const fs = require('fs'); const fetch = require('node-fetch'); const cookieParser = require('cookie-parser'); const mime = require("mime"); function logger (...p) { //return; console.log("[DEBUG.http]:", ...p); } function fetchProxy (req, res, reverseTo) { const params = { method: req.method.toUpperCase(), headers: Object.assign({}, req.headers), redirect: "follow", follow: 20, compress: false }; delete params.headers.host; delete params.headers.referer; if (!["GET", "HEAD"].includes(params.method)) params.body = req.body; const reqAddress = url.resolve(reverseTo, path.join("/", req.originalUrl)); fetch(reqAddress, params) .then(async result => { const content = await result.arrayBuffer(); //logger("result:", result); //logger("result.arrayBuffer():", content); //logger("result.Buffer:", Buffer.from(content).toString()); const headers = Object.fromEntries(result.headers); //logger("headers Headers", result.headers); //logger("headers Headers.raw()", result.headers.raw()); logger("headers object", headers); Object.entries(headers).forEach(([header, value]) => { res.set(header, value); }); const finalContent = Buffer.from(content); res.status(result.status).send(finalContent); }) .catch(err => { console.error(err); res.status(500).send(req.st500); }); } function authPage (self, req, res, next) { if (self.syntax.auth) { const ACCESS = "Basic " + Buffer.from(`${self.syntax.auth.login}:${self.syntax.auth.password}`).toString('base64'); if (req.headers['authorization'] === ACCESS) { return next(); } else { if (self.syntax.auth.mode === "strict" && req.headers['authorization'] !== undefined) { return res.status(403).send(req.st403); } res.set("WWW-Authenticate", "Basic"); return res.status(401).send(); } } next(); } function redirectPage (redirTo, req, res) { redirTo = url.resolve(redirTo, path.join("/", req.originalUrl)); res.redirect(redirTo); } function toNormalText (httpPath) { const findedUnicodeTexts = httpPath.match(/(\%[A-F0-9]{2}){1,}/g) ?? []; for (let matchedQuery of findedUnicodeTexts) { httpPath = httpPath .split(matchedQuery) .join(Buffer.from( matchedQuery .replace(/\%/g, ""), "hex" ).toString("utf8")) } return httpPath; } function renderDirContent (dirpath, displayedDir) { const files = fs.readdirSync(dirpath).map(f => ({ name: f, path: path.join(displayedDir, f), folder: displayedDir, lstat: fs.lstatSync(path.join(dirpath, f)), sizeReadable: utils.getStrSize(fs.lstatSync(path.join(dirpath, f)).size), hash: "1ac89f5efc", mime: mime.getType(f) })); files.folder = displayedDir; const ht = (i) => parseInt(Buffer.from(i ?? "0").toString("hex"), 16); files.sort((a, b) => a.name - b.name); files.sort((a, b) => ht(a.mime) - ht(b.mime)); files.sort((a, b) => +(b.lstat.isDirectory()) - +(a.lstat.isDirectory())); return `Content of dir: ${displayedDir}

Content of dir: ${displayedDir}


${renderDirContent.genTRs(files)}
Filename Size Checksum
`; } renderDirContent.genTRs = function (files) { const allFiles = files.map(f => `${renderDirContent.getIcon(f)}${f.name + (f.lstat.isDirectory() ? "/" : "")}${f.sizeReadable}${f.hash}`); return [`${renderDirContent.getIcon(null)}../`].concat(allFiles).join("\n\t\t\t"); } renderDirContent.getIcon = function (fileData) { let iconUrl = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAEiSURBVDhPjZHLaoYwFITtK/YJfQlFFAt1VTehvwsR3bgQ8bYQ8b5JnWOi6d+mdODDJOfMJDGGlGmazPd9/oznedxxHG5ZFuZvXde9Hu0vp0sRmvd9/8E4jrxpGg7Zto2wdxHyXbqAYRh4WZYUACEEJxG2W3+doKoqYT+F6wjbLV3Asiy873sKwUlwHfwTYbulC9i2jUJwElwHX/xYYbv1WwDM4HkdvcJ263hGvq7rxTzPtBvAWK2hV9huYXGapgvc+3guAmO1pg1Ao6Sua14UBYGxWtMGtG1L5HlOpGlKyLmsawPwTCBJEuLx+CTkXNa1AXKnKIqIMPwg5FzWtQFZlnHG2EUQBIS6hh5tQBzHx47hheu6hLqGHl0AQ+GfsNNlGF+M+aF9URxgkQAAAABJRU5ErkJggg=="; if (!fileData) iconUrl = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAEFSURBVDhPvZI7CsJAFEVDcANuwcrOxsZKd+ASxBi1cTMWWYBgLTb2ugNBsBTBD/5/KYwO+nzvZSaYGExsvHDIZObeO5MhWlC5dhrk8HcVu3lQyKn4agxKEEQu/UFiWrAQCMHS4kig+WH3PqB5JLpEyALYmD7eSoL4SwVOhhWokiDk56CSPcryggo5w0wkMuoqzHCdlX2IpQGwrwJc6rDqp/wFk04SxK4Jt7kB94XB5seqArDDwLHGIYbGWxPIL6Ouxq0EF/AOZ2k+oRnfn2uTC6ncmZcZ8suoK6+Agoca705GOjo91cmI7wV4gWRWQe9T6HLx6ASdKKRAt2gyPnq8PzRamvYCpw6whDcmQQIAAAAASUVORK5CYII="; else if (fileData.lstat.isDirectory()) { iconUrl = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADpSURBVDhP1ZLNDYJAEIUJsQFbsAEvFmArIGI9HijABrx5twQTE4/GRDT+4C8H0Q2OM8MucXEjHPUlX4DlvbfDBus3JBbtAAEDgVVFAs1pPPqA1pHyEiELYOdpvJUU0UsFLpoKVEkR8nNQKZ62+IUKJZNmKTKayWS4LR0NsXYBDl2Aaw8244ZeMB/WQUR9uIcuPFYum9NNByDCwMnnEEP3ew/IL6OZZoMaF/AOF2k+oxmfn1uPC6k8CR2G/DKaKS+g4NHn3clIo9NVTUZ8L8ADJLMK5p9Ch4ujEzSRocAOaLE6drU/9B9kWS8Dg5sSqHCbFwAAAABJRU5ErkJggg=="; } else { console.log("fileData:", fileData); if (fileData.mime?.includes("image/")) { iconUrl = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHxSURBVDhPpZA9SFtRGIYPgRKHTsJdukXomtHBjEohQwetmEySDpGmODRCf7wGLKUEhSrBUj2kJVZp0WKpdpFaEoz4kzjUItqtHQrSQU00ShJMvPft+T6T6w8UWvvBw3vOufd57+GKcDisK+QJfZWs0HdmzVx4rlwRCoUkqmNWksZQm7N7WtNZda2GXBEMBrng8eIhehb+kuQBF5Ar/H4/F9z7lEHX3N45gp/3LR7EcxhfzbD4anmHk1zh9Xq5oD+5i1gqg83tEidJq2sjML/bOR8lcvi1f8RiNckVbrdbmqaJ6Er23AtDyW2WzfUrnKPrRUTWjjC8eYzhjWOQQ65wuVxc8GbjEONfc3APNSC6vIXBhQzi31ZY/vIjjefpA9ye8WLup4H4lvqPyiFXOJ1OaRgGXqazLHvGmtAcq8eNkWsIz2ctfFMtuD+ro2O6nUvIIVc4HA5ZLpfRNtYI37ub6PjQCv/7FrS9brRKfFO3WH6afIHexDMuIadOuUKr1WSpVELnZCsC6oqBj56TnKH0cEn/UhSDqbcWT+YjIEfTNClq7HZZLBa5QI8HFHfRrdATp/BNFgcwkIpZkEOusNlsslAocMGfeDh7h0u61Qci6QkuIIdcoUbm83k++BfIIZcK9MriMpArriquXxJy/2eE+A1jEWc14ZrA1gAAAABJRU5ErkJggg=="; } else if (fileData.mime?.includes("html")) { iconUrl = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAFZSURBVDhPnZO/L0NRFMf7z0hM/oBGLEwkBmmZTd2EiUTs8sLQxcBAYtBEJBY/BjPVSJlodDA1lHQoIXQ58jnJ97X3ig69ybe3755zPufHuy9jA6xM7+KgVH209dOHVGu75674WQogPOBUf2lZo/Vpb+2PdOdM5xK+c4vFLoQ/5ae2G6lk9jBvE3tZG1q6tFyx7mfY0ftXJ60qhfBDlqSy7MF3zQs3EIDj2EZihZNNO7r/cYA0M7/qfg6gXBxZz99lh2W3Xz0QIBUBkP4AKBUDwfu1xCZLZzZ1ULWV62mHqbp/AfSLgZIXjm88uwAEAh3ZGu4PYA4KFkADjQEMPAAoC6XSNy0AAawhqs2dSscBo+O5LoAhkokZUDbZCEYEaJeoNgDoNQLR0GiHrASz9wVc3dZ8MLpIyh4HSlQcALhVumm6yohMsbCTMADwgQCRmHCvcI4VvIXBl9kvGykjPhc1ZOcAAAAASUVORK5CYII="; } else if (fileData.mime?.includes("video")) { iconUrl = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAF4SURBVDhPfZJXjsJQDEWzQSAQCL2F0EPbEL2GvgTEQmAnnrmWjN9EaCydz3PvsxMLM5lM6H6/03g8ptvtxlyvV+ZyuTDn85npdDp0Op2o2WwSyxgEQB6NRh85KkICrVaL5UajoQEiDofDf+UwDMn3fTocDuR53t8AyEEQML1ej8FzgdkK6vU61Wo1DRgMBtzY7/e/tgK0Asi73Y4qlYoGoBUyWkXGPJ/Pj7zf7xmI5XKZSqWSBogoF4Yk836/WUQrgLzdbqlQKGhAt9v97CpPjg5kiMVikeVcLqcB7XabjscjH0p2lXm9XiyC9XpN+XyeVqsVZbNZDYCIz4MLy66Yx+PB4mazYVlE13Upk8loAES5sOxrtooMIC6XS3IcRwMggmq1ylfGhWVXPNlsTafTLKdSKQ3AT2FeONqKRrBYLFiezWZk27YGmK2mbIpgPp9TMplkOZFIaABkHApP/tYK0CrN0+mU4vG4Bpi7fmsVWUQQi8V+AyzrB2TCUiLZmdTqAAAAAElFTkSuQmCC"; } else if (fileData.mime?.includes("audio/")) { iconUrl = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGkSURBVDhPYxicYNL2aJs5RxKn928Jn9u3NWIeAofNm3M0cXrftsgoqFLsoHSxbl77PrP/5csN/pctRcLLDP637TH9X7xIbwlUKXaQOkM2o2S1/P/k6aIubr0MgjAcPpFNKXOOzP+MmdLzoEqxg4zZ8hlla5X+Z8+TNYQKgUHIFAHu7Lny/zNnyxFnQNZceTOoEBgkz5YSpI8BqXOkYsrWKWIYAAKZc2T/Z86Smw/looKKhSbOOZPU03JnaCwpW60KNEAabEDzGke5/Ck6aVkT1AqKF6v9z54jPxusARlUzDeKalhl/n7iXuf/bRts/5cv1v+ft0BVt3KuvkjlQuOTPTvs//ftcPrftM7sf9FczelQbQiQM0lz2aRD9v/nnvH+P/2o2/+6lcb/E1tlNZO6FbWb1pv+n3Hc/f+c097/+/ba/geqnQvVhgAF09WLq5fp/+3da/G/YzswsczVflI0T0kmb6aibNFs7ScgMZBc9XL9v4UztIqg2hCg5n8NY/Ec7cyCaZrT86ZqTC9ZqGMBlWIAsUFiILnCmdqZILVQqQEHDAwA2QbKhGiFnzoAAAAASUVORK5CYII="; } } return ``; } function simpleHTTP (staticPath, req, res, dirContentFlag = false) { const curpath = toNormalText(req.originalUrl .replace(/\?.{0,}/gmi)); const pathToFile = path.join(staticPath, curpath); console.log("curpath:", curpath); console.log("pathToFile:", pathToFile); let finalPathToFile; let mimetype = "text/html"; let isDir = false; try { const lstat = fs.lstatSync(pathToFile); if (lstat.isFile()) { mimetype = mime.getType(pathToFile); finalPathToFile = pathToFile } else if (lstat.isDirectory()) { finalPathToFile = path.join(pathToFile, "index.html"); isDir = true; } fs.readFile(finalPathToFile, (err, data) => { if (err) { if (isDir && dirContentFlag) { try { return res.send(renderDirContent(pathToFile, curpath)); } catch (e) { return res.status(404).send(req.st404); /*if (e.code === "EACCES") return res.status(404).send(req.st404); return res.status(500).send(req.st500);*/ } } return res.status(404).send(req.st404); } res.set("Content-Type", mimetype); res.send(data); }); } catch (e) { return res.status(404).send(req.st404); } } module.exports = class HTTPServer extends Component { constructor (adr, syntaxTree) { super(adr, syntaxTree); this.routelist = []; } domainAction (context, domain, callCommand, req, res, next) { logger("DOMAIN ACT:", this.syntax); if (!(context instanceof HTTPServer)) throw new SyntaxError("You can't call revHTTP domain from unsupported type. Supported types: revHTTP, simpleHTTP, redirectHTTP"); switch (callCommand) { case "proxy": return fetchProxy(req, res, this.syntax.target); case "auth": const authContext = context.syntax.auth === undefined ? this : context; return authPage(authContext, req, res, next); case "redirect": return redirectPage(this.syntax.target, req, res); case "shttp": return simpleHTTP(this.syntax.target, req, res, !!this.syntax.showDir); } } preinit () { this.app = express(); this.app.use('/*', async (req, res, next) => { // Myself body parser :) bodyHand(req, res, { limit: 9999999999, cache: false, encoding: 'base64' }, (err, body) => { if (!err) { req.body = Buffer.from(body, 'base64'); } else { req.body = undefined; } next(); }); }); this.app.use((req, res, next) => { const currentDomain = req.hostname; const isNotDomained = !this.syntax.domains[currentDomain]; if (!isNotDomained) { return this .callDomain(currentDomain, this.syntax.domains[currentDomain]) .argv("auth", req, res, next); } authPage(this, req, res, next) }); this.app.use((req, res, next) => { req.st403 = "

403: Permission denied


Permission denied.

"; req.st404 = "

404: Page not finded


Please, recheck the URL address and try again.

"; req.st500 = "500: Internal Server Error

Error:


Error catched. Check logs

"; next(); }); this.app.use(cookieParser()); this.routelist.forEach(route => { this.app.use(route); }); } postinit (startHandler) { if (!this.syntax.encryption) { this.server = http.createServer(this.app); } else { this.server = https.createServer({ cert : fs.readFileSync(this.syntax.encryption.public, 'utf-8'), key : fs.readFileSync(this.syntax.encryption.private, 'utf-8') }, this.app); } this.server.listen(this.adr.port, this.adr.address, startHandler); } stop () { this.server.close(() => logger("Server stopped")); } } module.exports.fetchProxy = fetchProxy; module.exports.authPage = authPage; module.exports.redirectPage = redirectPage; module.exports.simpleHTTP = simpleHTTP;