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; 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(); const address = this.adr.address ?? "127.0.0.1"; 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) => auth(this, req, res, next)); this.app.use("*", async (req, res) => { logger("REQ HEADERS:>", req.headers); 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(req, res); } return fetchProxy(req, res, reverseTo); }); 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(`HTTP reverse proxy server listened at ${ address }:${this.adr.port}`); }) } };