const { io } = require('socket.io-client'); const http = require('http'); const debug = require('debug')('tunnel:client'); class SocketTunnel { constructor(options) { this.options = { serverUrl: options.serverUrl || process.env.TUNNEL_SERVER || 'http://localhost:23909', localPort: options.localPort || parseInt(process.env.LOCAL_PORT || '1081') }; this.maxRetries = options.maxRetries || parseInt(process.env.MAX_RETRIES || '10'); this.socket = null; this.tunnelId = null; this.url = null; this.connected = false; this.retryCount = 0; } async connect() { try { this.socket = io(this.options.serverUrl); this.socket.on('connect', () => { debug('已连接到服务器'); this.connected = true; this.retryCount = 0; // 注册隧道 this.socket.emit('register', { localPort: this.options.localPort }); }); this.socket.on('registered', (data) => { this.tunnelId = data.tunnelId; this.url = data.url; debug('隧道已注册:', this.url); }); // 处理请求 this.socket.on('request', async (data) => { debug('收到请求:', data.method, data.path); // 转发到本地服务器 const response = await this.forwardRequest(data); this.socket.emit('response', { requestId: data.id, response }); }); // 心跳检测 setInterval(() => { if (this.connected) { this.socket.emit('ping'); } }, 30000); this.socket.on('disconnect', () => { debug('与服务器断开连接'); this.connected = false; this.reconnect(); }); this.socket.on('error', (err) => { debug('连接错误:', err); this.reconnect(); }); } catch (err) { debug('连接失败:', err); this.reconnect(); } } async forwardRequest(data) { return new Promise((resolve) => { const options = { hostname: 'localhost', port: this.options.localPort, path: data.path || '/', // 确保路径存在 method: data.method, headers: { ...data.headers, host: 'localhost:' + this.options.localPort // 修改 host 头 } }; debug('转发请求到本地服务器:', options.method, options.path); const req = http.request(options, (res) => { const chunks = []; res.on('data', chunk => chunks.push(chunk)); res.on('end', () => { const body = Buffer.concat(chunks).toString(); debug('本地服务器响应:', res.statusCode); resolve({ status: res.statusCode, headers: res.headers, body }); }); }); req.on('error', (err) => { debug('本地请求错误:', err); resolve({ status: 502, headers: { 'content-type': 'text/plain' }, body: 'Bad Gateway' }); }); req.setTimeout(5000, () => { debug('本地请求超时'); req.destroy(); resolve({ status: 504, headers: { 'content-type': 'text/plain' }, body: 'Gateway Timeout' }); }); // 处理请求体 if (data.body) { try { // 如果是对象,转换为 JSON 字符串 const body = typeof data.body === 'object' ? JSON.stringify(data.body) : data.body.toString(); req.write(body); debug('已写入请求体'); } catch (err) { debug('写入请求体失败:', err); } } req.end(); }); } reconnect() { if (this.retryCount >= this.maxRetries) { debug('达到最大重试次数'); return; } const delay = Math.min(1000 * Math.pow(2, this.retryCount), 30000); debug(`${delay}ms 后重试连接`); setTimeout(() => { this.retryCount++; this.connect(); }, delay); } close() { if (this.socket) { this.socket.close(); } } } module.exports = { SocketTunnel };