@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,292 @@
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 HTTPS backend
8
+ */
9
+ export class HttpsTerminateToHttpsHandler extends ForwardingHandler {
10
+ private secureContext: plugins.tls.SecureContext | null = null;
11
+
12
+ /**
13
+ * Create a new HTTPS termination with HTTPS backend handler
14
+ * @param config The forwarding configuration
15
+ */
16
+ constructor(config: IForwardConfig) {
17
+ super(config);
18
+
19
+ // Validate that this is an HTTPS terminate to HTTPS configuration
20
+ if (config.type !== 'https-terminate-to-https') {
21
+ throw new Error(`Invalid configuration type for HttpsTerminateToHttpsHandler: ${config.type}`);
22
+ }
23
+ }
24
+
25
+ /**
26
+ * Initialize the handler, setting up TLS context
27
+ */
28
+ public async initialize(): Promise<void> {
29
+ // We need to load or create TLS certificates for termination
30
+ if (this.config.https?.customCert) {
31
+ // Use custom certificate from configuration
32
+ this.secureContext = plugins.tls.createSecureContext({
33
+ key: this.config.https.customCert.key,
34
+ cert: this.config.https.customCert.cert
35
+ });
36
+
37
+ this.emit(ForwardingHandlerEvents.CERTIFICATE_LOADED, {
38
+ source: 'config',
39
+ domain: this.config.target.host
40
+ });
41
+ } else if (this.config.acme?.enabled) {
42
+ // Request certificate through ACME if needed
43
+ this.emit(ForwardingHandlerEvents.CERTIFICATE_NEEDED, {
44
+ domain: Array.isArray(this.config.target.host)
45
+ ? this.config.target.host[0]
46
+ : this.config.target.host,
47
+ useProduction: this.config.acme.production || false
48
+ });
49
+
50
+ // In a real implementation, we would wait for the certificate to be issued
51
+ // For now, we'll use a dummy context
52
+ this.secureContext = plugins.tls.createSecureContext({
53
+ key: '-----BEGIN PRIVATE KEY-----\nDummy key\n-----END PRIVATE KEY-----',
54
+ cert: '-----BEGIN CERTIFICATE-----\nDummy cert\n-----END CERTIFICATE-----'
55
+ });
56
+ } else {
57
+ throw new Error('HTTPS termination requires either a custom certificate or ACME enabled');
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Set the secure context for TLS termination
63
+ * Called when a certificate is available
64
+ * @param context The secure context
65
+ */
66
+ public setSecureContext(context: plugins.tls.SecureContext): void {
67
+ this.secureContext = context;
68
+ }
69
+
70
+ /**
71
+ * Handle a TLS/SSL socket connection by terminating TLS and creating a new TLS connection to backend
72
+ * @param clientSocket The incoming socket from the client
73
+ */
74
+ public handleConnection(clientSocket: plugins.net.Socket): void {
75
+ // Make sure we have a secure context
76
+ if (!this.secureContext) {
77
+ clientSocket.destroy(new Error('TLS secure context not initialized'));
78
+ return;
79
+ }
80
+
81
+ const remoteAddress = clientSocket.remoteAddress || 'unknown';
82
+ const remotePort = clientSocket.remotePort || 0;
83
+
84
+ // Create a TLS socket using our secure context
85
+ const tlsSocket = new plugins.tls.TLSSocket(clientSocket, {
86
+ secureContext: this.secureContext,
87
+ isServer: true
88
+ });
89
+
90
+ this.emit(ForwardingHandlerEvents.CONNECTED, {
91
+ remoteAddress,
92
+ remotePort,
93
+ tls: true
94
+ });
95
+
96
+ // Handle TLS errors
97
+ tlsSocket.on('error', (error) => {
98
+ this.emit(ForwardingHandlerEvents.ERROR, {
99
+ remoteAddress,
100
+ error: `TLS error: ${error.message}`
101
+ });
102
+
103
+ if (!tlsSocket.destroyed) {
104
+ tlsSocket.destroy();
105
+ }
106
+ });
107
+
108
+ // The TLS socket will now emit HTTP traffic that can be processed
109
+ // In a real implementation, we would create an HTTP parser and handle
110
+ // the requests here, but for simplicity, we'll just forward the data
111
+
112
+ // Get the target from configuration
113
+ const target = this.getTargetFromConfig();
114
+
115
+ // Set up the connection to the HTTPS backend
116
+ const connectToBackend = () => {
117
+ const backendSocket = plugins.tls.connect({
118
+ host: target.host,
119
+ port: target.port,
120
+ // In a real implementation, we would configure TLS options
121
+ rejectUnauthorized: false // For testing only, never use in production
122
+ }, () => {
123
+ this.emit(ForwardingHandlerEvents.DATA_FORWARDED, {
124
+ direction: 'outbound',
125
+ target: `${target.host}:${target.port}`,
126
+ tls: true
127
+ });
128
+
129
+ // Set up bidirectional data flow
130
+ tlsSocket.pipe(backendSocket);
131
+ backendSocket.pipe(tlsSocket);
132
+ });
133
+
134
+ backendSocket.on('error', (error) => {
135
+ this.emit(ForwardingHandlerEvents.ERROR, {
136
+ remoteAddress,
137
+ error: `Backend connection error: ${error.message}`
138
+ });
139
+
140
+ if (!tlsSocket.destroyed) {
141
+ tlsSocket.destroy();
142
+ }
143
+ });
144
+
145
+ // Handle close
146
+ backendSocket.on('close', () => {
147
+ if (!tlsSocket.destroyed) {
148
+ tlsSocket.destroy();
149
+ }
150
+ });
151
+
152
+ // Set timeout
153
+ const timeout = this.getTimeout();
154
+ backendSocket.setTimeout(timeout);
155
+
156
+ backendSocket.on('timeout', () => {
157
+ this.emit(ForwardingHandlerEvents.ERROR, {
158
+ remoteAddress,
159
+ error: 'Backend connection timeout'
160
+ });
161
+
162
+ if (!backendSocket.destroyed) {
163
+ backendSocket.destroy();
164
+ }
165
+ });
166
+ };
167
+
168
+ // Wait for the TLS handshake to complete before connecting to backend
169
+ tlsSocket.on('secure', () => {
170
+ connectToBackend();
171
+ });
172
+
173
+ // Handle close
174
+ tlsSocket.on('close', () => {
175
+ this.emit(ForwardingHandlerEvents.DISCONNECTED, {
176
+ remoteAddress
177
+ });
178
+ });
179
+
180
+ // Set timeout
181
+ const timeout = this.getTimeout();
182
+ tlsSocket.setTimeout(timeout);
183
+
184
+ tlsSocket.on('timeout', () => {
185
+ this.emit(ForwardingHandlerEvents.ERROR, {
186
+ remoteAddress,
187
+ error: 'TLS connection timeout'
188
+ });
189
+
190
+ if (!tlsSocket.destroyed) {
191
+ tlsSocket.destroy();
192
+ }
193
+ });
194
+ }
195
+
196
+ /**
197
+ * Handle an HTTP request by forwarding to the HTTPS backend
198
+ * @param req The HTTP request
199
+ * @param res The HTTP response
200
+ */
201
+ public handleHttpRequest(req: plugins.http.IncomingMessage, res: plugins.http.ServerResponse): void {
202
+ // Check if we should redirect to HTTPS
203
+ if (this.config.http?.redirectToHttps) {
204
+ this.redirectToHttps(req, res);
205
+ return;
206
+ }
207
+
208
+ // Get the target from configuration
209
+ const target = this.getTargetFromConfig();
210
+
211
+ // Create custom headers with variable substitution
212
+ const variables = {
213
+ clientIp: req.socket.remoteAddress || 'unknown'
214
+ };
215
+
216
+ // Prepare headers, merging with any custom headers from config
217
+ const headers = this.applyCustomHeaders(req.headers, variables);
218
+
219
+ // Create the proxy request options
220
+ const options = {
221
+ hostname: target.host,
222
+ port: target.port,
223
+ path: req.url,
224
+ method: req.method,
225
+ headers,
226
+ // In a real implementation, we would configure TLS options
227
+ rejectUnauthorized: false // For testing only, never use in production
228
+ };
229
+
230
+ // Create the proxy request using HTTPS
231
+ const proxyReq = plugins.https.request(options, (proxyRes) => {
232
+ // Copy status code and headers from the proxied response
233
+ res.writeHead(proxyRes.statusCode || 500, proxyRes.headers);
234
+
235
+ // Pipe the proxy response to the client response
236
+ proxyRes.pipe(res);
237
+
238
+ // Track response size for logging
239
+ let responseSize = 0;
240
+ proxyRes.on('data', (chunk) => {
241
+ responseSize += chunk.length;
242
+ });
243
+
244
+ proxyRes.on('end', () => {
245
+ this.emit(ForwardingHandlerEvents.HTTP_RESPONSE, {
246
+ statusCode: proxyRes.statusCode,
247
+ headers: proxyRes.headers,
248
+ size: responseSize
249
+ });
250
+ });
251
+ });
252
+
253
+ // Handle errors in the proxy request
254
+ proxyReq.on('error', (error) => {
255
+ this.emit(ForwardingHandlerEvents.ERROR, {
256
+ remoteAddress: req.socket.remoteAddress,
257
+ error: `Proxy request error: ${error.message}`
258
+ });
259
+
260
+ // Send an error response if headers haven't been sent yet
261
+ if (!res.headersSent) {
262
+ res.writeHead(502, { 'Content-Type': 'text/plain' });
263
+ res.end(`Error forwarding request: ${error.message}`);
264
+ } else {
265
+ // Just end the response if headers have already been sent
266
+ res.end();
267
+ }
268
+ });
269
+
270
+ // Track request details for logging
271
+ let requestSize = 0;
272
+ req.on('data', (chunk) => {
273
+ requestSize += chunk.length;
274
+ });
275
+
276
+ // Log the request
277
+ this.emit(ForwardingHandlerEvents.HTTP_REQUEST, {
278
+ method: req.method,
279
+ url: req.url,
280
+ headers: req.headers,
281
+ remoteAddress: req.socket.remoteAddress,
282
+ target: `${target.host}:${target.port}`
283
+ });
284
+
285
+ // Pipe the client request to the proxy request
286
+ if (req.readable) {
287
+ req.pipe(proxyReq);
288
+ } else {
289
+ proxyReq.end();
290
+ }
291
+ }
292
+ }
@@ -0,0 +1,52 @@
1
+ // Export types
2
+ export type {
3
+ ForwardingType,
4
+ IForwardConfig,
5
+ IForwardingHandler,
6
+ ITargetConfig,
7
+ IHttpOptions,
8
+ IHttpsOptions,
9
+ IAcmeForwardingOptions,
10
+ ISecurityOptions,
11
+ IAdvancedOptions
12
+ } from '../types/forwarding.types.js';
13
+
14
+ // Export values
15
+ export {
16
+ ForwardingHandlerEvents,
17
+ httpOnly,
18
+ tlsTerminateToHttp,
19
+ tlsTerminateToHttps,
20
+ httpsPassthrough
21
+ } from '../types/forwarding.types.js';
22
+
23
+ // Export domain configuration
24
+ export * from './domain-config.js';
25
+
26
+ // Export handlers
27
+ export { ForwardingHandler } from './forwarding.handler.js';
28
+ export { HttpForwardingHandler } from './http.handler.js';
29
+ export { HttpsPassthroughHandler } from './https-passthrough.handler.js';
30
+ export { HttpsTerminateToHttpHandler } from './https-terminate-to-http.handler.js';
31
+ export { HttpsTerminateToHttpsHandler } from './https-terminate-to-https.handler.js';
32
+
33
+ // Export factory
34
+ export { ForwardingHandlerFactory } from './forwarding.factory.js';
35
+
36
+ // Export manager
37
+ export { DomainManager, DomainManagerEvents } from './domain-manager.js';
38
+
39
+ // Helper functions as a convenience object
40
+ import {
41
+ httpOnly,
42
+ tlsTerminateToHttp,
43
+ tlsTerminateToHttps,
44
+ httpsPassthrough
45
+ } from '../types/forwarding.types.js';
46
+
47
+ export const helpers = {
48
+ httpOnly,
49
+ tlsTerminateToHttp,
50
+ tlsTerminateToHttps,
51
+ sniPassthrough: httpsPassthrough // Alias for backward compatibility
52
+ };
@@ -0,0 +1,162 @@
1
+ import type * as plugins from '../../plugins.js';
2
+
3
+ /**
4
+ * The primary forwarding types supported by SmartProxy
5
+ */
6
+ export type ForwardingType =
7
+ | 'http-only' // HTTP forwarding only (no HTTPS)
8
+ | 'https-passthrough' // Pass-through TLS traffic (SNI forwarding)
9
+ | 'https-terminate-to-http' // Terminate TLS and forward to HTTP backend
10
+ | 'https-terminate-to-https'; // Terminate TLS and forward to HTTPS backend
11
+
12
+ /**
13
+ * Target configuration for forwarding
14
+ */
15
+ export interface ITargetConfig {
16
+ host: string | string[]; // Support single host or round-robin
17
+ port: number;
18
+ }
19
+
20
+ /**
21
+ * HTTP-specific options for forwarding
22
+ */
23
+ export interface IHttpOptions {
24
+ enabled?: boolean; // Whether HTTP is enabled
25
+ redirectToHttps?: boolean; // Redirect HTTP to HTTPS
26
+ headers?: Record<string, string>; // Custom headers for HTTP responses
27
+ }
28
+
29
+ /**
30
+ * HTTPS-specific options for forwarding
31
+ */
32
+ export interface IHttpsOptions {
33
+ customCert?: { // Use custom cert instead of auto-provisioned
34
+ key: string;
35
+ cert: string;
36
+ };
37
+ forwardSni?: boolean; // Forward SNI info in passthrough mode
38
+ }
39
+
40
+ /**
41
+ * ACME certificate handling options
42
+ */
43
+ export interface IAcmeForwardingOptions {
44
+ enabled?: boolean; // Enable ACME certificate provisioning
45
+ maintenance?: boolean; // Auto-renew certificates
46
+ production?: boolean; // Use production ACME servers
47
+ forwardChallenges?: { // Forward ACME challenges
48
+ host: string;
49
+ port: number;
50
+ useTls?: boolean;
51
+ };
52
+ }
53
+
54
+ /**
55
+ * Security options for forwarding
56
+ */
57
+ export interface ISecurityOptions {
58
+ allowedIps?: string[]; // IPs allowed to connect
59
+ blockedIps?: string[]; // IPs blocked from connecting
60
+ maxConnections?: number; // Max simultaneous connections
61
+ }
62
+
63
+ /**
64
+ * Advanced options for forwarding
65
+ */
66
+ export interface IAdvancedOptions {
67
+ portRanges?: Array<{ from: number; to: number }>; // Allowed port ranges
68
+ networkProxyPort?: number; // Custom NetworkProxy port if using terminate mode
69
+ keepAlive?: boolean; // Enable TCP keepalive
70
+ timeout?: number; // Connection timeout in ms
71
+ headers?: Record<string, string>; // Custom headers with support for variables like {sni}
72
+ }
73
+
74
+ /**
75
+ * Unified forwarding configuration interface
76
+ */
77
+ export interface IForwardConfig {
78
+ // Define the primary forwarding type - use-case driven approach
79
+ type: ForwardingType;
80
+
81
+ // Target configuration
82
+ target: ITargetConfig;
83
+
84
+ // Protocol options
85
+ http?: IHttpOptions;
86
+ https?: IHttpsOptions;
87
+ acme?: IAcmeForwardingOptions;
88
+
89
+ // Security and advanced options
90
+ security?: ISecurityOptions;
91
+ advanced?: IAdvancedOptions;
92
+ }
93
+
94
+ /**
95
+ * Event types emitted by forwarding handlers
96
+ */
97
+ export enum ForwardingHandlerEvents {
98
+ CONNECTED = 'connected',
99
+ DISCONNECTED = 'disconnected',
100
+ ERROR = 'error',
101
+ DATA_FORWARDED = 'data-forwarded',
102
+ HTTP_REQUEST = 'http-request',
103
+ HTTP_RESPONSE = 'http-response',
104
+ CERTIFICATE_NEEDED = 'certificate-needed',
105
+ CERTIFICATE_LOADED = 'certificate-loaded'
106
+ }
107
+
108
+ /**
109
+ * Base interface for forwarding handlers
110
+ */
111
+ export interface IForwardingHandler extends plugins.EventEmitter {
112
+ initialize(): Promise<void>;
113
+ handleConnection(socket: plugins.net.Socket): void;
114
+ handleHttpRequest(req: plugins.http.IncomingMessage, res: plugins.http.ServerResponse): void;
115
+ }
116
+
117
+ /**
118
+ * Helper function types for common forwarding patterns
119
+ */
120
+ export const httpOnly = (
121
+ partialConfig: Partial<IForwardConfig> & Pick<IForwardConfig, 'target'>
122
+ ): IForwardConfig => ({
123
+ type: 'http-only',
124
+ target: partialConfig.target,
125
+ http: { enabled: true, ...(partialConfig.http || {}) },
126
+ ...(partialConfig.security ? { security: partialConfig.security } : {}),
127
+ ...(partialConfig.advanced ? { advanced: partialConfig.advanced } : {})
128
+ });
129
+
130
+ export const tlsTerminateToHttp = (
131
+ partialConfig: Partial<IForwardConfig> & Pick<IForwardConfig, 'target'>
132
+ ): IForwardConfig => ({
133
+ type: 'https-terminate-to-http',
134
+ target: partialConfig.target,
135
+ https: { ...(partialConfig.https || {}) },
136
+ acme: { enabled: true, maintenance: true, ...(partialConfig.acme || {}) },
137
+ http: { enabled: true, redirectToHttps: true, ...(partialConfig.http || {}) },
138
+ ...(partialConfig.security ? { security: partialConfig.security } : {}),
139
+ ...(partialConfig.advanced ? { advanced: partialConfig.advanced } : {})
140
+ });
141
+
142
+ export const tlsTerminateToHttps = (
143
+ partialConfig: Partial<IForwardConfig> & Pick<IForwardConfig, 'target'>
144
+ ): IForwardConfig => ({
145
+ type: 'https-terminate-to-https',
146
+ target: partialConfig.target,
147
+ https: { ...(partialConfig.https || {}) },
148
+ acme: { enabled: true, maintenance: true, ...(partialConfig.acme || {}) },
149
+ http: { enabled: true, redirectToHttps: true, ...(partialConfig.http || {}) },
150
+ ...(partialConfig.security ? { security: partialConfig.security } : {}),
151
+ ...(partialConfig.advanced ? { advanced: partialConfig.advanced } : {})
152
+ });
153
+
154
+ export const httpsPassthrough = (
155
+ partialConfig: Partial<IForwardConfig> & Pick<IForwardConfig, 'target'>
156
+ ): IForwardConfig => ({
157
+ type: 'https-passthrough',
158
+ target: partialConfig.target,
159
+ https: { forwardSni: true, ...(partialConfig.https || {}) },
160
+ ...(partialConfig.security ? { security: partialConfig.security } : {}),
161
+ ...(partialConfig.advanced ? { advanced: partialConfig.advanced } : {})
162
+ });