Compare commits
2 Commits
be5b975a48
...
02a032023f
Author | SHA1 | Date | |
---|---|---|---|
02a032023f | |||
d17b53d049 |
@ -7,6 +7,9 @@ module.exports = class Component {
|
|||||||
// Run server
|
// Run server
|
||||||
run () {}
|
run () {}
|
||||||
|
|
||||||
|
// Stop server
|
||||||
|
stop () {}
|
||||||
|
|
||||||
// Working with domains
|
// Working with domains
|
||||||
domainAction (context, domain) {}
|
domainAction (context, domain) {}
|
||||||
|
|
||||||
|
280
components/_http.js
Normal file
280
components/_http.js
Normal file
@ -0,0 +1,280 @@
|
|||||||
|
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 `<html><head><title>Content of dir: ${displayedDir}</title></head><body>
|
||||||
|
<h3>Content of dir: ${displayedDir}</h3><hr/>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th scope="col"></th>
|
||||||
|
<th scope="col">Filename</th>
|
||||||
|
<th scope="col">Size</th>
|
||||||
|
<th scope="col">Checksum</th>
|
||||||
|
</tr>
|
||||||
|
${renderDirContent.genTRs(files)}
|
||||||
|
</table>
|
||||||
|
</body></html>`;
|
||||||
|
}
|
||||||
|
renderDirContent.genTRs = function (files) {
|
||||||
|
const allFiles = files.map(f => `<tr><td>${renderDirContent.getIcon(f)}</td><td><a href="${f.path}">${f.name + (f.lstat.isDirectory() ? "/" : "")}</a></td><td>${f.sizeReadable}</td><td>${f.hash}</td></tr>`);
|
||||||
|
return [`<tr><td>${renderDirContent.getIcon(null)}</td><td><a href="${path.join(files.folder, "..")}">../</a></td><td></td><td></td></tr>`].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 `<img src=${JSON.stringify(iconUrl)} />`;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = "<h1>403: Permission denied</h1><hr/><p>Permission denied.</p>";
|
||||||
|
req.st404 = "<h1>404: Page not finded</h1><hr/><p>Please, recheck the URL address and try again.</p>";
|
||||||
|
req.st500 = "<head><title>500: Internal Server Error</title></head><body><h1>Error:</h1><hr/><p>Error catched. Check logs</p></body>";
|
||||||
|
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;
|
40
components/http-redirector.js
Normal file
40
components/http-redirector.js
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
const HTTPServer = require("./_http");
|
||||||
|
const { redirectPage } = HTTPServer;
|
||||||
|
|
||||||
|
function logger (...p) {
|
||||||
|
return;
|
||||||
|
console.log("[DEBUG.http.redirector]:", ...p);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = class HTTPRedirector extends HTTPServer {
|
||||||
|
constructor (adr, syntaxTree) {
|
||||||
|
super(adr, syntaxTree);
|
||||||
|
}
|
||||||
|
|
||||||
|
run () {
|
||||||
|
this.preinit();
|
||||||
|
const redirectTo = this.syntax.target ?? "https://example.org";
|
||||||
|
|
||||||
|
this.app.use("*", async (req, res) => {
|
||||||
|
logger("originalUrl:>", req.originalUrl);
|
||||||
|
const currentDomain = req.hostname;
|
||||||
|
logger("hostname:", currentDomain);
|
||||||
|
const isNotDomained = !this.syntax.domains[currentDomain];
|
||||||
|
if (!isNotDomained) {
|
||||||
|
return this
|
||||||
|
.callDomain(currentDomain, this.syntax.domains[currentDomain])
|
||||||
|
.argv("redirect", req, res);
|
||||||
|
}
|
||||||
|
return redirectPage(redirectTo, req, res);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.postinit((err) => {
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
console.log(`HTTP redirect server listened at ${
|
||||||
|
this.adr.address
|
||||||
|
}:${this.adr.port}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
@ -1,127 +1,20 @@
|
|||||||
const Component = require("./_component");
|
const HTTPServer = require("./_http");
|
||||||
|
const { fetchProxy } = HTTPServer;
|
||||||
const https = require("https");
|
|
||||||
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')
|
|
||||||
|
|
||||||
function logger (...p) {
|
function logger (...p) {
|
||||||
return;
|
//return;
|
||||||
console.log("[DEBUG.main]:", ...p);
|
console.log("[DEBUG.http.reverse]:", ...p);
|
||||||
}
|
}
|
||||||
|
|
||||||
function fetchProxy (req, res, reverseTo) {
|
module.exports = class HTTPReverse extends HTTPServer {
|
||||||
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("<head><title>Error</title></head><body><h1>Error:</h1><hr/><p>Error catched. Check logs</p></body>");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
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("<h1>Permission denied</h1><hr/><p>Permission denied.</p>");
|
|
||||||
}
|
|
||||||
res.set("WWW-Authenticate", "Basic");
|
|
||||||
return res.status(401).send();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = class HTTPReverse extends Component {
|
|
||||||
constructor (adr, syntaxTree) {
|
constructor (adr, syntaxTree) {
|
||||||
super(adr, syntaxTree);
|
super(adr, syntaxTree);
|
||||||
}
|
}
|
||||||
|
|
||||||
domainAction (context, domain, callCommand, req, res, next) {
|
|
||||||
logger("DOMAIN ACT:", this.syntax);
|
|
||||||
if (!(context instanceof HTTPReverse))
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
run () {
|
run () {
|
||||||
this.app = express();
|
this.preinit();
|
||||||
|
|
||||||
const address = this.adr.address ?? "127.0.0.1";
|
|
||||||
const reverseTo = this.syntax.target ?? "https://example.org";
|
const reverseTo = this.syntax.target ?? "https://example.org";
|
||||||
|
|
||||||
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(cookieParser());
|
|
||||||
|
|
||||||
this.syntax.routelist.forEach(route => {
|
|
||||||
this.app.use(route);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.app.use("*", async (req, res) => {
|
this.app.use("*", async (req, res) => {
|
||||||
logger("REQ HEADERS:>", req.headers);
|
logger("REQ HEADERS:>", req.headers);
|
||||||
logger("originalUrl:>", req.originalUrl);
|
logger("originalUrl:>", req.originalUrl);
|
||||||
@ -136,18 +29,13 @@ module.exports = class HTTPReverse extends Component {
|
|||||||
return fetchProxy(req, res, reverseTo);
|
return fetchProxy(req, res, reverseTo);
|
||||||
});
|
});
|
||||||
|
|
||||||
const server = !this.syntax.encryption ? this.app : https.createServer({
|
this.postinit((err) => {
|
||||||
cert : fs.readFileSync(this.syntax.encryption.public, 'utf-8'),
|
|
||||||
key : fs.readFileSync(this.syntax.encryption.private, 'utf-8')
|
|
||||||
}, this.app);
|
|
||||||
|
|
||||||
server.listen(this.adr.port, address, (err) => {
|
|
||||||
if (err) {
|
if (err) {
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
console.log(`HTTP reverse proxy server listened at ${
|
console.log(`HTTP reverse proxy server listened at ${
|
||||||
address
|
this.adr.address
|
||||||
}:${this.adr.port}`);
|
}:${this.adr.port}`);
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
41
components/http-simple.js
Normal file
41
components/http-simple.js
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
const HTTPServer = require("./_http");
|
||||||
|
const mime = require("mime");
|
||||||
|
const { simpleHTTP } = HTTPServer;
|
||||||
|
|
||||||
|
function logger (...p) {
|
||||||
|
return;
|
||||||
|
console.log("[DEBUG.http.simple]:", ...p);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = class HTTPRedirector extends HTTPServer {
|
||||||
|
constructor (adr, syntaxTree) {
|
||||||
|
super(adr, syntaxTree);
|
||||||
|
}
|
||||||
|
|
||||||
|
run () {
|
||||||
|
this.preinit();
|
||||||
|
const redirectTo = this.syntax.target ?? "https://example.org";
|
||||||
|
|
||||||
|
this.app.use("*", async (req, res) => {
|
||||||
|
logger("originalUrl:>", req.originalUrl);
|
||||||
|
const currentDomain = req.hostname;
|
||||||
|
logger("hostname:", currentDomain);
|
||||||
|
const isNotDomained = !this.syntax.domains[currentDomain];
|
||||||
|
if (!isNotDomained) {
|
||||||
|
return this
|
||||||
|
.callDomain(currentDomain, this.syntax.domains[currentDomain])
|
||||||
|
.argv("shttp", req, res);
|
||||||
|
}
|
||||||
|
return simpleHTTP(redirectTo, req, res, !!this.syntax.showDir);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.postinit((err) => {
|
||||||
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
console.log(`Simple HTTP server listened at ${
|
||||||
|
this.adr.address
|
||||||
|
}:${this.adr.port}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
@ -1,5 +1,5 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
"revHTTP": require("./http-reverse"),
|
"revHTTP": require("./http-reverse"),
|
||||||
"simpleHTTP": require("./http-reverse"),
|
"simpleHTTP": require("./http-simple"),
|
||||||
"redirectHTTP": require("./http-reverse"),
|
"redirectHTTP": require("./http-redirector"),
|
||||||
};
|
};
|
||||||
|
3
components/tcp.js
Normal file
3
components/tcp.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
const Component = require("./_component");
|
||||||
|
|
||||||
|
class TCPServer extends Component {}
|
@ -1,8 +1,9 @@
|
|||||||
|
const EventEmitter = require("events");
|
||||||
// Func
|
// Func
|
||||||
|
|
||||||
class OperatorsStorage extends Array {
|
class OperatorStorage extends Array {
|
||||||
constructor (...p) {
|
constructor () {
|
||||||
super(...p);
|
super();
|
||||||
this._indexObject = {};
|
this._indexObject = {};
|
||||||
this._indexObjectIndexOf = {};
|
this._indexObjectIndexOf = {};
|
||||||
}
|
}
|
||||||
@ -15,7 +16,7 @@ class OperatorsStorage extends Array {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
throw new TypeError("Into OperatorsStorage you can add only Operator's instances");
|
throw new TypeError("Into OperatorStorage you can add only Operator's instances");
|
||||||
}
|
}
|
||||||
|
|
||||||
includes (item) {
|
includes (item) {
|
||||||
@ -23,7 +24,7 @@ class OperatorsStorage extends Array {
|
|||||||
return this._indexObject[item.operator] !== undefined;
|
return this._indexObject[item.operator] !== undefined;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
throw new TypeError("Into OperatorsStorage storage only Operator type");
|
throw new TypeError("Into OperatorStorage storage only Operator type");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,11 +40,18 @@ class Operator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = class NetHelperModule {
|
class NetHelperModule extends EventEmitter {
|
||||||
constructor () {
|
constructor () {
|
||||||
this.operators = new OperatorsStorage();
|
super();
|
||||||
|
this.operators = new OperatorStorage();
|
||||||
|
|
||||||
this.statics = new Object();
|
this.statics = { Operator };
|
||||||
this.statics.Operator = Operator;
|
this.serverType = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
bind (typeInstance) {
|
||||||
|
this.serverType = typeInstance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module.exports = { NetHelperModule };
|
33
modules/certbot.js
Normal file
33
modules/certbot.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
const express = require("express");
|
||||||
|
const { NetHelperModule } = global.moduleApi;
|
||||||
|
|
||||||
|
const HTTPReverse = require("../components/http-reverse");
|
||||||
|
|
||||||
|
module.exports = class CustomAuthPage extends NetHelperModule {
|
||||||
|
constructor () {
|
||||||
|
super();
|
||||||
|
this.router = express.Router();
|
||||||
|
|
||||||
|
this.certbot = new this.statics.Operator("certbot", (argv) => {
|
||||||
|
return argv.length === 2;
|
||||||
|
}, (argv, self, syntaxTree) => {
|
||||||
|
if (![HTTPReverse].includes(syntaxTree.Type)) {
|
||||||
|
throw new SyntaxError("Unsupported type of server");
|
||||||
|
}
|
||||||
|
const [ pathToFile, secretKey ] = argv;
|
||||||
|
const router = this.router;
|
||||||
|
|
||||||
|
router.get(pathToFile, (req, res, next) => {
|
||||||
|
res.send(secretKey);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
this.operators.push(this.certbot);
|
||||||
|
}
|
||||||
|
|
||||||
|
bind (type) {
|
||||||
|
super.bind(type);
|
||||||
|
this.serverType.routelist.push(this.router);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
const NetHelperModule = require("./");
|
const { NetHelperModule } = global.moduleApi;
|
||||||
|
|
||||||
module.exports = class ExampleMdoule extends NetHelperModule {
|
module.exports = class ExampleMdoule extends NetHelperModule {
|
||||||
constructor () {
|
constructor () {
|
||||||
|
@ -1,20 +1,21 @@
|
|||||||
const express = require("express");
|
const express = require("express");
|
||||||
const NetHelperModule = require("./");
|
const { NetHelperModule } = global.moduleApi;
|
||||||
|
|
||||||
const HTTPReverse = require("../components/http-reverse");
|
const HTTPReverse = require("../components/http-reverse");
|
||||||
|
|
||||||
module.exports = class CustomAuthPage extends NetHelperModule {
|
module.exports = class CustomAuthPage extends NetHelperModule {
|
||||||
constructor () {
|
constructor () {
|
||||||
super();
|
super();
|
||||||
|
this.router = express.Router();
|
||||||
|
|
||||||
const cauth = new this.statics.Operator("cauth", (argv) => {
|
this.cauth = new this.statics.Operator("cauth", (argv) => {
|
||||||
return argv.length === 2;
|
return argv.length === 2;
|
||||||
}, (argv, self, syntaxTree) => {
|
}, (argv, self, syntaxTree) => {
|
||||||
if (![HTTPReverse].includes(syntaxTree.Type)) {
|
if (![HTTPReverse].includes(syntaxTree.Type)) {
|
||||||
throw new SyntaxError("Unsupported type of server");
|
throw new SyntaxError("Unsupported type of server");
|
||||||
}
|
}
|
||||||
const [ login, password ] = argv;
|
const [ login, password ] = argv;
|
||||||
const router = express.Router();
|
const router = this.router;
|
||||||
|
|
||||||
router.get("/auth/:redirectTo", (req, res, next) => {
|
router.get("/auth/:redirectTo", (req, res, next) => {
|
||||||
if (req.cookies.auth === login + ":" + password)
|
if (req.cookies.auth === login + ":" + password)
|
||||||
@ -34,10 +35,17 @@ module.exports = class CustomAuthPage extends NetHelperModule {
|
|||||||
return next();
|
return next();
|
||||||
res.send('<form action=\"/auth/'+ Buffer.from(req.originalUrl).toString("hex") +'\"><h4>Needs auth</h4><hr/><p>Логин: <input type="text" size="40" name="login"></p><p>Пароль: <input type="password" size="40" name="password"></p><p><input type="submit"></p></form>');
|
res.send('<form action=\"/auth/'+ Buffer.from(req.originalUrl).toString("hex") +'\"><h4>Needs auth</h4><hr/><p>Логин: <input type="text" size="40" name="login"></p><p>Пароль: <input type="password" size="40" name="password"></p><p><input type="submit"></p></form>');
|
||||||
});
|
});
|
||||||
|
|
||||||
syntaxTree.routelist.push(router);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.operators.push(cauth);
|
this.operators.push(this.cauth);
|
||||||
|
|
||||||
|
this.emit("runned", () => {
|
||||||
|
this.serverType.app.use(router);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bind (type) {
|
||||||
|
super.bind(type);
|
||||||
|
this.serverType.routelist.push(this.router);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
20
package-lock.json
generated
20
package-lock.json
generated
@ -13,6 +13,7 @@
|
|||||||
"body-parser": "^1.20.3",
|
"body-parser": "^1.20.3",
|
||||||
"cookie-parser": "^1.4.7",
|
"cookie-parser": "^1.4.7",
|
||||||
"express": "^4.21.2",
|
"express": "^4.21.2",
|
||||||
|
"mime": "^3.0.0",
|
||||||
"node-fetch": "^2.6.7",
|
"node-fetch": "^2.6.7",
|
||||||
"sse": "^0.0.8"
|
"sse": "^0.0.8"
|
||||||
},
|
},
|
||||||
@ -683,14 +684,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/mime": {
|
"node_modules/mime": {
|
||||||
"version": "1.6.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz",
|
||||||
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
|
"integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==",
|
||||||
"bin": {
|
"bin": {
|
||||||
"mime": "cli.js"
|
"mime": "cli.js"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=4"
|
"node": ">=10.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/mime-db": {
|
"node_modules/mime-db": {
|
||||||
@ -1009,6 +1010,17 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/send/node_modules/mime": {
|
||||||
|
"version": "1.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
|
||||||
|
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
|
||||||
|
"bin": {
|
||||||
|
"mime": "cli.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/send/node_modules/ms": {
|
"node_modules/send/node_modules/ms": {
|
||||||
"version": "2.1.3",
|
"version": "2.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
"body-parser": "^1.20.3",
|
"body-parser": "^1.20.3",
|
||||||
"cookie-parser": "^1.4.7",
|
"cookie-parser": "^1.4.7",
|
||||||
"express": "^4.21.2",
|
"express": "^4.21.2",
|
||||||
|
"mime": "^3.0.0",
|
||||||
"node-fetch": "^2.6.7",
|
"node-fetch": "^2.6.7",
|
||||||
"sse": "^0.0.8"
|
"sse": "^0.0.8"
|
||||||
},
|
},
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
const fs = require("fs");
|
const fs = require("fs");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const components = require("./components");
|
const components = require("./components");
|
||||||
|
const HTTPServer = require("./components/_http");
|
||||||
|
const { createHash } = require("crypto");
|
||||||
|
|
||||||
// General regexps
|
// General regexps
|
||||||
// const FIND_ADDRESS_INSTR_REGEX = /([0-9]{1,3}\.){3}[0-9]{1,3}:[0-9]{1,5}\s{0,}\{[\s\n\r0-9a-z;:\/\."',]{0,}\}/gmi;
|
// const FIND_ADDRESS_INSTR_REGEX = /([0-9]{1,3}\.){3}[0-9]{1,3}:[0-9]{1,5}\s{0,}\{[\s\n\r0-9a-z;:\/\."',]{0,}\}/gmi;
|
||||||
@ -13,7 +15,7 @@ const CONTENT_INTO_CURLY_BRACES = /\{[\s\n\r0-9a-z;:\/\."',_]{0,}\}{0,}\}/gmi;
|
|||||||
|
|
||||||
// Instruction regexps
|
// Instruction regexps
|
||||||
function readInstructions (instructions, fullCode, forDomains=false) {
|
function readInstructions (instructions, fullCode, forDomains=false) {
|
||||||
const syntaxTree = { firewall: false, encryption: {}, modules: new Set(), routelist: [] };
|
const syntaxTree = { firewall: false, encryption: {}, modules: new Set(), showDir: false };
|
||||||
const moduleOpsQueue = new Array();
|
const moduleOpsQueue = new Array();
|
||||||
|
|
||||||
for (let instruction of instructions) {
|
for (let instruction of instructions) {
|
||||||
@ -58,6 +60,9 @@ function readInstructions (instructions, fullCode, forDomains=false) {
|
|||||||
password: instructionParts.slice(3,).join(" ")
|
password: instructionParts.slice(3,).join(" ")
|
||||||
} : null;
|
} : null;
|
||||||
break;
|
break;
|
||||||
|
case "showdir":
|
||||||
|
syntaxTree.showDir = true;
|
||||||
|
break;
|
||||||
case "firewall":
|
case "firewall":
|
||||||
break;
|
break;
|
||||||
case "import":
|
case "import":
|
||||||
@ -112,10 +117,8 @@ function readInstructions (instructions, fullCode, forDomains=false) {
|
|||||||
syntaxTree.modules = Array.from(syntaxTree.modules);
|
syntaxTree.modules = Array.from(syntaxTree.modules);
|
||||||
|
|
||||||
// Object.assign needs replace to object which give only-read access
|
// Object.assign needs replace to object which give only-read access
|
||||||
// Except: routelist
|
|
||||||
moduleOpsQueue.forEach(h => {
|
moduleOpsQueue.forEach(h => {
|
||||||
const st = Object.assign({}, syntaxTree);
|
const st = Object.assign({}, syntaxTree);
|
||||||
st.routelist = syntaxTree.routelist;
|
|
||||||
return h(st);
|
return h(st);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -123,7 +126,7 @@ function readInstructions (instructions, fullCode, forDomains=false) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function logger (...p) {
|
function logger (...p) {
|
||||||
//return;
|
return;
|
||||||
console.debug("[DEBUG.routemap]:", ...p);
|
console.debug("[DEBUG.routemap]:", ...p);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,3 +153,11 @@ module.exports.parse = function () {
|
|||||||
logger("Full syntax tree:", syntaxTree);
|
logger("Full syntax tree:", syntaxTree);
|
||||||
return syntaxTree;
|
return syntaxTree;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module.exports.hash = function () {
|
||||||
|
const content = fs.readFileSync(path.join(__dirname, "./routemap.txt"), { encoding: "utf-8" })
|
||||||
|
.replace(COMMENT_REGEX, "\n");
|
||||||
|
const hash = createHash('sha256').update(content, "utf8")
|
||||||
|
.digest().toString("hex");
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
19
routemap.txt
19
routemap.txt
@ -16,8 +16,23 @@
|
|||||||
import example, example_custom_auth;
|
import example, example_custom_auth;
|
||||||
type revHTTP;
|
type revHTTP;
|
||||||
target https://google.com;
|
target https://google.com;
|
||||||
#auth loopback admin qwerty;
|
auth loopback admin qwerty;
|
||||||
cauth admin 123;
|
#cauth admin 123;
|
||||||
cert public ./tests/pub.crt;
|
cert public ./tests/pub.crt;
|
||||||
cert private ./tests/priv.key;
|
cert private ./tests/priv.key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
127.0.0.1:9999 {
|
||||||
|
type redirectHTTP;
|
||||||
|
target https://duckduckgo.com;
|
||||||
|
domain localhost {
|
||||||
|
type redirectHTTP;
|
||||||
|
target https://google.com;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
127.0.0.1:8081 {
|
||||||
|
type simpleHTTP;
|
||||||
|
target /;
|
||||||
|
#showdir;
|
||||||
|
}
|
||||||
|
38
server.js
38
server.js
@ -1,11 +1,47 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
global.moduleApi = require("./module-api");
|
||||||
|
const EventEmitter = require("events");
|
||||||
|
|
||||||
const rtmap = require("./routemap-parser");
|
const rtmap = require("./routemap-parser");
|
||||||
const syntaxTree = rtmap.parse();
|
|
||||||
|
|
||||||
|
class ServersOrchestry extends EventEmitter {
|
||||||
|
constructor () {
|
||||||
|
super();
|
||||||
|
this.on("update-routemap", () => {
|
||||||
|
console.log("[Update routemap]: routemap.txt updated, reloading servers...");
|
||||||
|
this.stop();
|
||||||
|
this.run();
|
||||||
|
});
|
||||||
|
this.instances = new Array();
|
||||||
|
}
|
||||||
|
|
||||||
|
run () {
|
||||||
|
this.hash = rtmap.hash();
|
||||||
|
const syntaxTree = rtmap.parse();
|
||||||
for (let faddress in syntaxTree) {
|
for (let faddress in syntaxTree) {
|
||||||
const [address, port] = faddress.split(/:/gmi);
|
const [address, port] = faddress.split(/:/gmi);
|
||||||
const localSyntaxTree = syntaxTree[faddress];
|
const localSyntaxTree = syntaxTree[faddress];
|
||||||
const instance = new localSyntaxTree.Type({ address, port }, localSyntaxTree);
|
const instance = new localSyntaxTree.Type({ address, port }, localSyntaxTree);
|
||||||
|
localSyntaxTree.modules.forEach(module => module.bind(instance));
|
||||||
instance.run();
|
instance.run();
|
||||||
|
this.instances.push(instance);
|
||||||
|
localSyntaxTree.modules.forEach(module => module.emit("runned"));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stop () {
|
||||||
|
for (let instance of this.instances) {
|
||||||
|
instance.stop();
|
||||||
|
}
|
||||||
|
this.instances = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const orchestry = new ServersOrchestry();
|
||||||
|
|
||||||
|
orchestry.run();
|
||||||
|
|
||||||
|
setInterval(() => {
|
||||||
|
if (rtmap.hash() !== orchestry.hash)
|
||||||
|
orchestry.emit("update-routemap");
|
||||||
|
}, 100);
|
||||||
|
37
utils.js
Normal file
37
utils.js
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
function getNormalSizeString (val, sliceNumber=2) {
|
||||||
|
val = +val;
|
||||||
|
if (Number.isNaN(val))
|
||||||
|
return "NaN";
|
||||||
|
if (!Number.isFinite(val))
|
||||||
|
return val >= 0 ? "∞" : "-∞";
|
||||||
|
if (!(""+val).includes("."))
|
||||||
|
return ""+val + `.${"0".repeat(sliceNumber)}`;
|
||||||
|
let [
|
||||||
|
intVal, pointVal
|
||||||
|
] = (""+val).split(".");
|
||||||
|
if (pointVal.length < sliceNumber)
|
||||||
|
pointVal = pointVal + "0"
|
||||||
|
.repeat(sliceNumber - pointVal.length);
|
||||||
|
return `${intVal}.${pointVal.slice(0, sliceNumber)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStrSize (sizeBytes) {
|
||||||
|
if (sizeBytes < 512)
|
||||||
|
return sizeBytes + " B";
|
||||||
|
else {
|
||||||
|
//const failed = getNormalSizeString(0);
|
||||||
|
|
||||||
|
const kb = getNormalSizeString(sizeBytes / 1024);
|
||||||
|
const mb = getNormalSizeString(sizeBytes / 1048576);
|
||||||
|
const gb = getNormalSizeString(sizeBytes / 1073741824);
|
||||||
|
|
||||||
|
if (gb.split(".")[0] !== "0")
|
||||||
|
return gb + " GB";
|
||||||
|
if (mb.split(".")[0] !== "0")
|
||||||
|
return mb + " MB";
|
||||||
|
return kb + " KB";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.getNormalSizeString = getNormalSizeString;
|
||||||
|
module.exports.getStrSize = getStrSize;
|
Loading…
Reference in New Issue
Block a user