proxy_chain_rb 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +16 -16
  3. data/README.md +14 -5
  4. data/lib/proxy_chain_rb/node_js/proxy-chain-server/node_modules/.yarn-integrity +23 -0
  5. data/lib/proxy_chain_rb/node_js/proxy-chain-server/node_modules/bluebird/README.md +1 -1
  6. data/lib/proxy_chain_rb/node_js/proxy-chain-server/node_modules/bluebird/js/browser/bluebird.core.js +254 -121
  7. data/lib/proxy_chain_rb/node_js/proxy-chain-server/node_modules/bluebird/js/browser/bluebird.core.min.js +3 -3
  8. data/lib/proxy_chain_rb/node_js/proxy-chain-server/node_modules/bluebird/js/browser/bluebird.js +284 -129
  9. data/lib/proxy_chain_rb/node_js/proxy-chain-server/node_modules/bluebird/js/browser/bluebird.min.js +4 -4
  10. data/lib/proxy_chain_rb/node_js/proxy-chain-server/node_modules/bluebird/js/release/async.js +3 -48
  11. data/lib/proxy_chain_rb/node_js/proxy-chain-server/node_modules/bluebird/js/release/debuggability.js +145 -27
  12. data/lib/proxy_chain_rb/node_js/proxy-chain-server/node_modules/bluebird/js/release/join.js +4 -7
  13. data/lib/proxy_chain_rb/node_js/proxy-chain-server/node_modules/bluebird/js/release/map.js +10 -3
  14. data/lib/proxy_chain_rb/node_js/proxy-chain-server/node_modules/bluebird/js/release/promise.js +58 -28
  15. data/lib/proxy_chain_rb/node_js/proxy-chain-server/node_modules/bluebird/js/release/promise_array.js +1 -0
  16. data/lib/proxy_chain_rb/node_js/proxy-chain-server/node_modules/bluebird/js/release/reduce.js +16 -5
  17. data/lib/proxy_chain_rb/node_js/proxy-chain-server/node_modules/bluebird/js/release/settle.js +4 -0
  18. data/lib/proxy_chain_rb/node_js/proxy-chain-server/node_modules/bluebird/js/release/util.js +39 -7
  19. data/lib/proxy_chain_rb/node_js/proxy-chain-server/node_modules/bluebird/package.json +39 -63
  20. data/lib/proxy_chain_rb/node_js/proxy-chain-server/node_modules/commander/CHANGELOG.md +11 -0
  21. data/lib/proxy_chain_rb/node_js/proxy-chain-server/node_modules/commander/index.js +1 -1
  22. data/lib/proxy_chain_rb/node_js/proxy-chain-server/node_modules/commander/package.json +20 -52
  23. data/lib/proxy_chain_rb/node_js/proxy-chain-server/node_modules/debug/package.json +21 -60
  24. data/lib/proxy_chain_rb/node_js/proxy-chain-server/node_modules/ms/package.json +19 -51
  25. data/lib/proxy_chain_rb/node_js/proxy-chain-server/node_modules/portastic/node_modules/bluebird/package.json +41 -66
  26. data/lib/proxy_chain_rb/node_js/proxy-chain-server/node_modules/portastic/package.json +23 -51
  27. data/lib/proxy_chain_rb/node_js/proxy-chain-server/node_modules/proxy-chain/CHANGELOG.md +18 -0
  28. data/lib/proxy_chain_rb/node_js/proxy-chain-server/node_modules/proxy-chain/README.md +8 -0
  29. data/lib/proxy_chain_rb/node_js/proxy-chain-server/node_modules/proxy-chain/build/anonymize_proxy.js +5 -0
  30. data/lib/proxy_chain_rb/node_js/proxy-chain-server/node_modules/proxy-chain/build/handler_base.js +86 -53
  31. data/lib/proxy_chain_rb/node_js/proxy-chain-server/node_modules/proxy-chain/build/handler_forward.js +23 -5
  32. data/lib/proxy_chain_rb/node_js/proxy-chain-server/node_modules/proxy-chain/build/handler_tunnel_chain.js +3 -7
  33. data/lib/proxy_chain_rb/node_js/proxy-chain-server/node_modules/proxy-chain/build/handler_tunnel_direct.js +1 -1
  34. data/lib/proxy_chain_rb/node_js/proxy-chain-server/node_modules/proxy-chain/build/index.js +3 -5
  35. data/lib/proxy_chain_rb/node_js/proxy-chain-server/node_modules/proxy-chain/build/server.js +3 -1
  36. data/lib/proxy_chain_rb/node_js/proxy-chain-server/node_modules/proxy-chain/build/tcp_tunnel.js +245 -115
  37. data/lib/proxy_chain_rb/node_js/proxy-chain-server/node_modules/proxy-chain/build/tcp_tunnel_tools.js +138 -0
  38. data/lib/proxy_chain_rb/node_js/proxy-chain-server/node_modules/proxy-chain/node_modules/.bin/portastic +1 -0
  39. data/lib/proxy_chain_rb/node_js/proxy-chain-server/node_modules/proxy-chain/package.json +41 -68
  40. data/lib/proxy_chain_rb/node_js/proxy-chain-server/node_modules/underscore/package.json +27 -58
  41. data/lib/proxy_chain_rb/node_js/proxy-chain-server/package.json +1 -1
  42. data/lib/proxy_chain_rb/node_js/proxy-chain-server/server.js +4 -2
  43. data/lib/proxy_chain_rb/node_js/proxy-chain-server/yarn.lock +53 -0
  44. data/lib/proxy_chain_rb/server.rb +8 -6
  45. data/lib/proxy_chain_rb/version.rb +1 -1
  46. data/proxy_chain_rb.gemspec +3 -3
  47. metadata +16 -14
  48. data/lib/proxy_chain_rb/node_js/proxy-chain-server/node_modules/proxy-chain/build/handler_tunnel_tcp_chain.js +0 -271
  49. data/lib/proxy_chain_rb/node_js/proxy-chain-server/package-lock.json +0 -63
@@ -16,6 +16,8 @@ var _handler_base = require('./handler_base');
16
16
 
17
17
  var _handler_base2 = _interopRequireDefault(_handler_base);
18
18
 
19
+ var _server = require('./server');
20
+
19
21
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
20
22
 
21
23
  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
@@ -51,21 +53,30 @@ var HandlerForward = function (_HandlerBase) {
51
53
  // var hasVia = false;
52
54
  // var via = '1.1 ' + hostname + ' (proxy/' + version + ')';
53
55
 
56
+ var hostHeaderFound = false;
57
+
58
+ // TODO: We should probably use a raw HTTP message via socket instead of http.request(),
59
+ // since Node transforms the headers to lower case and thus makes it easy to detect the proxy
54
60
  for (var i = 0; i < this.srcRequest.rawHeaders.length; i += 2) {
55
61
  var headerName = this.srcRequest.rawHeaders[i];
56
62
  var headerValue = this.srcRequest.rawHeaders[i + 1];
57
63
 
58
- if (headerName === 'Connection' && headerValue === 'keep-alive') {
64
+ if (/^connection$/i.test(headerName) && /^keep-alive$/i.test(headerValue)) {
59
65
  // Keep the "Connection: keep-alive" header, to reduce the chance that the server
60
66
  // will detect we're not a browser and also to improve performance
61
67
  } else if ((0, _tools.isHopByHopHeader)(headerName)) {
62
68
  continue;
63
69
  } else if ((0, _tools.isInvalidHeader)(headerName, headerValue)) {
64
70
  continue;
71
+ } else if (/^host$/i.test(headerName)) {
72
+ // If Host header was used multiple times, only consider the first one.
73
+ // This is to prevent "TypeError: hostHeader.startsWith is not a function at calculateServerName (_http_agent.js:240:20)"
74
+ if (hostHeaderFound) continue;
75
+ hostHeaderFound = true;
65
76
  }
66
77
 
67
78
  /*
68
- if (!hasXForwardedFor && 'x-forwarded-for' === keyLower) {
79
+ if (!hasXForwardedFor && 'x-forwarded-for' === keyLower) {
69
80
  // append to existing "X-Forwarded-For" header
70
81
  // http://en.wikipedia.org/wiki/X-Forwarded-For
71
82
  hasXForwardedFor = true;
@@ -137,12 +148,9 @@ var HandlerForward = function (_HandlerBase) {
137
148
  value: function onTrgResponse(response) {
138
149
  if (this.isClosed) return;
139
150
  this.log('Received response from target (' + response.statusCode + ')');
140
- // console.dir(response);
141
151
 
142
152
  if (this.checkUpstreamProxy407(response)) return;
143
153
 
144
- this.srcGotResponse = true;
145
-
146
154
  // Prepare response headers
147
155
  var headers = {};
148
156
  for (var i = 0; i < response.rawHeaders.length; i += 2) {
@@ -155,6 +163,16 @@ var HandlerForward = function (_HandlerBase) {
155
163
  (0, _tools.addHeader)(headers, name, value);
156
164
  }
157
165
 
166
+ // Ensure status code is in the range accepted by Node, otherwise proxy will crash with
167
+ // "RangeError: Invalid status code: 0" (see writeHead in Node's _http_server.js)
168
+ // Fixes https://github.com/apifytech/proxy-chain/issues/35
169
+ if (response.statusCode < 100 || response.statusCode > 999) {
170
+ this.fail(new _server.RequestError('Target server responded with an invalid HTTP status code (' + response.statusCode + ')', 500));
171
+ return;
172
+ }
173
+
174
+ this.srcGotResponse = true;
175
+
158
176
  this.srcResponse.writeHead(response.statusCode, headers);
159
177
  response.pipe(this.srcResponse);
160
178
  }
@@ -24,10 +24,6 @@ function _possibleConstructorReturn(self, call) { if (!self) { throw new Referen
24
24
 
25
25
  function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
26
26
 
27
- // import { tee } from './tools';
28
-
29
- /* globals Buffer */
30
-
31
27
  /**
32
28
  * Represents a connection from source client to an external proxy using HTTP CONNECT tunnel.
33
29
  */
@@ -68,8 +64,8 @@ var HandlerTunnelChain = function (_HandlerBase) {
68
64
 
69
65
  this.trgRequest.once('connect', this.onTrgRequestConnect);
70
66
  this.trgRequest.once('abort', this.onTrgRequestAbort);
71
- this.trgRequest.once('error', this.onTrgRequestError);
72
- this.trgRequest.on('socket', this.onTrgSocket);
67
+ this.trgRequest.once('socket', this.onTrgSocket);
68
+ this.trgRequest.on('error', this.onTrgRequestError);
73
69
 
74
70
  // Send the data
75
71
  this.trgRequest.end();
@@ -84,7 +80,7 @@ var HandlerTunnelChain = function (_HandlerBase) {
84
80
 
85
81
  this.srcGotResponse = true;
86
82
  this.srcResponse.removeListener('finish', this.onSrcResponseFinish);
87
- this.srcResponse.writeHead(200, 'Connection established');
83
+ this.srcResponse.writeHead(200, 'Connection Established');
88
84
 
89
85
  // HACK: force a flush of the HTTP header. This is to ensure 'head' is empty to avoid
90
86
  // assert at https://github.com/request/tunnel-agent/blob/master/index.js#L160
@@ -56,7 +56,7 @@ var HandlerTunnelDirect = function (_HandlerBase) {
56
56
  this.srcGotResponse = true;
57
57
 
58
58
  this.srcResponse.removeListener('finish', this.onSrcResponseFinish);
59
- this.srcResponse.writeHead(200, 'Connection established');
59
+ this.srcResponse.writeHead(200, 'Connection Established');
60
60
 
61
61
  // HACK: force a flush of the HTTP header. This is to ensure 'head' is empty to avoid
62
62
  // assert at https://github.com/request/tunnel-agent/blob/master/index.js#L160
@@ -6,9 +6,7 @@ var _tools = require('./tools');
6
6
 
7
7
  var _anonymize_proxy = require('./anonymize_proxy');
8
8
 
9
- var _tcp_tunnel = require('./tcp_tunnel');
10
-
11
- /* globals module */
9
+ var _tcp_tunnel_tools = require('./tcp_tunnel_tools');
12
10
 
13
11
  // Publicly exported functions and classes
14
12
  var ProxyChain = {
@@ -19,8 +17,8 @@ var ProxyChain = {
19
17
  redactParsedUrl: _tools.redactParsedUrl,
20
18
  anonymizeProxy: _anonymize_proxy.anonymizeProxy,
21
19
  closeAnonymizedProxy: _anonymize_proxy.closeAnonymizedProxy,
22
- createTunnel: _tcp_tunnel.createTunnel,
23
- closeTunnel: _tcp_tunnel.closeTunnel
20
+ createTunnel: _tcp_tunnel_tools.createTunnel,
21
+ closeTunnel: _tcp_tunnel_tools.closeTunnel
24
22
  };
25
23
 
26
24
  module.exports = ProxyChain;
@@ -172,7 +172,7 @@ var Server = exports.Server = function (_EventEmitter) {
172
172
  value: function log(handlerId, str) {
173
173
  if (this.verbose) {
174
174
  var logPrefix = handlerId ? handlerId + ' | ' : '';
175
- console.log('Server[' + this.port + ']: ' + logPrefix + str);
175
+ console.log('ProxyServer[' + this.port + ']: ' + logPrefix + str);
176
176
  }
177
177
  }
178
178
  }, {
@@ -270,6 +270,7 @@ var Server = exports.Server = function (_EventEmitter) {
270
270
  this.log(handlerOpts.id, '!!! Handling ' + request.method + ' ' + request.url + ' HTTP/' + request.httpVersion);
271
271
 
272
272
  var socket = request.socket;
273
+
273
274
  var isHttp = false;
274
275
 
275
276
  return _bluebird2.default.resolve().then(function () {
@@ -585,6 +586,7 @@ var Server = exports.Server = function (_EventEmitter) {
585
586
  // TODO: keep track of all handlers and close them if closeConnections=true
586
587
  if (this.server) {
587
588
  var server = this.server;
589
+
588
590
  this.server = null;
589
591
  return _bluebird2.default.promisify(server.close).bind(server)().nodeify(callback);
590
592
  }
@@ -4,135 +4,265 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
 
7
- var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
7
+ 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; }; }();
8
8
 
9
- 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"); } }; }();
9
+ var _http = require('http');
10
10
 
11
- exports.createTunnel = createTunnel;
12
- exports.closeTunnel = closeTunnel;
11
+ var _http2 = _interopRequireDefault(_http);
13
12
 
14
- var _bluebird = require('bluebird');
13
+ var _tools = require('./tools');
15
14
 
16
- var _bluebird2 = _interopRequireDefault(_bluebird);
15
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
17
16
 
18
- var _net = require('net');
17
+ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
19
18
 
20
- var _net2 = _interopRequireDefault(_net);
19
+ /**
20
+ * Represents a connection from source client to an external proxy using HTTP CONNECT tunnel, allows TCP connection.
21
+ */
22
+ var TcpTunnel = function () {
23
+ function TcpTunnel(_ref) {
24
+ var srcSocket = _ref.srcSocket,
25
+ trgParsed = _ref.trgParsed,
26
+ upstreamProxyUrlParsed = _ref.upstreamProxyUrlParsed,
27
+ log = _ref.log;
21
28
 
22
- var _handler_tunnel_tcp_chain = require('./handler_tunnel_tcp_chain');
29
+ _classCallCheck(this, TcpTunnel);
23
30
 
24
- var _handler_tunnel_tcp_chain2 = _interopRequireDefault(_handler_tunnel_tcp_chain);
31
+ this.log = log;
25
32
 
26
- var _tools = require('./tools');
33
+ // Bind all event handlers to this instance
34
+ this.bindHandlersToThis(['onSrcSocketClose', 'onSrcSocketEnd', 'onSrcSocketError', 'onTrgSocket', 'onTrgSocketClose', 'onTrgSocketEnd', 'onTrgSocketError', 'onTrgRequestConnect', 'onTrgRequestAbort', 'onTrgRequestError']);
27
35
 
28
- function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
36
+ if (!trgParsed.hostname) throw new Error('The "trgParsed.hostname" option is required');
37
+ if (!trgParsed.port) throw new Error('The "trgParsed.port" option is required');
29
38
 
30
- var runningServers = {};
31
-
32
- function createTunnel(proxyUrl, targetHost) {
33
- var providedOptions = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
34
- var callback = arguments[3];
35
-
36
- // TODO: More and better validations - yeah, make sure targetHost is really a hostname
37
- var _targetHost$split = targetHost.split(':'),
38
- _targetHost$split2 = _slicedToArray(_targetHost$split, 2),
39
- trgHostname = _targetHost$split2[0],
40
- trgPort = _targetHost$split2[1];
41
-
42
- if (!trgHostname || !trgPort) throw new Error('target needs to include both hostname and port.');
43
-
44
- var parsedProxyUrl = (0, _tools.parseUrl)(proxyUrl);
45
- if (!parsedProxyUrl.hostname) throw new Error('proxyUrl needs to include atleast hostname');
46
- if (parsedProxyUrl.scheme !== 'http') throw new Error('Currently only "http" scheme is supported');
47
-
48
- var options = _extends({
49
- verbose: false,
50
- hostname: 'localhost',
51
- port: null
52
- }, providedOptions);
53
-
54
- return new _bluebird2.default(function (resolve, reject) {
55
- if (options.port) return resolve(options.port);
56
- (0, _tools.findFreePort)().then(resolve).catch(reject);
57
- }).then(function (port) {
58
- var server = _net2.default.createServer();
59
-
60
- var log = function log() {
61
- var _console;
62
-
63
- if (options.verbose) (_console = console).log.apply(_console, arguments);
64
- };
65
-
66
- server.on('connection', function (srcSocket) {
67
- runningServers[port].connections = srcSocket;
68
- var remoteAddress = srcSocket.remoteAddress + ':' + srcSocket.remotePort;
69
- log('new client connection from %s', remoteAddress);
70
-
71
- srcSocket.pause();
72
-
73
- var tunnel = new _handler_tunnel_tcp_chain2.default({
74
- srcSocket: srcSocket,
75
- upstreamProxyUrlParsed: parsedProxyUrl,
76
- trgParsed: {
77
- hostname: trgHostname,
78
- port: trgPort
79
- },
80
- log: log
81
- });
39
+ this.trgRequest = null;
40
+ this.trgSocket = null;
41
+ this.trgParsed = trgParsed;
42
+ this.trgParsed.port = this.trgParsed.port || DEFAULT_TARGET_PORT;
82
43
 
83
- tunnel.run();
44
+ this.srcSocket = srcSocket;
45
+ this.srcSocket.once('close', this.onSrcSocketClose);
46
+ this.srcSocket.once('end', this.onSrcSocketEnd);
47
+ this.srcSocket.on('error', this.onSrcSocketError);
84
48
 
85
- srcSocket.on('data', onConnData);
86
- srcSocket.once('close', onConnClose);
87
- srcSocket.on('error', onConnError);
49
+ this.upstreamProxyUrlParsed = upstreamProxyUrlParsed;
88
50
 
89
- function onConnData(d) {
90
- log('connection data from %s: %j', remoteAddress, d);
91
- }
51
+ this.isClosed = false;
52
+ }
92
53
 
93
- function onConnClose() {
94
- log('connection from %s closed', remoteAddress);
95
- }
54
+ _createClass(TcpTunnel, [{
55
+ key: 'bindHandlersToThis',
56
+ value: function bindHandlersToThis(handlerNames) {
57
+ var _this = this;
96
58
 
97
- function onConnError(err) {
98
- log('Connection %s error: %s', remoteAddress, err.message);
99
- }
100
- });
101
-
102
- return new _bluebird2.default(function (resolve) {
103
- server.listen(port, function (err) {
104
- if (err) return reject(err);
105
- log('server listening to ', server.address());
106
- runningServers[port] = { server: server, connections: [] };
107
- resolve(options.hostname + ':' + port);
108
- });
109
- });
110
- }).nodeify(callback);
111
- }
112
-
113
- function closeTunnel(serverPath, closeConnections, callback) {
114
- var _serverPath$split = serverPath.split(':'),
115
- _serverPath$split2 = _slicedToArray(_serverPath$split, 2),
116
- hostname = _serverPath$split2[0],
117
- port = _serverPath$split2[1];
118
-
119
- if (!hostname) throw new Error('serverPath must contain hostname');
120
- if (!port) throw new Error('serverPath must contain port');
121
-
122
- return new _bluebird2.default(function (resolve) {
123
- if (!runningServers[port]) return resolve(false);
124
- if (!closeConnections) return resolve();
125
- runningServers[port].connections.forEach(function (connection) {
126
- return connection.destroy();
127
- });
128
- resolve();
129
- }).then(function (serverExists) {
130
- return new _bluebird2.default(function (resolve) {
131
- if (!serverExists) return resolve(false);
132
- runningServers[port].close(function () {
133
- delete runningServers[port];
134
- resolve(true);
59
+ handlerNames.forEach(function (evt) {
60
+ _this[evt] = _this[evt].bind(_this);
135
61
  });
136
- });
137
- }).nodeify(callback);
138
- }
62
+ }
63
+ }, {
64
+ key: 'run',
65
+ value: function run() {
66
+ this.log('Connecting to upstream proxy...');
67
+
68
+ var options = {
69
+ method: 'CONNECT',
70
+ hostname: this.upstreamProxyUrlParsed.hostname,
71
+ port: this.upstreamProxyUrlParsed.port,
72
+ path: this.trgParsed.hostname + ':' + this.trgParsed.port,
73
+ headers: {}
74
+ };
75
+
76
+ (0, _tools.maybeAddProxyAuthorizationHeader)(this.upstreamProxyUrlParsed, options.headers);
77
+
78
+ this.trgRequest = _http2.default.request(options);
79
+
80
+ this.trgRequest.once('connect', this.onTrgRequestConnect);
81
+ this.trgRequest.once('abort', this.onTrgRequestAbort);
82
+ this.trgRequest.once('socket', this.onTrgSocket);
83
+ this.trgRequest.on('error', this.onTrgRequestError);
84
+
85
+ // Send the data
86
+ this.trgRequest.end();
87
+ }
88
+
89
+ // If the client closes the connection prematurely,
90
+ // then immediately destroy the upstream socket, there's nothing we can do with it
91
+
92
+ }, {
93
+ key: 'onSrcSocketClose',
94
+ value: function onSrcSocketClose() {
95
+ if (this.isClosed) return;
96
+ this.log('Source socket closed');
97
+ this.close();
98
+ }
99
+ }, {
100
+ key: 'onSrcSocketEnd',
101
+ value: function onSrcSocketEnd() {
102
+ if (this.isClosed) return;
103
+ this.log('Source socket ended');
104
+ this.close();
105
+ }
106
+ }, {
107
+ key: 'onSrcSocketError',
108
+ value: function onSrcSocketError(err) {
109
+ if (this.isClosed) return;
110
+ this.log('Source socket failed: ' + (err.stack || err));
111
+ this.close();
112
+ }
113
+ }, {
114
+ key: 'onTrgSocket',
115
+ value: function onTrgSocket(socket) {
116
+ if (this.isClosed) return;
117
+
118
+ this.log('Target socket assigned');
119
+
120
+ this.trgSocket = socket;
121
+
122
+ socket.once('close', this.onTrgSocketClose);
123
+ socket.once('end', this.onTrgSocketEnd);
124
+ socket.on('error', this.onTrgSocketError);
125
+ }
126
+
127
+ // Once target socket closes, we need to give time
128
+ // to source socket to receive pending data, so we only call end()
129
+
130
+ }, {
131
+ key: 'onTrgSocketClose',
132
+ value: function onTrgSocketClose() {
133
+ var _this2 = this;
134
+
135
+ if (this.isClosed) return;
136
+ this.log('Target socket closed');
137
+ setTimeout(function () {
138
+ if (_this2.srcSocket) _this2.srcSocket.end();
139
+ }, 100);
140
+ }
141
+ }, {
142
+ key: 'onTrgSocketEnd',
143
+ value: function onTrgSocketEnd() {
144
+ var _this3 = this;
145
+
146
+ if (this.isClosed) return;
147
+ this.log('Target socket ended');
148
+ setTimeout(function () {
149
+ if (_this3.srcSocket) _this3.srcSocket.end();
150
+ }, 100);
151
+ }
152
+ }, {
153
+ key: 'onTrgSocketError',
154
+ value: function onTrgSocketError(err) {
155
+ if (this.isClosed) return;
156
+ this.log('Target socket failed: ' + (err.stack || err));
157
+ this.fail(err);
158
+ }
159
+ }, {
160
+ key: 'onTrgRequestConnect',
161
+ value: function onTrgRequestConnect(response) {
162
+ if (this.isClosed) return;
163
+ this.log('Connected to upstream proxy');
164
+
165
+ if (this.checkUpstreamProxy407(response)) return;
166
+
167
+ // Setup bi-directional tunnel
168
+ this.trgSocket.pipe(this.srcSocket);
169
+ this.srcSocket.pipe(this.trgSocket);
170
+
171
+ this.srcSocket.resume();
172
+ }
173
+ }, {
174
+ key: 'onTrgRequestAbort',
175
+ value: function onTrgRequestAbort() {
176
+ if (this.isClosed) return;
177
+ this.log('Target aborted');
178
+ this.close();
179
+ }
180
+ }, {
181
+ key: 'onTrgRequestError',
182
+ value: function onTrgRequestError(err) {
183
+ if (this.isClosed) return;
184
+ this.log('Target request failed: ' + (err.stack || err));
185
+ this.fail(err);
186
+ }
187
+
188
+ /**
189
+ * Checks whether response from upstream proxy is 407 Proxy Authentication Required
190
+ * and if so, responds 502 Bad Gateway to client.
191
+ * @param response
192
+ * @return {boolean}
193
+ */
194
+
195
+ }, {
196
+ key: 'checkUpstreamProxy407',
197
+ value: function checkUpstreamProxy407(response) {
198
+ if (this.upstreamProxyUrlParsed && response.statusCode === 407) {
199
+ this.fail('Invalid credentials provided for the upstream proxy.', 502);
200
+ return true;
201
+ }
202
+ return false;
203
+ }
204
+ }, {
205
+ key: 'fail',
206
+ value: function fail(err, statusCode) {
207
+ if (this.srcGotResponse) {
208
+ this.log('Source already received a response, just destroying the socket...');
209
+ this.close();
210
+ } else if (statusCode) {
211
+ // Manual error
212
+ this.log(err + ', responding with custom status code ' + statusCode + ' to client');
213
+ } else if (err.code === 'ENOTFOUND' && !this.upstreamProxyUrlParsed) {
214
+ this.log('Target server not found, sending 404 to client');
215
+ } else if (err.code === 'ENOTFOUND' && this.upstreamProxyUrlParsed) {
216
+ this.log('Upstream proxy not found, sending 502 to client');
217
+ } else if (err.code === 'ECONNREFUSED') {
218
+ this.log('Upstream proxy refused connection, sending 502 to client');
219
+ } else if (err.code === 'ETIMEDOUT') {
220
+ this.log('Connection timed out, sending 502 to client');
221
+ } else if (err.code === 'ECONNRESET') {
222
+ this.log('Connection lost, sending 502 to client');
223
+ } else if (err.code === 'EPIPE') {
224
+ this.log('Socket closed before write, sending 502 to client');
225
+ } else {
226
+ this.log('Unknown error, sending 500 to client');
227
+ }
228
+ }
229
+
230
+ /**
231
+ * Detaches all listeners and destroys all sockets.
232
+ */
233
+
234
+ }, {
235
+ key: 'close',
236
+ value: function close() {
237
+ if (!this.isClosed) {
238
+ this.log('Closing handler');
239
+
240
+ if (this.srcRequest) {
241
+ this.srcRequest.destroy();
242
+ this.srcRequest = null;
243
+ }
244
+
245
+ if (this.srcSocket) {
246
+ this.srcSocket.destroy();
247
+ this.srcSocket = null;
248
+ }
249
+
250
+ if (this.trgRequest) {
251
+ this.trgRequest.abort();
252
+ this.trgRequest = null;
253
+ }
254
+
255
+ if (this.trgSocket) {
256
+ this.trgSocket.destroy();
257
+ this.trgSocket = null;
258
+ }
259
+
260
+ this.isClosed = true;
261
+ }
262
+ }
263
+ }]);
264
+
265
+ return TcpTunnel;
266
+ }();
267
+
268
+ exports.default = TcpTunnel;