diff --git a/components/_component.js b/components/_component.js index 8475a92..ec702c8 100644 --- a/components/_component.js +++ b/components/_component.js @@ -4,5 +4,15 @@ module.exports = class Component { this.syntax = syntaxTree; } + // Run server run () {} + + // Working with domains + domainAction (context, domain) {} + + // Call this function where need works with domain (DO NOT REWRITE) + callDomain (domain, domainST) { + const component = new domainST.Type(this.adr, domainST); + return { argv: (...argv) => component.domainAction(this, domain, ...argv) }; + } }; diff --git a/components/http-reverse.js b/components/http-reverse.js index 113312d..bddab2e 100644 --- a/components/http-reverse.js +++ b/components/http-reverse.js @@ -1,22 +1,82 @@ const Component = require("./_component"); +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'); function logger (...p) { - return; + //return; console.log("[DEBUG.main]:", ...p); } +function auth (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("
Permission denied.
"); + } + res.set("WWW-Authenticate", "Basic"); + return res.status(401).send(); + } + } + next(); +} + +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("Error catched. Check logs
"); + }); +} + module.exports = class HTTPReverse extends Component { constructor (adr, syntaxTree) { super(adr, syntaxTree); } + domainAction (context, domain, req, res) { + 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"); + fetchProxy(req, res, this.syntax.target); + } + run () { this.app = express(); @@ -38,39 +98,32 @@ module.exports = class HTTPReverse extends Component { }); }); + this.app.use((req, res, next) => auth(this, req, res, next)); + this.app.use("*", async (req, res) => { logger("REQ HEADERS:>", req.headers); logger("originalUrl:>", req.originalUrl); const currentDomain = req.hostname; - const params = { method: req.method.toUpperCase(), headers: Object.assign({}, req.headers), redirect: "follow", follow: 20 }; - delete params.headers.host; - delete params.headers.referer; - if (!["GET", "HEAD"].includes(params.method)) - params.body = req.body; logger("hostname:", currentDomain); - const reverseToLoc = !this.syntax.domains[currentDomain] ? reverseTo : this.syntax.domains[currentDomain].target; - const reqAddress = url.resolve(reverseToLoc, path.join("/", req.originalUrl)); - fetch(reqAddress, params) - .then(async result => { - const content = await result.text(); - logger("result:", result); - logger("result.text():", content); - Object.entries(result?.headers ? result.headers : new Object()).forEach(([header, value]) => { - res.set(header, value); - }); - res.status(result.status).send(content); - }) - .catch(err => { - console.error(err); - res.status(500).send("Error catched. Check logs
"); - }); + const isNotDomained = !this.syntax.domains[currentDomain]; + if (!isNotDomained) { + return this + .callDomain(currentDomain, this.syntax.domains[currentDomain]) + .argv(req, res); + } + return fetchProxy(req, res, reverseTo); }); - this.app.listen(this.adr.port, address, (err) => { + const server = !this.syntax.encryption ? this.app : https.createServer({ + 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) { throw err; } - console.log(`Koboldcpp reverse proxy server listened at ${ + console.log(`HTTP reverse proxy server listened at ${ address }:${this.adr.port}`); }) diff --git a/components/index.js b/components/index.js index 515bb0e..14c0914 100644 --- a/components/index.js +++ b/components/index.js @@ -1,3 +1,5 @@ module.exports = { "revHTTP": require("./http-reverse"), + "simpleHTTP": require("./http-reverse"), + "redirectHTTP": require("./http-reverse"), }; diff --git a/package-lock.json b/package-lock.json index a87ef5a..05caf17 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,8 @@ "body": "^5.1.0", "body-parser": "^1.20.3", "express": "^4.21.2", - "node-fetch": "^2.6.7" + "node-fetch": "^2.6.7", + "sse": "^0.0.8" }, "devDependencies": { "nodemon": "^3.1.9" @@ -816,6 +817,14 @@ "node": ">= 0.8" } }, + "node_modules/options": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/options/-/options-0.0.6.tgz", + "integrity": "sha512-bOj3L1ypm++N+n7CEbbe473A414AB7z+amKYshRb//iuL3MpdDCLhPnw6aVTdKB9g5ZRVHIEp8eUln6L2NUStg==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -1083,6 +1092,17 @@ "node": ">=10" } }, + "node_modules/sse": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/sse/-/sse-0.0.8.tgz", + "integrity": "sha512-cviG7JH31TUhZeaEVhac3zTzA+2FwA7qvHziAHpb7mC7RNVJ/RbHN+6LIGsS2ugP4o2H15DWmrSMK+91CboIcg==", + "dependencies": { + "options": "0.0.6" + }, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", diff --git a/package.json b/package.json index bbafe4d..e1c5237 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,8 @@ "body": "^5.1.0", "body-parser": "^1.20.3", "express": "^4.21.2", - "node-fetch": "^2.6.7" + "node-fetch": "^2.6.7", + "sse": "^0.0.8" }, "devDependencies": { "nodemon": "^3.1.9" diff --git a/routemap-parser.js b/routemap-parser.js index bda0569..9eb1d40 100644 --- a/routemap-parser.js +++ b/routemap-parser.js @@ -6,14 +6,14 @@ const components = require("./components"); // 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 DOMAINER_REGEX = /domain\s{1,}[a-z0-9\.]{1,}\s{1,}\{[\s\n\r0-9a-z;:\/\."',]{0,}\}/gmi; const DOMAINER_VALUE_FIND_REGEX = /domain\s{1,}[a-z0-9\.]{1,}\s{1,}\{/gmi; -const ADDRESS_WITH_PORT_REGEX = /([0-9]{1,3}\.){3}[0-9]{1,3}:[0-9]{1,5}/gmi +const ADDRESS_WITH_PORT_REGEX = /([0-9]{1,3}\.){3}[0-9]{1,3}:[0-9]{1,5}/mi; 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;:\/\."',]|domain\s{1,}[a-z0-9\.]{1,}\s{1,}\{[\s\n\r0-9a-z;:\/\."',]{0,}\}){0,}\}/gmi; const COMMENT_REGEX = /#.{0,}(\n|\r|$)/gmi; const CONTENT_INTO_CURLY_BRACES = /\{[\s\n\r0-9a-z;:\/\."',]{0,}\}{0,}\}/gmi; // Instruction regexps function readInstructions (instructions, fullCode, forDomains=false) { - const syntaxTree = { firewall: false }; + const syntaxTree = { firewall: false, encryption: {} }; for (let instruction of instructions) { if (!instruction) { @@ -39,6 +39,22 @@ function readInstructions (instructions, fullCode, forDomains=false) { syntaxTree.target = instructionParts[1]; break; case "cert": + if (instructionParts.length < 3) + throw new SyntaxError("Invalid syntax of operator `cert`: cert