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