@dev-swarup/http-mitm-proxy 0.9.6

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.
@@ -0,0 +1,321 @@
1
+ const net = require('net');
2
+ const tls = require('tls');
3
+ const http = require('http');
4
+ const https = require('https');
5
+ const events = require('events');
6
+
7
+ const debug = require('debug')('http-mitm-proxy:tunnelagent');
8
+
9
+ let agentCount = 0;
10
+
11
+ class SocketStore {
12
+ sockets = {};
13
+
14
+ insert(key, socket) {
15
+ if (this.sockets[key]) {
16
+ this.sockets[key].push(socket);
17
+ } else {
18
+ this.sockets[key] = [socket];
19
+ }
20
+ }
21
+
22
+ get(key) {
23
+ if (this.sockets[key] && this.sockets[key].length > 0) {
24
+ return this.sockets[key].pop();
25
+ }
26
+ return undefined;
27
+ }
28
+
29
+ length(key) {
30
+ if (this.sockets[key]) {
31
+ return this.sockets[key].length;
32
+ }
33
+ return 0;
34
+ }
35
+
36
+ count() {
37
+ let sum = 0;
38
+ for (const property in this.sockets) {
39
+ if (this.sockets.hasOwnProperty(property)) {
40
+ sum += this.sockets[property].length;
41
+ }
42
+ }
43
+ return sum;
44
+ }
45
+
46
+ remove(key, socket) {
47
+ const socketIndex = this.sockets[key].indexOf(socket);
48
+ if (socketIndex === -1) {
49
+ throw new Error('SocketStore: Attempt to remove non-existing socket');
50
+ }
51
+ this.sockets[key].splice(socketIndex, 1);
52
+ }
53
+
54
+ replace(key, socket, newSocket) {
55
+ this.sockets[key][this.sockets[key].indexOf(socket)] = newSocket;
56
+ }
57
+
58
+ destroy() {
59
+ for (const property in this.sockets) {
60
+ if (this.sockets.hasOwnProperty(property)) {
61
+ const sockets = this.sockets[property];
62
+ sockets.forEach((socket) => {
63
+ socket.destroy();
64
+ });
65
+ }
66
+ }
67
+ }
68
+ }
69
+
70
+ module.exports = class TunnelAgent extends events.EventEmitter {
71
+ request;
72
+ options;
73
+ defaultPort;
74
+ maxSockets;
75
+ requests;
76
+ sockets;
77
+ freeSockets;
78
+ keepAlive;
79
+ proxyOptions;
80
+ destroyPending = false;
81
+ agentId;
82
+ createSocket;
83
+
84
+ debugLog(message) {
85
+ debug('[' + this.agentId + ']: ' + message);
86
+ }
87
+
88
+ constructor(options, proxyOverHttps = false, targetUsesHttps = false) {
89
+ super();
90
+ const self = this;
91
+ this.options = options;
92
+ this.proxyOptions = options.proxy || {};
93
+ this.maxSockets = options.maxSockets || 1;
94
+ this.keepAlive = options.keepAlive || false;
95
+ this.requests = [];
96
+ this.sockets = new SocketStore();
97
+ this.freeSockets = new SocketStore();
98
+ this.request = proxyOverHttps ? https.request : http.request;
99
+ this.createSocket = targetUsesHttps ? this.createSecureSocket : this.createTcpSocket;
100
+ this.defaultPort = targetUsesHttps ? 443 : 80;
101
+ this.agentId = agentCount++;
102
+
103
+ // attempt to negotiate http/1.1 for proxy servers that support http/2
104
+ if (this.proxyOptions.secureProxy && !('ALPNProtocols' in this.proxyOptions)) {
105
+ this.proxyOptions.ALPNProtocols = ['http 1.1'];
106
+ }
107
+
108
+ self.on('free', function onFree(socket, request) {
109
+ for (let i = 0, len = self.requests.length; i < len; ++i) {
110
+ const pending = self.requests[i];
111
+ if (pending.socketKey === request.socketKey) {
112
+ self.debugLog('socket free, reusing for pending request');
113
+ // Detect the request to connect same origin server, reuse the connection.
114
+ self.requests.splice(i, 1);
115
+ pending.clientReq.reusedSocket = true;
116
+ pending.clientReq.onSocket(socket);
117
+ return;
118
+ }
119
+ }
120
+
121
+ self.sockets.remove(request.socketKey, socket);
122
+ if (!self.keepAlive) {
123
+ socket.destroy();
124
+ self.debugLog('socket free, non keep-alive => destroy socket');
125
+ } else {
126
+ // save the socket for reuse later
127
+ socket.removeAllListeners();
128
+ socket.unref();
129
+ self.freeSockets.insert(request.socketKey, socket);
130
+ socket.once('close', (_) => {
131
+ if (self.destroyPending) return;
132
+ self.debugLog('remove socket on socket close');
133
+ self.freeSockets.remove(request.socketKey, socket);
134
+ });
135
+ }
136
+ self.processPending();
137
+ });
138
+ }
139
+
140
+ /**
141
+ * Counts all sockets active in requests and pending (keep-alive)
142
+ *
143
+ * @returns {number} The number of sockets, free and in use
144
+ */
145
+ socketCount() {
146
+ return this.sockets.count() + this.freeSockets.count();
147
+ }
148
+
149
+ addRequest(req, _opts) {
150
+ const self = this;
151
+ const request = {
152
+ clientReq: req,
153
+ socketKey: `${_opts.host}:${_opts.port}`,
154
+ options: { ..._opts, ...self.options },
155
+ };
156
+
157
+ if (self.sockets.length(request.socketKey) >= this.maxSockets) {
158
+ // We are over limit for the host so we'll add it to the queue.
159
+ self.requests.push(request);
160
+ return;
161
+ }
162
+
163
+ if (self.keepAlive) {
164
+ const socket = self.freeSockets.get(request.socketKey);
165
+ if (socket) {
166
+ this.debugLog('addRequest: reuse free socket for ' + request.socketKey);
167
+ socket.removeAllListeners();
168
+ socket.ref();
169
+ self.sockets.insert(request.socketKey, socket);
170
+ req.reusedSocket = true;
171
+ self.executeRequest(request, socket);
172
+ return;
173
+ }
174
+ }
175
+
176
+ // If we are under maxSockets create a new one.
177
+ self.createSocket(request);
178
+ }
179
+
180
+ executeRequest(request, socket) {
181
+ const self = this;
182
+ socket.on('free', onFree);
183
+ socket.on('close', onCloseOrRemove);
184
+ socket.on('agentRemove', onCloseOrRemove);
185
+ request.clientReq.onSocket(socket);
186
+
187
+ function onFree() {
188
+ self.debugLog('onFree');
189
+ self.emit('free', socket, request);
190
+ }
191
+
192
+ function onCloseOrRemove(hadError) {
193
+ self.debugLog('onClose');
194
+ if (self.destroyPending) return;
195
+ socket.removeListener('free', onFree);
196
+ socket.removeListener('close', onCloseOrRemove);
197
+ socket.removeListener('agentRemove', onCloseOrRemove);
198
+ if (self.keepAlive) {
199
+ socket.emit('close', hadError); // Let the freeSocket event handler remove the socket
200
+ }
201
+ self.processPending();
202
+ }
203
+ }
204
+
205
+ escapeHost(hostname, port) {
206
+ if (hostname.indexOf(':') === -1) {
207
+ return `${hostname}:${port}`;
208
+ }
209
+ return `[${hostname}]:${port}`;
210
+ }
211
+
212
+ createSocketInternal(request, cb) {
213
+ const self = this;
214
+ const host = this.escapeHost(request.options.host, request.options.port);
215
+ const connectOptions = {
216
+ ...self.proxyOptions,
217
+ method: 'CONNECT',
218
+ path: host,
219
+ headers: {
220
+ host: host,
221
+ },
222
+ };
223
+ if (request.options.localAddress) {
224
+ connectOptions.localAddress = request.options.localAddress;
225
+ }
226
+ if (self.proxyOptions.proxyAuth) {
227
+ connectOptions.headers = connectOptions.headers || {};
228
+ connectOptions.headers['Proxy-Authorization'] = 'Basic ' + Buffer.from(self.proxyOptions.proxyAuth).toString('base64');
229
+ }
230
+
231
+ const connectReq = self.request(connectOptions);
232
+ connectReq.once('connect', onConnect);
233
+ connectReq.once('error', onError);
234
+ connectReq.end();
235
+
236
+ function onConnect(res, socket, head) {
237
+ connectReq.removeAllListeners();
238
+ socket.removeAllListeners();
239
+
240
+ if (res.statusCode !== 200) {
241
+ self.debugLog('tunneling socket could not be established, statusCode=' + res.statusCode);
242
+ socket.destroy();
243
+ request.clientReq.destroy(new Error('tunneling socket could not be established, ' + 'statusCode=' + res.statusCode));
244
+ self.processPending();
245
+ return;
246
+ }
247
+ if (head.length > 0) {
248
+ self.debugLog('got illegal response body from proxy');
249
+ socket.destroy();
250
+ request.clientReq.destroy(new Error('got illegal response body from proxy'));
251
+ self.processPending();
252
+ return;
253
+ }
254
+ self.debugLog('tunneling connection established');
255
+ self.sockets.insert(request.socketKey, socket);
256
+ return cb(socket);
257
+ }
258
+
259
+ function onError(cause) {
260
+ connectReq.removeAllListeners();
261
+ self.debugLog('tunneling socket could not be established, cause=' + cause.message + '\n' + cause.stack);
262
+ request.clientReq.destroy(new Error('tunneling socket could not be established, ' + 'cause=' + cause.message));
263
+ self.processPending();
264
+ }
265
+ }
266
+
267
+ processPending() {
268
+ const pending = this.requests.shift();
269
+ if (pending) {
270
+ // If we have pending requests and a socket gets closed a new one
271
+ // needs to be created to take over in the pool for the one that closed.
272
+ this.createSocket(pending);
273
+ }
274
+ }
275
+
276
+ createTcpSocket(request) {
277
+ const self = this;
278
+ self.createSocketInternal(request, (socket) => self.executeRequest(request, socket));
279
+ }
280
+
281
+ createSecureSocket(request) {
282
+ const self = this;
283
+ self.createSocketInternal(request, function (socket) {
284
+ const hostHeader = request.clientReq.getHeader('host');
285
+ const tlsOptions = {
286
+ ...omit(self.options, 'host', 'path', 'port'),
287
+ socket: socket,
288
+ };
289
+ let servername = '';
290
+ if (hostHeader) {
291
+ servername = new URL('https://' + hostHeader).hostname;
292
+ } else if (request.options.host) {
293
+ servername = request.options.host;
294
+ }
295
+ if (servername) {
296
+ tlsOptions.servername = servername;
297
+ }
298
+
299
+ const secureSocket = tls.connect(0, tlsOptions);
300
+ self.sockets.replace(request.socketKey, socket, secureSocket);
301
+ self.executeRequest(request, secureSocket);
302
+ });
303
+ }
304
+
305
+ destroy() {
306
+ this.debugLog('destroying agent');
307
+ this.destroyPending = true;
308
+ this.sockets.destroy();
309
+ this.freeSockets.destroy();
310
+ }
311
+ };
312
+
313
+ function omit(obj, ...keys) {
314
+ const ret = {};
315
+ for (var key in obj) {
316
+ if (!keys.includes(key)) {
317
+ ret[key] = obj[key];
318
+ }
319
+ }
320
+ return ret;
321
+ }
@@ -0,0 +1,64 @@
1
+ 123456789012345
2
+ 123456789012345
3
+ 123456789012345
4
+ 123456789012345
5
+ 123456789012345
6
+ 123456789012345
7
+ 123456789012345
8
+ 123456789012345
9
+ 123456789012345
10
+ 123456789012345
11
+ 123456789012345
12
+ 123456789012345
13
+ 123456789012345
14
+ 123456789012345
15
+ 123456789012345
16
+ 123456789012345
17
+ 123456789012345
18
+ 123456789012345
19
+ 123456789012345
20
+ 123456789012345
21
+ 123456789012345
22
+ 123456789012345
23
+ 123456789012345
24
+ 123456789012345
25
+ 123456789012345
26
+ 123456789012345
27
+ 123456789012345
28
+ 123456789012345
29
+ 123456789012345
30
+ 123456789012345
31
+ 123456789012345
32
+ 123456789012345
33
+ 123456789012345
34
+ 123456789012345
35
+ 123456789012345
36
+ 123456789012345
37
+ 123456789012345
38
+ 123456789012345
39
+ 123456789012345
40
+ 123456789012345
41
+ 123456789012345
42
+ 123456789012345
43
+ 123456789012345
44
+ 123456789012345
45
+ 123456789012345
46
+ 123456789012345
47
+ 123456789012345
48
+ 123456789012345
49
+ 123456789012345
50
+ 123456789012345
51
+ 123456789012345
52
+ 123456789012345
53
+ 123456789012345
54
+ 123456789012345
55
+ 123456789012345
56
+ 123456789012345
57
+ 123456789012345
58
+ 123456789012345
59
+ 123456789012345
60
+ 123456789012345
61
+ 123456789012345
62
+ 123456789012345
63
+ 123456789012345
64
+ 123456789012345
@@ -0,0 +1,64 @@
1
+ 123456789012345
2
+ 123456789012345
3
+ 123456789012345
4
+ 123456789012345
5
+ 123456789012345
6
+ 123456789012345
7
+ 123456789012345
8
+ 123456789012345
9
+ 123456789012345
10
+ 123456789012345
11
+ 123456789012345
12
+ 123456789012345
13
+ 123456789012345
14
+ 123456789012345
15
+ 123456789012345
16
+ 123456789012345
17
+ 123456789012345
18
+ 123456789012345
19
+ 123456789012345
20
+ 123456789012345
21
+ 123456789012345
22
+ 123456789012345
23
+ 123456789012345
24
+ 123456789012345
25
+ 123456789012345
26
+ 123456789012345
27
+ 123456789012345
28
+ 123456789012345
29
+ 123456789012345
30
+ 123456789012345
31
+ 123456789012345
32
+ 123456789012345
33
+ 123456789012345
34
+ 123456789012345
35
+ 123456789012345
36
+ 123456789012345
37
+ 123456789012345
38
+ 123456789012345
39
+ 123456789012345
40
+ 123456789012345
41
+ 123456789012345
42
+ 123456789012345
43
+ 123456789012345
44
+ 123456789012345
45
+ 123456789012345
46
+ 123456789012345
47
+ 123456789012345
48
+ 123456789012345
49
+ 123456789012345
50
+ 123456789012345
51
+ 123456789012345
52
+ 123456789012345
53
+ 123456789012345
54
+ 123456789012345
55
+ 123456789012345
56
+ 123456789012345
57
+ 123456789012345
58
+ 123456789012345
59
+ 123456789012345
60
+ 123456789012345
61
+ 123456789012345
62
+ 123456789012345
63
+ 123456789012345
64
+ 123456789012345
@@ -0,0 +1,8 @@
1
+ <html>
2
+ <head>
3
+ <title>example.com</title>
4
+ </head>
5
+ <body>
6
+ <h3>exmaple.com</h3>
7
+ </body>
8
+ </html>
File without changes
@@ -0,0 +1,64 @@
1
+ 123456789012345
2
+ 123456789012345
3
+ 123456789012345
4
+ 123456789012345
5
+ 123456789012345
6
+ 123456789012345
7
+ 123456789012345
8
+ 123456789012345
9
+ 123456789012345
10
+ 123456789012345
11
+ 123456789012345
12
+ 123456789012345
13
+ 123456789012345
14
+ 123456789012345
15
+ 123456789012345
16
+ 123456789012345
17
+ 123456789012345
18
+ 123456789012345
19
+ 123456789012345
20
+ 123456789012345
21
+ 123456789012345
22
+ 123456789012345
23
+ 123456789012345
24
+ 123456789012345
25
+ 123456789012345
26
+ 123456789012345
27
+ 123456789012345
28
+ 123456789012345
29
+ 123456789012345
30
+ 123456789012345
31
+ 123456789012345
32
+ 123456789012345
33
+ 123456789012345
34
+ 123456789012345
35
+ 123456789012345
36
+ 123456789012345
37
+ 123456789012345
38
+ 123456789012345
39
+ 123456789012345
40
+ 123456789012345
41
+ 123456789012345
42
+ 123456789012345
43
+ 123456789012345
44
+ 123456789012345
45
+ 123456789012345
46
+ 123456789012345
47
+ 123456789012345
48
+ 123456789012345
49
+ 123456789012345
50
+ 123456789012345
51
+ 123456789012345
52
+ 123456789012345
53
+ 123456789012345
54
+ 123456789012345
55
+ 123456789012345
56
+ 123456789012345
57
+ 123456789012345
58
+ 123456789012345
59
+ 123456789012345
60
+ 123456789012345
61
+ 123456789012345
62
+ 123456789012345
63
+ 123456789012345
64
+ 123456789012345
File without changes