@push.rocks/smartproxy 25.11.24 → 25.13.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 (39) hide show
  1. package/changelog.md +16 -0
  2. package/dist_rust/rustproxy_linux_amd64 +0 -0
  3. package/dist_rust/rustproxy_linux_arm64 +0 -0
  4. package/dist_ts/00_commitinfo_data.js +2 -2
  5. package/dist_ts/core/utils/index.d.ts +0 -1
  6. package/dist_ts/core/utils/index.js +1 -2
  7. package/dist_ts/protocols/proxy/index.d.ts +1 -2
  8. package/dist_ts/protocols/proxy/index.js +2 -3
  9. package/dist_ts/protocols/proxy/types.d.ts +1 -1
  10. package/dist_ts/proxies/smart-proxy/datagram-handler-server.d.ts +46 -0
  11. package/dist_ts/proxies/smart-proxy/datagram-handler-server.js +197 -0
  12. package/dist_ts/proxies/smart-proxy/models/metrics-types.d.ts +6 -0
  13. package/dist_ts/proxies/smart-proxy/models/route-types.d.ts +63 -4
  14. package/dist_ts/proxies/smart-proxy/models/route-types.js +1 -1
  15. package/dist_ts/proxies/smart-proxy/route-preprocessor.js +7 -2
  16. package/dist_ts/proxies/smart-proxy/rust-metrics-adapter.d.ts +6 -0
  17. package/dist_ts/proxies/smart-proxy/rust-metrics-adapter.js +7 -1
  18. package/dist_ts/proxies/smart-proxy/rust-proxy-bridge.d.ts +1 -0
  19. package/dist_ts/proxies/smart-proxy/rust-proxy-bridge.js +4 -1
  20. package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +1 -0
  21. package/dist_ts/proxies/smart-proxy/smart-proxy.js +19 -1
  22. package/package.json +1 -1
  23. package/ts/00_commitinfo_data.ts +1 -1
  24. package/ts/core/utils/index.ts +0 -1
  25. package/ts/protocols/proxy/index.ts +2 -3
  26. package/ts/protocols/proxy/types.ts +1 -1
  27. package/ts/proxies/smart-proxy/datagram-handler-server.ts +239 -0
  28. package/ts/proxies/smart-proxy/models/metrics-types.ts +8 -0
  29. package/ts/proxies/smart-proxy/models/route-types.ts +83 -5
  30. package/ts/proxies/smart-proxy/route-preprocessor.ts +7 -1
  31. package/ts/proxies/smart-proxy/rust-metrics-adapter.ts +7 -0
  32. package/ts/proxies/smart-proxy/rust-proxy-bridge.ts +5 -0
  33. package/ts/proxies/smart-proxy/smart-proxy.ts +23 -0
  34. package/dist_ts/core/utils/proxy-protocol.d.ts +0 -33
  35. package/dist_ts/core/utils/proxy-protocol.js +0 -117
  36. package/dist_ts/protocols/proxy/parser.d.ts +0 -44
  37. package/dist_ts/protocols/proxy/parser.js +0 -153
  38. package/ts/core/utils/proxy-protocol.ts +0 -129
  39. package/ts/protocols/proxy/parser.ts +0 -183
@@ -5,6 +5,7 @@ import { logger } from '../../core/utils/logger.js';
5
5
  import { RustProxyBridge } from './rust-proxy-bridge.js';
6
6
  import { RoutePreprocessor } from './route-preprocessor.js';
7
7
  import { SocketHandlerServer } from './socket-handler-server.js';
8
+ import { DatagramHandlerServer } from './datagram-handler-server.js';
8
9
  import { RustMetricsAdapter } from './rust-metrics-adapter.js';
9
10
 
10
11
  // Route management
@@ -36,6 +37,7 @@ export class SmartProxy extends plugins.EventEmitter {
36
37
  private bridge: RustProxyBridge;
37
38
  private preprocessor: RoutePreprocessor;
38
39
  private socketHandlerServer: SocketHandlerServer | null = null;
40
+ private datagramHandlerServer: DatagramHandlerServer | null = null;
39
41
  private metricsAdapter: RustMetricsAdapter;
40
42
  private routeUpdateLock: Mutex;
41
43
  private stopping = false;
@@ -145,6 +147,16 @@ export class SmartProxy extends plugins.EventEmitter {
145
147
  await this.socketHandlerServer.start();
146
148
  }
147
149
 
150
+ // Check if any routes need datagram handler relay (UDP socket-handler routes)
151
+ const hasDatagramHandlers = this.settings.routes.some(
152
+ (r) => r.action.type === 'socket-handler' && r.action.datagramHandler
153
+ );
154
+ if (hasDatagramHandlers) {
155
+ const dgPath = `/tmp/smartproxy-dgram-relay-${process.pid}.sock`;
156
+ this.datagramHandlerServer = new DatagramHandlerServer(dgPath, this.preprocessor);
157
+ await this.datagramHandlerServer.start();
158
+ }
159
+
148
160
  // Preprocess routes (strip JS functions, convert socket-handler routes)
149
161
  const rustRoutes = this.preprocessor.preprocessForRust(this.settings.routes);
150
162
 
@@ -167,6 +179,11 @@ export class SmartProxy extends plugins.EventEmitter {
167
179
  await this.bridge.setSocketHandlerRelay(this.socketHandlerServer.getSocketPath());
168
180
  }
169
181
 
182
+ // Configure datagram handler relay
183
+ if (this.datagramHandlerServer) {
184
+ await this.bridge.setDatagramHandlerRelay(this.datagramHandlerServer.getSocketPath());
185
+ }
186
+
170
187
  // Load default self-signed fallback certificate (domain: '*')
171
188
  if (!this.settings.disableDefaultCert) {
172
189
  try {
@@ -240,6 +257,12 @@ export class SmartProxy extends plugins.EventEmitter {
240
257
  this.socketHandlerServer = null;
241
258
  }
242
259
 
260
+ // Stop datagram handler relay
261
+ if (this.datagramHandlerServer) {
262
+ await this.datagramHandlerServer.stop();
263
+ this.datagramHandlerServer = null;
264
+ }
265
+
243
266
  logger.log('info', 'SmartProxy shutdown complete.', { component: 'smart-proxy' });
244
267
  }
245
268
 
@@ -1,33 +0,0 @@
1
- import * as plugins from '../../plugins.js';
2
- import { type IProxyInfo, type IProxyParseResult } from '../../protocols/proxy/index.js';
3
- export type { IProxyInfo, IProxyParseResult } from '../../protocols/proxy/index.js';
4
- /**
5
- * Parser for PROXY protocol v1 (text format)
6
- * Spec: https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
7
- *
8
- * This class now delegates to the protocol parser but adds
9
- * smartproxy-specific features like socket reading and logging
10
- */
11
- export declare class ProxyProtocolParser {
12
- static readonly PROXY_V1_SIGNATURE = "PROXY ";
13
- static readonly MAX_HEADER_LENGTH = 107;
14
- static readonly HEADER_TERMINATOR = "\r\n";
15
- /**
16
- * Parse PROXY protocol v1 header from buffer
17
- * Returns proxy info and remaining data after header
18
- */
19
- static parse(data: Buffer): IProxyParseResult;
20
- /**
21
- * Generate PROXY protocol v1 header
22
- */
23
- static generate(info: IProxyInfo): Buffer;
24
- /**
25
- * Validate IP address format
26
- */
27
- private static isValidIP;
28
- /**
29
- * Attempt to read a complete PROXY protocol header from a socket
30
- * Returns null if no PROXY protocol detected or incomplete
31
- */
32
- static readFromSocket(socket: plugins.net.Socket, timeout?: number): Promise<IProxyParseResult | null>;
33
- }
@@ -1,117 +0,0 @@
1
- import * as plugins from '../../plugins.js';
2
- import { logger } from './logger.js';
3
- import { ProxyProtocolParser as ProtocolParser } from '../../protocols/proxy/index.js';
4
- /**
5
- * Parser for PROXY protocol v1 (text format)
6
- * Spec: https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
7
- *
8
- * This class now delegates to the protocol parser but adds
9
- * smartproxy-specific features like socket reading and logging
10
- */
11
- export class ProxyProtocolParser {
12
- static { this.PROXY_V1_SIGNATURE = ProtocolParser.PROXY_V1_SIGNATURE; }
13
- static { this.MAX_HEADER_LENGTH = ProtocolParser.MAX_HEADER_LENGTH; }
14
- static { this.HEADER_TERMINATOR = ProtocolParser.HEADER_TERMINATOR; }
15
- /**
16
- * Parse PROXY protocol v1 header from buffer
17
- * Returns proxy info and remaining data after header
18
- */
19
- static parse(data) {
20
- // Delegate to protocol parser
21
- return ProtocolParser.parse(data);
22
- }
23
- /**
24
- * Generate PROXY protocol v1 header
25
- */
26
- static generate(info) {
27
- // Delegate to protocol parser
28
- return ProtocolParser.generate(info);
29
- }
30
- /**
31
- * Validate IP address format
32
- */
33
- static isValidIP(ip, protocol) {
34
- return ProtocolParser.isValidIP(ip, protocol);
35
- }
36
- /**
37
- * Attempt to read a complete PROXY protocol header from a socket
38
- * Returns null if no PROXY protocol detected or incomplete
39
- */
40
- static async readFromSocket(socket, timeout = 5000) {
41
- return new Promise((resolve) => {
42
- let buffer = Buffer.alloc(0);
43
- let resolved = false;
44
- const cleanup = () => {
45
- socket.removeListener('data', onData);
46
- socket.removeListener('error', onError);
47
- clearTimeout(timer);
48
- };
49
- const timer = setTimeout(() => {
50
- if (!resolved) {
51
- resolved = true;
52
- cleanup();
53
- resolve({
54
- proxyInfo: null,
55
- remainingData: buffer
56
- });
57
- }
58
- }, timeout);
59
- const onData = (chunk) => {
60
- buffer = Buffer.concat([buffer, chunk]);
61
- // Check if we have enough data
62
- if (!buffer.toString('ascii', 0, Math.min(6, buffer.length)).startsWith(this.PROXY_V1_SIGNATURE)) {
63
- // Not PROXY protocol
64
- resolved = true;
65
- cleanup();
66
- resolve({
67
- proxyInfo: null,
68
- remainingData: buffer
69
- });
70
- return;
71
- }
72
- // Try to parse
73
- try {
74
- const result = this.parse(buffer);
75
- if (result.proxyInfo) {
76
- // Successfully parsed
77
- resolved = true;
78
- cleanup();
79
- resolve(result);
80
- }
81
- else if (buffer.length > this.MAX_HEADER_LENGTH) {
82
- // Header too long
83
- resolved = true;
84
- cleanup();
85
- resolve({
86
- proxyInfo: null,
87
- remainingData: buffer
88
- });
89
- }
90
- // Otherwise continue reading
91
- }
92
- catch (error) {
93
- // Parse error
94
- logger.log('error', `PROXY protocol parse error: ${error.message}`);
95
- resolved = true;
96
- cleanup();
97
- resolve({
98
- proxyInfo: null,
99
- remainingData: buffer
100
- });
101
- }
102
- };
103
- const onError = (error) => {
104
- logger.log('error', `Socket error while reading PROXY protocol: ${error.message}`);
105
- resolved = true;
106
- cleanup();
107
- resolve({
108
- proxyInfo: null,
109
- remainingData: buffer
110
- });
111
- };
112
- socket.on('data', onData);
113
- socket.on('error', onError);
114
- });
115
- }
116
- }
117
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJveHktcHJvdG9jb2wuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9jb3JlL3V0aWxzL3Byb3h5LXByb3RvY29sLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sa0JBQWtCLENBQUM7QUFDNUMsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUNyQyxPQUFPLEVBQUUsbUJBQW1CLElBQUksY0FBYyxFQUEyQyxNQUFNLGdDQUFnQyxDQUFDO0FBS2hJOzs7Ozs7R0FNRztBQUNILE1BQU0sT0FBTyxtQkFBbUI7YUFDZCx1QkFBa0IsR0FBRyxjQUFjLENBQUMsa0JBQWtCLENBQUM7YUFDdkQsc0JBQWlCLEdBQUcsY0FBYyxDQUFDLGlCQUFpQixDQUFDO2FBQ3JELHNCQUFpQixHQUFHLGNBQWMsQ0FBQyxpQkFBaUIsQ0FBQztJQUVyRTs7O09BR0c7SUFDSCxNQUFNLENBQUMsS0FBSyxDQUFDLElBQVk7UUFDdkIsOEJBQThCO1FBQzlCLE9BQU8sY0FBYyxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUNwQyxDQUFDO0lBRUQ7O09BRUc7SUFDSCxNQUFNLENBQUMsUUFBUSxDQUFDLElBQWdCO1FBQzlCLDhCQUE4QjtRQUM5QixPQUFPLGNBQWMsQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDdkMsQ0FBQztJQUVEOztPQUVHO0lBQ0ssTUFBTSxDQUFDLFNBQVMsQ0FBQyxFQUFVLEVBQUUsUUFBcUM7UUFDeEUsT0FBTyxjQUFjLENBQUMsU0FBUyxDQUFDLEVBQUUsRUFBRSxRQUFRLENBQUMsQ0FBQztJQUNoRCxDQUFDO0lBRUQ7OztPQUdHO0lBQ0gsTUFBTSxDQUFDLEtBQUssQ0FBQyxjQUFjLENBQUMsTUFBMEIsRUFBRSxVQUFrQixJQUFJO1FBQzVFLE9BQU8sSUFBSSxPQUFPLENBQUMsQ0FBQyxPQUFPLEVBQUUsRUFBRTtZQUM3QixJQUFJLE1BQU0s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
@@ -1,44 +0,0 @@
1
- /**
2
- * PROXY Protocol Parser
3
- * Implementation of HAProxy PROXY protocol v1 (text format)
4
- * Spec: https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
5
- */
6
- import type { IProxyInfo, IProxyParseResult, TProxyProtocol } from './types.js';
7
- /**
8
- * PROXY protocol parser
9
- */
10
- export declare class ProxyProtocolParser {
11
- static readonly PROXY_V1_SIGNATURE = "PROXY ";
12
- static readonly MAX_HEADER_LENGTH = 107;
13
- static readonly HEADER_TERMINATOR = "\r\n";
14
- /**
15
- * Parse PROXY protocol v1 header from buffer
16
- * Returns proxy info and remaining data after header
17
- */
18
- static parse(data: Buffer): IProxyParseResult;
19
- /**
20
- * Generate PROXY protocol v1 header
21
- */
22
- static generate(info: IProxyInfo): Buffer;
23
- /**
24
- * Validate IP address format
25
- */
26
- static isValidIP(ip: string, protocol: TProxyProtocol): boolean;
27
- /**
28
- * Check if string is valid IPv4
29
- */
30
- static isIPv4(ip: string): boolean;
31
- /**
32
- * Check if string is valid IPv6
33
- */
34
- static isIPv6(ip: string): boolean;
35
- /**
36
- * Create a connection ID string for tracking
37
- */
38
- static createConnectionId(connectionInfo: {
39
- sourceIp?: string;
40
- sourcePort?: number;
41
- destIp?: string;
42
- destPort?: number;
43
- }): string;
44
- }
@@ -1,153 +0,0 @@
1
- /**
2
- * PROXY Protocol Parser
3
- * Implementation of HAProxy PROXY protocol v1 (text format)
4
- * Spec: https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
5
- */
6
- /**
7
- * PROXY protocol parser
8
- */
9
- export class ProxyProtocolParser {
10
- static { this.PROXY_V1_SIGNATURE = 'PROXY '; }
11
- static { this.MAX_HEADER_LENGTH = 107; } // Max length for v1 header
12
- static { this.HEADER_TERMINATOR = '\r\n'; }
13
- /**
14
- * Parse PROXY protocol v1 header from buffer
15
- * Returns proxy info and remaining data after header
16
- */
17
- static parse(data) {
18
- // Check if buffer starts with PROXY signature
19
- if (!data.toString('ascii', 0, 6).startsWith(this.PROXY_V1_SIGNATURE)) {
20
- return {
21
- proxyInfo: null,
22
- remainingData: data
23
- };
24
- }
25
- // Find header terminator
26
- const headerEndIndex = data.indexOf(this.HEADER_TERMINATOR);
27
- if (headerEndIndex === -1) {
28
- // Header incomplete, need more data
29
- if (data.length > this.MAX_HEADER_LENGTH) {
30
- // Header too long, invalid
31
- throw new Error('PROXY protocol header exceeds maximum length');
32
- }
33
- return {
34
- proxyInfo: null,
35
- remainingData: data
36
- };
37
- }
38
- // Extract header line
39
- const headerLine = data.toString('ascii', 0, headerEndIndex);
40
- const remainingData = data.slice(headerEndIndex + 2); // Skip \r\n
41
- // Parse header
42
- const parts = headerLine.split(' ');
43
- if (parts.length < 2) {
44
- throw new Error(`Invalid PROXY protocol header format: ${headerLine}`);
45
- }
46
- const [signature, protocol] = parts;
47
- // Validate protocol
48
- if (!['TCP4', 'TCP6', 'UNKNOWN'].includes(protocol)) {
49
- throw new Error(`Invalid PROXY protocol: ${protocol}`);
50
- }
51
- // For UNKNOWN protocol, ignore addresses
52
- if (protocol === 'UNKNOWN') {
53
- return {
54
- proxyInfo: {
55
- protocol: 'UNKNOWN',
56
- sourceIP: '',
57
- sourcePort: 0,
58
- destinationIP: '',
59
- destinationPort: 0
60
- },
61
- remainingData
62
- };
63
- }
64
- // For TCP4/TCP6, we need all 6 parts
65
- if (parts.length !== 6) {
66
- throw new Error(`Invalid PROXY protocol header format: ${headerLine}`);
67
- }
68
- const [, , srcIP, dstIP, srcPort, dstPort] = parts;
69
- // Validate and parse ports
70
- const sourcePort = parseInt(srcPort, 10);
71
- const destinationPort = parseInt(dstPort, 10);
72
- if (isNaN(sourcePort) || sourcePort < 0 || sourcePort > 65535) {
73
- throw new Error(`Invalid source port: ${srcPort}`);
74
- }
75
- if (isNaN(destinationPort) || destinationPort < 0 || destinationPort > 65535) {
76
- throw new Error(`Invalid destination port: ${dstPort}`);
77
- }
78
- // Validate IP addresses
79
- const protocolType = protocol;
80
- if (!this.isValidIP(srcIP, protocolType)) {
81
- throw new Error(`Invalid source IP for ${protocol}: ${srcIP}`);
82
- }
83
- if (!this.isValidIP(dstIP, protocolType)) {
84
- throw new Error(`Invalid destination IP for ${protocol}: ${dstIP}`);
85
- }
86
- return {
87
- proxyInfo: {
88
- protocol: protocolType,
89
- sourceIP: srcIP,
90
- sourcePort,
91
- destinationIP: dstIP,
92
- destinationPort
93
- },
94
- remainingData
95
- };
96
- }
97
- /**
98
- * Generate PROXY protocol v1 header
99
- */
100
- static generate(info) {
101
- if (info.protocol === 'UNKNOWN') {
102
- return Buffer.from(`PROXY UNKNOWN\r\n`, 'ascii');
103
- }
104
- const header = `PROXY ${info.protocol} ${info.sourceIP} ${info.destinationIP} ${info.sourcePort} ${info.destinationPort}\r\n`;
105
- if (header.length > this.MAX_HEADER_LENGTH) {
106
- throw new Error('Generated PROXY protocol header exceeds maximum length');
107
- }
108
- return Buffer.from(header, 'ascii');
109
- }
110
- /**
111
- * Validate IP address format
112
- */
113
- static isValidIP(ip, protocol) {
114
- if (protocol === 'TCP4') {
115
- return this.isIPv4(ip);
116
- }
117
- else if (protocol === 'TCP6') {
118
- return this.isIPv6(ip);
119
- }
120
- return false;
121
- }
122
- /**
123
- * Check if string is valid IPv4
124
- */
125
- static isIPv4(ip) {
126
- const parts = ip.split('.');
127
- if (parts.length !== 4)
128
- return false;
129
- for (const part of parts) {
130
- const num = parseInt(part, 10);
131
- if (isNaN(num) || num < 0 || num > 255 || part !== num.toString()) {
132
- return false;
133
- }
134
- }
135
- return true;
136
- }
137
- /**
138
- * Check if string is valid IPv6
139
- */
140
- static isIPv6(ip) {
141
- // Basic IPv6 validation
142
- const ipv6Regex = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/;
143
- return ipv6Regex.test(ip);
144
- }
145
- /**
146
- * Create a connection ID string for tracking
147
- */
148
- static createConnectionId(connectionInfo) {
149
- const { sourceIp, sourcePort, destIp, destPort } = connectionInfo;
150
- return `${sourceIp}:${sourcePort}-${destIp}:${destPort}`;
151
- }
152
- }
153
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGFyc2VyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vdHMvcHJvdG9jb2xzL3Byb3h5L3BhcnNlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7OztHQUlHO0FBSUg7O0dBRUc7QUFDSCxNQUFNLE9BQU8sbUJBQW1CO2FBQ2QsdUJBQWtCLEdBQUcsUUFBUSxDQUFDO2FBQzlCLHNCQUFpQixHQUFHLEdBQUcsQ0FBQyxHQUFDLDJCQUEyQjthQUNwRCxzQkFBaUIsR0FBRyxNQUFNLENBQUM7SUFFM0M7OztPQUdHO0lBQ0gsTUFBTSxDQUFDLEtBQUssQ0FBQyxJQUFZO1FBQ3ZCLDhDQUE4QztRQUM5QyxJQUFJLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsa0JBQWtCLENBQUMsRUFBRSxDQUFDO1lBQ3RFLE9BQU87Z0JBQ0wsU0FBUyxFQUFFLElBQUk7Z0JBQ2YsYUFBYSxFQUFFLElBQUk7YUFDcEIsQ0FBQztRQUNKLENBQUM7UUFFRCx5QkFBeUI7UUFDekIsTUFBTSxjQUFjLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsaUJBQWlCLENBQUMsQ0FBQztRQUM1RCxJQUFJLGNBQWMsS0FBSyxDQUFDLENBQUMsRUFBRSxDQUFDO1lBQzFCLG9DQUFvQztZQUNwQyxJQUFJLElBQUksQ0FBQyxNQUFNLEdBQUcsSUFBSSxDQUFDLGlCQUFpQixFQUFFLENBQUM7Z0JBQ3pDLDJCQUEyQjtnQkFDM0IsTUFBTSxJQUFJLEtBQUssQ0FBQyw4Q0FBOEMsQ0FBQyxDQUFDO1lBQ2xFLENBQUM7WUFDRCxPQUFPO2dCQUNMLFNBQVMsRUFBRSxJQUFJO2dCQUNmLGFBQWEsRUFBRSxJQUFJO2FBQ3BCLENBQUM7UUFDSixDQUFDO1FBRUQsc0JBQXNCO1FBQ3RCLE1BQU0sVUFBVSxHQUFHLElBQUksQ0FBQyxRQUFRLENBQUMsT0FBTyxFQUFFLENBQUMsRUFBRSxjQUFjLENBQUMsQ0FBQztRQUM3RCxNQUFNLGFBQWEsR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLGNBQWMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLFlBQVk7UUFFbEUsZUFBZTtRQUNmLE1BQU0sS0FBSyxHQUFHLFVBQVUsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUM7UUFFcEMsSUFBSSxLQUFLLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRSxDQUFDO1lBQ3JCLE1BQU0sSUFBSSxLQUFLLENBQUMseUNBQXlDLFVBQVUsRUFBRSxDQUFDLENBQUM7UUFDekUsQ0FBQztRQUVELE1BQU0sQ0FBQyxTQUFTLEVBQUUsUUFBUSxDQUFDLEdBQUcsS0FBSyxDQUFDO1FBRXBDLG9CQUFvQjtRQUNwQixJQUFJLENBQUMsQ0FBQyxNQUFNLEVBQUUsTUFBTSxFQUFFLFNBQVMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDO1lBQ3BELE1BQU0sSUFBSSxLQUFLLENBQUMsMkJBQTJCLFFBQVEsRUFBRSxDQUFDLENBQUM7UUFDekQsQ0FBQztRQUVELHlDQUF5QztRQUN6QyxJQUFJLFFBQVEsS0FBSyxTQUFTLEVBQUUsQ0FBQztZQUMzQixPQUFPO2dCQUNMLFNBQVMsRUFBRTtvQkFDVCxRQUFRLEVBQUUsU0FBUztvQkFDbkIsUUFBUSxFQUFFLEVBQUU7b0JBQ1osVUFBVSxFQUFFLENBQUM7b0JBQ2IsYUFBYSxFQUFFLEVBQUU7b0JBQ2pCLGVBQWUsRUFBRSxDQUFDO2lCQUNuQjtnQkFDRCxhQUFhO2FBQ2QsQ0FBQztRQUNKLENBQUM7UUFFRCxxQ0FBcUM7UUFDckMsSUFBSSxLQUFLLENBQUMsTUFBTSxLQUFLLENBQUMsRUFBRSxDQUFDO1lBQ3ZCLE1BQU0sSUFBSSxLQUFLLENBQUMseUNBQXlDLFVBQVUsRUFBRSxDQUFDLENBQUM7UUFDekUsQ0FBQztRQUVELE1BQU0sQ0FBQyxFQUFFLEFBQUQsRUFBRyxLQUFLLEVBQUUsS0FBSyxFQUFFLE9BQU8sRUFBRSxPQUFPLENBQUMsR0FBRyxLQUFLLENBQUM7UUFFbkQsMkJBQTJCO1FBQzNCLE1BQU0sVUFBVSxHQUFHLFFBQVEsQ0FBQyxPQUFPLEVBQUUsRUFBRSxDQUFDLENBQUM7UUFDekMsTUFBTSxlQUFlLEdBQUcsUUFBUSxDQUFDLE9BQU8sRUFBRSxFQUFFLENBQUMsQ0FBQztRQUU5QyxJQUFJLEtBQUssQ0FBQyxVQUFVLENBQUMsSUFBSSxVQUFVLEdBQUcsQ0FBQyxJQUFJLFVBQVUsR0FBRyxLQUFLLEVBQUUsQ0FBQztZQUM5RCxNQUFNLElBQUksS0FBSyxDQUFDLHdCQUF3QixPQUFPLEVBQUUsQ0FBQyxDQUFDO1FBQ3JELENBQUM7UUFFRCxJQUFJLEtBQUssQ0FBQyxlQUFlLENBQUMsSUFBSSxlQUFlLEdBQUcsQ0FBQyxJQUFJLGVBQWUsR0FBRyxLQUFLLEVBQUUsQ0FBQztZQUM3RSxNQUFNLElBQUksS0FBSyxDQUFDLDZCQUE2QixPQUFPLEVBQUUsQ0FBQyxDQUFDO1FBQzFELENBQUM7UUFFRCx3QkFBd0I7UUFDeEIsTUFBTSxZQUFZLEdBQUcsUUFBMEIsQ0FBQztRQUNoRCxJQUFJLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxLQUFLLEVBQUUsWUFBWSxDQUFDLEVBQUUsQ0FBQztZQUN6QyxNQUFNLElBQUksS0FBSyxDQUFDLHlCQUF5QixRQUFRLEtBQUssS0FBSyxFQUFFLENBQUMsQ0FBQztRQUNqRSxDQUFDO1FBRUQsSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsS0FBSyxFQUFFLFlBQVksQ0FBQyxFQUFFLENBQUM7WUFDekMsTUFBTSxJQUFJLEtBQUssQ0FBQyw4QkFBOEIsUUFBUSxLQUFLLEtBQUssRUFBRSxDQUFDLENBQUM7UUFDdEUsQ0FBQztRQUVELE9BQU87WUFDTCxTQUFTLEVBQUU7Z0JBQ1QsUUFBUSxFQUFFLFlBQVk7Z0JBQ3RCLFFBQVEsRUFBRSxLQUFLO2dCQUNmLFVBQVU7Z0JBQ1YsYUFBYSxFQUFFLEtBQUs7Z0JBQ3BCLGVBQWU7YUFDaEI7WUFDRCxhQUFhO1NBQ2QsQ0FBQztJQUNKLENBQUM7SUFFRDs7T0FFRztJQUNILE1BQU0sQ0FBQyxRQUFRLENBQUMsSUFBZ0I7UUFDOUIsSUFBSSxJQUFJLENBQUMsUUFBUSxLQUFLLFNBQVMsRUFBRSxDQUFDO1lBQ2hDLE9BQU8sTUFBTSxDQUFDLElBQUksQ0FBQyxtQkFBbUIsRUFBRSxPQUFPLENBQUMsQ0FBQztRQUNuRCxDQUFDO1FBRUQsTUFBTSxNQUFNLEdBQUcsU0FBUyxJQUFJLENBQUMsUUFBUSxJQUFJLElBQUksQ0FBQyxRQUFRLElBQUksSUFBSSxDQUFDLGFBQWEsSUFBSSxJQUFJLENBQUMsVUFBVSxJQUFJLElBQUksQ0FBQyxlQUFlLE1BQU0sQ0FBQztRQUU5SCxJQUFJLE1BQU0sQ0FBQyxNQUFNLEdBQUcsSUFBSSxDQUFDLGlCQUFpQixFQUFFLENBQUM7WUFDM0MsTUFBTSxJQUFJLEtBQUssQ0FBQyx3REFBd0QsQ0FBQyxDQUFDO1FBQzVFLENBQUM7UUFFRCxPQUFPLE1BQU0sQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLE9BQU8sQ0FBQyxDQUFDO0lBQ3RDLENBQUM7SUFFRDs7T0FFRztJQUNILE1BQU0sQ0FBQyxTQUFTLENBQUMsRUFBVSxFQUFFLFFBQXdCO1FBQ25ELElBQUksUUFBUSxLQUFLLE1BQU0sRUFBRSxDQUFDO1lBQ3hCLE9BQU8sSUFBSSxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUMsQ0FBQztRQUN6QixDQUFDO2FBQU0sSUFBSSxRQUFRLEtBQUssTUFBTSxFQUFFLENBQUM7WUFDL0IsT0FBTyxJQUFJLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQyxDQUFDO1FBQ3pCLENBQUM7UUFDRCxPQUFPLEtBQUssQ0FBQztJQUNmLENBQUM7SUFFRDs7T0FFRztJQUNILE1BQU0sQ0FBQyxNQUFNLENBQUMsRUFBVTtRQUN0QixNQUFNLEtBQUssR0FBRyxFQUFFLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQzVCLElBQUksS0FBSyxDQUFDLE1BQU0sS0FBSyxDQUFDO1lBQUUsT0FBTyxLQUFLLENBQUM7UUFFckMsS0FBSyxNQUFNLElBQUksSUFBSSxLQUFLLEVBQUUsQ0FBQztZQUN6QixNQUFNLEdBQUcsR0FBRyxRQUFRLENBQUMsSUFBSSxFQUFFLEVBQUUsQ0FBQyxDQUFDO1lBQy9CLElBQUksS0FBSyxDQUFDLEdBQUcsQ0FBQyxJQUFJLEdBQUcsR0FBRyxDQUFDLElBQUksR0FBRyxHQUFHLEdBQUcsSUFBSSxJQUFJLEtBQUssR0FBRyxDQUFDLFFBQVEsRUFBRSxFQUFFLENBQUM7Z0JBQ2xFLE9BQU8sS0FBSyxDQUFDO1lBQ2YsQ0FBQztRQUNILENBQUM7UUFDRCxPQUFPLElBQUksQ0FBQztJQUNkLENBQUM7SUFFRDs7T0FFRztJQUNILE1BQU0sQ0FBQyxNQUFNLENBQUMsRUFBVTtRQUN0Qix3QkFBd0I7UUFDeEIsTUFBTSxTQUFTLEdBQUcsaXBCQUFpcEIsQ0FBQztRQUNwcUIsT0FBTyxTQUFTLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDO0lBQzVCLENBQUM7SUFFRDs7T0FFRztJQUNILE1BQU0sQ0FBQyxrQkFBa0IsQ0FBQyxjQUt6QjtRQUNDLE1BQU0sRUFBRSxRQUFRLEVBQUUsVUFBVSxFQUFFLE1BQU0sRUFBRSxRQUFRLEVBQUUsR0FBRyxjQUFjLENBQUM7UUFDbEUsT0FBTyxHQUFHLFFBQVEsSUFBSSxVQUFVLElBQUksTUFBTSxJQUFJLFFBQVEsRUFBRSxDQUFDO0lBQzNELENBQUMifQ==
@@ -1,129 +0,0 @@
1
- import * as plugins from '../../plugins.js';
2
- import { logger } from './logger.js';
3
- import { ProxyProtocolParser as ProtocolParser, type IProxyInfo, type IProxyParseResult } from '../../protocols/proxy/index.js';
4
-
5
- // Re-export types from protocols for backward compatibility
6
- export type { IProxyInfo, IProxyParseResult } from '../../protocols/proxy/index.js';
7
-
8
- /**
9
- * Parser for PROXY protocol v1 (text format)
10
- * Spec: https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
11
- *
12
- * This class now delegates to the protocol parser but adds
13
- * smartproxy-specific features like socket reading and logging
14
- */
15
- export class ProxyProtocolParser {
16
- static readonly PROXY_V1_SIGNATURE = ProtocolParser.PROXY_V1_SIGNATURE;
17
- static readonly MAX_HEADER_LENGTH = ProtocolParser.MAX_HEADER_LENGTH;
18
- static readonly HEADER_TERMINATOR = ProtocolParser.HEADER_TERMINATOR;
19
-
20
- /**
21
- * Parse PROXY protocol v1 header from buffer
22
- * Returns proxy info and remaining data after header
23
- */
24
- static parse(data: Buffer): IProxyParseResult {
25
- // Delegate to protocol parser
26
- return ProtocolParser.parse(data);
27
- }
28
-
29
- /**
30
- * Generate PROXY protocol v1 header
31
- */
32
- static generate(info: IProxyInfo): Buffer {
33
- // Delegate to protocol parser
34
- return ProtocolParser.generate(info);
35
- }
36
-
37
- /**
38
- * Validate IP address format
39
- */
40
- private static isValidIP(ip: string, protocol: 'TCP4' | 'TCP6' | 'UNKNOWN'): boolean {
41
- return ProtocolParser.isValidIP(ip, protocol);
42
- }
43
-
44
- /**
45
- * Attempt to read a complete PROXY protocol header from a socket
46
- * Returns null if no PROXY protocol detected or incomplete
47
- */
48
- static async readFromSocket(socket: plugins.net.Socket, timeout: number = 5000): Promise<IProxyParseResult | null> {
49
- return new Promise((resolve) => {
50
- let buffer = Buffer.alloc(0);
51
- let resolved = false;
52
-
53
- const cleanup = () => {
54
- socket.removeListener('data', onData);
55
- socket.removeListener('error', onError);
56
- clearTimeout(timer);
57
- };
58
-
59
- const timer = setTimeout(() => {
60
- if (!resolved) {
61
- resolved = true;
62
- cleanup();
63
- resolve({
64
- proxyInfo: null,
65
- remainingData: buffer
66
- });
67
- }
68
- }, timeout);
69
-
70
- const onData = (chunk: Buffer) => {
71
- buffer = Buffer.concat([buffer, chunk]);
72
-
73
- // Check if we have enough data
74
- if (!buffer.toString('ascii', 0, Math.min(6, buffer.length)).startsWith(this.PROXY_V1_SIGNATURE)) {
75
- // Not PROXY protocol
76
- resolved = true;
77
- cleanup();
78
- resolve({
79
- proxyInfo: null,
80
- remainingData: buffer
81
- });
82
- return;
83
- }
84
-
85
- // Try to parse
86
- try {
87
- const result = this.parse(buffer);
88
- if (result.proxyInfo) {
89
- // Successfully parsed
90
- resolved = true;
91
- cleanup();
92
- resolve(result);
93
- } else if (buffer.length > this.MAX_HEADER_LENGTH) {
94
- // Header too long
95
- resolved = true;
96
- cleanup();
97
- resolve({
98
- proxyInfo: null,
99
- remainingData: buffer
100
- });
101
- }
102
- // Otherwise continue reading
103
- } catch (error) {
104
- // Parse error
105
- logger.log('error', `PROXY protocol parse error: ${error.message}`);
106
- resolved = true;
107
- cleanup();
108
- resolve({
109
- proxyInfo: null,
110
- remainingData: buffer
111
- });
112
- }
113
- };
114
-
115
- const onError = (error: Error) => {
116
- logger.log('error', `Socket error while reading PROXY protocol: ${error.message}`);
117
- resolved = true;
118
- cleanup();
119
- resolve({
120
- proxyInfo: null,
121
- remainingData: buffer
122
- });
123
- };
124
-
125
- socket.on('data', onData);
126
- socket.on('error', onError);
127
- });
128
- }
129
- }