@push.rocks/smartproxy 10.2.0 → 11.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 (60) hide show
  1. package/dist_ts/00_commitinfo_data.js +1 -1
  2. package/dist_ts/common/port80-adapter.d.ts +11 -0
  3. package/dist_ts/common/port80-adapter.js +61 -0
  4. package/dist_ts/examples/forwarding-example.d.ts +1 -0
  5. package/dist_ts/examples/forwarding-example.js +96 -0
  6. package/dist_ts/index.d.ts +1 -0
  7. package/dist_ts/index.js +3 -1
  8. package/dist_ts/smartproxy/classes.pp.connectionhandler.js +179 -30
  9. package/dist_ts/smartproxy/classes.pp.domainconfigmanager.d.ts +39 -0
  10. package/dist_ts/smartproxy/classes.pp.domainconfigmanager.js +172 -20
  11. package/dist_ts/smartproxy/classes.pp.interfaces.d.ts +3 -11
  12. package/dist_ts/smartproxy/classes.pp.portrangemanager.js +17 -10
  13. package/dist_ts/smartproxy/classes.pp.securitymanager.d.ts +19 -2
  14. package/dist_ts/smartproxy/classes.pp.securitymanager.js +27 -4
  15. package/dist_ts/smartproxy/classes.pp.timeoutmanager.js +3 -3
  16. package/dist_ts/smartproxy/classes.smartproxy.js +45 -13
  17. package/dist_ts/smartproxy/forwarding/domain-config.d.ts +12 -0
  18. package/dist_ts/smartproxy/forwarding/domain-config.js +12 -0
  19. package/dist_ts/smartproxy/forwarding/domain-manager.d.ts +86 -0
  20. package/dist_ts/smartproxy/forwarding/domain-manager.js +241 -0
  21. package/dist_ts/smartproxy/forwarding/forwarding.factory.d.ts +24 -0
  22. package/dist_ts/smartproxy/forwarding/forwarding.factory.js +137 -0
  23. package/dist_ts/smartproxy/forwarding/forwarding.handler.d.ts +55 -0
  24. package/dist_ts/smartproxy/forwarding/forwarding.handler.js +94 -0
  25. package/dist_ts/smartproxy/forwarding/http.handler.d.ts +25 -0
  26. package/dist_ts/smartproxy/forwarding/http.handler.js +123 -0
  27. package/dist_ts/smartproxy/forwarding/https-passthrough.handler.d.ts +24 -0
  28. package/dist_ts/smartproxy/forwarding/https-passthrough.handler.js +154 -0
  29. package/dist_ts/smartproxy/forwarding/https-terminate-to-http.handler.d.ts +36 -0
  30. package/dist_ts/smartproxy/forwarding/https-terminate-to-http.handler.js +229 -0
  31. package/dist_ts/smartproxy/forwarding/https-terminate-to-https.handler.d.ts +35 -0
  32. package/dist_ts/smartproxy/forwarding/https-terminate-to-https.handler.js +254 -0
  33. package/dist_ts/smartproxy/forwarding/index.d.ts +16 -0
  34. package/dist_ts/smartproxy/forwarding/index.js +23 -0
  35. package/dist_ts/smartproxy/types/forwarding.types.d.ts +104 -0
  36. package/dist_ts/smartproxy/types/forwarding.types.js +50 -0
  37. package/package.json +2 -2
  38. package/readme.md +158 -8
  39. package/readme.plan.md +471 -42
  40. package/ts/00_commitinfo_data.ts +1 -1
  41. package/ts/common/port80-adapter.ts +87 -0
  42. package/ts/examples/forwarding-example.ts +128 -0
  43. package/ts/index.ts +3 -0
  44. package/ts/smartproxy/classes.pp.connectionhandler.ts +231 -44
  45. package/ts/smartproxy/classes.pp.domainconfigmanager.ts +198 -24
  46. package/ts/smartproxy/classes.pp.interfaces.ts +3 -11
  47. package/ts/smartproxy/classes.pp.portrangemanager.ts +17 -10
  48. package/ts/smartproxy/classes.pp.securitymanager.ts +29 -5
  49. package/ts/smartproxy/classes.pp.timeoutmanager.ts +3 -3
  50. package/ts/smartproxy/classes.smartproxy.ts +68 -15
  51. package/ts/smartproxy/forwarding/domain-config.ts +28 -0
  52. package/ts/smartproxy/forwarding/domain-manager.ts +283 -0
  53. package/ts/smartproxy/forwarding/forwarding.factory.ts +155 -0
  54. package/ts/smartproxy/forwarding/forwarding.handler.ts +127 -0
  55. package/ts/smartproxy/forwarding/http.handler.ts +140 -0
  56. package/ts/smartproxy/forwarding/https-passthrough.handler.ts +182 -0
  57. package/ts/smartproxy/forwarding/https-terminate-to-http.handler.ts +264 -0
  58. package/ts/smartproxy/forwarding/https-terminate-to-https.handler.ts +292 -0
  59. package/ts/smartproxy/forwarding/index.ts +52 -0
  60. package/ts/smartproxy/types/forwarding.types.ts +162 -0
@@ -0,0 +1,182 @@
1
+ import * as plugins from '../../plugins.js';
2
+ import { ForwardingHandler } from './forwarding.handler.js';
3
+ import type { IForwardConfig } from '../types/forwarding.types.js';
4
+ import { ForwardingHandlerEvents } from '../types/forwarding.types.js';
5
+
6
+ /**
7
+ * Handler for HTTPS passthrough (SNI forwarding without termination)
8
+ */
9
+ export class HttpsPassthroughHandler extends ForwardingHandler {
10
+ /**
11
+ * Create a new HTTPS passthrough handler
12
+ * @param config The forwarding configuration
13
+ */
14
+ constructor(config: IForwardConfig) {
15
+ super(config);
16
+
17
+ // Validate that this is an HTTPS passthrough configuration
18
+ if (config.type !== 'https-passthrough') {
19
+ throw new Error(`Invalid configuration type for HttpsPassthroughHandler: ${config.type}`);
20
+ }
21
+ }
22
+
23
+ /**
24
+ * Handle a TLS/SSL socket connection by forwarding it without termination
25
+ * @param clientSocket The incoming socket from the client
26
+ */
27
+ public handleConnection(clientSocket: plugins.net.Socket): void {
28
+ // Get the target from configuration
29
+ const target = this.getTargetFromConfig();
30
+
31
+ // Log the connection
32
+ const remoteAddress = clientSocket.remoteAddress || 'unknown';
33
+ const remotePort = clientSocket.remotePort || 0;
34
+
35
+ this.emit(ForwardingHandlerEvents.CONNECTED, {
36
+ remoteAddress,
37
+ remotePort,
38
+ target: `${target.host}:${target.port}`
39
+ });
40
+
41
+ // Create a connection to the target server
42
+ const serverSocket = plugins.net.connect(target.port, target.host);
43
+
44
+ // Handle errors on the server socket
45
+ serverSocket.on('error', (error) => {
46
+ this.emit(ForwardingHandlerEvents.ERROR, {
47
+ remoteAddress,
48
+ error: `Target connection error: ${error.message}`
49
+ });
50
+
51
+ // Close the client socket if it's still open
52
+ if (!clientSocket.destroyed) {
53
+ clientSocket.destroy();
54
+ }
55
+ });
56
+
57
+ // Handle errors on the client socket
58
+ clientSocket.on('error', (error) => {
59
+ this.emit(ForwardingHandlerEvents.ERROR, {
60
+ remoteAddress,
61
+ error: `Client connection error: ${error.message}`
62
+ });
63
+
64
+ // Close the server socket if it's still open
65
+ if (!serverSocket.destroyed) {
66
+ serverSocket.destroy();
67
+ }
68
+ });
69
+
70
+ // Track data transfer for logging
71
+ let bytesSent = 0;
72
+ let bytesReceived = 0;
73
+
74
+ // Forward data from client to server
75
+ clientSocket.on('data', (data) => {
76
+ bytesSent += data.length;
77
+
78
+ // Check if server socket is writable
79
+ if (serverSocket.writable) {
80
+ const flushed = serverSocket.write(data);
81
+
82
+ // Handle backpressure
83
+ if (!flushed) {
84
+ clientSocket.pause();
85
+ serverSocket.once('drain', () => {
86
+ clientSocket.resume();
87
+ });
88
+ }
89
+ }
90
+
91
+ this.emit(ForwardingHandlerEvents.DATA_FORWARDED, {
92
+ direction: 'outbound',
93
+ bytes: data.length,
94
+ total: bytesSent
95
+ });
96
+ });
97
+
98
+ // Forward data from server to client
99
+ serverSocket.on('data', (data) => {
100
+ bytesReceived += data.length;
101
+
102
+ // Check if client socket is writable
103
+ if (clientSocket.writable) {
104
+ const flushed = clientSocket.write(data);
105
+
106
+ // Handle backpressure
107
+ if (!flushed) {
108
+ serverSocket.pause();
109
+ clientSocket.once('drain', () => {
110
+ serverSocket.resume();
111
+ });
112
+ }
113
+ }
114
+
115
+ this.emit(ForwardingHandlerEvents.DATA_FORWARDED, {
116
+ direction: 'inbound',
117
+ bytes: data.length,
118
+ total: bytesReceived
119
+ });
120
+ });
121
+
122
+ // Handle connection close
123
+ const handleClose = () => {
124
+ if (!clientSocket.destroyed) {
125
+ clientSocket.destroy();
126
+ }
127
+
128
+ if (!serverSocket.destroyed) {
129
+ serverSocket.destroy();
130
+ }
131
+
132
+ this.emit(ForwardingHandlerEvents.DISCONNECTED, {
133
+ remoteAddress,
134
+ bytesSent,
135
+ bytesReceived
136
+ });
137
+ };
138
+
139
+ // Set up close handlers
140
+ clientSocket.on('close', handleClose);
141
+ serverSocket.on('close', handleClose);
142
+
143
+ // Set timeouts
144
+ const timeout = this.getTimeout();
145
+ clientSocket.setTimeout(timeout);
146
+ serverSocket.setTimeout(timeout);
147
+
148
+ // Handle timeouts
149
+ clientSocket.on('timeout', () => {
150
+ this.emit(ForwardingHandlerEvents.ERROR, {
151
+ remoteAddress,
152
+ error: 'Client connection timeout'
153
+ });
154
+ handleClose();
155
+ });
156
+
157
+ serverSocket.on('timeout', () => {
158
+ this.emit(ForwardingHandlerEvents.ERROR, {
159
+ remoteAddress,
160
+ error: 'Server connection timeout'
161
+ });
162
+ handleClose();
163
+ });
164
+ }
165
+
166
+ /**
167
+ * Handle an HTTP request - HTTPS passthrough doesn't support HTTP
168
+ * @param req The HTTP request
169
+ * @param res The HTTP response
170
+ */
171
+ public handleHttpRequest(req: plugins.http.IncomingMessage, res: plugins.http.ServerResponse): void {
172
+ // HTTPS passthrough doesn't support HTTP requests
173
+ res.writeHead(404, { 'Content-Type': 'text/plain' });
174
+ res.end('HTTP not supported for this domain');
175
+
176
+ this.emit(ForwardingHandlerEvents.HTTP_RESPONSE, {
177
+ statusCode: 404,
178
+ headers: { 'Content-Type': 'text/plain' },
179
+ size: 'HTTP not supported for this domain'.length
180
+ });
181
+ }
182
+ }
@@ -0,0 +1,264 @@
1
+ import * as plugins from '../../plugins.js';
2
+ import { ForwardingHandler } from './forwarding.handler.js';
3
+ import type { IForwardConfig } from '../types/forwarding.types.js';
4
+ import { ForwardingHandlerEvents } from '../types/forwarding.types.js';
5
+
6
+ /**
7
+ * Handler for HTTPS termination with HTTP backend
8
+ */
9
+ export class HttpsTerminateToHttpHandler extends ForwardingHandler {
10
+ private tlsServer: plugins.tls.Server | null = null;
11
+ private secureContext: plugins.tls.SecureContext | null = null;
12
+
13
+ /**
14
+ * Create a new HTTPS termination with HTTP backend handler
15
+ * @param config The forwarding configuration
16
+ */
17
+ constructor(config: IForwardConfig) {
18
+ super(config);
19
+
20
+ // Validate that this is an HTTPS terminate to HTTP configuration
21
+ if (config.type !== 'https-terminate-to-http') {
22
+ throw new Error(`Invalid configuration type for HttpsTerminateToHttpHandler: ${config.type}`);
23
+ }
24
+ }
25
+
26
+ /**
27
+ * Initialize the handler, setting up TLS context
28
+ */
29
+ public async initialize(): Promise<void> {
30
+ // We need to load or create TLS certificates
31
+ if (this.config.https?.customCert) {
32
+ // Use custom certificate from configuration
33
+ this.secureContext = plugins.tls.createSecureContext({
34
+ key: this.config.https.customCert.key,
35
+ cert: this.config.https.customCert.cert
36
+ });
37
+
38
+ this.emit(ForwardingHandlerEvents.CERTIFICATE_LOADED, {
39
+ source: 'config',
40
+ domain: this.config.target.host
41
+ });
42
+ } else if (this.config.acme?.enabled) {
43
+ // Request certificate through ACME if needed
44
+ this.emit(ForwardingHandlerEvents.CERTIFICATE_NEEDED, {
45
+ domain: Array.isArray(this.config.target.host)
46
+ ? this.config.target.host[0]
47
+ : this.config.target.host,
48
+ useProduction: this.config.acme.production || false
49
+ });
50
+
51
+ // In a real implementation, we would wait for the certificate to be issued
52
+ // For now, we'll use a dummy context
53
+ this.secureContext = plugins.tls.createSecureContext({
54
+ key: '-----BEGIN PRIVATE KEY-----\nDummy key\n-----END PRIVATE KEY-----',
55
+ cert: '-----BEGIN CERTIFICATE-----\nDummy cert\n-----END CERTIFICATE-----'
56
+ });
57
+ } else {
58
+ throw new Error('HTTPS termination requires either a custom certificate or ACME enabled');
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Set the secure context for TLS termination
64
+ * Called when a certificate is available
65
+ * @param context The secure context
66
+ */
67
+ public setSecureContext(context: plugins.tls.SecureContext): void {
68
+ this.secureContext = context;
69
+ }
70
+
71
+ /**
72
+ * Handle a TLS/SSL socket connection by terminating TLS and forwarding to HTTP backend
73
+ * @param clientSocket The incoming socket from the client
74
+ */
75
+ public handleConnection(clientSocket: plugins.net.Socket): void {
76
+ // Make sure we have a secure context
77
+ if (!this.secureContext) {
78
+ clientSocket.destroy(new Error('TLS secure context not initialized'));
79
+ return;
80
+ }
81
+
82
+ const remoteAddress = clientSocket.remoteAddress || 'unknown';
83
+ const remotePort = clientSocket.remotePort || 0;
84
+
85
+ // Create a TLS socket using our secure context
86
+ const tlsSocket = new plugins.tls.TLSSocket(clientSocket, {
87
+ secureContext: this.secureContext,
88
+ isServer: true,
89
+ server: this.tlsServer || undefined
90
+ });
91
+
92
+ this.emit(ForwardingHandlerEvents.CONNECTED, {
93
+ remoteAddress,
94
+ remotePort,
95
+ tls: true
96
+ });
97
+
98
+ // Handle TLS errors
99
+ tlsSocket.on('error', (error) => {
100
+ this.emit(ForwardingHandlerEvents.ERROR, {
101
+ remoteAddress,
102
+ error: `TLS error: ${error.message}`
103
+ });
104
+
105
+ if (!tlsSocket.destroyed) {
106
+ tlsSocket.destroy();
107
+ }
108
+ });
109
+
110
+ // The TLS socket will now emit HTTP traffic that can be processed
111
+ // In a real implementation, we would create an HTTP parser and handle
112
+ // the requests here, but for simplicity, we'll just log the data
113
+
114
+ let dataBuffer = Buffer.alloc(0);
115
+
116
+ tlsSocket.on('data', (data) => {
117
+ // Append to buffer
118
+ dataBuffer = Buffer.concat([dataBuffer, data]);
119
+
120
+ // Very basic HTTP parsing - in a real implementation, use http-parser
121
+ if (dataBuffer.includes(Buffer.from('\r\n\r\n'))) {
122
+ const target = this.getTargetFromConfig();
123
+
124
+ // Simple example: forward the data to an HTTP server
125
+ const socket = plugins.net.connect(target.port, target.host, () => {
126
+ socket.write(dataBuffer);
127
+ dataBuffer = Buffer.alloc(0);
128
+
129
+ // Set up bidirectional data flow
130
+ tlsSocket.pipe(socket);
131
+ socket.pipe(tlsSocket);
132
+ });
133
+
134
+ socket.on('error', (error) => {
135
+ this.emit(ForwardingHandlerEvents.ERROR, {
136
+ remoteAddress,
137
+ error: `Target connection error: ${error.message}`
138
+ });
139
+
140
+ if (!tlsSocket.destroyed) {
141
+ tlsSocket.destroy();
142
+ }
143
+ });
144
+ }
145
+ });
146
+
147
+ // Handle close
148
+ tlsSocket.on('close', () => {
149
+ this.emit(ForwardingHandlerEvents.DISCONNECTED, {
150
+ remoteAddress
151
+ });
152
+ });
153
+
154
+ // Set timeout
155
+ const timeout = this.getTimeout();
156
+ tlsSocket.setTimeout(timeout);
157
+
158
+ tlsSocket.on('timeout', () => {
159
+ this.emit(ForwardingHandlerEvents.ERROR, {
160
+ remoteAddress,
161
+ error: 'TLS connection timeout'
162
+ });
163
+
164
+ if (!tlsSocket.destroyed) {
165
+ tlsSocket.destroy();
166
+ }
167
+ });
168
+ }
169
+
170
+ /**
171
+ * Handle an HTTP request by forwarding to the HTTP backend
172
+ * @param req The HTTP request
173
+ * @param res The HTTP response
174
+ */
175
+ public handleHttpRequest(req: plugins.http.IncomingMessage, res: plugins.http.ServerResponse): void {
176
+ // Check if we should redirect to HTTPS
177
+ if (this.config.http?.redirectToHttps) {
178
+ this.redirectToHttps(req, res);
179
+ return;
180
+ }
181
+
182
+ // Get the target from configuration
183
+ const target = this.getTargetFromConfig();
184
+
185
+ // Create custom headers with variable substitution
186
+ const variables = {
187
+ clientIp: req.socket.remoteAddress || 'unknown'
188
+ };
189
+
190
+ // Prepare headers, merging with any custom headers from config
191
+ const headers = this.applyCustomHeaders(req.headers, variables);
192
+
193
+ // Create the proxy request options
194
+ const options = {
195
+ hostname: target.host,
196
+ port: target.port,
197
+ path: req.url,
198
+ method: req.method,
199
+ headers
200
+ };
201
+
202
+ // Create the proxy request
203
+ const proxyReq = plugins.http.request(options, (proxyRes) => {
204
+ // Copy status code and headers from the proxied response
205
+ res.writeHead(proxyRes.statusCode || 500, proxyRes.headers);
206
+
207
+ // Pipe the proxy response to the client response
208
+ proxyRes.pipe(res);
209
+
210
+ // Track response size for logging
211
+ let responseSize = 0;
212
+ proxyRes.on('data', (chunk) => {
213
+ responseSize += chunk.length;
214
+ });
215
+
216
+ proxyRes.on('end', () => {
217
+ this.emit(ForwardingHandlerEvents.HTTP_RESPONSE, {
218
+ statusCode: proxyRes.statusCode,
219
+ headers: proxyRes.headers,
220
+ size: responseSize
221
+ });
222
+ });
223
+ });
224
+
225
+ // Handle errors in the proxy request
226
+ proxyReq.on('error', (error) => {
227
+ this.emit(ForwardingHandlerEvents.ERROR, {
228
+ remoteAddress: req.socket.remoteAddress,
229
+ error: `Proxy request error: ${error.message}`
230
+ });
231
+
232
+ // Send an error response if headers haven't been sent yet
233
+ if (!res.headersSent) {
234
+ res.writeHead(502, { 'Content-Type': 'text/plain' });
235
+ res.end(`Error forwarding request: ${error.message}`);
236
+ } else {
237
+ // Just end the response if headers have already been sent
238
+ res.end();
239
+ }
240
+ });
241
+
242
+ // Track request details for logging
243
+ let requestSize = 0;
244
+ req.on('data', (chunk) => {
245
+ requestSize += chunk.length;
246
+ });
247
+
248
+ // Log the request
249
+ this.emit(ForwardingHandlerEvents.HTTP_REQUEST, {
250
+ method: req.method,
251
+ url: req.url,
252
+ headers: req.headers,
253
+ remoteAddress: req.socket.remoteAddress,
254
+ target: `${target.host}:${target.port}`
255
+ });
256
+
257
+ // Pipe the client request to the proxy request
258
+ if (req.readable) {
259
+ req.pipe(proxyReq);
260
+ } else {
261
+ proxyReq.end();
262
+ }
263
+ }
264
+ }