| /** | |
| * CORS 代理服务器模块 | |
| * | |
| * 这是一个基于 Node.js 的 HTTP 代理服务器,专门用于解决跨域问题。 | |
| * 服务器接收请求后,将请求转发到目标服务器,并自动添加 CORS 头部。 | |
| * | |
| * 主要功能: | |
| * - 跨域请求代理 | |
| * - 自动 CORS 头部处理 | |
| * - 错误处理和响应 | |
| * - 支持多种配置选项 | |
| * - 增强:WebSocket、超时、Keep-Alive | |
| * | |
| * @fileoverview CORS 代理服务器核心模块 | |
| * @author cors-sp | |
| * @version 0.1.4 | |
| */ | |
| // 基础 HTTP 服务器,ESM 语法 | |
| import http from "http"; | |
| import https from "https"; | |
| import { createProxyHandler } from "./proxyHandler.js"; | |
| import { withCORS } from "./cors.js"; | |
| import { fileURLToPath } from "url"; | |
| import { createRequire } from "node:module"; | |
| // 兼容 http-proxy 内部 util._extend 的弃用告警 | |
| const require = createRequire(import.meta.url); | |
| try { | |
| const cjsUtil = require("node:util"); | |
| cjsUtil._extend = Object.assign; | |
| } catch {} | |
| // 延迟加载 http-proxy,确保上述替换生效 | |
| const httpProxy = require("http-proxy"); | |
| /** | |
| * 默认服务器端口 | |
| * 可通过环境变量 PORT 覆盖,默认为 4399 | |
| * @constant {number} | |
| */ | |
| const PORT = process.env.PORT || 4399; | |
| /** | |
| * 创建 CORS 代理服务器 | |
| * | |
| * 该函数创建一个配置了代理功能的 HTTP 服务器,能够处理跨域请求。 | |
| * 服务器会自动添加 CORS 头部,并支持多种安全配置选项。 | |
| * | |
| * @param {Object} options - 服务器配置选项 | |
| * @param {string[]} [options.originBlacklist=[]] - 被禁止的源列表 | |
| * @param {string[]} [options.originWhitelist=[]] - 允许的源列表(如果设置,只允许列表中的源) | |
| * @param {string[]} [options.requireHeader=[]] - 必需的请求头列表 | |
| * @param {string[]} [options.removeHeaders=[]] - 需要移除的请求头列表 | |
| * @param {Object} [options.setHeaders={}] - 需要设置的请求头键值对 | |
| * @param {Function} [options.checkRateLimit=null] - 限流检查函数 | |
| * @param {boolean} [options.redirectSameOrigin=false] - 是否只允许同源重定向 | |
| * - 默认超时:client/proxy 15s | |
| * - WebSocket 代理支持 | |
| * - Keep-Alive Agent(WS 路径与必要处) | |
| * | |
| * @returns {http.Server} 配置好的 HTTP 服务器实例 | |
| * | |
| * @example | |
| * // 基本用法 | |
| * const server = createServer(); | |
| * server.listen(3000); | |
| * | |
| * @example | |
| * // 带安全配置的用法 | |
| * const server = createServer({ | |
| * originWhitelist: ['https://example.com', 'https://app.example.com'], | |
| * requireHeader: ['authorization'], | |
| * setHeaders: { | |
| * 'x-forwarded-for': 'proxy-server' | |
| * } | |
| * }); | |
| * | |
| * @example | |
| * // 带限流的用法 | |
| * const rateLimitMap = new Map(); | |
| * const checkRateLimit = (req) => { | |
| * const ip = req.connection.remoteAddress; | |
| * const now = Date.now(); | |
| * const requests = rateLimitMap.get(ip) || []; | |
| * const recentRequests = requests.filter(time => now - time < 60000); | |
| * if (recentRequests.length >= 100) return false; | |
| * recentRequests.push(now); | |
| * rateLimitMap.set(ip, recentRequests); | |
| * return true; | |
| * }; | |
| * | |
| * const server = createServer({ | |
| * checkRateLimit | |
| * }); | |
| */ | |
| const createServer = (options = {}) => { | |
| const proxy = httpProxy.createProxyServer({ | |
| timeout: options.timeout ?? 15000, | |
| proxyTimeout: options.proxyTimeout ?? 15000, | |
| ...options, | |
| }); | |
| // 代理错误统一为 JSON 文本 | |
| proxy.on("error", (err, req, res) => { | |
| try { | |
| const headers = withCORS({ "Content-Type": "application/json" }, req || { headers: {} }); | |
| res.writeHead(502, headers); | |
| res.end(JSON.stringify({ error: "proxy_error", message: err?.message || "unknown" })); | |
| } catch {} | |
| }); | |
| // HTTP 处理器 | |
| const handler = createProxyHandler(options, proxy); | |
| const server = http.createServer(handler); | |
| // WebSocket 代理支持 | |
| server.on("upgrade", (req, socket, head) => { | |
| try { | |
| const raw = decodeURIComponent((req.url || "").substring(1)); | |
| if (!raw) { | |
| socket.destroy(); | |
| return; | |
| } | |
| const target = new URL(raw); | |
| const agent = target.protocol === "https:" ? new https.Agent({ keepAlive: true, maxSockets: 100 }) : new http.Agent({ keepAlive: true, maxSockets: 100 }); | |
| proxy.ws(req, socket, head, { | |
| target: target.href, | |
| changeOrigin: true, | |
| agent, | |
| }); | |
| } catch { | |
| socket.destroy(); | |
| } | |
| }); | |
| return server; | |
| }; | |
| if (process.argv[1] === fileURLToPath(import.meta.url)) { | |
| // 使用默认配置创建服务器 | |
| const server = createServer({}); | |
| // 启动服务器并监听指定端口 | |
| server.listen(PORT, () => { | |
| console.log(`Proxy Server listening on http://localhost:${PORT}`); | |
| console.log(`Usage: http://localhost:${PORT}/<target-url>`); | |
| console.log(`Example: http://localhost:${PORT}/https://api.example.com/data`); | |
| }); | |
| } | |
| /** | |
| * 导出 createServer 函数 | |
| * | |
| * 允许其他模块导入并使用 createServer 函数来创建自定义配置的服务器实例。 | |
| * | |
| * @exports {Function} createServer - 创建 CORS 代理服务器的函数 | |
| */ | |
| export { createServer }; | |