UNPKG

node-xhttp

Version:

A high-performance VLESS-XHTTP proxy server

1,492 lines (1,289 loc) 50.5 kB
#!/usr/bin/env node const os = require('os'); const fs = require('fs'); const net = require('net'); const dns = require('dns'); const http = require('http'); const axios = require('axios'); const { Buffer } = require('buffer'); const { exec } = require('child_process'); // 环境变量 const UUID = process.env.UUID || 'a2056d0d-c98e-4aeb-9aab-37f64edd5710'; // 使用哪吒v1,在不同的平台部署需修改UUID,否则会覆盖 const NEZHA_SERVER = process.env.NEZHA_SERVER || ''; // 哪吒v1填写形式:nz.abc.com:8008 哪吒v0填写形式:nz.abc.com const NEZHA_PORT = process.env.NEZHA_PORT || ''; // 哪吒v1没有此变量,v0的agent端口为{443,8443,2096,2087,2083,2053}其中之一时开启tls const NEZHA_KEY = process.env.NEZHA_KEY || ''; // v1的NZ_CLIENT_SECRET或v0的agent端口 const AUTO_ACCESS = process.env.AUTO_ACCESS || false; // 是否开启自动访问保活,false为关闭,true为开启,需同时填写DOMAIN变量 const XPATH = process.env.XPATH || UUID.slice(0, 8); // xhttp路径,自动获取uuid前8位 const SUB_PATH = process.env.SUB_PATH || 'sub'; // 节点订阅路径 const DOMAIN = process.env.DOMAIN || ''; // 域名或ip,留空将自动获取服务器ip const NAME = process.env.NAME || ''; // 节点名称 const PORT = process.env.PORT || 3000; // http服务 // 核心配置 const SETTINGS = { ['UUID']: UUID, ['LOG_LEVEL']: 'none', // 日志级别,调试使用,none,info,warn,error ['BUFFER_SIZE']: '8192', // 增加缓冲区大小到8KB ['XPATH']: `%2F${XPATH}`, // xhttp路径 ['MAX_BUFFERED_POSTS']: 50, // 最大缓存POST请求数 ['MAX_POST_SIZE']: 2000000, // 每个POST最大字节数到2MB ['SESSION_TIMEOUT']: 30000, // 会话超时时间(30秒) ['CHUNK_SIZE']: 64 * 1024, // 64KB 的数据块大小,更高效 ['TCP_NODELAY']: true, // 启用 TCP_NODELAY ['TCP_KEEPALIVE']: true, // 启用 TCP keepalive ['SESSION_CLEANUP_INTERVAL']: 60000, // 会话清理间隔(60秒) ['MAX_SESSION_AGE']: 300000, // 最大会话存活时间(5分钟) ['CONNECTION_POOL_SIZE']: 100, // 连接池大小 ['WRITE_BUFFER_SIZE']: 64 * 1024, // 写缓冲区大小 ['READ_BUFFER_SIZE']: 64 * 1024, // 读缓冲区大小 ['BATCH_PROCESS_SIZE']: 10, // 批处理大小 ['ENABLE_COMPRESSION']: false, // 禁用压缩以提升速度 } // 自定义DNS解析器 const customDnsResolver = { servers: ['1.1.1.1', '8.8.8.8'], currentServerIndex: 0, async resolve(hostname) { const servers = this.servers; let lastError; for (let i = 0; i < servers.length; i++) { const serverIndex = (this.currentServerIndex + i) % servers.length; const server = servers[serverIndex]; try { log('debug', `Resolving ${hostname} using DNS server: ${server}`); const result = await this.resolveWithServer(hostname, server); this.currentServerIndex = (serverIndex + 1) % servers.length; log('debug', `Resolved ${hostname} to ${result} using ${server}`); return result; } catch (err) { lastError = err; log('warn', `DNS resolution failed with ${server}: ${err.message}`); } } throw new Error(`All DNS servers failed. Last error: ${lastError?.message}`); }, async resolveWithServer(hostname, server) { return new Promise((resolve, reject) => { const dns = require('dns'); const originalServers = dns.getServers(); dns.setServers([server]); dns.resolve4(hostname, (err, addresses) => { dns.setServers(originalServers); if (err) { reject(err); } else if (addresses && addresses.length > 0) { resolve(addresses[0]); } else { reject(new Error('No addresses found')); } }); }); } }; // 设置默认DNS解析器 dns.setDefaultResultOrder('ipv4first'); dns.setServers(['1.1.1.1', '8.8.8.8']); function validate_uuid(left, right) { for (let i = 0; i < 16; i++) { if (left[i] !== right[i]) return false } return true } function concat_typed_arrays(first, ...args) { if (!args || args.length < 1) return first let len = first.length for (let a of args) len += a.length const r = new first.constructor(len) r.set(first, 0) len = first.length for (let a of args) { r.set(a, len) len += a.length } return r } // 扩展日志函数 function log(type, ...args) { if (SETTINGS.LOG_LEVEL === 'none') return; const levels = { 'debug': 0, 'info': 1, 'warn': 2, 'error': 3 }; const colors = { 'debug': '\x1b[36m', // 青色 'info': '\x1b[32m', // 绿色 'warn': '\x1b[33m', // 黄色 'error': '\x1b[31m', // 红色 'reset': '\x1b[0m' // 重置 }; const configLevel = levels[SETTINGS.LOG_LEVEL] || 1; const messageLevel = levels[type] || 0; if (messageLevel >= configLevel) { const time = new Date().toISOString(); const color = colors[type] || colors.reset; console.log(`${color}[${time}] [${type}]`, ...args, colors.reset); } } const getDownloadUrl = () => { const arch = os.arch(); if (arch === 'arm' || arch === 'arm64' || arch === 'aarch64') { if (!NEZHA_PORT) { return 'https://arm64.ssss.nyc.mn/v1'; } else { return 'https://arm64.ssss.nyc.mn/agent'; } } else { if (!NEZHA_PORT) { return 'https://amd64.ssss.nyc.mn/v1'; } else { return 'https://amd64.ssss.nyc.mn/agent'; } } }; const downloadFile = async () => { if (!NEZHA_KEY) return; try { const url = getDownloadUrl(); // console.log(`Start downloading file from ${url}`); const response = await axios({ method: 'get', url: url, responseType: 'stream' }); const writer = fs.createWriteStream('npm'); response.data.pipe(writer); return new Promise((resolve, reject) => { writer.on('finish', () => { console.log('npm download successfully'); exec('chmod +x npm', (err) => { if (err) reject(err); resolve(); }); }); writer.on('error', reject); }); } catch (err) { throw err; } }; const runnz = async () => { await downloadFile(); let NEZHA_TLS = ''; let command = ''; if (NEZHA_SERVER && NEZHA_PORT && NEZHA_KEY) { const tlsPorts = ['443', '8443', '2096', '2087', '2083', '2053']; NEZHA_TLS = tlsPorts.includes(NEZHA_PORT) ? '--tls' : ''; command = `nohup ./npm -s ${NEZHA_SERVER}:${NEZHA_PORT} -p ${NEZHA_KEY} ${NEZHA_TLS} >/dev/null 2>&1 &`; } else if (NEZHA_SERVER && NEZHA_KEY) { if (!NEZHA_PORT) { const port = NEZHA_SERVER.includes(':') ? NEZHA_SERVER.split(':').pop() : ''; const tlsPorts = new Set(['443', '8443', '2096', '2087', '2083', '2053']); const nezhatls = tlsPorts.has(port) ? 'true' : 'false'; const configYaml = ` client_secret: ${NEZHA_KEY} debug: false disable_auto_update: true disable_command_execute: false disable_force_update: true disable_nat: false disable_send_query: false gpu: false insecure_tls: false ip_report_period: 1800 report_delay: 1 server: ${NEZHA_SERVER} skip_connection_count: false skip_procs_count: false temperature: false tls: ${nezhatls} use_gitee_to_upgrade: false use_ipv6_country_code: false uuid: ${UUID}`; fs.writeFileSync('config.yaml', configYaml); } command = `nohup ./npm -c config.yaml >/dev/null 2>&1 &`; } else { // console.log('NEZHA variable is empty, skip running'); return; } try { exec(command, { shell: '/bin/bash' }); console.log('npm is running'); } catch (error) { console.error(`npm running error: ${error}`); } }; // 添加自动任务 async function addAccessTask() { if (AUTO_ACCESS !== true) return; try { if (!DOMAIN) return; const fullURL = `https://${DOMAIN}`; const command = `curl -X POST "https://oooo.serv00.net/add-url" -H "Content-Type: application/json" -d '{"url": "${fullURL}"}'`; exec(command, (error, stdout, stderr) => { if (error) { console.error('Error sending request:', error.message); return; } console.log('Automatic Access Task added successfully:', stdout); }); } catch (error) { console.error('Error added Task:', error.message); } } // VLESS 协议解析 function parse_uuid(uuid) { uuid = uuid.replaceAll('-', '') const r = [] for (let index = 0; index < 16; index++) { r.push(parseInt(uuid.substr(index * 2, 2), 16)) } return r } async function read_vless_header(reader, cfg_uuid_str) { let readed_len = 0 let header = new Uint8Array() let read_result = { value: header, done: false } async function inner_read_until(offset) { if (read_result.done) { throw new Error('header length too short') } const len = offset - readed_len if (len < 1) { return } read_result = await read_atleast(reader, len) readed_len += read_result.value.length header = concat_typed_arrays(header, read_result.value) } await inner_read_until(1 + 16 + 1) const version = header[0] const uuid = header.slice(1, 1 + 16) const cfg_uuid = parse_uuid(cfg_uuid_str) if (!validate_uuid(uuid, cfg_uuid)) { throw new Error(`invalid UUID`) } const pb_len = header[1 + 16] const addr_plus1 = 1 + 16 + 1 + pb_len + 1 + 2 + 1 await inner_read_until(addr_plus1 + 1) const cmd = header[1 + 16 + 1 + pb_len] const COMMAND_TYPE_TCP = 1 if (cmd !== COMMAND_TYPE_TCP) { throw new Error(`unsupported command: ${cmd}`) } const port = (header[addr_plus1 - 1 - 2] << 8) + header[addr_plus1 - 1 - 1] const atype = header[addr_plus1 - 1] const ADDRESS_TYPE_IPV4 = 1 const ADDRESS_TYPE_STRING = 2 const ADDRESS_TYPE_IPV6 = 3 let header_len = -1 if (atype === ADDRESS_TYPE_IPV4) { header_len = addr_plus1 + 4 } else if (atype === ADDRESS_TYPE_IPV6) { header_len = addr_plus1 + 16 } else if (atype === ADDRESS_TYPE_STRING) { header_len = addr_plus1 + 1 + header[addr_plus1] } if (header_len < 0) { throw new Error('read address type failed') } await inner_read_until(header_len) const idx = addr_plus1 let hostname = '' if (atype === ADDRESS_TYPE_IPV4) { hostname = header.slice(idx, idx + 4).join('.') } else if (atype === ADDRESS_TYPE_STRING) { hostname = new TextDecoder().decode( header.slice(idx + 1, idx + 1 + header[idx]), ) } else if (atype === ADDRESS_TYPE_IPV6) { hostname = header .slice(idx, idx + 16) .reduce( (s, b2, i2, a) => i2 % 2 ? s.concat(((a[i2 - 1] << 8) + b2).toString(16)) : s, [], ) .join(':') } if (!hostname) { log('error', 'Failed to parse hostname'); throw new Error('parse hostname failed') } log('info', `VLESS connection to ${hostname}:${port}`); return { hostname, port, data: header.slice(header_len), resp: new Uint8Array([version, 0]), } } // read_atleast 函数 async function read_atleast(reader, n) { const buffs = [] let done = false while (n > 0 && !done) { const r = await reader.read() if (r.value) { const b = new Uint8Array(r.value) buffs.push(b) n -= b.length } done = r.done } if (n > 0) { throw new Error(`not enough data to read`) } return { value: concat_typed_arrays(...buffs), done, } } // parse_header 函数 async function parse_header(uuid_str, client) { log('debug', 'Starting to parse VLESS header'); const reader = client.readable.getReader() try { const vless = await read_vless_header(reader, uuid_str) log('debug', 'VLESS header parsed successfully'); return vless } catch (err) { log('error', `VLESS header parse error: ${err.message}`); throw new Error(`read vless header error: ${err.message}`) } finally { reader.releaseLock() } } // connect_remote 函数 async function connect_remote(hostname, port) { const timeout = 10000; // 增加超时时间 try { // 使用自定义DNS解析器 let resolvedHostname = hostname; // 如果不是IP地址,则进行DNS解析 if (!/^\d+\.\d+\.\d+\.\d+$/.test(hostname) && !hostname.startsWith('[')) { try { resolvedHostname = await customDnsResolver.resolve(hostname); log('debug', `DNS resolved ${hostname} to ${resolvedHostname}`); } catch (dnsErr) { log('warn', `DNS resolution failed for ${hostname}, using original hostname: ${dnsErr.message}`); // 如果DNS解析失败,仍然尝试使用原始主机名 } } const conn = await timed_connect(resolvedHostname, port, timeout); // 优化 TCP 连接设置 conn.setNoDelay(SETTINGS.TCP_NODELAY); conn.setKeepAlive(SETTINGS.TCP_KEEPALIVE, 1000); // 设置更大的缓冲区大小 if (conn.setReadBuffer) { conn.setReadBuffer(SETTINGS.READ_BUFFER_SIZE); } if (conn.setWriteBuffer) { conn.setWriteBuffer(SETTINGS.WRITE_BUFFER_SIZE); } // 设置socket选项 if (conn._handle && conn._handle.setNoDelay) { conn._handle.setNoDelay(true); } log('info', `Connected to ${hostname}(${resolvedHostname}):${port} with optimized settings`); return conn; } catch (err) { log('error', `Connection failed: ${err.message}`); throw err; } } // timed_connect 函数 function timed_connect(hostname, port, ms) { return new Promise((resolve, reject) => { const conn = net.createConnection({ host: hostname, port: port }) const handle = setTimeout(() => { reject(new Error(`connect timeout`)) }, ms) conn.on('connect', () => { clearTimeout(handle) resolve(conn) }) conn.on('error', (err) => { clearTimeout(handle) reject(err) }) }) } // 网络传输 - 优化版本 function pipe_relay() { async function pump(src, dest, first_packet) { const chunkSize = SETTINGS.CHUNK_SIZE; let totalBytes = 0; const startTime = Date.now(); if (first_packet.length > 0) { if (dest.write) { // 使用 cork/uncork 优化小包传输 dest.cork(); dest.write(first_packet); process.nextTick(() => dest.uncork()); totalBytes += first_packet.length; } else { const writer = dest.writable.getWriter(); try { await writer.write(first_packet); totalBytes += first_packet.length; } finally { writer.releaseLock(); } } } try { if (src.pipe) { // 优化 Node.js Stream 传输 src.pause(); // 设置高水位线以优化内存使用 src._readableState.highWaterMark = chunkSize; if (dest._writableState) { dest._writableState.highWaterMark = chunkSize; } // 使用 Transform 流进行数据优化 const { Transform } = require('stream'); const optimizer = new Transform({ transform(chunk, encoding, callback) { totalBytes += chunk.length; // 批量处理小数据包 if (chunk.length < 1024) { this.push(chunk); } else { // 大块数据直接传输 this.push(chunk); } callback(); }, highWaterMark: chunkSize }); src.pipe(optimizer).pipe(dest, { end: true, highWaterMark: chunkSize }); src.resume(); } else { // 优化 Web Stream 传输 const reader = src.readable.getReader(); const writer = dest.writable.getWriter(); try { while (true) { const { done, value } = await reader.read(); if (done) break; totalBytes += value.length; await writer.write(value); } } finally { reader.releaseLock(); writer.releaseLock(); } } // 记录传输统计 const duration = Date.now() - startTime; if (totalBytes > 0 && duration > 0) { const speed = Math.round((totalBytes / duration) * 1000 / 1024); // KB/s log('debug', `Transfer completed: ${totalBytes} bytes in ${duration}ms (${speed} KB/s)`); } } catch (err) { if (!err.message.includes('aborted')) { log('error', 'Relay error:', err.message); } throw err; } } return pump; } // socketToWebStream 函数 function socketToWebStream(socket, session) { let readController; let writeController; let isClosed = false; const errorHandler = (err) => { if (isClosed) return; log('error', 'Socket error:', err.message); readController?.error(err); writeController?.error(err); }; const dataHandler = (chunk) => { if (isClosed) return; try { readController?.enqueue(chunk); } catch (err) { log('error', 'Read controller error:', err.message); } }; const endHandler = () => { if (isClosed) return; try { readController?.close(); } catch (err) { log('error', 'Read controller close error:', err.message); } }; socket.on('error', errorHandler); socket.on('data', dataHandler); socket.on('end', endHandler); // 将事件监听器添加到会话跟踪中 if (session) { session.eventListeners.add('error'); session.eventListeners.add('data'); session.eventListeners.add('end'); } return { readable: new ReadableStream({ start(controller) { readController = controller; }, cancel() { isClosed = true; socket.removeListener('error', errorHandler); socket.removeListener('data', dataHandler); socket.removeListener('end', endHandler); socket.destroy(); } }), writable: new WritableStream({ start(controller) { writeController = controller; }, write(chunk) { return new Promise((resolve, reject) => { if (socket.destroyed || isClosed) { reject(new Error('Socket is destroyed')); return; } socket.write(chunk, (err) => { if (err) reject(err); else resolve(); }); }); }, close() { isClosed = true; socket.removeListener('error', errorHandler); socket.removeListener('data', dataHandler); socket.removeListener('end', endHandler); if (!socket.destroyed) { socket.end(); } }, abort(err) { isClosed = true; socket.removeListener('error', errorHandler); socket.removeListener('data', dataHandler); socket.removeListener('end', endHandler); socket.destroy(err); } }) }; } // relay 函数 function relay(cfg, client, remote, vless, session) { const pump = pipe_relay(); let isClosing = false; const remoteStream = socketToWebStream(remote, session); function cleanup() { if (!isClosing) { isClosing = true; try { remote.destroy(); } catch (err) { // 忽略常规断开错误 if (!err.message.includes('aborted') && !err.message.includes('socket hang up')) { log('error', `Cleanup error: ${err.message}`); } } } } const uploader = pump(client, remoteStream, vless.data) .catch(err => { // 只记录非预期错误 if (!err.message.includes('aborted') && !err.message.includes('socket hang up')) { log('error', `Upload error: ${err.message}`); } }) .finally(() => { client.reading_done && client.reading_done(); }); const downloader = pump(remoteStream, client, vless.resp) .catch(err => { // 只记录非预期错误 if (!err.message.includes('aborted') && !err.message.includes('socket hang up')) { log('error', `Download error: ${err.message}`); } }); downloader .finally(() => uploader) .finally(cleanup); } // 会话管理 const sessions = new Map(); // 定期清理过期会话 function cleanupExpiredSessions() { const now = Date.now(); const expiredSessions = []; for (const [uuid, session] of sessions) { if (now - session.lastActivity > SETTINGS.MAX_SESSION_AGE) { expiredSessions.push(uuid); } } for (const uuid of expiredSessions) { const session = sessions.get(uuid); if (session) { log('debug', `Cleaning up expired session: ${uuid}`); session.cleanup(); } } if (expiredSessions.length > 0) { log('info', `Cleaned up ${expiredSessions.length} expired sessions`); } } // 启动定期清理 setInterval(cleanupExpiredSessions, SETTINGS.SESSION_CLEANUP_INTERVAL); // 性能统计 const performanceStats = { totalConnections: 0, activeConnections: 0, totalBytesTransferred: 0, averageSpeed: 0, peakMemoryUsage: 0, startTime: Date.now() }; // 内存监控和垃圾回收优化 function monitorMemory() { const memUsage = process.memoryUsage(); const memMB = Math.round(memUsage.heapUsed / 1024 / 1024); const rssMB = Math.round(memUsage.rss / 1024 / 1024); // 更新峰值内存使用 performanceStats.peakMemoryUsage = Math.max(performanceStats.peakMemoryUsage, memMB); // 计算运行时间 const uptime = Math.round((Date.now() - performanceStats.startTime) / 1000); const avgSpeed = performanceStats.totalBytesTransferred > 0 ? Math.round(performanceStats.totalBytesTransferred / uptime / 1024) : 0; log('debug', `Memory: Heap ${memMB}MB, RSS ${rssMB}MB, Sessions: ${sessions.size}, Speed: ${avgSpeed}KB/s`); // 如果内存使用过高,强制垃圾回收 if (memMB > 500) { // 500MB 阈值 log('warn', `High memory usage detected: ${memMB}MB, forcing garbage collection`); if (global.gc) { global.gc(); const newMemUsage = process.memoryUsage(); const newMemMB = Math.round(newMemUsage.heapUsed / 1024 / 1024); log('info', `After GC: ${newMemMB}MB (freed ${memMB - newMemMB}MB)`); } } } // 性能监控 function logPerformanceStats() { const uptime = Math.round((Date.now() - performanceStats.startTime) / 1000); const avgSpeed = performanceStats.totalBytesTransferred > 0 ? Math.round(performanceStats.totalBytesTransferred / uptime / 1024) : 0; log('info', `Performance Stats - Uptime: ${uptime}s, Connections: ${performanceStats.totalConnections}, Active: ${sessions.size}, Transferred: ${Math.round(performanceStats.totalBytesTransferred / 1024 / 1024)}MB, Avg Speed: ${avgSpeed}KB/s, Peak Memory: ${performanceStats.peakMemoryUsage}MB`); } // 每30秒监控一次内存 setInterval(monitorMemory, 30000); // 每5分钟记录一次性能统计 setInterval(logPerformanceStats, 300000); class Session { constructor(uuid) { this.uuid = uuid; this.nextSeq = 0; this.downstreamStarted = false; this.lastActivity = Date.now(); this.vlessHeader = null; this.remote = null; this.initialized = false; this.responseHeader = null; this.headerSent = false; this.bufferedData = new Map(); this.cleaned = false; this.pendingPackets = []; // 存储待处理的数据包 this.currentStreamRes = null; // 当前下行流响应 this.pendingBuffers = new Map(); // 存储未按序到达的数据包 this.cleanupTimer = null; // 清理定时器 this.eventListeners = new Set(); // 跟踪事件监听器 this.bytesTransferred = 0; // 传输字节数统计 this.startTime = Date.now(); // 会话开始时间 log('debug', `Created new session with UUID: ${uuid}`); // 更新性能统计 performanceStats.totalConnections++; performanceStats.activeConnections++; // 设置自动清理定时器 this.cleanupTimer = setTimeout(() => { this.cleanup(); }, SETTINGS.MAX_SESSION_AGE); } async initializeVLESS(firstPacket) { if (this.initialized) return true; try { log('debug', 'Initializing VLESS connection from first packet'); // 创建可读流来解析VLESS头 const readable = new ReadableStream({ start(controller) { controller.enqueue(firstPacket); controller.close(); } }); const client = { readable: readable, writable: new WritableStream() }; this.vlessHeader = await parse_header(SETTINGS.UUID, client); log('info', `VLESS header parsed: ${this.vlessHeader.hostname}:${this.vlessHeader.port}`); // 建立远程连接 this.remote = await connect_remote(this.vlessHeader.hostname, this.vlessHeader.port); log('info', 'Remote connection established'); this.initialized = true; return true; } catch (err) { log('error', `Failed to initialize VLESS: ${err.message}`); return false; } } async processPacket(seq, data) { try { // 更新活动时间 this.lastActivity = Date.now(); // 保存数据到pendingBuffers this.pendingBuffers.set(seq, data); log('debug', `Buffered packet seq=${seq}, size=${data.length}`); // 特殊处理:如果收到seq=0,立即处理,不等待其他包 if (seq === 0 && !this.initialized) { const packetData = this.pendingBuffers.get(0); this.pendingBuffers.delete(0); if (!await this.initializeVLESS(packetData)) { throw new Error('Failed to initialize VLESS connection'); } // 存储响应头 this.responseHeader = Buffer.from([0x00, 0x00]); // 写入VLESS头部数据到远程 await this._writeToRemote(this.vlessHeader.data); // 处理所有待处理的数据包 await this._processPendingPackets(); return true; } // 如果已经初始化,直接处理数据包 if (this.initialized) { await this._writeToRemote(data); return true; } // 如果还没初始化但不是seq=0,等待初始化完成 if (!this.initialized) { log('debug', `Waiting for initialization, buffering packet seq=${seq}`); return true; } // 检查缓存大小 if (this.pendingBuffers.size > SETTINGS.MAX_BUFFERED_POSTS) { throw new Error('Too many buffered packets'); } return true; } catch (err) { log('error', `Process packet error: ${err.message}`); throw err; } } async _processPendingPackets() { // 处理所有待处理的数据包 const sortedSeqs = Array.from(this.pendingBuffers.keys()).sort((a, b) => a - b); for (const seq of sortedSeqs) { if (seq > 0) { // 跳过seq=0,已经处理过了 const data = this.pendingBuffers.get(seq); this.pendingBuffers.delete(seq); if (this.initialized) { await this._writeToRemote(data); log('debug', `Processed pending packet seq=${seq}`); } } } } _startDownstreamResponse() { if (!this.currentStreamRes || !this.responseHeader) return; try { const protocol = this.currentStreamRes.socket?.alpnProtocol || 'http/1.1'; const isH2 = protocol === 'h2'; if (!this.headerSent) { log('debug', `Sending VLESS response header (${protocol}): ${this.responseHeader.length} bytes`); this.currentStreamRes.write(this.responseHeader); this.headerSent = true; } // 根据协议使用不同的传输策略 if (isH2) { // HTTP/2 优化 this.currentStreamRes.socket.setNoDelay(true); // 使用 Transform 流进行数据分块 const transform = new require('stream').Transform({ transform(chunk, encoding, callback) { const size = 16384; // 16KB chunks for (let i = 0; i < chunk.length; i += size) { this.push(chunk.slice(i, i + size)); } callback(); } }); this.remote.pipe(transform).pipe(this.currentStreamRes); } else { // HTTP/1.1 直接传输 this.remote.pipe(this.currentStreamRes); } // 处理关闭事件 this.remote.on('end', () => { if (!this.currentStreamRes.writableEnded) { this.currentStreamRes.end(); } }); this.remote.on('error', (err) => { log('error', `Remote error: ${err.message}`); if (!this.currentStreamRes.writableEnded) { this.currentStreamRes.end(); } }); } catch (err) { log('error', `Error starting downstream: ${err.message}`); this.cleanup(); } } startDownstream(res, headers) { if (!res.headersSent) { res.writeHead(200, headers); } this.currentStreamRes = res; if (this.initialized && this.responseHeader) { this._startDownstreamResponse(); } const closeHandler = () => { log('info', 'Client connection closed'); this.cleanup(); }; res.on('close', closeHandler); this.eventListeners.add('close'); // 更新活动时间 this.lastActivity = Date.now(); return true; } async _writeToRemote(data) { if (!this.remote || this.remote.destroyed) { throw new Error('Remote connection not available'); } return new Promise((resolve, reject) => { this.remote.write(data, (err) => { if (err) { log('error', `Failed to write to remote: ${err.message}`); reject(err); } else { // 更新传输字节统计 this.bytesTransferred += data.length; resolve(); } }); }); } _startDownstreamResponse() { if (!this.currentStreamRes || !this.responseHeader) return; try { if (!this.headerSent) { this.currentStreamRes.write(this.responseHeader); this.headerSent = true; } this.remote.pipe(this.currentStreamRes); const endHandler = () => { if (!this.currentStreamRes.writableEnded) { this.currentStreamRes.end(); } }; const errorHandler = (err) => { log('error', `Remote error: ${err.message}`); if (!this.currentStreamRes.writableEnded) { this.currentStreamRes.end(); } }; this.remote.on('end', endHandler); this.remote.on('error', errorHandler); this.eventListeners.add('end'); this.eventListeners.add('error'); } catch (err) { log('error', `Error starting downstream: ${err.message}`); this.cleanup(); } } cleanup() { if (!this.cleaned) { this.cleaned = true; // 更新性能统计 performanceStats.totalBytesTransferred += this.bytesTransferred; performanceStats.activeConnections--; const duration = Date.now() - this.startTime; const speed = this.bytesTransferred > 0 ? Math.round(this.bytesTransferred / duration * 1000 / 1024) : 0; log('debug', `Cleaning up session ${this.uuid} - Duration: ${Math.round(duration/1000)}s, Transferred: ${Math.round(this.bytesTransferred/1024)}KB, Speed: ${speed}KB/s`); // 清除定时器 if (this.cleanupTimer) { clearTimeout(this.cleanupTimer); this.cleanupTimer = null; } // 清理远程连接 if (this.remote) { this.remote.removeAllListeners(); this.remote.destroy(); this.remote = null; } // 清理当前流响应 if (this.currentStreamRes) { this.currentStreamRes.removeAllListeners(); this.currentStreamRes = null; } // 清理缓冲区 this.pendingBuffers.clear(); this.bufferedData.clear(); this.pendingPackets = []; // 清理事件监听器 this.eventListeners.clear(); this.initialized = false; this.headerSent = false; // 从全局会话中移除 sessions.delete(this.uuid); } } } // 获取ISP信息 async function getISPInfo() { try { const response = await axios.get('https://speed.cloudflare.com/meta', { timeout: 8000, headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' } }); const data = response.data; const country = data.country || 'Unknown'; const asOrganization = data.asOrganization || 'Unknown'; const isp = `${country}-${asOrganization}`.replace(/[^a-zA-Z0-9\-_]/g, '_'); log('info', `ISP info obtained: ${isp}`); return isp; } catch (err) { log('error', `Failed to get ISP info: ${err.message}`); return 'Unknown_ISP'; } } // 获取服务器IP async function getServerIP() { if (DOMAIN) { return DOMAIN; } const ipServices = [ 'https://ipv4.ip.sb', 'https://ipinfo.io/ip', 'https://ifconfig.me' ]; for (const service of ipServices) { try { const response = await axios.get(service, { timeout: 5000, headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' } }); const ip = response.data.trim(); if (ip && /^\d+\.\d+\.\d+\.\d+$/.test(ip)) { log('info', `Got server IP: ${ip} from ${service}`); return ip; } } catch (err) { log('debug', `Failed to get IP from ${service}: ${err.message}`); } } // 如果所有IPv4服务都失败,尝试IPv6 try { const response = await axios.get('https://ipv6.ip.sb', { timeout: 5000, headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' } }); const ipv6 = response.data.trim(); if (ipv6) { log('info', `Got IPv6 address: ${ipv6}`); return `[${ipv6}]`; } } catch (ipv6Err) { log('debug', 'IPv6 fallback failed:', ipv6Err.message); } log('warn', 'Failed to get server IP, using localhost'); return 'localhost'; } // 异步获取IP和ISP信息 let IP = 'localhost'; let ISP = 'Unknown_ISP'; Promise.all([ getServerIP().then(ip => { IP = ip; }), getISPInfo().then(isp => { ISP = isp; }) ]).then(() => { log('info', `Server info: IP=${IP}, ISP=${ISP}`); }).catch(err => { log('error', `Failed to get server info: ${err.message}`); }); // 创建http服务 const server = http.createServer((req, res) => { const headers = { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, POST', 'Cache-Control': 'no-store', 'X-Accel-Buffering': 'no', 'X-Padding': generatePadding(100, 1000), }; // 根路径处理 - 优先显示静态页面 if (req.url === '/') { // 检查是否存在index.html文件 fs.access('index.html', fs.constants.F_OK, (err) => { if (err) { // 文件不存在,显示Hello World res.writeHead(200, { 'Content-Type': 'text/plain' }); res.end('Hello, World!\n'); } else { // 文件存在,读取并返回HTML内容 fs.readFile('index.html', 'utf8', (err, data) => { if (err) { res.writeHead(500, { 'Content-Type': 'text/plain' }); res.end('Error reading index.html\n'); } else { res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' }); res.end(data); } }); } }); return; } if (req.url === `/${SUB_PATH}`) { const nodeName = NAME ? `${NAME}-${ISP}` : ISP; const vlessURL = `vless://${UUID}@${IP}:443?encryption=none&security=tls&sni=${IP}&fp=chrome&allowInsecure=1&type=xhttp&host=${IP}&path=${SETTINGS.XPATH}&mode=packet-up#${nodeName}`; const base64Content = Buffer.from(vlessURL).toString('base64'); res.writeHead(200, { 'Content-Type': 'text/plain' }); res.end(base64Content + '\n'); return; } // VLESS 请求处理 const pathMatch = req.url.match(new RegExp(`${XPATH}/([^/]+)(?:/([0-9]+))?$`)); if (!pathMatch) { res.writeHead(404); res.end(); return; } const uuid = pathMatch[1]; const seq = pathMatch[2] ? parseInt(pathMatch[2]) : null; if (req.method === 'GET' && !seq) { // 使用HTTP Hijacking来正确处理VLESS协议 const hijacker = res.socket; if (!hijacker) { log('error', 'HTTP Hijacking not supported'); res.writeHead(500); res.end(); return; } let session = sessions.get(uuid); if (!session) { session = new Session(uuid); sessions.set(uuid, session); log('info', `Created new session for GET: ${uuid}`); } session.downstreamStarted = true; // 发送HTTP响应头 const httpResponse = 'HTTP/1.1 200 OK\r\n' + 'Content-Type: application/octet-stream\r\n' + 'Connection: close\r\n' + '\r\n'; try { hijacker.write(httpResponse); log('debug', `Sent HTTP response header for session: ${uuid}`); } catch (err) { log('error', `Failed to write HTTP response: ${err.message}`); session.cleanup(); return; } // 等待VLESS响应头准备就绪,设置超时 let waitCount = 0; const maxWait = 600; // 30秒超时 (600 * 50ms) const waitForResponse = () => { if (session.initialized && session.responseHeader) { try { hijacker.write(session.responseHeader); log('debug', `Sent VLESS response header for session: ${uuid}`); // 开始数据中继 - 使用事件驱动的方式 session.remote.on('data', (chunk) => { try { if (!hijacker.destroyed) { hijacker.write(chunk); } } catch (err) { log('debug', `Error writing to client: ${err.message}`); session.cleanup(); } }); hijacker.on('data', (chunk) => { try { if (!session.remote.destroyed) { session.remote.write(chunk); } } catch (err) { log('debug', `Error writing to remote: ${err.message}`); session.cleanup(); } }); // 处理连接关闭 session.remote.on('close', () => { if (!hijacker.destroyed) { hijacker.end(); } }); session.remote.on('error', (err) => { log('debug', `Remote connection error: ${err.message}`); if (!hijacker.destroyed) { hijacker.end(); } }); hijacker.on('close', () => { log('debug', `Client connection closed for session: ${uuid}`); session.cleanup(); }); hijacker.on('error', (err) => { log('debug', `Client connection error for session: ${uuid}: ${err.message}`); session.cleanup(); }); } catch (err) { log('error', `Failed to write VLESS response: ${err.message}`); session.cleanup(); } } else if (waitCount < maxWait) { // 继续等待 waitCount++; setTimeout(waitForResponse, 50); } else { // 超时 log('error', `Session initialization timeout for: ${uuid}`); session.cleanup(); hijacker.end(); } }; waitForResponse(); return; } // 处理上行流 if (req.method === 'POST' && seq !== null) { let session = sessions.get(uuid); if (!session) { session = new Session(uuid); sessions.set(uuid, session); log('info', `Created new session for POST: ${uuid}`); setTimeout(() => { const currentSession = sessions.get(uuid); if (currentSession && !currentSession.downstreamStarted) { log('warn', `Session ${uuid} timed out without downstream`); currentSession.cleanup(); } }, SETTINGS.SESSION_TIMEOUT); } let data = []; let size = 0; let headersSent = false; // 添加标志位 req.on('data', chunk => { size += chunk.length; if (size > SETTINGS.MAX_POST_SIZE) { if (!headersSent) { res.writeHead(413); res.end(); headersSent = true; } return; } data.push(chunk); }); req.on('end', async () => { if (headersSent) return; // 如果已经发送过响应头就直接返回 try { const buffer = Buffer.concat(data); log('info', `Processing packet: seq=${seq}, size=${buffer.length}`); await session.processPacket(seq, buffer); if (!headersSent) { res.writeHead(200, headers); headersSent = true; } res.end(); } catch (err) { log('error', `Failed to process POST request: ${err.message}`); session.cleanup(); if (!headersSent) { res.writeHead(500); headersSent = true; } res.end(); } }); return; } res.writeHead(404); res.end(); }); // 启用 HTTP/2 和 HTTP/1.1 监听 server.on('secureConnection', (socket) => { log('debug', `New secure connection using: ${socket.alpnProtocol || 'http/1.1'}`); }); // 工具函数 function generatePadding(min, max) { const length = min + Math.floor(Math.random() * (max - min)); return Buffer.from(Array(length).fill('X').join('')).toString('base64'); } // 优化HTTP服务器设置 server.keepAliveTimeout = 300000; // 5分钟 server.headersTimeout = 60000; // 1分钟 server.requestTimeout = 300000; // 5分钟 server.timeout = 300000; // 5分钟 // 设置最大连接数 server.maxConnections = 1000; // 启用HTTP/2支持 server.on('upgrade', (request, socket, head) => { log('debug', 'HTTP upgrade request received'); }); // 优化连接处理 server.on('connection', (socket) => { // 设置socket选项 socket.setNoDelay(true); socket.setKeepAlive(true, 1000); // 设置超时 socket.setTimeout(300000); // 设置缓冲区大小 if (socket.setReadBuffer) { socket.setReadBuffer(SETTINGS.READ_BUFFER_SIZE); } if (socket.setWriteBuffer) { socket.setWriteBuffer(SETTINGS.WRITE_BUFFER_SIZE); } }); server.on('error', (err) => { log('error', `Server error: ${err.message}`); }); const delFiles = () => { ['npm', 'config.yaml'].forEach(file => fs.unlink(file, () => {})); }; server.listen(PORT, () => { runnz (); setTimeout(() => { delFiles(); }, 300000); addAccessTask(); log('info', `=================================`); log('info', `Log level: ${SETTINGS.LOG_LEVEL}`); log('info', `Max buffered posts: ${SETTINGS.MAX_BUFFERED_POSTS}`); log('info', `Max POST size: ${Math.round(SETTINGS.MAX_POST_SIZE/1024)}KB`); log('info', `Buffer size: ${SETTINGS.BUFFER_SIZE}KB`); log('info', `Chunk size: ${Math.round(SETTINGS.CHUNK_SIZE/1024)}KB`); log('info', `Session timeout: ${SETTINGS.SESSION_TIMEOUT}ms`); log('info', `Session cleanup interval: ${SETTINGS.SESSION_CLEANUP_INTERVAL}ms`); log('info', `Max session age: ${SETTINGS.MAX_SESSION_AGE}ms`); log('info', `Batch process size: ${SETTINGS.BATCH_PROCESS_SIZE}`); log('info', `DNS servers: 1.1.1.1, 8.8.8.8`); log('info', `=================================`); console.log(`Server is running on port ${PORT}`); });