UNPKG

node-mitmproxy1

Version:
438 lines (368 loc) 16.1 kB
'use strict'; var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var net = require('net'); var url = require('url'); var colors = require('colors'); var localIP = '127.0.0.1'; // 性能优化:连接池管理 var ConnectionPool = function () { function ConnectionPool() { var _this = this; _classCallCheck(this, ConnectionPool); this.pools = new Map(); // hostname:port -> socket数组 this.maxPoolSize = 20; this.maxIdleTime = 30000; // 30秒空闲超时 this.cleanupInterval = 60000; // 1分钟清理一次 // 定期清理空闲连接 setInterval(function () { return _this.cleanup(); }, this.cleanupInterval); } _createClass(ConnectionPool, [{ key: 'getKey', value: function getKey(hostname, port) { return hostname + ':' + port; } }, { key: 'getConnection', value: function getConnection(hostname, port) { var key = this.getKey(hostname, port); var pool = this.pools.get(key); if (pool && pool.length > 0) { // 查找可用的连接 for (var i = pool.length - 1; i >= 0; i--) { var socket = pool[i]; if (socket.readyState === 'open' && !socket.destroyed && !socket.busy) { socket.busy = true; socket.lastUsed = Date.now(); return socket; } } } return null; } }, { key: 'releaseConnection', value: function releaseConnection(socket, hostname, port) { if (socket.destroyed || socket.readyState !== 'open') { return; } socket.busy = false; socket.lastUsed = Date.now(); var key = this.getKey(hostname, port); var pool = this.pools.get(key); if (!pool) { pool = []; this.pools.set(key, pool); } // 限制池大小 if (pool.length < this.maxPoolSize) { pool.push(socket); } else { socket.destroy(); } } }, { key: 'cleanup', value: function cleanup() { var now = Date.now(); var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = this.pools.entries()[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var _step$value = _slicedToArray(_step.value, 2), key = _step$value[0], pool = _step$value[1]; for (var i = pool.length - 1; i >= 0; i--) { var socket = pool[i]; // 清理过期或损坏的连接 if (socket.destroyed || socket.readyState !== 'open' || now - socket.lastUsed > this.maxIdleTime) { if (!socket.destroyed) { socket.destroy(); } pool.splice(i, 1); } } // 清理空池 if (pool.length === 0) { this.pools.delete(key); } } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } } }, { key: 'getStats', value: function getStats() { var totalConnections = 0; var activeConnections = 0; var _iteratorNormalCompletion2 = true; var _didIteratorError2 = false; var _iteratorError2 = undefined; try { for (var _iterator2 = this.pools.values()[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { var pool = _step2.value; totalConnections += pool.length; activeConnections += pool.filter(function (s) { return s.busy; }).length; } } catch (err) { _didIteratorError2 = true; _iteratorError2 = err; } finally { try { if (!_iteratorNormalCompletion2 && _iterator2.return) { _iterator2.return(); } } finally { if (_didIteratorError2) { throw _iteratorError2; } } } return { totalPools: this.pools.size, totalConnections: totalConnections, activeConnections: activeConnections, idleConnections: totalConnections - activeConnections }; } }]); return ConnectionPool; }(); // 性能优化:URL解析缓存 var urlCache = new Map(); var maxUrlCacheSize = 1000; function parseUrlCached(urlStr) { if (urlCache.has(urlStr)) { return urlCache.get(urlStr); } var parsed = url.parse('https://' + urlStr); // 限制缓存大小 if (urlCache.size >= maxUrlCacheSize) { var firstKey = urlCache.keys().next().value; urlCache.delete(firstKey); } urlCache.set(urlStr, parsed); return parsed; } // 性能优化:错误处理优化 var connectErrorHandlers = { ECONNREFUSED: function ECONNREFUSED(cltSocket, hostname, port) { cltSocket.write('HTTP/1.1 502 Bad Gateway\r\n' + 'Content-Type: text/plain\r\n' + 'Connection: close\r\n\r\n' + ('Cannot connect to ' + hostname + ':' + port)); cltSocket.end(); }, ETIMEDOUT: function ETIMEDOUT(cltSocket, hostname, port) { cltSocket.write('HTTP/1.1 504 Gateway Timeout\r\n' + 'Content-Type: text/plain\r\n' + 'Connection: close\r\n\r\n' + ('Connection timeout to ' + hostname + ':' + port)); cltSocket.end(); }, EHOSTUNREACH: function EHOSTUNREACH(cltSocket, hostname, port) { cltSocket.write('HTTP/1.1 502 Bad Gateway\r\n' + 'Content-Type: text/plain\r\n' + 'Connection: close\r\n\r\n' + ('Host unreachable: ' + hostname + ':' + port)); cltSocket.end(); }, default: function _default(cltSocket, error, hostname, port) { cltSocket.write('HTTP/1.1 500 Internal Server Error\r\n' + 'Content-Type: text/plain\r\n' + 'Connection: close\r\n\r\n' + ('Proxy error: ' + error.message)); cltSocket.end(); } }; // 性能监控 var connectMetrics = { connectionCount: 0, successCount: 0, errorCount: 0, totalConnectTime: 0, poolHits: 0, recordConnection: function recordConnection(connectTime) { var fromPool = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; this.connectionCount++; this.successCount++; this.totalConnectTime += connectTime; if (fromPool) this.poolHits++; }, recordError: function recordError() { this.connectionCount++; this.errorCount++; }, getStats: function getStats() { return { totalConnections: this.connectionCount, successRate: this.connectionCount > 0 ? this.successCount / this.connectionCount : 0, avgConnectTime: this.successCount > 0 ? this.totalConnectTime / this.successCount : 0, poolHitRate: this.connectionCount > 0 ? this.poolHits / this.connectionCount : 0 }; }, reset: function reset() { this.connectionCount = 0; this.successCount = 0; this.errorCount = 0; this.totalConnectTime = 0; this.poolHits = 0; } }; // 每分钟输出连接统计 setInterval(function () { var stats = connectMetrics.getStats(); var poolStats = connectionPool.getStats(); if (connectMetrics.connectionCount > 0) { console.log('Connect Performance Stats:', { connections: connectMetrics.connectionCount, successRate: (stats.successRate * 100).toFixed(2) + '%', avgConnectTime: stats.avgConnectTime.toFixed(2) + 'ms', poolHitRate: (stats.poolHitRate * 100).toFixed(2) + '%', poolStats: poolStats }); } connectMetrics.reset(); }, 60000); var connectionPool = new ConnectionPool(); // create connectHandler function module.exports = function createConnectHandler(sslConnectInterceptor, fakeServerCenter) { // 性能优化:预编译拦截器检查 var hasInterceptor = typeof sslConnectInterceptor === 'function'; return function connectHandler(req, cltSocket, head) { var startTime = process.hrtime.bigint(); try { // 性能优化:使用缓存的URL解析 var srvUrl = parseUrlCached(req.url); if (hasInterceptor && sslConnectInterceptor.call(null, req, cltSocket, head)) { // SSL拦截模式 fakeServerCenter.getServerPromise(srvUrl.hostname, srvUrl.port).then(function (serverObj) { var endTime = process.hrtime.bigint(); var connectTime = Number(endTime - startTime) / 1000000; connectMetrics.recordConnection(connectTime, false); connect(req, cltSocket, head, localIP, serverObj.port, false); }).catch(function (error) { connectMetrics.recordError(); console.error('SSL server creation error:', error); var handler = connectErrorHandlers.default; handler(cltSocket, error, srvUrl.hostname, srvUrl.port); }); } else { // 直接代理模式 var endTime = process.hrtime.bigint(); var connectTime = Number(endTime - startTime) / 1000000; connect(req, cltSocket, head, srvUrl.hostname, srvUrl.port, true, connectTime); } } catch (error) { connectMetrics.recordError(); console.error('Connect handler error:', error); var handler = connectErrorHandlers.default; handler(cltSocket, error, 'unknown', 'unknown'); } }; }; // 性能优化:连接函数 function connect(req, cltSocket, head, hostname, port) { var usePool = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true; var baseConnectTime = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : 0; var proxySocket = null; var fromPool = false; // 尝试从连接池获取连接 if (usePool) { proxySocket = connectionPool.getConnection(hostname, port); if (proxySocket) { fromPool = true; // 立即建立隧道 establishTunnel(cltSocket, proxySocket, head, hostname, port, baseConnectTime, fromPool); return proxySocket; } } // 创建新连接 var connectStart = process.hrtime.bigint(); proxySocket = net.connect({ port: port, host: hostname, // 性能优化:连接选项 keepAlive: true, keepAliveInitialDelay: 30000, noDelay: true }, function () { var connectEnd = process.hrtime.bigint(); var connectTime = baseConnectTime + Number(connectEnd - connectStart) / 1000000; connectMetrics.recordConnection(connectTime, fromPool); establishTunnel(cltSocket, proxySocket, head, hostname, port, connectTime, fromPool); }); // 性能优化:错误处理 proxySocket.on('error', function (error) { connectMetrics.recordError(); console.log(colors.red('Connection error to ' + hostname + ':' + port + ':', error.message)); var handler = connectErrorHandlers[error.code] || connectErrorHandlers.default; handler(cltSocket, error, hostname, port); }); // 连接超时处理 proxySocket.setTimeout(30000, function () { connectMetrics.recordError(); proxySocket.destroy(); connectErrorHandlers.ETIMEDOUT(cltSocket, hostname, port); }); return proxySocket; } // 性能优化:建立隧道 function establishTunnel(cltSocket, proxySocket, head, hostname, port, connectTime, fromPool) { // 发送连接建立响应 cltSocket.write('HTTP/1.1 200 Connection Established\r\n' + 'Proxy-agent: node-mitmproxy-optimized\r\n' + ('X-Connect-Time: ' + connectTime.toFixed(2) + 'ms\r\n') + ('X-From-Pool: ' + fromPool + '\r\n') + '\r\n'); // 转发初始数据 if (head && head.length > 0) { proxySocket.write(head); } // 建立双向数据流 proxySocket.pipe(cltSocket); cltSocket.pipe(proxySocket); // 性能优化:连接清理处理 var cleanup = function cleanup() { if (proxySocket && !proxySocket.destroyed) { if (fromPool) { // 释放回连接池 connectionPool.releaseConnection(proxySocket, hostname, port); } else { // 尝试加入连接池 connectionPool.releaseConnection(proxySocket, hostname, port); } } }; cltSocket.on('close', cleanup); cltSocket.on('error', function (error) { console.log(colors.yellow('Client socket error: ' + error.message)); cleanup(); }); proxySocket.on('close', function () { if (!cltSocket.destroyed) { cltSocket.end(); } }); proxySocket.on('error', function (error) { console.log(colors.yellow('Proxy socket error: ' + error.message)); if (!cltSocket.destroyed) { cltSocket.end(); } }); } // 导出性能指标和连接池统计 module.exports.getConnectMetrics = function () { return connectMetrics.getStats(); }; module.exports.getPoolStats = function () { return connectionPool.getStats(); }; module.exports.resetConnectMetrics = function () { return connectMetrics.reset(); }; module.exports.cleanupConnectionPool = function () { return connectionPool.cleanup(); };