index.mjs 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857
  1. import httpNative, { request } from "node:http";
  2. import httpsNative, { request as request$1 } from "node:https";
  3. import http2 from "node:http2";
  4. import { EventEmitter } from "node:events";
  5. import "node:net";
  6. import { Readable } from "node:stream";
  7. const upgradeHeader = /(^|,)\s*upgrade\s*($|,)/i;
  8. const transferEncodingConnectionToken = /(^|,)\s*transfer-encoding\s*($|,)/i;
  9. const defaultAgents = {
  10. http: new httpNative.Agent({
  11. keepAlive: true,
  12. maxSockets: 256,
  13. maxFreeSockets: 64
  14. }),
  15. https: new httpsNative.Agent({
  16. keepAlive: true,
  17. maxSockets: 256,
  18. maxFreeSockets: 64
  19. })
  20. };
  21. const isSSL = /^https|wss/;
  22. function forceConnectionCloseForTransferEncoding(headers) {
  23. let carriesTransferEncoding = false;
  24. for (const key in headers) if (headers[key] !== void 0 && key.toLowerCase() === "transfer-encoding") {
  25. carriesTransferEncoding = true;
  26. break;
  27. }
  28. const connection = headers.connection;
  29. const connectionMarksTransferEncoding = typeof connection === "string" && transferEncodingConnectionToken.test(connection);
  30. if (carriesTransferEncoding || connectionMarksTransferEncoding) headers.connection = "close";
  31. }
  32. const HTTP2_HEADER_BLACKLIST = [
  33. ":method",
  34. ":path",
  35. ":scheme",
  36. ":authority"
  37. ];
  38. function setupOutgoing(outgoing, options, req, forward) {
  39. outgoing.port = options[forward || "target"].port || (isSSL.test(options[forward || "target"].protocol ?? "http") ? 443 : 80);
  40. for (const e of [
  41. "host",
  42. "hostname",
  43. "socketPath",
  44. "pfx",
  45. "key",
  46. "passphrase",
  47. "cert",
  48. "ca",
  49. "ciphers",
  50. "secureProtocol"
  51. ]) {
  52. const value = options[forward || "target"][e];
  53. if (value !== void 0) outgoing[e] = value;
  54. }
  55. if (outgoing.host === void 0 && typeof outgoing.hostname === "string") {
  56. const bracketedHost = outgoing.hostname.includes(":") && !outgoing.hostname.startsWith("[") ? `[${outgoing.hostname}]` : outgoing.hostname;
  57. outgoing.host = outgoing.port ? `${bracketedHost}:${outgoing.port}` : bracketedHost;
  58. }
  59. outgoing.method = options.method || req.method;
  60. outgoing.headers = { ...req.headers };
  61. if (req.headers?.[":authority"]) outgoing.headers.host = req.headers[":authority"];
  62. if (options.headers) for (const key of Object.keys(options.headers)) outgoing.headers[key] = options.headers[key];
  63. if (req.httpVersionMajor > 1) for (const header of HTTP2_HEADER_BLACKLIST) delete outgoing.headers[header];
  64. if (options.auth) outgoing.auth = options.auth;
  65. if (options.ca) outgoing.ca = options.ca;
  66. if (isSSL.test(options[forward || "target"].protocol ?? "http")) outgoing.rejectUnauthorized = options.secure === void 0 ? true : options.secure;
  67. if (options.agent !== void 0) outgoing.agent = options.agent || false;
  68. else if (req.httpVersionMajor > 1 || upgradeHeader.test(req.headers.connection || "")) outgoing.agent = false;
  69. else {
  70. const targetProto = options[forward || "target"].protocol ?? "http";
  71. outgoing.agent = isSSL.test(targetProto) ? defaultAgents.https : defaultAgents.http;
  72. }
  73. outgoing.localAddress = options.localAddress;
  74. if (!outgoing.agent) {
  75. outgoing.headers = outgoing.headers || {};
  76. if (typeof outgoing.headers.connection !== "string" || !upgradeHeader.test(outgoing.headers.connection)) outgoing.headers.connection = "close";
  77. }
  78. outgoing.headers = outgoing.headers || {};
  79. forceConnectionCloseForTransferEncoding(outgoing.headers);
  80. const target = options[forward || "target"];
  81. const targetPath = target && options.prependPath !== false ? target.pathname || "" : "";
  82. const targetSearch = target instanceof URL && options.prependPath !== false ? target.search || "" : "";
  83. const reqUrl = req.url || "";
  84. const qIdx = reqUrl.indexOf("?");
  85. const reqPath = qIdx === -1 ? reqUrl : reqUrl.slice(0, qIdx);
  86. const reqSearch = qIdx === -1 ? "" : reqUrl.slice(qIdx);
  87. const normalizedPath = reqPath ? reqPath[0] === "/" ? reqPath : "/" + reqPath : "/";
  88. let outgoingPath = options.toProxy ? "/" + reqUrl : normalizedPath + reqSearch;
  89. outgoingPath = options.ignorePath ? "" : outgoingPath;
  90. let fullPath = joinURL(targetPath, outgoingPath);
  91. if (targetSearch) fullPath = fullPath.includes("?") ? fullPath.replace("?", targetSearch + "&") : fullPath + targetSearch;
  92. outgoing.path = fullPath;
  93. if (options.changeOrigin) outgoing.headers.host = requiresPort(outgoing.port, options[forward || "target"].protocol) && !hasPort(outgoing.host) ? outgoing.host + ":" + outgoing.port : outgoing.host ?? void 0;
  94. return outgoing;
  95. }
  96. function joinURL(base, path) {
  97. if (!base || base === "/") return path || "/";
  98. if (!path || path === "/") return base || "/";
  99. const baseHasTrailing = base[base.length - 1] === "/";
  100. const pathHasLeading = path[0] === "/";
  101. if (baseHasTrailing && pathHasLeading) return base + path.slice(1);
  102. if (!baseHasTrailing && !pathHasLeading) return base + "/" + path;
  103. return base + path;
  104. }
  105. function setupSocket(socket) {
  106. socket.setTimeout(0);
  107. socket.setNoDelay(true);
  108. socket.setKeepAlive(true, 0);
  109. return socket;
  110. }
  111. function getPort(req) {
  112. const hostHeader = req.headers[":authority"] || req.headers.host;
  113. const res = hostHeader ? hostHeader.match(/:(\d+)/) : "";
  114. if (res) return res[1];
  115. return hasEncryptedConnection(req) ? "443" : "80";
  116. }
  117. function hasEncryptedConnection(req) {
  118. const socket = req.socket;
  119. return !!socket && "encrypted" in socket && socket.encrypted;
  120. }
  121. function rewriteCookieProperty(header, config, property) {
  122. if (Array.isArray(header)) return header.map(function(headerElement) {
  123. return rewriteCookieProperty(headerElement, config, property);
  124. });
  125. return header.replace(new RegExp(String.raw`(;\s*` + property + "=)([^;]+)", "i"), function(match, prefix, previousValue) {
  126. let newValue;
  127. if (previousValue in config) newValue = config[previousValue];
  128. else if ("*" in config) newValue = config["*"];
  129. else return match;
  130. return newValue ? prefix + newValue : "";
  131. });
  132. }
  133. function parseAddr(addr) {
  134. if (typeof addr === "string") {
  135. if (addr.startsWith("unix:")) return { socketPath: addr.slice(5) };
  136. const url = new URL(addr);
  137. return {
  138. host: url.hostname,
  139. port: Number(url.port) || (isSSL.test(url.protocol) ? 443 : 80)
  140. };
  141. }
  142. if (!addr.socketPath && !addr.port) throw new Error("ProxyAddr must have either `port` or `socketPath`");
  143. return addr;
  144. }
  145. function hasPort(host) {
  146. return host ? !!~host.indexOf(":") : false;
  147. }
  148. function requiresPort(_port, _protocol) {
  149. const protocol = _protocol?.split(":")[0];
  150. const port = +_port;
  151. if (!port) return false;
  152. switch (protocol) {
  153. case "http":
  154. case "ws": return port !== 80;
  155. case "https":
  156. case "wss": return port !== 443;
  157. case "ftp": return port !== 21;
  158. case "gopher": return port !== 70;
  159. case "file": return false;
  160. }
  161. return port !== 0;
  162. }
  163. function defineProxyMiddleware(m) {
  164. return m;
  165. }
  166. function defineProxyOutgoingMiddleware(m) {
  167. return m;
  168. }
  169. const redirectRegex = /^201|30([12378])$/;
  170. const webOutgoingMiddleware = [
  171. defineProxyOutgoingMiddleware((req, res, proxyRes) => {
  172. if (req.httpVersion === "1.0" || req.httpVersionMajor >= 2 || proxyRes.statusCode === 204 || proxyRes.statusCode === 304) delete proxyRes.headers["transfer-encoding"];
  173. }),
  174. defineProxyOutgoingMiddleware((req, res, proxyRes) => {
  175. if (req.httpVersion === "1.0") proxyRes.headers.connection = req.headers.connection || "close";
  176. else if (req.httpVersionMajor < 2 && !proxyRes.headers.connection) proxyRes.headers.connection = req.headers.connection || "keep-alive";
  177. else if (req.httpVersionMajor >= 2) delete proxyRes.headers.connection;
  178. }),
  179. defineProxyOutgoingMiddleware((req, res, proxyRes, options) => {
  180. if ((options.hostRewrite || options.autoRewrite || options.protocolRewrite) && proxyRes.headers.location && redirectRegex.test(String(proxyRes.statusCode))) {
  181. const target = _toURL(options.target);
  182. const keepProtocolRelative = proxyRes.headers.location.startsWith("//") && !options.protocolRewrite;
  183. const u = new URL(proxyRes.headers.location, target);
  184. if (target.host !== u.host) return;
  185. if (options.hostRewrite) u.host = options.hostRewrite;
  186. else if (options.autoRewrite) {
  187. if (req.headers[":authority"]) u.host = req.headers[":authority"];
  188. else if (req.headers.host) u.host = req.headers.host;
  189. }
  190. if (options.protocolRewrite) u.protocol = options.protocolRewrite;
  191. proxyRes.headers.location = keepProtocolRelative ? u.href.slice(u.protocol.length) : u.href;
  192. }
  193. }),
  194. defineProxyOutgoingMiddleware((req, res, proxyRes, options) => {
  195. const rewriteCookieDomainConfig = typeof options.cookieDomainRewrite === "string" ? { "*": options.cookieDomainRewrite } : options.cookieDomainRewrite;
  196. const rewriteCookiePathConfig = typeof options.cookiePathRewrite === "string" ? { "*": options.cookiePathRewrite } : options.cookiePathRewrite;
  197. const preserveHeaderKeyCase = options.preserveHeaderKeyCase;
  198. let rawHeaderKeyMap;
  199. const setHeader = function(key, header) {
  200. if (header === void 0 || !String(key).trim()) return;
  201. if (rewriteCookieDomainConfig && key.toLowerCase() === "set-cookie") header = rewriteCookieProperty(header, rewriteCookieDomainConfig, "domain");
  202. if (rewriteCookiePathConfig && key.toLowerCase() === "set-cookie") header = rewriteCookieProperty(header, rewriteCookiePathConfig, "path");
  203. try {
  204. res.setHeader(String(key).trim(), header);
  205. } catch {}
  206. };
  207. if (preserveHeaderKeyCase && proxyRes.rawHeaders !== void 0) {
  208. rawHeaderKeyMap = {};
  209. for (let i = 0; i < proxyRes.rawHeaders.length; i += 2) {
  210. const key = proxyRes.rawHeaders[i];
  211. rawHeaderKeyMap[key.toLowerCase()] = key;
  212. }
  213. }
  214. for (let key of Object.keys(proxyRes.headers)) {
  215. const header = proxyRes.headers[key];
  216. if (preserveHeaderKeyCase && rawHeaderKeyMap) key = rawHeaderKeyMap[key] || key;
  217. setHeader(key, header);
  218. }
  219. }),
  220. defineProxyOutgoingMiddleware((req, res, proxyRes) => {
  221. res.statusCode = proxyRes.statusCode;
  222. if (proxyRes.statusMessage && req.httpVersionMajor < 2) res.statusMessage = proxyRes.statusMessage;
  223. })
  224. ];
  225. function _toURL(target) {
  226. if (target instanceof URL) return target;
  227. if (typeof target === "string") return new URL(target);
  228. const protocol = target.protocol || "http:";
  229. const host = target.host || target.hostname || "localhost";
  230. const port = target.port;
  231. return new URL(`${protocol}//${host}${port ? ":" + port : ""}`);
  232. }
  233. const nativeAgents = {
  234. http: httpNative,
  235. https: httpsNative
  236. };
  237. const redirectStatuses = /* @__PURE__ */ new Set([
  238. 301,
  239. 302,
  240. 303,
  241. 307,
  242. 308
  243. ]);
  244. const webIncomingMiddleware = [
  245. defineProxyMiddleware((req) => {
  246. if ((req.method === "DELETE" || req.method === "OPTIONS") && !req.headers["content-length"] && !req.headers["transfer-encoding"]) req.headers["content-length"] = "0";
  247. }),
  248. defineProxyMiddleware((req, res, options) => {
  249. if (options.timeout) req.socket.setTimeout(options.timeout, () => {
  250. req.socket.destroy();
  251. });
  252. }),
  253. defineProxyMiddleware((req, res, options) => {
  254. if (!options.xfwd) return;
  255. const encrypted = req.isSpdy || hasEncryptedConnection(req);
  256. const values = {
  257. for: req.connection.remoteAddress || req.socket.remoteAddress,
  258. port: getPort(req),
  259. proto: encrypted ? "https" : "http"
  260. };
  261. for (const header of [
  262. "for",
  263. "port",
  264. "proto"
  265. ]) {
  266. const key = "x-forwarded-" + header;
  267. if (!req.headers[key] && values[header] !== void 0) req.headers[key] = values[header];
  268. }
  269. req.headers["x-forwarded-host"] = req.headers["x-forwarded-host"] || req.headers[":authority"] || req.headers.host || "";
  270. }),
  271. defineProxyMiddleware((req, res, options, server, head, callback) => {
  272. server.emit("start", req, res, options.target || options.forward);
  273. const http = nativeAgents.http;
  274. const https = nativeAgents.https;
  275. const maxRedirects = typeof options.followRedirects === "number" ? options.followRedirects : options.followRedirects ? 5 : 0;
  276. if (options.forward) {
  277. const forwardReq = (isSSL.test(options.forward.protocol || "http") ? https : http).request(setupOutgoing(options.ssl || {}, options, req, "forward"));
  278. const forwardError = createErrorHandler(forwardReq, options.forward);
  279. req.on("error", forwardError);
  280. forwardReq.on("error", forwardError);
  281. (options.buffer || req).pipe(forwardReq);
  282. if (!options.target) {
  283. res.end();
  284. return;
  285. }
  286. }
  287. const proxyReq = (isSSL.test(options.target.protocol || "http") ? https : http).request(setupOutgoing(options.ssl || {}, options, req));
  288. proxyReq.on("socket", (_socket) => {
  289. if (server && !proxyReq.getHeader("expect")) server.emit("proxyReq", proxyReq, req, res, options);
  290. });
  291. if (options.proxyTimeout) proxyReq.setTimeout(options.proxyTimeout, function() {
  292. proxyReq.destroy();
  293. });
  294. res.on("close", function() {
  295. if (!res.writableFinished) proxyReq.destroy();
  296. });
  297. const proxyError = createErrorHandler(proxyReq, options.target);
  298. req.on("error", proxyError);
  299. proxyReq.on("error", proxyError);
  300. function createErrorHandler(proxyReq, url) {
  301. return function proxyError(err) {
  302. if (!req.socket?.writable && err.code === "ECONNRESET") {
  303. server.emit("econnreset", err, req, res, url);
  304. return proxyReq.destroy();
  305. }
  306. if (callback) callback(err, req, res, url);
  307. else server.emit("error", err, req, res, url);
  308. };
  309. }
  310. let bodyBuffer;
  311. if (maxRedirects > 0) {
  312. const chunks = [];
  313. const source = options.buffer || req;
  314. source.on("data", (chunk) => {
  315. chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
  316. proxyReq.write(chunk);
  317. });
  318. source.on("end", () => {
  319. bodyBuffer = Buffer.concat(chunks);
  320. proxyReq.end();
  321. });
  322. source.on("error", (err) => {
  323. proxyReq.destroy(err);
  324. });
  325. } else proxyReq.on("socket", (socket) => {
  326. if (socket.pending) socket.on("connect", () => (options.buffer || req).pipe(proxyReq));
  327. else (options.buffer || req).pipe(proxyReq);
  328. });
  329. function handleResponse(proxyRes, redirectCount, currentUrl) {
  330. const statusCode = proxyRes.statusCode;
  331. if (maxRedirects > 0 && redirectStatuses.has(statusCode) && redirectCount < maxRedirects && proxyRes.headers.location) {
  332. proxyRes.resume();
  333. const location = new URL(proxyRes.headers.location, currentUrl);
  334. const preserveMethod = statusCode === 307 || statusCode === 308;
  335. const redirectMethod = preserveMethod ? req.method || "GET" : "GET";
  336. const isHTTPS = isSSL.test(location.protocol);
  337. const agent = isHTTPS ? https : http;
  338. const redirectHeaders = { ...req.headers };
  339. if (options.headers) Object.assign(redirectHeaders, options.headers);
  340. redirectHeaders.host = location.host;
  341. if (location.host !== currentUrl.host) {
  342. delete redirectHeaders.authorization;
  343. delete redirectHeaders.cookie;
  344. }
  345. if (!preserveMethod) {
  346. delete redirectHeaders["content-length"];
  347. delete redirectHeaders["content-type"];
  348. delete redirectHeaders["transfer-encoding"];
  349. }
  350. forceConnectionCloseForTransferEncoding(redirectHeaders);
  351. const redirectOpts = {
  352. hostname: location.hostname,
  353. port: location.port || (isHTTPS ? 443 : 80),
  354. path: location.pathname + location.search,
  355. method: redirectMethod,
  356. headers: redirectHeaders,
  357. agent: options.agent || false
  358. };
  359. if (isHTTPS) redirectOpts.rejectUnauthorized = options.secure === void 0 ? true : options.secure;
  360. const redirectReq = agent.request(redirectOpts);
  361. if (server && !redirectReq.getHeader("expect")) server.emit("proxyReq", redirectReq, req, res, options);
  362. if (options.proxyTimeout) redirectReq.setTimeout(options.proxyTimeout, () => {
  363. redirectReq.destroy();
  364. });
  365. const redirectError = createErrorHandler(redirectReq, location);
  366. redirectReq.on("error", redirectError);
  367. redirectReq.on("response", (nextRes) => {
  368. handleResponse(nextRes, redirectCount + 1, location);
  369. });
  370. if (preserveMethod && bodyBuffer && bodyBuffer.length > 0) redirectReq.end(bodyBuffer);
  371. else redirectReq.end();
  372. return;
  373. }
  374. if (server) server.emit("proxyRes", proxyRes, req, res);
  375. if (!res.headersSent && !options.selfHandleResponse) {
  376. for (const pass of webOutgoingMiddleware) if (pass(req, res, proxyRes, options)) break;
  377. }
  378. if (res.finished) {
  379. if (server) server.emit("end", req, res, proxyRes);
  380. } else {
  381. res.on("close", function() {
  382. proxyRes.destroy();
  383. });
  384. proxyRes.on("close", function() {
  385. if (!proxyRes.complete && !res.destroyed) res.destroy();
  386. });
  387. proxyRes.on("error", function(err) {
  388. if (!res.destroyed) res.destroy(err);
  389. if (server.listenerCount("error") > 0) server.emit("error", err, req, res, currentUrl);
  390. });
  391. proxyRes.on("end", function() {
  392. if (server) server.emit("end", req, res, proxyRes);
  393. });
  394. if (!options.selfHandleResponse) proxyRes.pipe(res);
  395. }
  396. }
  397. proxyReq.on("response", function(proxyRes) {
  398. handleResponse(proxyRes, 0, options.target);
  399. });
  400. })
  401. ];
  402. const websocketIncomingMiddleware = [
  403. defineProxyMiddleware((req, socket) => {
  404. if (req.method !== "GET" || !req.headers.upgrade) {
  405. socket.destroy();
  406. return true;
  407. }
  408. if (req.headers.upgrade.toLowerCase() !== "websocket") {
  409. socket.destroy();
  410. return true;
  411. }
  412. }),
  413. defineProxyMiddleware((req, socket, options) => {
  414. if (!options.xfwd) return;
  415. const values = {
  416. for: req.connection.remoteAddress || req.socket.remoteAddress,
  417. port: getPort(req),
  418. proto: hasEncryptedConnection(req) ? "wss" : "ws"
  419. };
  420. for (const header of [
  421. "for",
  422. "port",
  423. "proto"
  424. ]) {
  425. const key = "x-forwarded-" + header;
  426. if (!req.headers[key] && values[header] !== void 0) req.headers[key] = values[header];
  427. }
  428. }),
  429. defineProxyMiddleware((req, socket, options, server, head, callback) => {
  430. const createHttpHeader = function(line, headers) {
  431. return Object.keys(headers).reduce(function(head, key) {
  432. const value = headers[key];
  433. if (!Array.isArray(value)) {
  434. head.push(key + ": " + value);
  435. return head;
  436. }
  437. for (const element of value) head.push(key + ": " + element);
  438. return head;
  439. }, [line]).join("\r\n") + "\r\n\r\n";
  440. };
  441. setupSocket(socket);
  442. if (head && head.length > 0) socket.unshift(head);
  443. socket.on("error", onSocketError);
  444. const proxyReq = (isSSL.test(options.target.protocol || "http") ? httpsNative : httpNative).request(setupOutgoing(options.ssl || {}, options, req));
  445. if (server) server.emit("proxyReqWs", proxyReq, req, socket, options, head);
  446. proxyReq.on("error", onOutgoingError);
  447. proxyReq.on("response", function(res) {
  448. if (!res.upgrade) if (!socket.destroyed && socket.writable) {
  449. socket.write(createHttpHeader("HTTP/" + res.httpVersion + " " + res.statusCode + " " + res.statusMessage, res.headers));
  450. res.on("error", onOutgoingError);
  451. res.pipe(socket);
  452. } else res.resume();
  453. });
  454. proxyReq.on("upgrade", function(proxyRes, proxySocket, proxyHead) {
  455. proxySocket.on("error", onOutgoingError);
  456. proxySocket.on("end", function() {
  457. server.emit("close", proxyRes, proxySocket, proxyHead);
  458. });
  459. socket.removeListener("error", onSocketError);
  460. socket.on("error", function() {
  461. proxySocket.end();
  462. });
  463. setupSocket(proxySocket);
  464. if (proxyHead && proxyHead.length > 0) proxySocket.unshift(proxyHead);
  465. socket.write(createHttpHeader("HTTP/1.1 101 Switching Protocols", proxyRes.headers));
  466. proxySocket.pipe(socket).pipe(proxySocket);
  467. server.emit("open", proxySocket);
  468. server.emit("proxySocket", proxySocket);
  469. });
  470. proxyReq.end();
  471. function onSocketError(err) {
  472. if (callback) callback(err, req, socket);
  473. else server.emit("error", err, req, socket);
  474. proxyReq.destroy();
  475. }
  476. function onOutgoingError(err) {
  477. if (callback) callback(err, req, socket);
  478. else server.emit("error", err, req, socket);
  479. socket.end();
  480. }
  481. })
  482. ];
  483. var ProxyServer = class extends EventEmitter {
  484. _server;
  485. _webPasses = [...webIncomingMiddleware];
  486. _wsPasses = [...websocketIncomingMiddleware];
  487. options;
  488. web;
  489. ws;
  490. constructor(options = {}) {
  491. super();
  492. this.options = options || {};
  493. this.options.prependPath = options.prependPath !== false;
  494. this.web = _createProxyFn("web", this);
  495. this.ws = _createProxyFn("ws", this);
  496. }
  497. listen(port, hostname, listeningListener) {
  498. const closure = (req, res) => {
  499. return this.web(req, res);
  500. };
  501. if (this.options.http2) {
  502. if (!this.options.ssl) throw new Error("HTTP/2 requires ssl option");
  503. this._server = http2.createSecureServer({
  504. ...this.options.ssl,
  505. allowHTTP1: true
  506. }, closure);
  507. } else if (this.options.ssl) this._server = httpsNative.createServer(this.options.ssl, closure);
  508. else this._server = httpNative.createServer(closure);
  509. if (this.options.ws) this._server.on("upgrade", (req, socket, head) => {
  510. this.ws(req, socket, this.options, head).catch(() => {});
  511. });
  512. this._server.listen(port, hostname, listeningListener);
  513. return this;
  514. }
  515. close(callback) {
  516. if (this._server) this._server.close((...args) => {
  517. this._server = void 0;
  518. if (callback) Reflect.apply(callback, void 0, args);
  519. });
  520. }
  521. before(type, passName, pass) {
  522. if (type !== "ws" && type !== "web") throw new Error("type must be `web` or `ws`");
  523. const passes = this._getPasses(type);
  524. let i = false;
  525. for (const [idx, v] of passes.entries()) if (v.name === passName) i = idx;
  526. if (i === false) throw new Error("No such pass");
  527. passes.splice(i, 0, pass);
  528. }
  529. after(type, passName, pass) {
  530. if (type !== "ws" && type !== "web") throw new Error("type must be `web` or `ws`");
  531. const passes = this._getPasses(type);
  532. let i = false;
  533. for (const [idx, v] of passes.entries()) if (v.name === passName) i = idx;
  534. if (i === false) throw new Error("No such pass");
  535. passes.splice(i++, 0, pass);
  536. }
  537. _getPasses(type) {
  538. return type === "ws" ? this._wsPasses : this._webPasses;
  539. }
  540. };
  541. function createProxyServer(options = {}) {
  542. return new ProxyServer(options);
  543. }
  544. function _createProxyFn(type, server) {
  545. return function(req, res, opts, head) {
  546. const requestOptions = {
  547. ...opts,
  548. ...server.options
  549. };
  550. for (const key of ["target", "forward"]) if (typeof requestOptions[key] === "string") requestOptions[key] = new URL(requestOptions[key]);
  551. if (!requestOptions.target && !requestOptions.forward) {
  552. this.emit("error", /* @__PURE__ */ new Error("Must provide a proper URL as target"));
  553. return Promise.resolve();
  554. }
  555. let _resolve;
  556. let _reject;
  557. const callbackPromise = new Promise((resolve, reject) => {
  558. _resolve = resolve;
  559. _reject = reject;
  560. });
  561. res.on("close", () => {
  562. _resolve();
  563. });
  564. res.on("error", (error) => {
  565. _reject(error);
  566. });
  567. for (const pass of server._getPasses(type)) {
  568. let stop;
  569. try {
  570. stop = pass(req, res, requestOptions, server, head, (error, _req, _res, url) => {
  571. if (server.listenerCount("error") > 0) {
  572. server.emit("error", error, req, res, url);
  573. _resolve();
  574. } else _reject(error);
  575. });
  576. } catch (error) {
  577. if (server.listenerCount("error") > 0) {
  578. server.emit("error", error, req, res, requestOptions.target || requestOptions.forward);
  579. _resolve();
  580. } else _reject(error);
  581. break;
  582. }
  583. if (stop) {
  584. _resolve();
  585. break;
  586. }
  587. }
  588. return callbackPromise;
  589. };
  590. }
  591. async function proxyFetch(addr, input, inputInit, opts) {
  592. const resolvedAddr = parseAddr(addr);
  593. let useHTTPS = false;
  594. let addrBasePath = "";
  595. if (typeof addr === "string" && !addr.startsWith("unix:")) {
  596. const addrURL = new URL(addr);
  597. useHTTPS = isSSL.test(addrURL.protocol);
  598. if (addrURL.pathname && addrURL.pathname !== "/") addrBasePath = addrURL.pathname;
  599. }
  600. let url;
  601. let init;
  602. if (input instanceof Request) {
  603. url = new URL(input.url);
  604. init = {
  605. ...toInit(input),
  606. ...toInit(inputInit)
  607. };
  608. } else {
  609. url = new URL(input);
  610. init = toInit(inputInit);
  611. }
  612. init = {
  613. redirect: "manual",
  614. ...init
  615. };
  616. if (init.body) init.duplex = "half";
  617. const requestPath = url.pathname + url.search;
  618. const path = addrBasePath ? joinURL(addrBasePath, requestPath) : requestPath;
  619. const reqHeaders = {};
  620. if (init.headers) if (!(init.headers instanceof Headers) && !Array.isArray(init.headers)) Object.assign(reqHeaders, init.headers);
  621. else for (const [key, value] of init.headers) {
  622. const existing = reqHeaders[key];
  623. if (existing === void 0) reqHeaders[key] = value;
  624. else reqHeaders[key] = Array.isArray(existing) ? [...existing, value] : [existing, value];
  625. }
  626. if (opts?.xfwd) {
  627. if (!reqHeaders["x-forwarded-for"]) reqHeaders["x-forwarded-for"] = url.hostname;
  628. if (!reqHeaders["x-forwarded-port"]) reqHeaders["x-forwarded-port"] = url.port || (url.protocol === "https:" ? "443" : "80");
  629. if (!reqHeaders["x-forwarded-proto"]) reqHeaders["x-forwarded-proto"] = url.protocol.replace(":", "");
  630. if (!reqHeaders["x-forwarded-host"]) reqHeaders["x-forwarded-host"] = url.host;
  631. }
  632. if (opts?.changeOrigin) if (resolvedAddr.socketPath) reqHeaders.host = "localhost";
  633. else {
  634. const targetHost = resolvedAddr.host || "localhost";
  635. const targetPort = resolvedAddr.port;
  636. reqHeaders.host = targetPort && targetPort !== (useHTTPS ? 443 : 80) ? `${targetHost}:${targetPort}` : targetHost;
  637. }
  638. const maxRedirects = typeof opts?.followRedirects === "number" ? opts.followRedirects : opts?.followRedirects ? 5 : 0;
  639. const body = maxRedirects > 0 ? await _bufferBody(init.body) : _toNodeStream(init.body);
  640. const agent = opts?.agent !== void 0 ? opts.agent || false : useHTTPS ? defaultAgents.https : defaultAgents.http;
  641. const res = await _sendRequest(useHTTPS ? request$1 : request, init.method || "GET", path, reqHeaders, resolvedAddr, body, {
  642. signal: init.signal || void 0,
  643. agent,
  644. timeout: opts?.timeout,
  645. ssl: opts?.ssl,
  646. maxRedirects,
  647. redirectCount: 0,
  648. originalHeaders: reqHeaders
  649. });
  650. const resHeaders = [];
  651. const rawHeaders = res.rawHeaders;
  652. for (let i = 0; i < rawHeaders.length; i += 2) {
  653. const key = rawHeaders[i];
  654. const keyLower = key.toLowerCase();
  655. if (keyLower === "transfer-encoding" || keyLower === "keep-alive" || keyLower === "connection") continue;
  656. resHeaders.push([key, rawHeaders[i + 1]]);
  657. }
  658. const hasBody = res.statusCode !== 204 && res.statusCode !== 304;
  659. return new Response(hasBody ? Readable.toWeb(res) : null, {
  660. status: res.statusCode,
  661. statusText: res.statusMessage,
  662. headers: resHeaders
  663. });
  664. }
  665. function toInit(init) {
  666. if (!init) return;
  667. if (init instanceof Request) return {
  668. method: init.method,
  669. headers: init.headers,
  670. body: init.body,
  671. duplex: init.body ? "half" : void 0
  672. };
  673. return init;
  674. }
  675. function _toNodeStream(body) {
  676. if (!body) return;
  677. if (typeof body === "string") return Buffer.from(body);
  678. if (body instanceof ArrayBuffer || ArrayBuffer.isView(body)) return Buffer.from(body);
  679. if (body instanceof ReadableStream) return Readable.fromWeb(body);
  680. if (body instanceof Blob) return Readable.fromWeb(body.stream());
  681. return Buffer.from(String(body));
  682. }
  683. async function _bufferBody(body) {
  684. if (!body) return;
  685. if (typeof body === "string") return Buffer.from(body);
  686. if (body instanceof ArrayBuffer || ArrayBuffer.isView(body)) return Buffer.from(body);
  687. if (body instanceof ReadableStream) {
  688. const readable = Readable.fromWeb(body);
  689. const chunks = [];
  690. for await (const chunk of readable) chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
  691. return Buffer.concat(chunks);
  692. }
  693. if (body instanceof Blob) return Buffer.from(await body.arrayBuffer());
  694. return Buffer.from(String(body));
  695. }
  696. const _redirectStatuses = /* @__PURE__ */ new Set([
  697. 301,
  698. 302,
  699. 303,
  700. 307,
  701. 308
  702. ]);
  703. function _sendRequest(doRequest, method, path, headers, addr, body, opts) {
  704. forceConnectionCloseForTransferEncoding(headers);
  705. return new Promise((resolve, reject) => {
  706. const reqOpts = {
  707. method,
  708. path,
  709. headers,
  710. agent: opts.agent
  711. };
  712. if (addr.socketPath) reqOpts.socketPath = addr.socketPath;
  713. else {
  714. reqOpts.hostname = addr.host || "localhost";
  715. reqOpts.port = addr.port;
  716. }
  717. if (opts.signal) reqOpts.signal = opts.signal;
  718. if (opts.ssl) Object.assign(reqOpts, opts.ssl);
  719. const req = doRequest(reqOpts, (res) => {
  720. const statusCode = res.statusCode;
  721. if (opts.maxRedirects > 0 && _redirectStatuses.has(statusCode) && opts.redirectCount < opts.maxRedirects && res.headers.location) {
  722. res.resume();
  723. const currentURL = new URL(path, `http://${addr.host || "localhost"}:${addr.port || 80}`);
  724. const location = new URL(res.headers.location, currentURL);
  725. const redirectHTTPS = isSSL.test(location.protocol);
  726. const preserveMethod = statusCode === 307 || statusCode === 308;
  727. const redirectMethod = preserveMethod ? method : "GET";
  728. const redirectHeaders = { ...opts.originalHeaders };
  729. redirectHeaders.host = location.host;
  730. if (location.host !== currentURL.host) {
  731. delete redirectHeaders.authorization;
  732. delete redirectHeaders.cookie;
  733. }
  734. if (!preserveMethod) {
  735. delete redirectHeaders["content-length"];
  736. delete redirectHeaders["content-type"];
  737. delete redirectHeaders["transfer-encoding"];
  738. }
  739. _sendRequest(redirectHTTPS ? request$1 : request, redirectMethod, location.pathname + location.search, redirectHeaders, {
  740. host: location.hostname,
  741. port: Number(location.port) || (redirectHTTPS ? 443 : 80)
  742. }, preserveMethod ? body : void 0, {
  743. ...opts,
  744. redirectCount: opts.redirectCount + 1
  745. }).then(resolve, reject);
  746. return;
  747. }
  748. resolve(res);
  749. });
  750. req.on("error", reject);
  751. if (opts.timeout) req.setTimeout(opts.timeout, () => {
  752. req.destroy(/* @__PURE__ */ new Error("Proxy request timed out"));
  753. });
  754. if (body instanceof Readable) {
  755. body.on("error", (err) => {
  756. req.destroy(err);
  757. reject(err);
  758. });
  759. body.pipe(req);
  760. } else if (body) req.end(body);
  761. else req.end();
  762. });
  763. }
  764. function proxyUpgrade(addr, req, socket, head, opts) {
  765. const resolvedAddr = parseAddr(addr);
  766. let useSSL = false;
  767. if (typeof addr === "string" && !addr.startsWith("unix:")) useSSL = isSSL.test(new URL(addr).protocol);
  768. if (req.method !== "GET" || req.headers.upgrade?.toLowerCase() !== "websocket") {
  769. socket.destroy();
  770. return Promise.reject(/* @__PURE__ */ new Error("Not a valid WebSocket upgrade request"));
  771. }
  772. if (opts?.xfwd !== false) {
  773. const xfFor = req.headers["x-forwarded-for"];
  774. const xfPort = req.headers["x-forwarded-port"];
  775. const xfProto = req.headers["x-forwarded-proto"];
  776. req.headers["x-forwarded-for"] = `${xfFor ? `${xfFor},` : ""}${req.socket?.remoteAddress}`;
  777. req.headers["x-forwarded-port"] = `${xfPort ? `${xfPort},` : ""}${getPort(req)}`;
  778. req.headers["x-forwarded-proto"] = `${xfProto ? `${xfProto},` : ""}${hasEncryptedConnection(req) ? "wss" : "ws"}`;
  779. }
  780. const target = _buildTargetURL(resolvedAddr, useSSL);
  781. const requestOptions = {
  782. ...opts,
  783. target,
  784. prependPath: opts?.prependPath !== false
  785. };
  786. const outgoing = setupOutgoing(requestOptions.ssl || {}, requestOptions, req);
  787. const sock = socket;
  788. return new Promise((resolve, reject) => {
  789. let settled = false;
  790. setupSocket(sock);
  791. if (head && head.length > 0) sock.unshift(head);
  792. sock.once("error", onSocketError);
  793. const proxyReq = (isSSL.test(target.protocol) ? request$1 : request)(outgoing);
  794. proxyReq.once("error", onOutgoingError);
  795. proxyReq.once("response", (res) => {
  796. if (!res.upgrade) {
  797. if (!sock.destroyed && sock.writable) {
  798. sock.write(_createHttpHeader(`HTTP/${res.httpVersion} ${res.statusCode} ${res.statusMessage}`, res.headers));
  799. res.on("error", onOutgoingError);
  800. res.pipe(sock);
  801. } else res.resume();
  802. if (!settled) {
  803. settled = true;
  804. reject(/* @__PURE__ */ new Error("Upstream server did not upgrade the connection"));
  805. }
  806. }
  807. });
  808. proxyReq.once("upgrade", (proxyRes, proxySocket, proxyHead) => {
  809. proxySocket.once("error", onOutgoingError);
  810. sock.removeListener("error", onSocketError);
  811. sock.once("error", () => {
  812. proxySocket.end();
  813. });
  814. setupSocket(proxySocket);
  815. if (proxyHead && proxyHead.length > 0) proxySocket.unshift(proxyHead);
  816. sock.write(_createHttpHeader("HTTP/1.1 101 Switching Protocols", proxyRes.headers));
  817. proxySocket.pipe(sock).pipe(proxySocket);
  818. settled = true;
  819. resolve(proxySocket);
  820. });
  821. proxyReq.end();
  822. function onSocketError(err) {
  823. proxyReq.destroy();
  824. if (!settled) {
  825. settled = true;
  826. reject(err);
  827. }
  828. }
  829. function onOutgoingError(err) {
  830. sock.end();
  831. if (!settled) {
  832. settled = true;
  833. reject(err);
  834. }
  835. }
  836. });
  837. }
  838. function _buildTargetURL(addr, useSSL = false) {
  839. const protocol = useSSL ? "https" : "http";
  840. if (addr.socketPath) {
  841. const url = new URL(`${protocol}://unix`);
  842. url.socketPath = addr.socketPath;
  843. return url;
  844. }
  845. return new URL(`${protocol}://${addr.host || "localhost"}${addr.port ? `:${addr.port}` : ""}`);
  846. }
  847. function _createHttpHeader(line, headers) {
  848. let result = line;
  849. for (const key of Object.keys(headers)) {
  850. const value = headers[key];
  851. if (value === void 0) continue;
  852. if (Array.isArray(value)) for (const element of value) result += `\r\n${key}: ${element}`;
  853. else result += `\r\n${key}: ${value}`;
  854. }
  855. return `${result}\r\n\r\n`;
  856. }
  857. export { ProxyServer, createProxyServer, proxyFetch, proxyUpgrade };