@push.rocks/smartproxy 10.2.0 → 12.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 (59) 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/index.ts +3 -0
  43. package/ts/smartproxy/classes.pp.connectionhandler.ts +231 -44
  44. package/ts/smartproxy/classes.pp.domainconfigmanager.ts +198 -24
  45. package/ts/smartproxy/classes.pp.interfaces.ts +3 -11
  46. package/ts/smartproxy/classes.pp.portrangemanager.ts +17 -10
  47. package/ts/smartproxy/classes.pp.securitymanager.ts +29 -5
  48. package/ts/smartproxy/classes.pp.timeoutmanager.ts +3 -3
  49. package/ts/smartproxy/classes.smartproxy.ts +68 -15
  50. package/ts/smartproxy/forwarding/domain-config.ts +28 -0
  51. package/ts/smartproxy/forwarding/domain-manager.ts +283 -0
  52. package/ts/smartproxy/forwarding/forwarding.factory.ts +155 -0
  53. package/ts/smartproxy/forwarding/forwarding.handler.ts +127 -0
  54. package/ts/smartproxy/forwarding/http.handler.ts +140 -0
  55. package/ts/smartproxy/forwarding/https-passthrough.handler.ts +182 -0
  56. package/ts/smartproxy/forwarding/https-terminate-to-http.handler.ts +264 -0
  57. package/ts/smartproxy/forwarding/https-terminate-to-https.handler.ts +292 -0
  58. package/ts/smartproxy/forwarding/index.ts +52 -0
  59. package/ts/smartproxy/types/forwarding.types.ts +162 -0
@@ -0,0 +1,137 @@
1
+ import { HttpForwardingHandler } from './http.handler.js';
2
+ import { HttpsPassthroughHandler } from './https-passthrough.handler.js';
3
+ import { HttpsTerminateToHttpHandler } from './https-terminate-to-http.handler.js';
4
+ import { HttpsTerminateToHttpsHandler } from './https-terminate-to-https.handler.js';
5
+ /**
6
+ * Factory for creating forwarding handlers based on the configuration type
7
+ */
8
+ export class ForwardingHandlerFactory {
9
+ /**
10
+ * Create a forwarding handler based on the configuration
11
+ * @param config The forwarding configuration
12
+ * @returns The appropriate forwarding handler
13
+ */
14
+ static createHandler(config) {
15
+ // Create the appropriate handler based on the forwarding type
16
+ switch (config.type) {
17
+ case 'http-only':
18
+ return new HttpForwardingHandler(config);
19
+ case 'https-passthrough':
20
+ return new HttpsPassthroughHandler(config);
21
+ case 'https-terminate-to-http':
22
+ return new HttpsTerminateToHttpHandler(config);
23
+ case 'https-terminate-to-https':
24
+ return new HttpsTerminateToHttpsHandler(config);
25
+ default:
26
+ // Type system should prevent this, but just in case:
27
+ throw new Error(`Unknown forwarding type: ${config.type}`);
28
+ }
29
+ }
30
+ /**
31
+ * Apply default values to a forwarding configuration based on its type
32
+ * @param config The original forwarding configuration
33
+ * @returns A configuration with defaults applied
34
+ */
35
+ static applyDefaults(config) {
36
+ // Create a deep copy of the configuration
37
+ const result = JSON.parse(JSON.stringify(config));
38
+ // Apply defaults based on forwarding type
39
+ switch (config.type) {
40
+ case 'http-only':
41
+ // Set defaults for HTTP-only mode
42
+ result.http = {
43
+ enabled: true,
44
+ ...config.http
45
+ };
46
+ break;
47
+ case 'https-passthrough':
48
+ // Set defaults for HTTPS passthrough
49
+ result.https = {
50
+ forwardSni: true,
51
+ ...config.https
52
+ };
53
+ // SNI forwarding doesn't do HTTP
54
+ result.http = {
55
+ enabled: false,
56
+ ...config.http
57
+ };
58
+ break;
59
+ case 'https-terminate-to-http':
60
+ // Set defaults for HTTPS termination to HTTP
61
+ result.https = {
62
+ ...config.https
63
+ };
64
+ // Support HTTP access by default in this mode
65
+ result.http = {
66
+ enabled: true,
67
+ redirectToHttps: true,
68
+ ...config.http
69
+ };
70
+ // Enable ACME by default
71
+ result.acme = {
72
+ enabled: true,
73
+ maintenance: true,
74
+ ...config.acme
75
+ };
76
+ break;
77
+ case 'https-terminate-to-https':
78
+ // Similar to terminate-to-http but with different target handling
79
+ result.https = {
80
+ ...config.https
81
+ };
82
+ result.http = {
83
+ enabled: true,
84
+ redirectToHttps: true,
85
+ ...config.http
86
+ };
87
+ result.acme = {
88
+ enabled: true,
89
+ maintenance: true,
90
+ ...config.acme
91
+ };
92
+ break;
93
+ }
94
+ return result;
95
+ }
96
+ /**
97
+ * Validate a forwarding configuration
98
+ * @param config The configuration to validate
99
+ * @throws Error if the configuration is invalid
100
+ */
101
+ static validateConfig(config) {
102
+ // Validate common properties
103
+ if (!config.target) {
104
+ throw new Error('Forwarding configuration must include a target');
105
+ }
106
+ if (!config.target.host || (Array.isArray(config.target.host) && config.target.host.length === 0)) {
107
+ throw new Error('Target must include a host or array of hosts');
108
+ }
109
+ if (!config.target.port || config.target.port <= 0 || config.target.port > 65535) {
110
+ throw new Error('Target must include a valid port (1-65535)');
111
+ }
112
+ // Type-specific validation
113
+ switch (config.type) {
114
+ case 'http-only':
115
+ // HTTP-only needs http.enabled to be true
116
+ if (config.http?.enabled === false) {
117
+ throw new Error('HTTP-only forwarding must have HTTP enabled');
118
+ }
119
+ break;
120
+ case 'https-passthrough':
121
+ // HTTPS passthrough doesn't support HTTP
122
+ if (config.http?.enabled === true) {
123
+ throw new Error('HTTPS passthrough does not support HTTP');
124
+ }
125
+ // HTTPS passthrough doesn't work with ACME
126
+ if (config.acme?.enabled === true) {
127
+ throw new Error('HTTPS passthrough does not support ACME');
128
+ }
129
+ break;
130
+ case 'https-terminate-to-http':
131
+ case 'https-terminate-to-https':
132
+ // These modes support all options, nothing specific to validate
133
+ break;
134
+ }
135
+ }
136
+ }
137
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZm9yd2FyZGluZy5mYWN0b3J5LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vdHMvc21hcnRwcm94eS9mb3J3YXJkaW5nL2ZvcndhcmRpbmcuZmFjdG9yeS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFDQSxPQUFPLEVBQUUscUJBQXFCLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQztBQUMxRCxPQUFPLEVBQUUsdUJBQXVCLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUN6RSxPQUFPLEVBQUUsMkJBQTJCLEVBQUUsTUFBTSxzQ0FBc0MsQ0FBQztBQUNuRixPQUFPLEVBQUUsNEJBQTRCLEVBQUUsTUFBTSx1Q0FBdUMsQ0FBQztBQUVyRjs7R0FFRztBQUNILE1BQU0sT0FBTyx3QkFBd0I7SUFDbkM7Ozs7T0FJRztJQUNJLE1BQU0sQ0FBQyxhQUFhLENBQUMsTUFBc0I7UUFDaEQsOERBQThEO1FBQzlELFFBQVEsTUFBTSxDQUFDLElBQUksRUFBRSxDQUFDO1lBQ3BCLEtBQUssV0FBVztnQkFDZCxPQUFPLElBQUkscUJBQXFCLENBQUMsTUFBTSxDQUFDLENBQUM7WUFFM0MsS0FBSyxtQkFBbUI7Z0JBQ3RCLE9BQU8sSUFBSSx1QkFBdUIsQ0FBQyxNQUFNLENBQUMsQ0FBQztZQUU3QyxLQUFLLHlCQUF5QjtnQkFDNUIsT0FBTyxJQUFJLDJCQUEyQixDQUFDLE1BQU0sQ0FBQyxDQUFDO1lBRWpELEtBQUssMEJBQTBCO2dCQUM3QixPQUFPLElBQUksNEJBQTRCLENBQUMsTUFBTSxDQUFDLENBQUM7WUFFbEQ7Z0JBQ0UscURBQXFEO2dCQUNyRCxNQUFNLElBQUksS0FBSyxDQUFDLDRCQUE2QixNQUFjLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQztRQUN4RSxDQUFDO0lBQ0gsQ0FBQztJQUVEOzs7O09BSUc7SUFDSSxNQUFNLENBQUMsYUFBYSxDQUFDLE1BQXNCO1FBQ2hELDBDQUEwQztRQUMxQyxNQUFNLE1BQU0sR0FBbUIsSUFBSSxDQUFDLEtB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
@@ -0,0 +1,55 @@
1
+ import * as plugins from '../../plugins.js';
2
+ import type { IForwardConfig, IForwardingHandler } from '../types/forwarding.types.js';
3
+ /**
4
+ * Base class for all forwarding handlers
5
+ */
6
+ export declare abstract class ForwardingHandler extends plugins.EventEmitter implements IForwardingHandler {
7
+ protected config: IForwardConfig;
8
+ /**
9
+ * Create a new ForwardingHandler
10
+ * @param config The forwarding configuration
11
+ */
12
+ constructor(config: IForwardConfig);
13
+ /**
14
+ * Initialize the handler
15
+ * Base implementation does nothing, subclasses should override as needed
16
+ */
17
+ initialize(): Promise<void>;
18
+ /**
19
+ * Handle a new socket connection
20
+ * @param socket The incoming socket connection
21
+ */
22
+ abstract handleConnection(socket: plugins.net.Socket): void;
23
+ /**
24
+ * Handle an HTTP request
25
+ * @param req The HTTP request
26
+ * @param res The HTTP response
27
+ */
28
+ abstract handleHttpRequest(req: plugins.http.IncomingMessage, res: plugins.http.ServerResponse): void;
29
+ /**
30
+ * Get a target from the configuration, supporting round-robin selection
31
+ * @returns A resolved target object with host and port
32
+ */
33
+ protected getTargetFromConfig(): {
34
+ host: string;
35
+ port: number;
36
+ };
37
+ /**
38
+ * Redirect an HTTP request to HTTPS
39
+ * @param req The HTTP request
40
+ * @param res The HTTP response
41
+ */
42
+ protected redirectToHttps(req: plugins.http.IncomingMessage, res: plugins.http.ServerResponse): void;
43
+ /**
44
+ * Apply custom headers from configuration
45
+ * @param headers The original headers
46
+ * @param variables Variables to replace in the headers
47
+ * @returns The headers with custom values applied
48
+ */
49
+ protected applyCustomHeaders(headers: Record<string, string | string[] | undefined>, variables: Record<string, string>): Record<string, string | string[] | undefined>;
50
+ /**
51
+ * Get the timeout for this connection from configuration
52
+ * @returns Timeout in milliseconds
53
+ */
54
+ protected getTimeout(): number;
55
+ }
@@ -0,0 +1,94 @@
1
+ import * as plugins from '../../plugins.js';
2
+ import { ForwardingHandlerEvents } from '../types/forwarding.types.js';
3
+ /**
4
+ * Base class for all forwarding handlers
5
+ */
6
+ export class ForwardingHandler extends plugins.EventEmitter {
7
+ /**
8
+ * Create a new ForwardingHandler
9
+ * @param config The forwarding configuration
10
+ */
11
+ constructor(config) {
12
+ super();
13
+ this.config = config;
14
+ }
15
+ /**
16
+ * Initialize the handler
17
+ * Base implementation does nothing, subclasses should override as needed
18
+ */
19
+ async initialize() {
20
+ // Base implementation - no initialization needed
21
+ }
22
+ /**
23
+ * Get a target from the configuration, supporting round-robin selection
24
+ * @returns A resolved target object with host and port
25
+ */
26
+ getTargetFromConfig() {
27
+ const { target } = this.config;
28
+ // Handle round-robin host selection
29
+ if (Array.isArray(target.host)) {
30
+ if (target.host.length === 0) {
31
+ throw new Error('No target hosts specified');
32
+ }
33
+ // Simple round-robin selection
34
+ const randomIndex = Math.floor(Math.random() * target.host.length);
35
+ return {
36
+ host: target.host[randomIndex],
37
+ port: target.port
38
+ };
39
+ }
40
+ // Single host
41
+ return {
42
+ host: target.host,
43
+ port: target.port
44
+ };
45
+ }
46
+ /**
47
+ * Redirect an HTTP request to HTTPS
48
+ * @param req The HTTP request
49
+ * @param res The HTTP response
50
+ */
51
+ redirectToHttps(req, res) {
52
+ const host = req.headers.host || '';
53
+ const path = req.url || '/';
54
+ const redirectUrl = `https://${host}${path}`;
55
+ res.writeHead(301, {
56
+ 'Location': redirectUrl,
57
+ 'Cache-Control': 'no-cache'
58
+ });
59
+ res.end(`Redirecting to ${redirectUrl}`);
60
+ this.emit(ForwardingHandlerEvents.HTTP_RESPONSE, {
61
+ statusCode: 301,
62
+ headers: { 'Location': redirectUrl },
63
+ size: 0
64
+ });
65
+ }
66
+ /**
67
+ * Apply custom headers from configuration
68
+ * @param headers The original headers
69
+ * @param variables Variables to replace in the headers
70
+ * @returns The headers with custom values applied
71
+ */
72
+ applyCustomHeaders(headers, variables) {
73
+ const customHeaders = this.config.advanced?.headers || {};
74
+ const result = { ...headers };
75
+ // Apply custom headers with variable substitution
76
+ for (const [key, value] of Object.entries(customHeaders)) {
77
+ let processedValue = value;
78
+ // Replace variables in the header value
79
+ for (const [varName, varValue] of Object.entries(variables)) {
80
+ processedValue = processedValue.replace(`{${varName}}`, varValue);
81
+ }
82
+ result[key] = processedValue;
83
+ }
84
+ return result;
85
+ }
86
+ /**
87
+ * Get the timeout for this connection from configuration
88
+ * @returns Timeout in milliseconds
89
+ */
90
+ getTimeout() {
91
+ return this.config.advanced?.timeout || 60000; // Default: 60 seconds
92
+ }
93
+ }
94
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZm9yd2FyZGluZy5oYW5kbGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vdHMvc21hcnRwcm94eS9mb3J3YXJkaW5nL2ZvcndhcmRpbmcuaGFuZGxlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGtCQUFrQixDQUFDO0FBSzVDLE9BQU8sRUFBRSx1QkFBdUIsRUFBRSxNQUFNLDhCQUE4QixDQUFDO0FBRXZFOztHQUVHO0FBQ0gsTUFBTSxPQUFnQixpQkFBa0IsU0FBUSxPQUFPLENBQUMsWUFBWTtJQUNsRTs7O09BR0c7SUFDSCxZQUFzQixNQUFzQjtRQUMxQyxLQUFLLEVBQUUsQ0FBQztRQURZLFdBQU0sR0FBTixNQUFNLENBQWdCO0lBRTVDLENBQUM7SUFFRDs7O09BR0c7SUFDSSxLQUFLLENBQUMsVUFBVTtRQUNyQixpREFBaUQ7SUFDbkQsQ0FBQztJQWVEOzs7T0FHRztJQUNPLG1CQUFtQjtRQUMzQixNQUFNLEVBQUUsTUFBTSxFQUFFLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQztRQUUvQixvQ0FBb0M7UUFDcEMsSUFBSSxLQUFLLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDO1lBQy9CLElBQUksTUFBTSxDQUFDLElBQUksQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFLENBQUM7Z0JBQzdCLE1BQU0sSUFBSSxLQUFLLENBQUMsMkJBQTJCLENBQUMsQ0FBQztZQUMvQyxDQUFDO1lBRUQsK0JBQStCO1lBQy9CLE1BQU0sV0FBVyxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLE1BQU0sRUFBRSxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUM7WUFDbkUsT0FBTztnQkFDTCxJQUFJLEVBQUUsTUFBTSxDQUFDLElBQUksQ0FBQyxXQUFXLENBQUM7Z0JBQzlCLElBQUksRUFBRSxNQUFNLENBQUMsSUFBSTthQUNsQixDQUFDO1FBQ0osQ0FBQztRQUVELGNBQWM7UUFDZCxPQUFPO1lBQ0wsSUFBSSxFQUFFLE1BQU0sQ0FBQyxJQUFJO1lBQ2pCLElBQUksRUFBRSxNQUFNLENBQUMsSUFBSTtTQUNsQixDQUFDO0lBQ0osQ0FBQztJQUVEOzs7O09BSUc7SUFDTyxlQUFlLENBQUMsR0FBaUMsRUFBRSxHQUFnQztRQUMzRixNQUFNLElBQUksR0FBRyxHQUFHLENBQUMsT0FBTyxDQUFDLElBQUksSUFBSSxFQUFFLENBQUM7UUFDcEMsTUFBTSxJQUFJLEdBQUcsR0FBRyxDQUFDLEdBQUcsSUFBSSxHQUFHLENBQUM7UUFDNUIsTUFBTSxXQUFXLEdBQUcsV0FBVyxJQUFJLEdBQUcsSUFBSSxFQUFFLENBQUM7UUFFN0MsR0FBRyxDQUFDLFNBQVMsQ0FBQyxHQUFHLEVBQUU7WUFDakIsVUFBVSxFQUFFLFdBQVc7WUFDdkIsZUFBZSxFQUFFLFVBQVU7U0FDNUIsQ0FBQyxDQUFDO1FBQ0gsR0FBRyxDQUFDLEdBQUcsQ0FBQyxrQkFBa0IsV0FBVyxFQUFFLENBQUMsQ0FBQztRQUV6QyxJQUFJLENBQUMsSUFBSSxDQUFDLHVCQUF1QixDQUFDLGFBQWEsRUFBRTtZQUMvQyxVQUFVLEVBQUUsR0FBRztZQUNmLE9BQU8sRUFBRSxFQUFFLFVBQVUsRUFBRSxXQUFXLEVBQUU7WUFDcEMsSUFBSSxFQUFFLENBQUM7U0FDUixDQUFDLENBQUM7SUFDTCxDQUFDO0lBRUQ7Ozs7O09BS0c7SUFDTyxrQkFBa0IsQ0FDMUIsT0FBc0QsRUFDdEQsU0FBaUM7UUFFakMsTUFBTSxhQUFhLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQyxRQUFRLEVBQUUsT0FBTyxJQUFJLEVBQUUsQ0FBQztRQUMxRCxNQUFNLE1BQU0sR0FBRyxFQUFFLEdBQUcsT0FBTyxFQUFFLENBQUM7UUFFOUIsa0RBQWtEO1FBQ2xELEtBQUssTUFBTSxDQUFDLEdBQUcsRUFBRSxLQUFLLENBQUMsSUFBSSxNQUFNLENBQUMsT0FBTyxDQUFDLGFBQWEsQ0FBQyxFQUFFLENBQUM7WUFDekQsSUFBSSxjQUFjLEdBQUcsS0FBSyxDQUFDO1lBRTNCLHdDQUF3QztZQUN4QyxLQUFLLE1BQU0sQ0FBQyxPQUFPLEVBQUUsUUFBUSxDQUFDLElBQUksTUFBTSxDQUFDLE9BQU8sQ0FBQyxTQUFTLENBQUMsRUFBRSxDQUFDO2dCQUM1RCxjQUFjLEdBQUcsY0FBYyxDQUFDLE9BQU8sQ0FBQyxJQUFJLE9BQU8sR0FBRyxFQUFFLFFBQVEsQ0FBQyxDQUFDO1lBQ3BFLENBQUM7WUFFRCxNQUFNLENBQUMsR0FBRyxDQUFDLEdBQUcsY0FBYyxDQUFDO1FBQy9CLENBQUM7UUFFRCxPQUFPLE1BQU0sQ0FBQztJQUNoQixDQUFDO0lBRUQ7OztPQUdHO0lBQ08sVUFBVTtRQUNsQixPQUFPLElBQUksQ0FBQyxNQUFNLENBQUMsUUFBUSxFQUFFLE9BQU8sSUFBSSxLQUFLLENBQUMsQ0FBQyxzQkFBc0I7SUFDdkUsQ0FBQztDQUNGIn0=
@@ -0,0 +1,25 @@
1
+ import * as plugins from '../../plugins.js';
2
+ import { ForwardingHandler } from './forwarding.handler.js';
3
+ import type { IForwardConfig } from '../types/forwarding.types.js';
4
+ /**
5
+ * Handler for HTTP-only forwarding
6
+ */
7
+ export declare class HttpForwardingHandler extends ForwardingHandler {
8
+ /**
9
+ * Create a new HTTP forwarding handler
10
+ * @param config The forwarding configuration
11
+ */
12
+ constructor(config: IForwardConfig);
13
+ /**
14
+ * Handle a raw socket connection
15
+ * HTTP handler doesn't do much with raw sockets as it mainly processes
16
+ * parsed HTTP requests
17
+ */
18
+ handleConnection(socket: plugins.net.Socket): void;
19
+ /**
20
+ * Handle an HTTP request
21
+ * @param req The HTTP request
22
+ * @param res The HTTP response
23
+ */
24
+ handleHttpRequest(req: plugins.http.IncomingMessage, res: plugins.http.ServerResponse): void;
25
+ }
@@ -0,0 +1,123 @@
1
+ import * as plugins from '../../plugins.js';
2
+ import { ForwardingHandler } from './forwarding.handler.js';
3
+ import { ForwardingHandlerEvents } from '../types/forwarding.types.js';
4
+ /**
5
+ * Handler for HTTP-only forwarding
6
+ */
7
+ export class HttpForwardingHandler extends ForwardingHandler {
8
+ /**
9
+ * Create a new HTTP forwarding handler
10
+ * @param config The forwarding configuration
11
+ */
12
+ constructor(config) {
13
+ super(config);
14
+ // Validate that this is an HTTP-only configuration
15
+ if (config.type !== 'http-only') {
16
+ throw new Error(`Invalid configuration type for HttpForwardingHandler: ${config.type}`);
17
+ }
18
+ }
19
+ /**
20
+ * Handle a raw socket connection
21
+ * HTTP handler doesn't do much with raw sockets as it mainly processes
22
+ * parsed HTTP requests
23
+ */
24
+ handleConnection(socket) {
25
+ // For HTTP, we mainly handle parsed requests, but we can still set up
26
+ // some basic connection tracking
27
+ const remoteAddress = socket.remoteAddress || 'unknown';
28
+ socket.on('close', (hadError) => {
29
+ this.emit(ForwardingHandlerEvents.DISCONNECTED, {
30
+ remoteAddress,
31
+ hadError
32
+ });
33
+ });
34
+ socket.on('error', (error) => {
35
+ this.emit(ForwardingHandlerEvents.ERROR, {
36
+ remoteAddress,
37
+ error: error.message
38
+ });
39
+ });
40
+ this.emit(ForwardingHandlerEvents.CONNECTED, {
41
+ remoteAddress
42
+ });
43
+ }
44
+ /**
45
+ * Handle an HTTP request
46
+ * @param req The HTTP request
47
+ * @param res The HTTP response
48
+ */
49
+ handleHttpRequest(req, res) {
50
+ // Get the target from configuration
51
+ const target = this.getTargetFromConfig();
52
+ // Create a custom headers object with variables for substitution
53
+ const variables = {
54
+ clientIp: req.socket.remoteAddress || 'unknown'
55
+ };
56
+ // Prepare headers, merging with any custom headers from config
57
+ const headers = this.applyCustomHeaders(req.headers, variables);
58
+ // Create the proxy request options
59
+ const options = {
60
+ hostname: target.host,
61
+ port: target.port,
62
+ path: req.url,
63
+ method: req.method,
64
+ headers
65
+ };
66
+ // Create the proxy request
67
+ const proxyReq = plugins.http.request(options, (proxyRes) => {
68
+ // Copy status code and headers from the proxied response
69
+ res.writeHead(proxyRes.statusCode || 500, proxyRes.headers);
70
+ // Pipe the proxy response to the client response
71
+ proxyRes.pipe(res);
72
+ // Track bytes for logging
73
+ let responseSize = 0;
74
+ proxyRes.on('data', (chunk) => {
75
+ responseSize += chunk.length;
76
+ });
77
+ proxyRes.on('end', () => {
78
+ this.emit(ForwardingHandlerEvents.HTTP_RESPONSE, {
79
+ statusCode: proxyRes.statusCode,
80
+ headers: proxyRes.headers,
81
+ size: responseSize
82
+ });
83
+ });
84
+ });
85
+ // Handle errors in the proxy request
86
+ proxyReq.on('error', (error) => {
87
+ this.emit(ForwardingHandlerEvents.ERROR, {
88
+ remoteAddress: req.socket.remoteAddress,
89
+ error: `Proxy request error: ${error.message}`
90
+ });
91
+ // Send an error response if headers haven't been sent yet
92
+ if (!res.headersSent) {
93
+ res.writeHead(502, { 'Content-Type': 'text/plain' });
94
+ res.end(`Error forwarding request: ${error.message}`);
95
+ }
96
+ else {
97
+ // Just end the response if headers have already been sent
98
+ res.end();
99
+ }
100
+ });
101
+ // Track request details for logging
102
+ let requestSize = 0;
103
+ req.on('data', (chunk) => {
104
+ requestSize += chunk.length;
105
+ });
106
+ // Log the request
107
+ this.emit(ForwardingHandlerEvents.HTTP_REQUEST, {
108
+ method: req.method,
109
+ url: req.url,
110
+ headers: req.headers,
111
+ remoteAddress: req.socket.remoteAddress,
112
+ target: `${target.host}:${target.port}`
113
+ });
114
+ // Pipe the client request to the proxy request
115
+ if (req.readable) {
116
+ req.pipe(proxyReq);
117
+ }
118
+ else {
119
+ proxyReq.end();
120
+ }
121
+ }
122
+ }
123
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaHR0cC5oYW5kbGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vdHMvc21hcnRwcm94eS9mb3J3YXJkaW5nL2h0dHAuaGFuZGxlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGtCQUFrQixDQUFDO0FBQzVDLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBRTVELE9BQU8sRUFBRSx1QkFBdUIsRUFBRSxNQUFNLDhCQUE4QixDQUFDO0FBRXZFOztHQUVHO0FBQ0gsTUFBTSxPQUFPLHFCQUFzQixTQUFRLGlCQUFpQjtJQUMxRDs7O09BR0c7SUFDSCxZQUFZLE1BQXNCO1FBQ2hDLEtBQUssQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUVkLG1EQUFtRDtRQUNuRCxJQUFJLE1BQU0sQ0FBQyxJQUFJLEtBQUssV0FBVyxFQUFFLENBQUM7WUFDaEMsTUFBTSxJQUFJLEtBQUssQ0FBQyx5REFBeUQsTUFBTSxDQUFDLElBQUksRUFBRSxDQUFDLENBQUM7UUFDMUYsQ0FBQztJQUNILENBQUM7SUFFRDs7OztPQUlHO0lBQ0ksZ0JBQWdCLENBQUMsTUFBMEI7UUFDaEQsc0VBQXNFO1FBQ3RFLGlDQUFpQztRQUNqQyxNQUFNLGFBQWEsR0FBRyxNQUFNLENBQUMsYUFBYSxJQUFJLFNBQVMsQ0FBQztRQUV4RCxNQUFNLENBQUMsRUFBRSxDQUFDLE9BQU8sRUFBRSxDQUFDLFFBQVEsRUFBRSxFQUFFO1lBQzlCLElBQUksQ0FBQyxJQUFJLENBQUMsdUJBQXVCLENBQUMsWUFBWSxFQUFFO2dCQUM5QyxhQUFhO2dCQUNiLFFBQVE7YUFDVCxDQUFDLENBQUM7UUFDTCxDQUFDLENBQUMsQ0FBQztRQUVILE1BQU0sQ0FBQyxFQUFFLENBQUMsT0FBTyxFQUFFLENBQUMsS0FBSyxFQUFFLEVBQUU7WUFDM0IsSUFBSSxDQUFDLElBQUksQ0FBQyx1QkFBdUIsQ0FBQyxLQUFLLEVBQUU7Z0JBQ3ZD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
@@ -0,0 +1,24 @@
1
+ import * as plugins from '../../plugins.js';
2
+ import { ForwardingHandler } from './forwarding.handler.js';
3
+ import type { IForwardConfig } from '../types/forwarding.types.js';
4
+ /**
5
+ * Handler for HTTPS passthrough (SNI forwarding without termination)
6
+ */
7
+ export declare class HttpsPassthroughHandler extends ForwardingHandler {
8
+ /**
9
+ * Create a new HTTPS passthrough handler
10
+ * @param config The forwarding configuration
11
+ */
12
+ constructor(config: IForwardConfig);
13
+ /**
14
+ * Handle a TLS/SSL socket connection by forwarding it without termination
15
+ * @param clientSocket The incoming socket from the client
16
+ */
17
+ handleConnection(clientSocket: plugins.net.Socket): void;
18
+ /**
19
+ * Handle an HTTP request - HTTPS passthrough doesn't support HTTP
20
+ * @param req The HTTP request
21
+ * @param res The HTTP response
22
+ */
23
+ handleHttpRequest(req: plugins.http.IncomingMessage, res: plugins.http.ServerResponse): void;
24
+ }
@@ -0,0 +1,154 @@
1
+ import * as plugins from '../../plugins.js';
2
+ import { ForwardingHandler } from './forwarding.handler.js';
3
+ import { ForwardingHandlerEvents } from '../types/forwarding.types.js';
4
+ /**
5
+ * Handler for HTTPS passthrough (SNI forwarding without termination)
6
+ */
7
+ export class HttpsPassthroughHandler extends ForwardingHandler {
8
+ /**
9
+ * Create a new HTTPS passthrough handler
10
+ * @param config The forwarding configuration
11
+ */
12
+ constructor(config) {
13
+ super(config);
14
+ // Validate that this is an HTTPS passthrough configuration
15
+ if (config.type !== 'https-passthrough') {
16
+ throw new Error(`Invalid configuration type for HttpsPassthroughHandler: ${config.type}`);
17
+ }
18
+ }
19
+ /**
20
+ * Handle a TLS/SSL socket connection by forwarding it without termination
21
+ * @param clientSocket The incoming socket from the client
22
+ */
23
+ handleConnection(clientSocket) {
24
+ // Get the target from configuration
25
+ const target = this.getTargetFromConfig();
26
+ // Log the connection
27
+ const remoteAddress = clientSocket.remoteAddress || 'unknown';
28
+ const remotePort = clientSocket.remotePort || 0;
29
+ this.emit(ForwardingHandlerEvents.CONNECTED, {
30
+ remoteAddress,
31
+ remotePort,
32
+ target: `${target.host}:${target.port}`
33
+ });
34
+ // Create a connection to the target server
35
+ const serverSocket = plugins.net.connect(target.port, target.host);
36
+ // Handle errors on the server socket
37
+ serverSocket.on('error', (error) => {
38
+ this.emit(ForwardingHandlerEvents.ERROR, {
39
+ remoteAddress,
40
+ error: `Target connection error: ${error.message}`
41
+ });
42
+ // Close the client socket if it's still open
43
+ if (!clientSocket.destroyed) {
44
+ clientSocket.destroy();
45
+ }
46
+ });
47
+ // Handle errors on the client socket
48
+ clientSocket.on('error', (error) => {
49
+ this.emit(ForwardingHandlerEvents.ERROR, {
50
+ remoteAddress,
51
+ error: `Client connection error: ${error.message}`
52
+ });
53
+ // Close the server socket if it's still open
54
+ if (!serverSocket.destroyed) {
55
+ serverSocket.destroy();
56
+ }
57
+ });
58
+ // Track data transfer for logging
59
+ let bytesSent = 0;
60
+ let bytesReceived = 0;
61
+ // Forward data from client to server
62
+ clientSocket.on('data', (data) => {
63
+ bytesSent += data.length;
64
+ // Check if server socket is writable
65
+ if (serverSocket.writable) {
66
+ const flushed = serverSocket.write(data);
67
+ // Handle backpressure
68
+ if (!flushed) {
69
+ clientSocket.pause();
70
+ serverSocket.once('drain', () => {
71
+ clientSocket.resume();
72
+ });
73
+ }
74
+ }
75
+ this.emit(ForwardingHandlerEvents.DATA_FORWARDED, {
76
+ direction: 'outbound',
77
+ bytes: data.length,
78
+ total: bytesSent
79
+ });
80
+ });
81
+ // Forward data from server to client
82
+ serverSocket.on('data', (data) => {
83
+ bytesReceived += data.length;
84
+ // Check if client socket is writable
85
+ if (clientSocket.writable) {
86
+ const flushed = clientSocket.write(data);
87
+ // Handle backpressure
88
+ if (!flushed) {
89
+ serverSocket.pause();
90
+ clientSocket.once('drain', () => {
91
+ serverSocket.resume();
92
+ });
93
+ }
94
+ }
95
+ this.emit(ForwardingHandlerEvents.DATA_FORWARDED, {
96
+ direction: 'inbound',
97
+ bytes: data.length,
98
+ total: bytesReceived
99
+ });
100
+ });
101
+ // Handle connection close
102
+ const handleClose = () => {
103
+ if (!clientSocket.destroyed) {
104
+ clientSocket.destroy();
105
+ }
106
+ if (!serverSocket.destroyed) {
107
+ serverSocket.destroy();
108
+ }
109
+ this.emit(ForwardingHandlerEvents.DISCONNECTED, {
110
+ remoteAddress,
111
+ bytesSent,
112
+ bytesReceived
113
+ });
114
+ };
115
+ // Set up close handlers
116
+ clientSocket.on('close', handleClose);
117
+ serverSocket.on('close', handleClose);
118
+ // Set timeouts
119
+ const timeout = this.getTimeout();
120
+ clientSocket.setTimeout(timeout);
121
+ serverSocket.setTimeout(timeout);
122
+ // Handle timeouts
123
+ clientSocket.on('timeout', () => {
124
+ this.emit(ForwardingHandlerEvents.ERROR, {
125
+ remoteAddress,
126
+ error: 'Client connection timeout'
127
+ });
128
+ handleClose();
129
+ });
130
+ serverSocket.on('timeout', () => {
131
+ this.emit(ForwardingHandlerEvents.ERROR, {
132
+ remoteAddress,
133
+ error: 'Server connection timeout'
134
+ });
135
+ handleClose();
136
+ });
137
+ }
138
+ /**
139
+ * Handle an HTTP request - HTTPS passthrough doesn't support HTTP
140
+ * @param req The HTTP request
141
+ * @param res The HTTP response
142
+ */
143
+ handleHttpRequest(req, res) {
144
+ // HTTPS passthrough doesn't support HTTP requests
145
+ res.writeHead(404, { 'Content-Type': 'text/plain' });
146
+ res.end('HTTP not supported for this domain');
147
+ this.emit(ForwardingHandlerEvents.HTTP_RESPONSE, {
148
+ statusCode: 404,
149
+ headers: { 'Content-Type': 'text/plain' },
150
+ size: 'HTTP not supported for this domain'.length
151
+ });
152
+ }
153
+ }
154
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaHR0cHMtcGFzc3Rocm91Z2guaGFuZGxlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3RzL3NtYXJ0cHJveHkvZm9yd2FyZGluZy9odHRwcy1wYXNzdGhyb3VnaC5oYW5kbGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sa0JBQWtCLENBQUM7QUFDNUMsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFFNUQsT0FBTyxFQUFFLHVCQUF1QixFQUFFLE1BQU0sOEJBQThCLENBQUM7QUFFdkU7O0dBRUc7QUFDSCxNQUFNLE9BQU8sdUJBQXdCLFNBQVEsaUJBQWlCO0lBQzVEOzs7T0FHRztJQUNILFlBQVksTUFBc0I7UUFDaEMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBRWQsMkRBQTJEO1FBQzNELElBQUksTUFBTSxDQUFDLElBQUksS0FBSyxtQkFBbUIsRUFBRSxDQUFDO1lBQ3hDLE1BQU0sSUFBSSxLQUFLLENBQUMsMkRBQTJELE1BQU0sQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDO1FBQzVGLENBQUM7SUFDSCxDQUFDO0lBRUQ7OztPQUdHO0lBQ0ksZ0JBQWdCLENBQUMsWUFBZ0M7UUFDdEQsb0NBQW9DO1FBQ3BDLE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxtQkFBbUIsRUFBRSxDQUFDO1FBRTFDLHFCQUFxQjtRQUNyQixNQUFNLGFBQWEsR0FBRyxZQUFZLENBQUMsYUFBYSxJQUFJLFNBQVMsQ0FBQztRQUM5RCxNQUFNLFVBQVUsR0FBRyxZQUFZLENBQUMsVUFBVSxJQUFJLENBQUMsQ0FBQztRQUVoRCxJQUFJLENBQUMsSUFBSSxDQUFDLHVCQUF1QixDQUFDLFNBQVMsRUFBRTtZQUMzQyxhQUFhO1lBQ2IsVUFBVTtZQUNWLE1BQU0sRUFBRSxHQUFHLE1BQU0sQ0FBQyxJQUFJLElBQUksTUFBTSxDQUFDLElBQUksRUFBRTtTQUN4QyxDQUFDLENBQUM7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