UNPKG

http-proxy-3

Version:
236 lines (235 loc) 9.94 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ProxyServer = void 0; const http = __importStar(require("node:http")); const http2 = __importStar(require("node:http2")); const web_incoming_1 = require("./passes/web-incoming"); const ws_incoming_1 = require("./passes/ws-incoming"); const node_events_1 = require("node:events"); const debug_1 = __importDefault(require("debug")); const common_1 = require("./common"); const log = (0, debug_1.default)("http-proxy-3"); class ProxyServer extends node_events_1.EventEmitter { /** * Creates the proxy server with specified options. * @param options - Config object passed to the proxy */ constructor(options = {}) { super(); // createRightProxy - Returns a function that when called creates the loader for // either `ws` or `web`'s passes. this.createRightProxy = (type) => { log("createRightProxy", { type }); return (options) => { return (...args /* req, res, [head], [opts] */) => { const req = args[0]; log("proxy: ", { type, path: req.url }); const res = args[1]; const passes = type === "ws" ? this.wsPasses : this.webPasses; if (type == "ws") { // socket -- proxy websocket errors to our error handler; // see https://github.com/sagemathinc/http-proxy-3/issues/5 // NOTE: as mentioned below, res is the socket in this case. // One of the passes does add an error handler, but there's no // guarantee we even get to that pass before something bad happens, // and there's no way for a user of http-proxy-3 to get ahold // of this res object and attach their own error handler until // after the passes. So we better attach one ASAP right here: res.on("error", (err) => { this.emit("error", err, req, res); }); } let counter = args.length - 1; let head; let cb; // optional args parse begin if (typeof args[counter] === "function") { cb = args[counter]; counter--; } let requestOptions; if (!(args[counter] instanceof Buffer) && args[counter] !== res) { // Copy global options, and overwrite with request options requestOptions = { ...options, ...args[counter] }; counter--; } else { requestOptions = { ...options }; } if (args[counter] instanceof Buffer) { head = args[counter]; } for (const e of ["target", "forward"]) { if (typeof requestOptions[e] === "string") { requestOptions[e] = (0, common_1.toURL)(requestOptions[e]); } } if (!requestOptions.target && !requestOptions.forward) { this.emit("error", new Error("Must set target or forward"), req, res); return; } for (const pass of passes) { /** * Call of passes functions * pass(req, res, options, head) * * In WebSockets case, the `res` variable * refer to the connection socket * pass(req, socket, options, head) */ if (pass(req, res, requestOptions, head, this, cb)) { // passes can return a truthy value to halt the loop break; } } }; }; }; this.onError = (err) => { // Force people to handle their own errors if (this.listeners("error").length === 1) { throw err; } }; /** * A function that wraps the object in a webserver, for your convenience * @param port - Port to listen on * @param hostname - The hostname to listen on */ this.listen = (port, hostname) => { log("listen", { port, hostname }); const requestListener = (req, res) => { this.web(req, res); }; this._server = this.options.ssl ? http2.createSecureServer({ ...this.options.ssl, allowHTTP1: true }, requestListener) : http.createServer(requestListener); if (this.options.ws) { this._server.on("upgrade", (req, socket, head) => { this.ws(req, socket, head); }); } this._server.listen(port, hostname); return this; }; // if the proxy started its own http server, this is the address of that server. this.address = () => { return this._server?.address(); }; /** * A function that closes the inner webserver and stops listening on given port */ this.close = (cb) => { if (this._server == null) { cb?.(); return; } // Wrap cb anb nullify server after all open connections are closed. this._server.close((err) => { this._server = null; cb?.(err); }); }; this.before = (type, passName, cb) => { if (type !== "ws" && type !== "web") { throw new Error("type must be `web` or `ws`"); } const passes = (type === "ws" ? this.wsPasses : this.webPasses); let i = false; passes.forEach((v, idx) => { if (v.name === passName) { i = idx; } }); if (i === false) { throw new Error("No such pass"); } passes.splice(i, 0, cb); }; this.after = (type, passName, cb) => { if (type !== "ws" && type !== "web") { throw new Error("type must be `web` or `ws`"); } const passes = (type === "ws" ? this.wsPasses : this.webPasses); let i = false; passes.forEach((v, idx) => { if (v.name === passName) { i = idx; } }); if (i === false) { throw new Error("No such pass"); } passes.splice(i++, 0, cb); }; log("creating a ProxyServer", options); options.prependPath = options.prependPath !== false; this.options = options; this.web = this.createRightProxy("web")(options); this.ws = this.createRightProxy("ws")(options); this.webPasses = Object.values(web_incoming_1.WEB_PASSES); this.wsPasses = Object.values(ws_incoming_1.WS_PASSES); this.on("error", this.onError); } /** * Creates the proxy server with specified options. * @param options Config object passed to the proxy * @returns Proxy object with handlers for `ws` and `web` requests */ static createProxyServer(options) { return new ProxyServer(options); } /** * Creates the proxy server with specified options. * @param options Config object passed to the proxy * @returns Proxy object with handlers for `ws` and `web` requests */ static createServer(options) { return new ProxyServer(options); } /** * Creates the proxy server with specified options. * @param options Config object passed to the proxy * @returns Proxy object with handlers for `ws` and `web` requests */ static createProxy(options) { return new ProxyServer(options); } } exports.ProxyServer = ProxyServer;