davechri 1.0.0

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.

Potentially problematic release.


This version of davechri might be problematic. Click here for more details.

Files changed (113) hide show
  1. package/README.md +277 -0
  2. package/bin/proxyall +5 -0
  3. package/build/app.js +89 -0
  4. package/build/app.js.map +1 -0
  5. package/build/common/Message.js +34 -0
  6. package/build/common/Message.js.map +1 -0
  7. package/build/common/ProxyConfig.js +27 -0
  8. package/build/common/ProxyConfig.js.map +1 -0
  9. package/build/node-http-mitm-proxy/.gitattributes +2 -0
  10. package/build/node-http-mitm-proxy/.travis.yml +10 -0
  11. package/build/node-http-mitm-proxy/README.md +539 -0
  12. package/build/node-http-mitm-proxy/bin/mitm-proxy.js +36 -0
  13. package/build/node-http-mitm-proxy/examples/forwardHttps.js +62 -0
  14. package/build/node-http-mitm-proxy/examples/modifyGoogle.js +44 -0
  15. package/build/node-http-mitm-proxy/examples/onCertificateMissing.js +30 -0
  16. package/build/node-http-mitm-proxy/examples/onCertificateRequired.js +23 -0
  17. package/build/node-http-mitm-proxy/examples/preventRequest.js +20 -0
  18. package/build/node-http-mitm-proxy/examples/processFullResponseBody.js +36 -0
  19. package/build/node-http-mitm-proxy/examples/removeProxyToServerContentLength.js +17 -0
  20. package/build/node-http-mitm-proxy/examples/websocket.js +31 -0
  21. package/build/node-http-mitm-proxy/examples/wildcard.js +17 -0
  22. package/build/node-http-mitm-proxy/index.d.ts +233 -0
  23. package/build/node-http-mitm-proxy/index.js +3 -0
  24. package/build/node-http-mitm-proxy/lib/ca.js +245 -0
  25. package/build/node-http-mitm-proxy/lib/middleware/gunzip.js +19 -0
  26. package/build/node-http-mitm-proxy/lib/middleware/wildcard.js +22 -0
  27. package/build/node-http-mitm-proxy/lib/proxy.js +1137 -0
  28. package/build/node-http-mitm-proxy/package-lock.json +1406 -0
  29. package/build/node-http-mitm-proxy/package.json +45 -0
  30. package/build/node-http-mitm-proxy/test/01_proxy.js +449 -0
  31. package/build/node-http-mitm-proxy/test/www/1024.bin +64 -0
  32. package/build/node-http-mitm-proxy/test/wwwA/1024.bin +64 -0
  33. package/build/node-http-mitm-proxy/test/wwwA/example.com.html +8 -0
  34. package/build/node-http-mitm-proxy/test/wwwA/index.html +0 -0
  35. package/build/node-http-mitm-proxy/test/wwwB/1024.bin +64 -0
  36. package/build/node-http-mitm-proxy/test/wwwB/index.html +0 -0
  37. package/build/private/keys/README.md +2 -0
  38. package/build/private/keys/server.crt +20 -0
  39. package/build/private/keys/server.key +28 -0
  40. package/build/private/keys/ssl.conf +25 -0
  41. package/build/server/src/Global.js +38 -0
  42. package/build/server/src/Global.js.map +1 -0
  43. package/build/server/src/Http2Proxy.js +197 -0
  44. package/build/server/src/Http2Proxy.js.map +1 -0
  45. package/build/server/src/HttpMessage.js +99 -0
  46. package/build/server/src/HttpMessage.js.map +1 -0
  47. package/build/server/src/HttpProxy.js +196 -0
  48. package/build/server/src/HttpProxy.js.map +1 -0
  49. package/build/server/src/HttpsProxy.js +110 -0
  50. package/build/server/src/HttpsProxy.js.map +1 -0
  51. package/build/server/src/LogProxy.js +115 -0
  52. package/build/server/src/LogProxy.js.map +1 -0
  53. package/build/server/src/Paths.js +28 -0
  54. package/build/server/src/Paths.js.map +1 -0
  55. package/build/server/src/Ping.js +21 -0
  56. package/build/server/src/Ping.js.map +1 -0
  57. package/build/server/src/ProxyAllApp.js +70 -0
  58. package/build/server/src/ProxyAllApp.js.map +1 -0
  59. package/build/server/src/Resend.js +89 -0
  60. package/build/server/src/Resend.js.map +1 -0
  61. package/build/server/src/SocketIoManager.js +312 -0
  62. package/build/server/src/SocketIoManager.js.map +1 -0
  63. package/build/server/src/SocketMessage.js +68 -0
  64. package/build/server/src/SocketMessage.js.map +1 -0
  65. package/build/server/src/TcpProxy.js +202 -0
  66. package/build/server/src/TcpProxy.js.map +1 -0
  67. package/build/server/src/formatters/HexFormatter.js +33 -0
  68. package/build/server/src/formatters/HexFormatter.js.map +1 -0
  69. package/build/server/src/formatters/MongoFormatter.js +97 -0
  70. package/build/server/src/formatters/MongoFormatter.js.map +1 -0
  71. package/build/server/src/formatters/MongoOpCode.js +51 -0
  72. package/build/server/src/formatters/MongoOpCode.js.map +1 -0
  73. package/build/server/src/formatters/RedisFormatter.js +25 -0
  74. package/build/server/src/formatters/RedisFormatter.js.map +1 -0
  75. package/build/server/src/formatters/SqlCommand.js +111 -0
  76. package/build/server/src/formatters/SqlCommand.js.map +1 -0
  77. package/build/server/src/formatters/SqlFormatter.js +150 -0
  78. package/build/server/src/formatters/SqlFormatter.js.map +1 -0
  79. package/client/README.md +46 -0
  80. package/client/build/asset-manifest.json +26 -0
  81. package/client/build/favicon.ico +0 -0
  82. package/client/build/index.html +1 -0
  83. package/client/build/manifest.json +8 -0
  84. package/client/build/robots.txt +3 -0
  85. package/client/build/static/css/2.bbde182e.chunk.css +12 -0
  86. package/client/build/static/css/2.bbde182e.chunk.css.map +1 -0
  87. package/client/build/static/css/main.5bf489b2.chunk.css +2 -0
  88. package/client/build/static/css/main.5bf489b2.chunk.css.map +1 -0
  89. package/client/build/static/js/2.ba308ba1.chunk.js +3 -0
  90. package/client/build/static/js/2.ba308ba1.chunk.js.LICENSE.txt +100 -0
  91. package/client/build/static/js/2.ba308ba1.chunk.js.map +1 -0
  92. package/client/build/static/js/3.7073b0d8.chunk.js +2 -0
  93. package/client/build/static/js/3.7073b0d8.chunk.js.map +1 -0
  94. package/client/build/static/js/main.494a7501.chunk.js +2 -0
  95. package/client/build/static/js/main.494a7501.chunk.js.map +1 -0
  96. package/client/build/static/js/runtime-main.00264969.js +2 -0
  97. package/client/build/static/js/runtime-main.00264969.js.map +1 -0
  98. package/client/build/static/media/fa-brands-400.2285773e.woff +0 -0
  99. package/client/build/static/media/fa-brands-400.23f19bb0.eot +0 -0
  100. package/client/build/static/media/fa-brands-400.2f517e09.svg +3717 -0
  101. package/client/build/static/media/fa-brands-400.527940b1.ttf +0 -0
  102. package/client/build/static/media/fa-brands-400.d878b0a6.woff2 +0 -0
  103. package/client/build/static/media/fa-regular-400.4689f52c.svg +801 -0
  104. package/client/build/static/media/fa-regular-400.491974d1.ttf +0 -0
  105. package/client/build/static/media/fa-regular-400.77206a6b.eot +0 -0
  106. package/client/build/static/media/fa-regular-400.7a333762.woff2 +0 -0
  107. package/client/build/static/media/fa-regular-400.bb58e57c.woff +0 -0
  108. package/client/build/static/media/fa-solid-900.1551f4f6.woff2 +0 -0
  109. package/client/build/static/media/fa-solid-900.7a8b4f13.svg +5034 -0
  110. package/client/build/static/media/fa-solid-900.9bbb245e.eot +0 -0
  111. package/client/build/static/media/fa-solid-900.be9ee23c.ttf +0 -0
  112. package/client/build/static/media/fa-solid-900.eeccf4f6.woff +0 -0
  113. package/package.json +80 -0
@@ -0,0 +1,1137 @@
1
+ 'use strict';
2
+
3
+ var async = require('async');
4
+ var net = require('net');
5
+ var http = require('http');
6
+ var https = require('https');
7
+ var util = require('util');
8
+ var fs = require('fs');
9
+ var path = require('path');
10
+ var events = require('events');
11
+ var WebSocket = require('ws');
12
+ var url = require('url');
13
+ var semaphore = require('semaphore');
14
+ var ca = require('./ca.js');
15
+ const nodeCommon = require('_http_common');
16
+ const debug = require('debug')('http-mitm-proxy');
17
+
18
+ module.exports = function() {
19
+ return new Proxy();
20
+ };
21
+
22
+ module.exports.gunzip = require('./middleware/gunzip');
23
+ module.exports.wildcard = require('./middleware/wildcard');
24
+
25
+ var Proxy = function() {
26
+ this.onConnectHandlers = [];
27
+ this.onRequestHandlers = [];
28
+ this.onRequestHeadersHandlers = [];
29
+ this.onWebSocketConnectionHandlers = [];
30
+ this.onWebSocketFrameHandlers = [];
31
+ this.onWebSocketCloseHandlers = [];
32
+ this.onWebSocketErrorHandlers = [];
33
+ this.onErrorHandlers = [];
34
+ this.onRequestDataHandlers = [];
35
+ this.onRequestEndHandlers = [];
36
+ this.onResponseHandlers = [];
37
+ this.onResponseHeadersHandlers = [];
38
+ this.onResponseDataHandlers = [];
39
+ this.onResponseEndHandlers = [];
40
+ this.responseContentPotentiallyModified = false;
41
+ };
42
+
43
+ module.exports.Proxy = Proxy;
44
+
45
+ Proxy.prototype.listen = function(options, callback = e => {}) {
46
+ var self = this;
47
+ this.options = options || {};
48
+ this.httpPort = options.port || options.port === 0 ? options.port : 8080;
49
+ this.httpHost = options.host;
50
+ this.timeout = options.timeout || 0;
51
+ this.keepAlive = !!options.keepAlive;
52
+ this.httpAgent = typeof(options.httpAgent) !== "undefined" ? options.httpAgent : new http.Agent({ keepAlive: this.keepAlive });
53
+ this.httpsAgent = typeof(options.httpsAgent) !== "undefined" ? options.httpsAgent : new https.Agent({ keepAlive: this.keepAlive });
54
+ this.forceSNI = !!options.forceSNI;
55
+ if (this.forceSNI) {
56
+ debug('SNI enabled. Clients not supporting SNI may fail');
57
+ }
58
+ this.httpsPort = this.forceSNI ? options.httpsPort : undefined;
59
+ this.sslCaDir = options.sslCaDir || path.resolve(process.cwd(), '.http-mitm-proxy');
60
+ ca.create(this.sslCaDir, function(err, ca) {
61
+ if (err) {
62
+ return callback(err);
63
+ }
64
+ self.ca = ca;
65
+ self.sslServers = {};
66
+ self.sslSemaphores = {};
67
+ self.connectRequests = {};
68
+ self.httpServer = http.createServer();
69
+ self.httpServer.timeout = self.timeout;
70
+ self.httpServer.on('connect', self._onHttpServerConnect.bind(self));
71
+ self.httpServer.on('request', self._onHttpServerRequest.bind(self, false));
72
+ self.wsServer = new WebSocket.Server({ server: self.httpServer });
73
+ self.wsServer.on('error', self._onError.bind(self, 'HTTP_SERVER_ERROR', null));
74
+ self.wsServer.on('connection', (ws, req) => {
75
+ ws.upgradeReq = req;
76
+ self._onWebSocketServerConnect.call(self, false, ws, req);
77
+ });
78
+ const listenOptions = {
79
+ host: self.httpHost,
80
+ port: self.httpPort
81
+ };
82
+ if (self.forceSNI) {
83
+ // start the single HTTPS server now
84
+ self._createHttpsServer({}, function(port, httpsServer, wssServer) {
85
+ debug('https server started on '+port);
86
+ self.httpsServer = httpsServer;
87
+ self.wssServer = wssServer;
88
+ self.httpsPort = port;
89
+ self.httpServer.listen(listenOptions, () => {
90
+ self.httpPort = self.httpServer.address().port;
91
+ callback();
92
+ });
93
+ });
94
+ } else {
95
+ self.httpServer.listen(listenOptions, () => {
96
+ self.httpPort = self.httpServer.address().port;
97
+ callback();
98
+ });
99
+ }
100
+ });
101
+ return this;
102
+ };
103
+
104
+
105
+ Proxy.prototype._createHttpsServer = function (options, callback) {
106
+ var httpsServer = https.createServer(options);
107
+ httpsServer.timeout = this.timeout;
108
+ httpsServer.on('error', this._onError.bind(this, 'HTTPS_SERVER_ERROR', null));
109
+ httpsServer.on('clientError', this._onError.bind(this, 'HTTPS_CLIENT_ERROR', null));
110
+ httpsServer.on('connect', this._onHttpServerConnect.bind(this));
111
+ httpsServer.on('request', this._onHttpServerRequest.bind(this, true));
112
+ var self = this;
113
+ var wssServer = new WebSocket.Server({ server: httpsServer });
114
+ wssServer.on('connection', function(ws, req){
115
+ ws.upgradeReq = req;
116
+ self._onWebSocketServerConnect.call(self, true, ws, req)
117
+ });
118
+ var listenArgs = [function() {
119
+ if (callback) callback(httpsServer.address().port, httpsServer, wssServer);
120
+ }];
121
+ // Using listenOptions to bind the server to a particular IP if requested via options.host
122
+ // port 0 to get the first available port
123
+ var listenOptions = {
124
+ port: 0
125
+ };
126
+ if (this.httpsPort && !options.hosts) {
127
+ listenOptions.port = this.httpsPort;
128
+ }
129
+ if (this.httpHost)
130
+ listenOptions.host = this.httpHost;
131
+ listenArgs.unshift(listenOptions);
132
+
133
+ httpsServer.listen.apply(httpsServer, listenArgs);
134
+ };
135
+
136
+ Proxy.prototype.close = function () {
137
+ var self = this;
138
+ this.httpServer.close();
139
+ delete this.httpServer;
140
+ if (this.httpsServer) {
141
+ this.httpsServer.close();
142
+ delete this.httpsServer;
143
+ delete this.wssServer;
144
+ delete this.sslServers;
145
+ }
146
+ if (this.sslServers) {
147
+ (Object.keys(this.sslServers)).forEach(function (srvName) {
148
+ var server = self.sslServers[srvName].server;
149
+ if (server) server.close();
150
+ delete self.sslServers[srvName];
151
+ });
152
+ }
153
+ return this;
154
+ };
155
+
156
+ Proxy.prototype.onError = function(fn) {
157
+ this.onErrorHandlers.push(fn);
158
+ return this;
159
+ };
160
+ /**
161
+ * Add custom handler for CONNECT method
162
+ * @augments fn(req,socket,head,callback) be called on receiving CONNECT method
163
+ */
164
+ Proxy.prototype.onConnect = function(fn) {
165
+ this.onConnectHandlers.push(fn);
166
+ return this;
167
+ };
168
+
169
+ Proxy.prototype.onRequestHeaders = function(fn) {
170
+ this.onRequestHeadersHandlers.push(fn);
171
+ return this;
172
+ };
173
+
174
+ Proxy.prototype.onRequest = function(fn) {
175
+ this.onRequestHandlers.push(fn);
176
+ return this;
177
+ };
178
+
179
+ Proxy.prototype.onWebSocketConnection = function(fn) {
180
+ this.onWebSocketConnectionHandlers.push(fn);
181
+ return this;
182
+ };
183
+
184
+ Proxy.prototype.onWebSocketSend = function(fn) {
185
+ this.onWebSocketFrameHandlers.push(function(ctx, type, fromServer, data, flags, callback) {
186
+ if (!fromServer && type === 'message') return this(ctx, data, flags, callback);
187
+ else callback(null, data, flags);
188
+ }.bind(fn));
189
+ return this;
190
+ };
191
+
192
+ Proxy.prototype.onWebSocketMessage = function(fn) {
193
+ this.onWebSocketFrameHandlers.push(function(ctx, type, fromServer, data, flags, callback) {
194
+ if (fromServer && type === 'message') return this(ctx, data, flags, callback);
195
+ else callback(null, data, flags);
196
+ }.bind(fn));
197
+ return this;
198
+ };
199
+
200
+ Proxy.prototype.onWebSocketFrame = function(fn) {
201
+ this.onWebSocketFrameHandlers.push(fn);
202
+ return this;
203
+ };
204
+
205
+ Proxy.prototype.onWebSocketClose = function(fn) {
206
+ this.onWebSocketCloseHandlers.push(fn);
207
+ return this;
208
+ };
209
+
210
+ Proxy.prototype.onWebSocketError = function(fn) {
211
+ this.onWebSocketErrorHandlers.push(fn);
212
+ return this;
213
+ };
214
+
215
+ Proxy.prototype.onRequestData = function(fn) {
216
+ this.onRequestDataHandlers.push(fn);
217
+ return this;
218
+ };
219
+
220
+ Proxy.prototype.onRequestEnd = function(fn) {
221
+ this.onRequestEndHandlers.push(fn);
222
+ return this;
223
+ };
224
+
225
+ Proxy.prototype.onResponse = function(fn) {
226
+ this.onResponseHandlers.push(fn);
227
+ return this;
228
+ };
229
+
230
+ Proxy.prototype.onResponseHeaders = function(fn) {
231
+ this.onResponseHeadersHandlers.push(fn);
232
+ return this;
233
+ };
234
+
235
+ Proxy.prototype.onResponseData = function(fn) {
236
+ this.onResponseDataHandlers.push(fn);
237
+ this.responseContentPotentiallyModified = true;
238
+ return this;
239
+ };
240
+
241
+ Proxy.prototype.onResponseEnd = function(fn) {
242
+ this.onResponseEndHandlers.push(fn);
243
+ return this;
244
+ };
245
+
246
+ Proxy.prototype.use = function(mod) {
247
+ if (mod.onError) {
248
+ this.onError(mod.onError);
249
+ }
250
+ if (mod.onCertificateRequired) {
251
+ this.onCertificateRequired = mod.onCertificateRequired;
252
+ }
253
+ if (mod.onCertificateMissing) {
254
+ this.onCertificateMissing = mod.onCertificateMissing;
255
+ }
256
+ if (mod.onConnect) {
257
+ this.onConnect(mod.onConnect);
258
+ }
259
+ if (mod.onRequest) {
260
+ this.onRequest(mod.onRequest);
261
+ }
262
+ if (mod.onRequestHeaders) {
263
+ this.onRequestHeaders(mod.onRequestHeaders);
264
+ }
265
+ if (mod.onRequestData) {
266
+ this.onRequestData(mod.onRequestData);
267
+ }
268
+ if (mod.onResponse) {
269
+ this.onResponse(mod.onResponse);
270
+ }
271
+ if (mod.onResponseHeaders) {
272
+ this.onResponseHeaders(mod.onResponseHeaders);
273
+ }
274
+ if (mod.onResponseData) {
275
+ this.onResponseData(mod.onResponseData);
276
+ }
277
+ if (mod.onWebSocketConnection) {
278
+ this.onWebSocketConnection(mod.onWebSocketConnection);
279
+ }
280
+ if (mod.onWebSocketSend) {
281
+ this.onWebSocketFrame(function(ctx, type, fromServer, data, flags, callback) {
282
+ if (!fromServer && type === 'message') return this(ctx, data, flags, callback);
283
+ else callback(null, data, flags);
284
+ }.bind(mod.onWebSocketSend));
285
+ }
286
+ if (mod.onWebSocketMessage) {
287
+ this.onWebSocketFrame(function(ctx, type, fromServer, data, flags, callback) {
288
+ if (fromServer && type === 'message') return this(ctx, data, flags, callback);
289
+ else callback(null, data, flags);
290
+ }.bind(mod.onWebSocketMessage));
291
+ }
292
+ if (mod.onWebSocketFrame) {
293
+ this.onWebSocketFrame(mod.onWebSocketFrame);
294
+ }
295
+ if (mod.onWebSocketClose) {
296
+ this.onWebSocketClose(mod.onWebSocketClose);
297
+ }
298
+ if (mod.onWebSocketError) {
299
+ this.onWebSocketError(mod.onWebSocketError);
300
+ }
301
+ return this;
302
+ };
303
+
304
+ // Since node 0.9.9, ECONNRESET on sockets are no longer hidden
305
+ Proxy.prototype._onSocketError = function(socketDescription, err) {
306
+ if (err.errno === 'ECONNRESET') {
307
+ debug('Got ECONNRESET on ' + socketDescription + ', ignoring.');
308
+ } else {
309
+ this._onError(socketDescription + '_ERROR', null, err);
310
+ }
311
+ };
312
+
313
+ Proxy.prototype._onHttpServerConnect = function(req, socket, head) {
314
+ var self = this;
315
+
316
+ socket.on('error', self._onSocketError.bind(self, 'CLIENT_TO_PROXY_SOCKET'));
317
+
318
+ // you can forward HTTPS request directly by adding custom CONNECT method handler
319
+ return async.forEach(self.onConnectHandlers, function (fn, callback) {
320
+ return fn.call(self, req, socket, head, callback)
321
+ }, function (err) {
322
+ if (err) {
323
+ return self._onError('ON_CONNECT_ERROR', null, err);
324
+ }
325
+ // we need first byte of data to detect if request is SSL encrypted
326
+ if (!head || head.length === 0) {
327
+ socket.once('data', self._onHttpServerConnectData.bind(self, req, socket));
328
+ socket.write('HTTP/1.1 200 OK\r\n');
329
+ if (self.keepAlive && req.headers['proxy-connection'] === 'keep-alive') {
330
+ socket.write('Proxy-Connection: keep-alive\r\n');
331
+ socket.write('Connection: keep-alive\r\n');
332
+ }
333
+ return socket.write('\r\n');
334
+ } else {
335
+ self._onHttpServerConnectData(req, socket, head)
336
+ }
337
+ })
338
+ };
339
+
340
+ Proxy.prototype._onHttpServerConnectData = function(req, socket, head) {
341
+ var self = this;
342
+
343
+ socket.pause();
344
+
345
+ /*
346
+ * Detect TLS from first bytes of data
347
+ * Inspired from https://gist.github.com/tg-x/835636
348
+ * used heuristic:
349
+ * - an incoming connection using SSLv3/TLSv1 records should start with 0x16
350
+ * - an incoming connection using SSLv2 records should start with the record size
351
+ * and as the first record should not be very big we can expect 0x80 or 0x00 (the MSB is a flag)
352
+ * - everything else is considered to be unencrypted
353
+ */
354
+ if (head[0] == 0x16 || head[0] == 0x80 || head[0] == 0x00) {
355
+ // URL is in the form 'hostname:port'
356
+ var hostname = req.url.split(':', 2)[0];
357
+ var sslServer = this.sslServers[hostname];
358
+ if (sslServer) {
359
+ return makeConnection(sslServer.port);
360
+ }
361
+ var wildcardHost = hostname.replace(/[^\.]+\./, '*.');
362
+ var sem = self.sslSemaphores[wildcardHost];
363
+ if (!sem) {
364
+ sem = self.sslSemaphores[wildcardHost] = semaphore(1);
365
+ }
366
+ sem.take(function() {
367
+ if (self.sslServers[hostname]) {
368
+ process.nextTick(sem.leave.bind(sem));
369
+ return makeConnection(self.sslServers[hostname].port);
370
+ }
371
+ if (self.sslServers[wildcardHost]) {
372
+ process.nextTick(sem.leave.bind(sem));
373
+ self.sslServers[hostname] = {
374
+ port: self.sslServers[wildcardHost]
375
+ };
376
+ return makeConnection(self.sslServers[hostname].port);
377
+ }
378
+ getHttpsServer(hostname, function(err, port) {
379
+ process.nextTick(sem.leave.bind(sem));
380
+ if (err) {
381
+ return self._onError('OPEN_HTTPS_SERVER_ERROR', null, err);
382
+ }
383
+ return makeConnection(port);
384
+ });
385
+ });
386
+ } else {
387
+ return makeConnection(this.httpPort);
388
+ }
389
+
390
+ function makeConnection(port) {
391
+ // open a TCP connection to the remote host
392
+ var conn = net.connect({
393
+ port: port,
394
+ allowHalfOpen: true
395
+ }, function() {
396
+ // create a tunnel between the two hosts
397
+ conn.on('finish', () => {
398
+ socket.destroy();
399
+ });
400
+ socket.on('close', () => {
401
+ conn.end();
402
+ });
403
+ var connectKey = conn.localPort + ':' + conn.remotePort;
404
+ self.connectRequests[connectKey] = req;
405
+ socket.pipe(conn);
406
+ conn.pipe(socket);
407
+ socket.emit('data', head);
408
+ conn.on('end', function() { delete self.connectRequests[connectKey]; });
409
+ return socket.resume();
410
+ });
411
+ conn.on('error', self._onSocketError.bind(self, 'PROXY_TO_PROXY_SOCKET'));
412
+ }
413
+
414
+ function getHttpsServer(hostname, callback) {
415
+ self.onCertificateRequired(hostname, function (err, files) {
416
+ if (err) {
417
+ return callback(err);
418
+ }
419
+ async.auto({
420
+ 'keyFileExists': function(callback) {
421
+ return fs.exists(files.keyFile, function(exists) {
422
+ return callback(null, exists);
423
+ });
424
+ },
425
+ 'certFileExists': function(callback) {
426
+ return fs.exists(files.certFile, function(exists) {
427
+ return callback(null, exists);
428
+ });
429
+ },
430
+ 'httpsOptions': ['keyFileExists', 'certFileExists', function(data, callback) {
431
+ if (data.keyFileExists && data.certFileExists) {
432
+ return fs.readFile(files.keyFile, function(err, keyFileData) {
433
+ if (err) {
434
+ return callback(err);
435
+ }
436
+
437
+ return fs.readFile(files.certFile, function(err, certFileData) {
438
+ if (err) {
439
+ return callback(err);
440
+ }
441
+
442
+ return callback(null, {
443
+ key: keyFileData,
444
+ cert: certFileData,
445
+ hosts: files.hosts
446
+ });
447
+ });
448
+ });
449
+ } else {
450
+ var ctx = {
451
+ 'hostname': hostname,
452
+ 'files': files,
453
+ 'data': data
454
+ };
455
+
456
+ return self.onCertificateMissing(ctx, files, function(err, files) {
457
+ if (err) {
458
+ return callback(err);
459
+ }
460
+
461
+ return callback(null, {
462
+ key: files.keyFileData,
463
+ cert: files.certFileData,
464
+ hosts: files.hosts
465
+ });
466
+ });
467
+ }
468
+ }]
469
+ }, function(err, results) {
470
+ if (err) {
471
+ return callback(err);
472
+ }
473
+ var hosts;
474
+ if (results.httpsOptions && results.httpsOptions.hosts && results.httpsOptions.hosts.length) {
475
+ hosts = results.httpsOptions.hosts;
476
+ if (hosts.indexOf(hostname) === -1) {
477
+ hosts.push(hostname);
478
+ }
479
+ } else {
480
+ hosts = [hostname];
481
+ }
482
+ delete results.httpsOptions.hosts;
483
+ if (self.forceSNI && !hostname.match(/^[\d\.]+$/)) {
484
+ debug('creating SNI context for ' + hostname);
485
+ hosts.forEach(function(host) {
486
+ self.httpsServer.addContext(host, results.httpsOptions);
487
+ self.sslServers[host] = { port : self.httpsPort };
488
+ });
489
+ return callback(null, self.httpsPort);
490
+ } else {
491
+ debug('starting server for ' + hostname);
492
+ results.httpsOptions.hosts = hosts;
493
+ self._createHttpsServer(results.httpsOptions, function(port, httpsServer, wssServer) {
494
+ debug('https server started for %s on %s', hostname, port);
495
+ var sslServer = {
496
+ server: httpsServer,
497
+ wsServer: wssServer,
498
+ port: port
499
+ };
500
+ hosts.forEach(function(host) {
501
+ self.sslServers[hostname] = sslServer;
502
+ });
503
+ return callback(null, port);
504
+ });
505
+ }
506
+ });
507
+ });
508
+ }
509
+ };
510
+
511
+ Proxy.prototype.onCertificateRequired = function (hostname, callback) {
512
+ var self = this;
513
+ return callback(null, {
514
+ keyFile: self.sslCaDir + '/keys/' + hostname + '.key',
515
+ certFile: self.sslCaDir + '/certs/' + hostname + '.pem',
516
+ hosts: [hostname]
517
+ });
518
+ };
519
+
520
+ Proxy.prototype.onCertificateMissing = function (ctx, files, callback) {
521
+ var hosts = files.hosts || [ctx.hostname];
522
+ this.ca.generateServerCertificateKeys(hosts, function (certPEM, privateKeyPEM) {
523
+ callback(null, {
524
+ certFileData: certPEM,
525
+ keyFileData: privateKeyPEM,
526
+ hosts: hosts
527
+ });
528
+ });
529
+ return this;
530
+ };
531
+
532
+ Proxy.prototype._onError = function(kind, ctx, err) {
533
+ this.onErrorHandlers.forEach(function(handler) {
534
+ return handler(ctx, err, kind);
535
+ });
536
+ if (ctx) {
537
+ ctx.onErrorHandlers.forEach(function(handler) {
538
+ return handler(ctx, err, kind);
539
+ });
540
+
541
+ if (ctx.proxyToClientResponse && !ctx.proxyToClientResponse.headersSent) {
542
+ ctx.proxyToClientResponse.writeHead(504, 'Proxy Error');
543
+ }
544
+ if (ctx.proxyToClientResponse && !ctx.proxyToClientResponse.finished) {
545
+ ctx.proxyToClientResponse.end(''+kind+': '+err, 'utf8');
546
+ }
547
+ }
548
+ };
549
+
550
+ Proxy.prototype._onWebSocketServerConnect = function(isSSL, ws, upgradeReq) {
551
+ var self = this;
552
+ var ctx = {
553
+ isSSL: isSSL,
554
+ connectRequest: self.connectRequests[ws._socket.remotePort + ':' + ws._socket.localPort] || {},
555
+ clientToProxyWebSocket: ws,
556
+ onWebSocketConnectionHandlers: [],
557
+ onWebSocketFrameHandlers: [],
558
+ onWebSocketCloseHandlers: [],
559
+ onWebSocketErrorHandlers: [],
560
+ onWebSocketConnection: function(fn) {
561
+ ctx.onWebSocketConnectionHandlers.push(fn);
562
+ return ctx;
563
+ },
564
+ onWebSocketSend: function(fn) {
565
+ ctx.onWebSocketFrameHandlers.push(function(ctx, type, fromServer, data, flags, callback) {
566
+ if (!fromServer && type === 'message') return this(ctx, data, flags, callback);
567
+ else callback(null, data, flags);
568
+ }.bind(fn));
569
+ return ctx;
570
+ },
571
+ onWebSocketMessage: function(fn) {
572
+ ctx.onWebSocketFrameHandlers.push(function(ctx, type, fromServer, data, flags, callback) {
573
+ if (fromServer && type === 'message') return this(ctx, data, flags, callback);
574
+ else callback(null, data, flags);
575
+ }.bind(fn));
576
+ return ctx;
577
+ },
578
+ onWebSocketFrame: function(fn) {
579
+ ctx.onWebSocketFrameHandlers.push(fn);
580
+ return ctx;
581
+ },
582
+ onWebSocketClose: function(fn) {
583
+ ctx.onWebSocketCloseHandlers.push(fn);
584
+ return ctx;
585
+ },
586
+ onWebSocketError: function(fn) {
587
+ ctx.onWebSocketErrorHandlers.push(fn);
588
+ return ctx;
589
+ },
590
+ use: function(mod) {
591
+ if (mod.onWebSocketConnection) {
592
+ ctx.onWebSocketConnection(mod.onWebSocketConnection);
593
+ }
594
+ if (mod.onWebSocketSend) {
595
+ ctx.onWebSocketFrame(function(ctx, type, fromServer, data, flags, callback) {
596
+ if (!fromServer && type === 'message') return this(ctx, data, flags, callback);
597
+ else callback(null, data, flags);
598
+ }.bind(mod.onWebSocketSend));
599
+ }
600
+ if (mod.onWebSocketMessage) {
601
+ ctx.onWebSocketFrame(function(ctx, type, fromServer, data, flags, callback) {
602
+ if (fromServer && type === 'message') return this(ctx, data, flags, callback);
603
+ else callback(null, data, flags);
604
+ }.bind(mod.onWebSocketMessage));
605
+ }
606
+ if (mod.onWebSocketFrame) {
607
+ ctx.onWebSocketFrame(mod.onWebSocketFrame);
608
+ }
609
+ if (mod.onWebSocketClose) {
610
+ ctx.onWebSocketClose(mod.onWebSocketClose);
611
+ }
612
+ if (mod.onWebSocketError) {
613
+ ctx.onWebSocketError(mod.onWebSocketError);
614
+ }
615
+ return ctx;
616
+ }
617
+ };
618
+ ctx.clientToProxyWebSocket.on('message', self._onWebSocketFrame.bind(self, ctx, 'message', false));
619
+ ctx.clientToProxyWebSocket.on('ping', self._onWebSocketFrame.bind(self, ctx, 'ping', false));
620
+ ctx.clientToProxyWebSocket.on('pong', self._onWebSocketFrame.bind(self, ctx, 'pong', false));
621
+ ctx.clientToProxyWebSocket.on('error', self._onWebSocketError.bind(self, ctx));
622
+ ctx.clientToProxyWebSocket.on('close', self._onWebSocketClose.bind(self, ctx, false));
623
+ if(ctx.clientToProxyWebSocket.pause) {
624
+ ctx.clientToProxyWebSocket.pause();
625
+ }
626
+ var url;
627
+ if (upgradeReq.url == '' || /^\//.test(upgradeReq.url)) {
628
+ var hostPort = Proxy.parseHostAndPort(upgradeReq);
629
+ url = (ctx.isSSL ? 'wss' : 'ws') + '://' + hostPort.host + (hostPort.port ? ':' + hostPort.port : '') + upgradeReq.url;
630
+ } else {
631
+ url = upgradeReq.url;
632
+ }
633
+ var ptosHeaders = {};
634
+ var ctopHeaders = upgradeReq.headers;
635
+ for (var key in ctopHeaders) {
636
+ if (key.indexOf('sec-websocket') !== 0) {
637
+ ptosHeaders[key] = ctopHeaders[key];
638
+ }
639
+ }
640
+ ctx.proxyToServerWebSocketOptions = {
641
+ url: url,
642
+ agent: ctx.isSSL ? self.httpsAgent : self.httpAgent,
643
+ headers: ptosHeaders
644
+ };
645
+ return self._onWebSocketConnection(ctx, function(err) {
646
+ if (err) {
647
+ return self._onWebSocketError(ctx, err);
648
+ }
649
+ return makeProxyToServerWebSocket();
650
+ });
651
+
652
+ function makeProxyToServerWebSocket() {
653
+ ctx.proxyToServerWebSocket = new WebSocket(ctx.proxyToServerWebSocketOptions.url, ctx.proxyToServerWebSocketOptions);
654
+ ctx.proxyToServerWebSocket.on('message', self._onWebSocketFrame.bind(self, ctx, 'message', true));
655
+ ctx.proxyToServerWebSocket.on('ping', self._onWebSocketFrame.bind(self, ctx, 'ping', true));
656
+ ctx.proxyToServerWebSocket.on('pong', self._onWebSocketFrame.bind(self, ctx, 'pong', true));
657
+ ctx.proxyToServerWebSocket.on('error', self._onWebSocketError.bind(self, ctx));
658
+ ctx.proxyToServerWebSocket.on('close', self._onWebSocketClose.bind(self, ctx, true));
659
+ ctx.proxyToServerWebSocket.on('open', function() {
660
+ if (ctx.clientToProxyWebSocket.readyState === WebSocket.OPEN) {
661
+ if (ctx.clientToProxyWebSocket.resume) {
662
+ ctx.clientToProxyWebSocket.resume();
663
+ }
664
+ }
665
+ });
666
+ }
667
+ };
668
+
669
+ Proxy.prototype._onHttpServerRequest = function(isSSL, clientToProxyRequest, proxyToClientResponse) {
670
+ var self = this;
671
+ var ctx = {
672
+ isSSL: isSSL,
673
+ connectRequest: self.connectRequests[clientToProxyRequest.socket.remotePort + ':' + clientToProxyRequest.socket.localPort] || {},
674
+ clientToProxyRequest: clientToProxyRequest,
675
+ proxyToClientResponse: proxyToClientResponse,
676
+ onRequestHandlers: [],
677
+ onErrorHandlers: [],
678
+ onRequestDataHandlers: [],
679
+ onRequestEndHandlers: [],
680
+ onResponseHandlers: [],
681
+ onResponseDataHandlers: [],
682
+ onResponseEndHandlers: [],
683
+ requestFilters: [],
684
+ responseFilters: [],
685
+ responseContentPotentiallyModified: false,
686
+ onRequest: function(fn) {
687
+ ctx.onRequestHandlers.push(fn);
688
+ return ctx;
689
+ },
690
+ onError: function(fn) {
691
+ ctx.onErrorHandlers.push(fn);
692
+ return ctx;
693
+ },
694
+ onRequestData: function(fn) {
695
+ ctx.onRequestDataHandlers.push(fn);
696
+ return ctx;
697
+ },
698
+ onRequestEnd: function(fn) {
699
+ ctx.onRequestEndHandlers.push(fn);
700
+ return ctx;
701
+ },
702
+ addRequestFilter: function(filter) {
703
+ ctx.requestFilters.push(filter);
704
+ return ctx;
705
+ },
706
+ onResponse: function(fn) {
707
+ ctx.onResponseHandlers.push(fn);
708
+ return ctx;
709
+ },
710
+ onResponseData: function(fn) {
711
+ ctx.onResponseDataHandlers.push(fn);
712
+ ctx.responseContentPotentiallyModified = true;
713
+ return ctx;
714
+ },
715
+ onResponseEnd: function(fn) {
716
+ ctx.onResponseEndHandlers.push(fn);
717
+ return ctx;
718
+ },
719
+ addResponseFilter: function(filter) {
720
+ ctx.responseFilters.push(filter);
721
+ ctx.responseContentPotentiallyModified = true;
722
+ return ctx;
723
+ },
724
+ use: function(mod) {
725
+ if (mod.onError) {
726
+ ctx.onError(mod.onError);
727
+ }
728
+ if (mod.onRequest) {
729
+ ctx.onRequest(mod.onRequest);
730
+ }
731
+ if (mod.onRequestHeaders) {
732
+ ctx.onRequestHeaders(mod.onRequestHeaders);
733
+ }
734
+ if (mod.onRequestData) {
735
+ ctx.onRequestData(mod.onRequestData);
736
+ }
737
+ if (mod.onResponse) {
738
+ ctx.onResponse(mod.onResponse);
739
+ }
740
+ if (mod.onResponseData) {
741
+ ctx.onResponseData(mod.onResponseData);
742
+ }
743
+ return ctx;
744
+ }
745
+ };
746
+
747
+ ctx.clientToProxyRequest.on('error', self._onError.bind(self, 'CLIENT_TO_PROXY_REQUEST_ERROR', ctx));
748
+ ctx.proxyToClientResponse.on('error', self._onError.bind(self, 'PROXY_TO_CLIENT_RESPONSE_ERROR', ctx));
749
+ ctx.clientToProxyRequest.pause();
750
+ var hostPort = Proxy.parseHostAndPort(ctx.clientToProxyRequest, ctx.isSSL ? 443 : 80);
751
+ if (hostPort === null) {
752
+ ctx.clientToProxyRequest.resume();
753
+ ctx.proxyToClientResponse.writeHeader(400, {
754
+ 'Content-Type': 'text/html; charset=utf-8'
755
+ });
756
+ ctx.proxyToClientResponse.end('Bad request: Host missing...', 'UTF-8');
757
+ } else {
758
+ var headers = {};
759
+ for (var h in ctx.clientToProxyRequest.headers) {
760
+ // don't forward proxy- headers
761
+ if (!/^proxy\-/i.test(h)) {
762
+ headers[h] = ctx.clientToProxyRequest.headers[h];
763
+ }
764
+ }
765
+ if (this.options.forceChunkedRequest){
766
+ delete headers['content-length'];
767
+ }
768
+
769
+ ctx.proxyToServerRequestOptions = {
770
+ method: ctx.clientToProxyRequest.method,
771
+ path: ctx.clientToProxyRequest.url,
772
+ host: hostPort.host,
773
+ port: hostPort.port,
774
+ headers: headers,
775
+ agent: ctx.isSSL ? self.httpsAgent : self.httpAgent
776
+ };
777
+ return self._onRequest(ctx, function(err) {
778
+ if (err) {
779
+ return self._onError('ON_REQUEST_ERROR', ctx, err);
780
+ }
781
+ return self._onRequestHeaders(ctx, function(err) {
782
+ if (err) {
783
+ return self._onError('ON_REQUESTHEADERS_ERROR', ctx, err);
784
+ }
785
+ return makeProxyToServerRequest();
786
+ });
787
+ });
788
+ }
789
+
790
+ function makeProxyToServerRequest() {
791
+ var proto = ctx.isSSL ? https : http;
792
+ ctx.proxyToServerRequest = proto.request(ctx.proxyToServerRequestOptions, proxyToServerRequestComplete);
793
+ ctx.proxyToServerRequest.on('error', self._onError.bind(self, 'PROXY_TO_SERVER_REQUEST_ERROR', ctx));
794
+ ctx.requestFilters.push(new ProxyFinalRequestFilter(self, ctx));
795
+ var prevRequestPipeElem = ctx.clientToProxyRequest;
796
+ ctx.requestFilters.forEach(function(filter) {
797
+ filter.on('error', self._onError.bind(self, 'REQUEST_FILTER_ERROR', ctx));
798
+ prevRequestPipeElem = prevRequestPipeElem.pipe(filter);
799
+ });
800
+ ctx.clientToProxyRequest.resume();
801
+ }
802
+
803
+ function proxyToServerRequestComplete(serverToProxyResponse) {
804
+ serverToProxyResponse.on('error', self._onError.bind(self, 'SERVER_TO_PROXY_RESPONSE_ERROR', ctx));
805
+ serverToProxyResponse.pause();
806
+ ctx.serverToProxyResponse = serverToProxyResponse;
807
+ return self._onResponse(ctx, function(err) {
808
+ if (err) {
809
+ return self._onError('ON_RESPONSE_ERROR', ctx, err);
810
+ }
811
+ if (self.responseContentPotentiallyModified || ctx.responseContentPotentiallyModified) {
812
+ ctx.serverToProxyResponse.headers['transfer-encoding'] = 'chunked';
813
+ delete ctx.serverToProxyResponse.headers['content-length'];
814
+ }
815
+ if (self.keepAlive) {
816
+ if (ctx.clientToProxyRequest.headers['proxy-connection']) {
817
+ ctx.serverToProxyResponse.headers['proxy-connection'] = 'keep-alive';
818
+ ctx.serverToProxyResponse.headers['connection'] = 'keep-alive';
819
+ }
820
+ } else {
821
+ ctx.serverToProxyResponse.headers['connection'] = 'close';
822
+ }
823
+ return self._onResponseHeaders(ctx, function (err) {
824
+ if (err) {
825
+ return self._onError('ON_RESPONSEHEADERS_ERROR', ctx, err);
826
+ }
827
+ ctx.proxyToClientResponse.writeHead(ctx.serverToProxyResponse.statusCode, Proxy.filterAndCanonizeHeaders(ctx.serverToProxyResponse.headers));
828
+ ctx.responseFilters.push(new ProxyFinalResponseFilter(self, ctx));
829
+ var prevResponsePipeElem = ctx.serverToProxyResponse;
830
+ ctx.responseFilters.forEach(function(filter) {
831
+ filter.on('error', self._onError.bind(self, 'RESPONSE_FILTER_ERROR', ctx));
832
+ prevResponsePipeElem = prevResponsePipeElem.pipe(filter);
833
+ });
834
+ return ctx.serverToProxyResponse.resume();
835
+ });
836
+ });
837
+ }
838
+ };
839
+
840
+ var ProxyFinalRequestFilter = function(proxy, ctx) {
841
+ events.EventEmitter.call(this);
842
+ this.writable = true;
843
+
844
+ this.write = function(chunk) {
845
+ proxy._onRequestData(ctx, chunk, function(err, chunk) {
846
+ if (err) {
847
+ return proxy._onError('ON_REQUEST_DATA_ERROR', ctx, err);
848
+ }
849
+ if (chunk) {
850
+ return ctx.proxyToServerRequest.write(chunk);
851
+ }
852
+ });
853
+ return true;
854
+ };
855
+
856
+ this.end = function(chunk) {
857
+ if (chunk) {
858
+ return proxy._onRequestData(ctx, chunk, function(err, chunk) {
859
+ if (err) {
860
+ return proxy._onError('ON_REQUEST_DATA_ERROR', ctx, err);
861
+ }
862
+
863
+ return proxy._onRequestEnd(ctx, function (err) {
864
+ if (err) {
865
+ return proxy._onError('ON_REQUEST_END_ERROR', ctx, err);
866
+ }
867
+ return ctx.proxyToServerRequest.end(chunk);
868
+ });
869
+ });
870
+ } else {
871
+ return proxy._onRequestEnd(ctx, function (err) {
872
+ if (err) {
873
+ return proxy._onError('ON_REQUEST_END_ERROR', ctx, err);
874
+ }
875
+ return ctx.proxyToServerRequest.end(chunk || undefined);
876
+ });
877
+ }
878
+ };
879
+ };
880
+ util.inherits(ProxyFinalRequestFilter, events.EventEmitter);
881
+
882
+ var ProxyFinalResponseFilter = function(proxy, ctx) {
883
+ events.EventEmitter.call(this);
884
+ this.writable = true;
885
+
886
+ this.write = function(chunk) {
887
+ proxy._onResponseData(ctx, chunk, function(err, chunk) {
888
+ if (err) {
889
+ return proxy._onError('ON_RESPONSE_DATA_ERROR', ctx, err);
890
+ }
891
+ if (chunk) {
892
+ return ctx.proxyToClientResponse.write(chunk);
893
+ }
894
+ });
895
+ return true;
896
+ };
897
+
898
+ this.end = function(chunk) {
899
+ if (chunk) {
900
+ return proxy._onResponseData(ctx, chunk, function(err, chunk) {
901
+ if (err) {
902
+ return proxy._onError('ON_RESPONSE_DATA_ERROR', ctx, err);
903
+ }
904
+
905
+ return proxy._onResponseEnd(ctx, function (err) {
906
+ if (err) {
907
+ return proxy._onError('ON_RESPONSE_END_ERROR', ctx, err);
908
+ }
909
+ return ctx.proxyToClientResponse.end(chunk || undefined);
910
+ });
911
+ });
912
+ } else {
913
+ return proxy._onResponseEnd(ctx, function (err) {
914
+ if (err) {
915
+ return proxy._onError('ON_RESPONSE_END_ERROR', ctx, err);
916
+ }
917
+ return ctx.proxyToClientResponse.end(chunk || undefined);
918
+ });
919
+ }
920
+ };
921
+
922
+ return this;
923
+ };
924
+ util.inherits(ProxyFinalResponseFilter, events.EventEmitter);
925
+
926
+ Proxy.prototype._onRequestHeaders = function(ctx, callback) {
927
+ async.forEach(this.onRequestHeadersHandlers, function(fn, callback) {
928
+ return fn(ctx, callback);
929
+ }, callback);
930
+ };
931
+
932
+ Proxy.prototype._onRequest = function(ctx, callback) {
933
+ async.forEach(this.onRequestHandlers.concat(ctx.onRequestHandlers), function(fn, callback) {
934
+ return fn(ctx, callback);
935
+ }, callback);
936
+ };
937
+
938
+ Proxy.prototype._onWebSocketConnection = function(ctx, callback) {
939
+ async.forEach(this.onWebSocketConnectionHandlers.concat(ctx.onWebSocketConnectionHandlers), function(fn, callback) {
940
+ return fn(ctx, callback);
941
+ }, callback);
942
+ };
943
+
944
+ Proxy.prototype._onWebSocketFrame = function(ctx, type, fromServer, data, flags) {
945
+ var self = this;
946
+ async.forEach(this.onWebSocketFrameHandlers.concat(ctx.onWebSocketFrameHandlers), function(fn, callback) {
947
+ return fn(ctx, type, fromServer, data, flags, function(err, newData, newFlags) {
948
+ if (err) {
949
+ return callback(err);
950
+ }
951
+ data = newData;
952
+ flags = newFlags;
953
+ return callback(null, data, flags);
954
+ });
955
+ }, function(err) {
956
+ if (err) {
957
+ return self._onWebSocketError(ctx, err);
958
+ }
959
+ var destWebSocket = fromServer ? ctx.clientToProxyWebSocket : ctx.proxyToServerWebSocket;
960
+ if (destWebSocket.readyState === WebSocket.OPEN) {
961
+ switch(type) {
962
+ case 'message': destWebSocket.send(data, flags);
963
+ break;
964
+ case 'ping': destWebSocket.ping(data, flags, false);
965
+ break;
966
+ case 'pong': destWebSocket.pong(data, flags, false);
967
+ break;
968
+ }
969
+ } else {
970
+ self._onWebSocketError(ctx, new Error('Cannot send ' + type + ' because ' + (fromServer ? 'clientToProxy' : 'proxyToServer') + ' WebSocket connection state is not OPEN'));
971
+ }
972
+ });
973
+ };
974
+
975
+ Proxy.prototype._onWebSocketClose = function(ctx, closedByServer, code, message) {
976
+ try {
977
+ if (!ctx.closedByServer && !ctx.closedByClient) {
978
+ ctx.closedByServer = closedByServer;
979
+ ctx.closedByClient = !closedByServer;
980
+ async.forEach(this.onWebSocketCloseHandlers.concat(ctx.onWebSocketCloseHandlers), function(fn, callback) {
981
+ return fn(ctx, code, message, callback);
982
+ }, function(err) {
983
+ if (err) {
984
+ return self._onWebSocketError(ctx, err);
985
+ }
986
+ if (ctx.clientToProxyWebSocket.readyState !== ctx.proxyToServerWebSocket.readyState) {
987
+ if (ctx.clientToProxyWebSocket.readyState === WebSocket.CLOSED && ctx.proxyToServerWebSocket.readyState === WebSocket.OPEN) {
988
+ ctx.proxyToServerWebSocket.close(code, message);
989
+ } else if (ctx.proxyToServerWebSocket.readyState === WebSocket.CLOSED && ctx.clientToProxyWebSocket.readyState === WebSocket.OPEN) {
990
+ ctx.clientToProxyWebSocket.close(code, message);
991
+ }
992
+ }
993
+ });
994
+ }
995
+ } catch(e) {
996
+ console.error(e);
997
+ }
998
+ };
999
+
1000
+ Proxy.prototype._onWebSocketError = function(ctx, err) {
1001
+ this.onWebSocketErrorHandlers.forEach(function(handler) {
1002
+ return handler(ctx, err);
1003
+ });
1004
+ if (ctx) {
1005
+ ctx.onWebSocketErrorHandlers.forEach(function(handler) {
1006
+ return handler(ctx, err);
1007
+ });
1008
+ }
1009
+ if (ctx.proxyToServerWebSocket && ctx.clientToProxyWebSocket.readyState !== ctx.proxyToServerWebSocket.readyState) {
1010
+ if (ctx.clientToProxyWebSocket.readyState === WebSocket.CLOSED && ctx.proxyToServerWebSocket.readyState === WebSocket.OPEN) {
1011
+ ctx.proxyToServerWebSocket.close();
1012
+ } else if (ctx.proxyToServerWebSocket.readyState === WebSocket.CLOSED && ctx.clientToProxyWebSocket.readyState === WebSocket.OPEN) {
1013
+ ctx.clientToProxyWebSocket.close();
1014
+ }
1015
+ }
1016
+ };
1017
+
1018
+ Proxy.prototype._onRequestData = function(ctx, chunk, callback) {
1019
+ var self = this;
1020
+ async.forEach(this.onRequestDataHandlers.concat(ctx.onRequestDataHandlers), function(fn, callback) {
1021
+ return fn(ctx, chunk, function(err, newChunk) {
1022
+ if (err) {
1023
+ return callback(err);
1024
+ }
1025
+ chunk = newChunk;
1026
+ return callback(null, newChunk);
1027
+ });
1028
+ }, function(err) {
1029
+ if (err) {
1030
+ return self._onError('ON_REQUEST_DATA_ERROR', ctx, err);
1031
+ }
1032
+ return callback(null, chunk);
1033
+ });
1034
+ };
1035
+
1036
+ Proxy.prototype._onRequestEnd = function(ctx, callback) {
1037
+ var self = this;
1038
+ async.forEach(this.onRequestEndHandlers.concat(ctx.onRequestEndHandlers), function(fn, callback) {
1039
+ return fn(ctx, callback);
1040
+ }, function(err) {
1041
+ if (err) {
1042
+ return self._onError('ON_REQUEST_END_ERROR', ctx, err);
1043
+ }
1044
+ return callback(null);
1045
+ });
1046
+ };
1047
+
1048
+ Proxy.prototype._onResponse = function(ctx, callback) {
1049
+ async.forEach(this.onResponseHandlers.concat(ctx.onResponseHandlers), function(fn, callback) {
1050
+ return fn(ctx, callback);
1051
+ }, callback);
1052
+ };
1053
+
1054
+ Proxy.prototype._onResponseHeaders = function(ctx, callback) {
1055
+ async.forEach(this.onResponseHeadersHandlers, function(fn, callback) {
1056
+ return fn(ctx, callback);
1057
+ }, callback);
1058
+ };
1059
+
1060
+ Proxy.prototype._onResponseData = function(ctx, chunk, callback) {
1061
+ var self = this;
1062
+ async.forEach(this.onResponseDataHandlers.concat(ctx.onResponseDataHandlers), function(fn, callback) {
1063
+ return fn(ctx, chunk, function(err, newChunk) {
1064
+ if (err) {
1065
+ return callback(err);
1066
+ }
1067
+ chunk = newChunk;
1068
+ return callback(null, newChunk);
1069
+ });
1070
+ }, function(err) {
1071
+ if (err) {
1072
+ return self._onError('ON_RESPONSE_DATA_ERROR', ctx, err);
1073
+ }
1074
+ return callback(null, chunk);
1075
+ });
1076
+ };
1077
+
1078
+ Proxy.prototype._onResponseEnd = function(ctx, callback) {
1079
+ var self = this;
1080
+ async.forEach(this.onResponseEndHandlers.concat(ctx.onResponseEndHandlers), function(fn, callback) {
1081
+ return fn(ctx, callback);
1082
+ }, function(err) {
1083
+ if (err) {
1084
+ return self._onError('ON_RESPONSE_END_ERROR', ctx, err);
1085
+ }
1086
+ return callback(null);
1087
+ });
1088
+ };
1089
+
1090
+ Proxy.parseHostAndPort = function(req, defaultPort) {
1091
+ var m = req.url.match(/^http:\/\/([^\/]+)(.*)/);
1092
+ if (m) {
1093
+ req.url = m[2] || '/';
1094
+ return Proxy.parseHost(m[1], defaultPort);
1095
+ } else if (req.headers.host) {
1096
+ return Proxy.parseHost(req.headers.host, defaultPort);
1097
+ } else {
1098
+ return null;
1099
+ }
1100
+ };
1101
+
1102
+ Proxy.parseHost = function(hostString, defaultPort) {
1103
+ var m = hostString.match(/^http:\/\/(.*)/);
1104
+ if (m) {
1105
+ var parsedUrl = url.parse(hostString);
1106
+ return {
1107
+ host: parsedUrl.hostname,
1108
+ port: parsedUrl.port
1109
+ };
1110
+ }
1111
+
1112
+ var hostPort = hostString.split(':');
1113
+ var host = hostPort[0];
1114
+ var port = hostPort.length === 2 ? +hostPort[1] : defaultPort;
1115
+
1116
+ return {
1117
+ host: host,
1118
+ port: port
1119
+ };
1120
+ };
1121
+
1122
+ Proxy.filterAndCanonizeHeaders = function(originalHeaders) {
1123
+ var headers = {};
1124
+ for (var key in originalHeaders) {
1125
+ var canonizedKey = key.trim();
1126
+ if (/^public\-key\-pins/i.test(canonizedKey)) {
1127
+ // HPKP header => filter
1128
+ continue;
1129
+ }
1130
+
1131
+ if (!nodeCommon._checkInvalidHeaderChar(originalHeaders[key])) {
1132
+ headers[canonizedKey] = originalHeaders[key];
1133
+ }
1134
+ }
1135
+
1136
+ return headers;
1137
+ };