proxy_chain_rb 0.1.0 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
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;