UNPKG

@ajejoseph22/proxx

Version:

A lightweight HTTPS/HTTP proxy server with bandwidth tracking, basic auth and real-time analytics.

162 lines (161 loc) 7.57 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Proxx = void 0; const express_1 = __importDefault(require("express")); const http_proxy_middleware_1 = require("http-proxy-middleware"); const http_1 = __importDefault(require("http")); const net_1 = __importDefault(require("net")); const url_1 = require("url"); const metrics_service_1 = require("./services/metrics-service"); const auth_service_1 = require("./services/auth-service"); const database_service_1 = require("./services/database-service"); class Proxx { constructor(port) { this.port = port; this.app = (0, express_1.default)(); this.server = http_1.default.createServer(this.app); this.dbService = new database_service_1.DatabaseService(); this.metricsService = new metrics_service_1.MetricsService(this.dbService); this.authService = new auth_service_1.AuthService(this.dbService); this.endpointPaths = ["/metrics"]; this.setupMiddleware(); this.setupEndpoints(); this.setupProxy(); } isProxyRequest(req) { return !this.endpointPaths.includes(req.url); } setupMiddleware() { this.app.use((req, res, next) => __awaiter(this, void 0, void 0, function* () { const { isAuthenticated, message, code } = this.isProxyRequest(req) ? yield this.authService.proxyAuth(req, res) : yield this.authService.endpointAuth(req, res); if (!isAuthenticated) { res.status(code).send(message); return; } next(); })); } setupProxy() { this.app.use("/", (0, http_proxy_middleware_1.createProxyMiddleware)({ target: "https://example.com", router: (req) => { console.log(`Proxying request to: ${req.url}`); return req.url; }, changeOrigin: true, on: { proxyReq: (_, req, res) => { let requestBytes = 0; req.on("data", (chunk) => { requestBytes += chunk.length; }); req.on("end", () => __awaiter(this, void 0, void 0, function* () { req.requestBytes = requestBytes; })); }, proxyRes: (proxyRes, req, res) => { const url = new url_1.URL(req.url || "", `http://${req.headers.host}`) .hostname; let responseBytes = 0; proxyRes.on("data", (chunk) => { responseBytes += chunk.length; }); proxyRes.on("end", () => __awaiter(this, void 0, void 0, function* () { console.log("Request bytes:", req.requestBytes); console.log("Response bytes:", responseBytes); const totalBytes = (req.requestBytes || 0) + responseBytes; yield this.metricsService.updateMetrics(url, totalBytes); })); }, }, })); } setupEndpoints() { this.app.get("/metrics", (_, res) => { const metrics = this.metricsService.getAllMetrics(); res.json(metrics); }); } start() { return __awaiter(this, void 0, void 0, function* () { yield this.dbService.initialize(); yield this.authService.initialize(); this.server = this.app.listen(this.port, () => { console.log(`Proxx is running on port ${this.port}`); }); // Handle HTTPS tunneling (CONNECT requests) this.server.on("connect", (req, clientSocket, head) => __awaiter(this, void 0, void 0, function* () { const { isAuthenticated, message, code } = yield this.authService.proxyAuth(req); if (!isAuthenticated) { clientSocket.write(`HTTP/1.1 ${code} ${message}`); clientSocket.end(); return; } if (!req.url) { console.error("Invalid CONNECT request:", req.url); clientSocket.end("HTTP/1.1 400 Bad Request\r\n\r\n"); return; } const [hostname, port] = req.url.split(":"); if (!hostname || !port) { console.error("Invalid CONNECT request:", req.url); clientSocket.end("HTTP/1.1 400 Bad Request\r\n\r\n"); return; } // Establish TCP connection to the target server const serverSocket = net_1.default.connect(Number(port), hostname, () => { console.log(`Tunnel established to ${hostname}:${port}`); clientSocket.write("HTTP/1.1 200 Connection Established\r\n\r\n"); serverSocket.write(head); serverSocket.pipe(clientSocket); clientSocket.pipe(serverSocket); }); let clientBytes = 0; let serverBytes = 0; clientSocket.on("data", (chunk) => { clientBytes += chunk.length; }); serverSocket.on("data", (chunk) => { serverBytes += chunk.length; }); clientSocket.on("end", () => __awaiter(this, void 0, void 0, function* () { console.log("Client bytes:", clientBytes); console.log("Server bytes:", serverBytes); const totalBytes = clientBytes + serverBytes; yield this.metricsService.updateMetrics(hostname, totalBytes); })); serverSocket.on("error", (err) => { console.error(`Error in tunnel to ${hostname}:${port} - ${err.message}`); clientSocket.end("HTTP/1.1 500 Internal Server Error\r\n\r\n"); }); clientSocket.on("error", (err) => { console.error(`Client socket error: ${err.message}`); }); })); }); } stop() { return __awaiter(this, void 0, void 0, function* () { var _a; console.log("Gracefully shutting down Proxx..."); const metrics = this.metricsService.getAllMetrics(); console.log("Total Metrics:", JSON.stringify(metrics, null, 2)); (_a = this.server) === null || _a === void 0 ? void 0 : _a.close(); }); } } exports.Proxx = Proxx;