index.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. 'use strict';
  2. const net = require('net');
  3. const through = require('through2');
  4. const debug = require('debug')('tcp-proxy');
  5. const EventEmitter = require('events').EventEmitter;
  6. function genThrough(interceptor, connection) {
  7. return through.obj(function(chunk, enc, done) {
  8. const context = {
  9. client: {
  10. ip: connection.client.address().address,
  11. port: connection.client.address().port,
  12. },
  13. server: {
  14. ip: connection.server.address().address,
  15. port: connection.server.address().port,
  16. },
  17. self: {
  18. ip: connection.forwardHost,
  19. port: connection.forwardPort,
  20. },
  21. size: chunk.length,
  22. };
  23. const result = interceptor(chunk, enc, context);
  24. const handle = data => {
  25. if (data && !Buffer.isBuffer(data)) {
  26. data = Buffer.from(data);
  27. }
  28. done(null, data || chunk);
  29. };
  30. if (result) {
  31. if (typeof result.then === 'function') {
  32. result.then(handle).catch(e => {
  33. console.error(e);
  34. handle();
  35. });
  36. } else {
  37. handle(result);
  38. }
  39. } else {
  40. handle();
  41. }
  42. });
  43. }
  44. module.exports = class TCPProxy extends EventEmitter {
  45. constructor(options = {}) {
  46. super();
  47. this.host = options.host;
  48. this.port = options.port;
  49. this.clients = [];
  50. }
  51. createProxy({ host, port, forwardPort, forwardHost, interceptor }) {
  52. const proxyHost = host || this.host;
  53. const proxyPort = port || this.port;
  54. forwardHost = forwardHost || '127.0.0.1';
  55. interceptor = interceptor || {};
  56. if (this.server) {
  57. const args = [].slice.call(arguments);
  58. return this.end().then(() => {
  59. return this.createProxy.apply(this, args);
  60. });
  61. }
  62. return new Promise((resolve, reject) => {
  63. this.server = net
  64. .createServer(client => {
  65. let interceptorClient;
  66. let interceptorServer;
  67. debug('new connection from %o', client.address());
  68. const server = net.connect({
  69. port: forwardPort,
  70. host: forwardHost,
  71. }, () => {
  72. let _client = client;
  73. let _server = server;
  74. // client interceptor
  75. if (interceptor.client) {
  76. interceptorClient = genThrough(interceptor.client, { client, server, forwardHost, forwardPort });
  77. _client = _client.pipe(interceptorClient);
  78. }
  79. // server interceptor
  80. if (interceptor.server) {
  81. interceptorServer = genThrough(interceptor.server, { client, server, forwardHost, forwardPort });
  82. _server = _server.pipe(interceptorServer);
  83. }
  84. _client.pipe(server);
  85. _server.pipe(client);
  86. debug(`proxy {$proxyHost}:${proxyPort} connect to ${forwardHost}:${forwardPort}`);
  87. this.emit('connection', _client, _server);
  88. });
  89. const onClose = err => {
  90. client.destroy();
  91. server.destroy();
  92. interceptorClient && interceptorClient.end();
  93. interceptorServer && interceptorServer.end();
  94. debug(`${forwardHost}:${forwardPort} disconnect [${err ? `error: ${err.message}` : 'close'}]`);
  95. this.removeListener('close', onClose);
  96. };
  97. server.once('close', onClose);
  98. server.once('error', onClose);
  99. client.once('close', onClose);
  100. client.once('error', onClose);
  101. this.once('close', onClose);
  102. })
  103. .listen(proxyPort, proxyHost);
  104. this.server.once('error', e => {
  105. debug(`proxy server error: ${e.message}`);
  106. reject(e);
  107. });
  108. this.server.once('listening', () => {
  109. debug(`proxy server listening on ${proxyPort}`);
  110. this.server.removeListener('error', reject);
  111. resolve(this.server);
  112. });
  113. });
  114. }
  115. end() {
  116. if (!this.server) {
  117. return Promise.resolve();
  118. }
  119. return new Promise(resolve => {
  120. this.emit('close');
  121. this.server.close(() => {
  122. debug('proxy server closed');
  123. this.server = null;
  124. resolve();
  125. });
  126. });
  127. }
  128. };