@push.rocks/smartproxy 25.11.24 → 25.12.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.
package/changelog.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## 2026-03-19 - 25.12.0 - feat(proxy-protocol)
4
+ add PROXY protocol v2 support to the Rust passthrough listener and streamline TypeScript proxy protocol exports
5
+
6
+ - detect and parse PROXY protocol v2 headers in the Rust TCP listener, including TCP and UDP address families
7
+ - add Rust v2 header generation, incomplete-header handling, and broader parser test coverage
8
+ - remove deprecated TypeScript proxy protocol parser exports and tests, leaving shared type definitions only
9
+
3
10
  ## 2026-03-17 - 25.11.24 - fix(rustproxy-http)
4
11
  improve async static file serving, websocket handshake buffering, and shared metric metadata handling
5
12
 
Binary file
Binary file
@@ -3,7 +3,7 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@push.rocks/smartproxy',
6
- version: '25.11.24',
6
+ version: '25.12.0',
7
7
  description: 'A powerful proxy package with unified route-based configuration for high traffic management. Features include SSL/TLS support, flexible routing patterns, WebSocket handling, advanced security options, and automatic ACME certificate management.'
8
8
  };
9
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSx3QkFBd0I7SUFDOUIsT0FBTyxFQUFFLFVBQVU7SUFDbkIsV0FBVyxFQUFFLHFQQUFxUDtDQUNuUSxDQUFBIn0=
9
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSx3QkFBd0I7SUFDOUIsT0FBTyxFQUFFLFNBQVM7SUFDbEIsV0FBVyxFQUFFLHFQQUFxUDtDQUNuUSxDQUFBIn0=
@@ -14,4 +14,3 @@ export * from './lifecycle-component.js';
14
14
  export * from './binary-heap.js';
15
15
  export * from './enhanced-connection-pool.js';
16
16
  export * from './socket-utils.js';
17
- export * from './proxy-protocol.js';
@@ -14,5 +14,4 @@ export * from './lifecycle-component.js';
14
14
  export * from './binary-heap.js';
15
15
  export * from './enhanced-connection-pool.js';
16
16
  export * from './socket-utils.js';
17
- export * from './proxy-protocol.js';
18
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9jb3JlL3V0aWxzL2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBRUgsY0FBYyx1QkFBdUIsQ0FBQztBQUN0QyxjQUFjLGVBQWUsQ0FBQztBQUM5QixjQUFjLHFCQUFxQixDQUFDO0FBQ3BDLGNBQWMscUJBQXFCLENBQUM7QUFDcEMsY0FBYyw4QkFBOEIsQ0FBQztBQUM3QyxjQUFjLHNCQUFzQixDQUFDO0FBQ3JDLGNBQWMsYUFBYSxDQUFDO0FBQzVCLGNBQWMsa0JBQWtCLENBQUM7QUFDakMsY0FBYyxlQUFlLENBQUM7QUFDOUIsY0FBYywwQkFBMEIsQ0FBQztBQUN6QyxjQUFjLGtCQUFrQixDQUFDO0FBQ2pDLGNBQWMsK0JBQStCLENBQUM7QUFDOUMsY0FBYyxtQkFBbUIsQ0FBQztBQUNsQyxjQUFjLHFCQUFxQixDQUFDIn0=
17
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9jb3JlL3V0aWxzL2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBRUgsY0FBYyx1QkFBdUIsQ0FBQztBQUN0QyxjQUFjLGVBQWUsQ0FBQztBQUM5QixjQUFjLHFCQUFxQixDQUFDO0FBQ3BDLGNBQWMscUJBQXFCLENBQUM7QUFDcEMsY0FBYyw4QkFBOEIsQ0FBQztBQUM3QyxjQUFjLHNCQUFzQixDQUFDO0FBQ3JDLGNBQWMsYUFBYSxDQUFDO0FBQzVCLGNBQWMsa0JBQWtCLENBQUM7QUFDakMsY0FBYyxlQUFlLENBQUM7QUFDOUIsY0FBYywwQkFBMEIsQ0FBQztBQUN6QyxjQUFjLGtCQUFrQixDQUFDO0FBQ2pDLGNBQWMsK0JBQStCLENBQUM7QUFDOUMsY0FBYyxtQkFBbUIsQ0FBQyJ9
@@ -1,6 +1,5 @@
1
1
  /**
2
2
  * PROXY Protocol Module
3
- * HAProxy PROXY protocol implementation
3
+ * Type definitions for HAProxy PROXY protocol v1/v2
4
4
  */
5
5
  export * from './types.js';
6
- export * from './parser.js';
@@ -1,7 +1,6 @@
1
1
  /**
2
2
  * PROXY Protocol Module
3
- * HAProxy PROXY protocol implementation
3
+ * Type definitions for HAProxy PROXY protocol v1/v2
4
4
  */
5
5
  export * from './types.js';
6
- export * from './parser.js';
7
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9wcm90b2NvbHMvcHJveHkvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7OztHQUdHO0FBRUgsY0FBYyxZQUFZLENBQUM7QUFDM0IsY0FBYyxhQUFhLENBQUMifQ==
6
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9wcm90b2NvbHMvcHJveHkvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7OztHQUdHO0FBRUgsY0FBYyxZQUFZLENBQUMifQ==
@@ -9,7 +9,7 @@ export type TProxyProtocolVersion = 'v1' | 'v2';
9
9
  /**
10
10
  * Connection protocol type
11
11
  */
12
- export type TProxyProtocol = 'TCP4' | 'TCP6' | 'UNKNOWN';
12
+ export type TProxyProtocol = 'TCP4' | 'TCP6' | 'UDP4' | 'UDP6' | 'UNKNOWN';
13
13
  /**
14
14
  * Interface representing parsed PROXY protocol information
15
15
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@push.rocks/smartproxy",
3
- "version": "25.11.24",
3
+ "version": "25.12.0",
4
4
  "private": false,
5
5
  "description": "A powerful proxy package with unified route-based configuration for high traffic management. Features include SSL/TLS support, flexible routing patterns, WebSocket handling, advanced security options, and automatic ACME certificate management.",
6
6
  "main": "dist_ts/index.js",
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@push.rocks/smartproxy',
6
- version: '25.11.24',
6
+ version: '25.12.0',
7
7
  description: 'A powerful proxy package with unified route-based configuration for high traffic management. Features include SSL/TLS support, flexible routing patterns, WebSocket handling, advanced security options, and automatic ACME certificate management.'
8
8
  }
@@ -15,4 +15,3 @@ export * from './lifecycle-component.js';
15
15
  export * from './binary-heap.js';
16
16
  export * from './enhanced-connection-pool.js';
17
17
  export * from './socket-utils.js';
18
- export * from './proxy-protocol.js';
@@ -1,7 +1,6 @@
1
1
  /**
2
2
  * PROXY Protocol Module
3
- * HAProxy PROXY protocol implementation
3
+ * Type definitions for HAProxy PROXY protocol v1/v2
4
4
  */
5
5
 
6
- export * from './types.js';
7
- export * from './parser.js';
6
+ export * from './types.js';
@@ -11,7 +11,7 @@ export type TProxyProtocolVersion = 'v1' | 'v2';
11
11
  /**
12
12
  * Connection protocol type
13
13
  */
14
- export type TProxyProtocol = 'TCP4' | 'TCP6' | 'UNKNOWN';
14
+ export type TProxyProtocol = 'TCP4' | 'TCP6' | 'UDP4' | 'UDP6' | 'UNKNOWN';
15
15
 
16
16
  /**
17
17
  * Interface representing parsed PROXY protocol information
@@ -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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGFyc2VyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vdHMvcHJvdG9jb2xzL3Byb3h5L3BhcnNlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7OztHQUlHO0FBSUg7O0dBRUc7QUFDSCxNQUFNLE9BQU8sbUJBQW1CO2FBQ2QsdUJBQWtCLEdBQUcsUUFBUSxDQUFDO2FBQzlCLHNCQUFpQixHQUFHLEdBQUcsQ0FBQyxHQUFDLDJCQUEyQjthQUNwRCxzQkFBaUIsR0FBRyxNQUFNLENBQUM7SUFFM0M7OztPQUdHO0lBQ0gsTUFBTSxDQUFDLEtBQUssQ0FBQyxJQUFZO1FBQ3ZCLDhDQUE4QztRQUM5QyxJQUFJLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsa0JBQWtCLENBQUMsRUFBRSxDQUFDO1lBQ3RFLE9BQU87Z0JBQ0wsU0FBUyxFQUFFLElBQUk7Z0JBQ2YsYUFBYSxFQUFFLElBQUk7YUFDcEIsQ0FBQztRQUNKLENBQUM7UUFFRCx5QkFBeUI7UUFDekIsTUFBTSxjQUFjLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsaUJBQWlCLENBQUMsQ0FBQztRQUM1RCxJQUFJLGNBQWMsS0FBSyxDQUFDLENBQUMsRUFBRSxDQUFDO1lBQzFCLG9DQUFvQztZQUNwQyxJQUFJLElBQUksQ0FBQyxNQUFNLEdBQUcsSUFBSSxDQUFDLGlCQUFpQixFQUFFLENBQUM7Z0JBQ3pDLDJCQUEyQjtnQkFDM0IsTUFBTSxJQUFJLEtBQUssQ0FBQyw4Q0FBOEMsQ0FBQyxDQUFDO1lBQ2xFLENBQUM7WUFDRCxPQUFPO2dCQUNMLFNBQVMsRUFBRSxJQUFJO2dCQUNmLGFBQWEsRUFBRSxJQUFJO2FBQ3BCLENBQUM7UUFDSixDQUFDO1FBRUQsc0JBQXNCO1FBQ3RCLE1BQU0sVUFBVSxHQUFHLElBQUksQ0FBQyxRQUFRLENBQUMsT0FBTyxF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
@@ -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
- }
@@ -1,183 +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
- import type { IProxyInfo, IProxyParseResult, TProxyProtocol } from './types.js';
8
-
9
- /**
10
- * PROXY protocol parser
11
- */
12
- export class ProxyProtocolParser {
13
- static readonly PROXY_V1_SIGNATURE = 'PROXY ';
14
- static readonly MAX_HEADER_LENGTH = 107; // Max length for v1 header
15
- static readonly HEADER_TERMINATOR = '\r\n';
16
-
17
- /**
18
- * Parse PROXY protocol v1 header from buffer
19
- * Returns proxy info and remaining data after header
20
- */
21
- static parse(data: Buffer): IProxyParseResult {
22
- // Check if buffer starts with PROXY signature
23
- if (!data.toString('ascii', 0, 6).startsWith(this.PROXY_V1_SIGNATURE)) {
24
- return {
25
- proxyInfo: null,
26
- remainingData: data
27
- };
28
- }
29
-
30
- // Find header terminator
31
- const headerEndIndex = data.indexOf(this.HEADER_TERMINATOR);
32
- if (headerEndIndex === -1) {
33
- // Header incomplete, need more data
34
- if (data.length > this.MAX_HEADER_LENGTH) {
35
- // Header too long, invalid
36
- throw new Error('PROXY protocol header exceeds maximum length');
37
- }
38
- return {
39
- proxyInfo: null,
40
- remainingData: data
41
- };
42
- }
43
-
44
- // Extract header line
45
- const headerLine = data.toString('ascii', 0, headerEndIndex);
46
- const remainingData = data.slice(headerEndIndex + 2); // Skip \r\n
47
-
48
- // Parse header
49
- const parts = headerLine.split(' ');
50
-
51
- if (parts.length < 2) {
52
- throw new Error(`Invalid PROXY protocol header format: ${headerLine}`);
53
- }
54
-
55
- const [signature, protocol] = parts;
56
-
57
- // Validate protocol
58
- if (!['TCP4', 'TCP6', 'UNKNOWN'].includes(protocol)) {
59
- throw new Error(`Invalid PROXY protocol: ${protocol}`);
60
- }
61
-
62
- // For UNKNOWN protocol, ignore addresses
63
- if (protocol === 'UNKNOWN') {
64
- return {
65
- proxyInfo: {
66
- protocol: 'UNKNOWN',
67
- sourceIP: '',
68
- sourcePort: 0,
69
- destinationIP: '',
70
- destinationPort: 0
71
- },
72
- remainingData
73
- };
74
- }
75
-
76
- // For TCP4/TCP6, we need all 6 parts
77
- if (parts.length !== 6) {
78
- throw new Error(`Invalid PROXY protocol header format: ${headerLine}`);
79
- }
80
-
81
- const [, , srcIP, dstIP, srcPort, dstPort] = parts;
82
-
83
- // Validate and parse ports
84
- const sourcePort = parseInt(srcPort, 10);
85
- const destinationPort = parseInt(dstPort, 10);
86
-
87
- if (isNaN(sourcePort) || sourcePort < 0 || sourcePort > 65535) {
88
- throw new Error(`Invalid source port: ${srcPort}`);
89
- }
90
-
91
- if (isNaN(destinationPort) || destinationPort < 0 || destinationPort > 65535) {
92
- throw new Error(`Invalid destination port: ${dstPort}`);
93
- }
94
-
95
- // Validate IP addresses
96
- const protocolType = protocol as TProxyProtocol;
97
- if (!this.isValidIP(srcIP, protocolType)) {
98
- throw new Error(`Invalid source IP for ${protocol}: ${srcIP}`);
99
- }
100
-
101
- if (!this.isValidIP(dstIP, protocolType)) {
102
- throw new Error(`Invalid destination IP for ${protocol}: ${dstIP}`);
103
- }
104
-
105
- return {
106
- proxyInfo: {
107
- protocol: protocolType,
108
- sourceIP: srcIP,
109
- sourcePort,
110
- destinationIP: dstIP,
111
- destinationPort
112
- },
113
- remainingData
114
- };
115
- }
116
-
117
- /**
118
- * Generate PROXY protocol v1 header
119
- */
120
- static generate(info: IProxyInfo): Buffer {
121
- if (info.protocol === 'UNKNOWN') {
122
- return Buffer.from(`PROXY UNKNOWN\r\n`, 'ascii');
123
- }
124
-
125
- const header = `PROXY ${info.protocol} ${info.sourceIP} ${info.destinationIP} ${info.sourcePort} ${info.destinationPort}\r\n`;
126
-
127
- if (header.length > this.MAX_HEADER_LENGTH) {
128
- throw new Error('Generated PROXY protocol header exceeds maximum length');
129
- }
130
-
131
- return Buffer.from(header, 'ascii');
132
- }
133
-
134
- /**
135
- * Validate IP address format
136
- */
137
- static isValidIP(ip: string, protocol: TProxyProtocol): boolean {
138
- if (protocol === 'TCP4') {
139
- return this.isIPv4(ip);
140
- } else if (protocol === 'TCP6') {
141
- return this.isIPv6(ip);
142
- }
143
- return false;
144
- }
145
-
146
- /**
147
- * Check if string is valid IPv4
148
- */
149
- static isIPv4(ip: string): boolean {
150
- const parts = ip.split('.');
151
- if (parts.length !== 4) return false;
152
-
153
- for (const part of parts) {
154
- const num = parseInt(part, 10);
155
- if (isNaN(num) || num < 0 || num > 255 || part !== num.toString()) {
156
- return false;
157
- }
158
- }
159
- return true;
160
- }
161
-
162
- /**
163
- * Check if string is valid IPv6
164
- */
165
- static isIPv6(ip: string): boolean {
166
- // Basic IPv6 validation
167
- 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]))$/;
168
- return ipv6Regex.test(ip);
169
- }
170
-
171
- /**
172
- * Create a connection ID string for tracking
173
- */
174
- static createConnectionId(connectionInfo: {
175
- sourceIp?: string;
176
- sourcePort?: number;
177
- destIp?: string;
178
- destPort?: number;
179
- }): string {
180
- const { sourceIp, sourcePort, destIp, destPort } = connectionInfo;
181
- return `${sourceIp}:${sourcePort}-${destIp}:${destPort}`;
182
- }
183
- }