@push.rocks/smartproxy 22.4.2 → 22.6.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 (72) hide show
  1. package/changelog.md +28 -0
  2. package/dist_rust/rustproxy +0 -0
  3. package/dist_ts/00_commitinfo_data.js +1 -1
  4. package/dist_ts/index.d.ts +1 -5
  5. package/dist_ts/index.js +3 -9
  6. package/dist_ts/protocols/common/fragment-handler.js +5 -1
  7. package/dist_ts/proxies/index.d.ts +1 -5
  8. package/dist_ts/proxies/index.js +2 -6
  9. package/dist_ts/proxies/smart-proxy/index.d.ts +5 -10
  10. package/dist_ts/proxies/smart-proxy/index.js +7 -13
  11. package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +5 -2
  12. package/dist_ts/proxies/smart-proxy/route-preprocessor.d.ts +37 -0
  13. package/dist_ts/proxies/smart-proxy/route-preprocessor.js +103 -0
  14. package/dist_ts/proxies/smart-proxy/rust-binary-locator.d.ts +23 -0
  15. package/dist_ts/proxies/smart-proxy/rust-binary-locator.js +104 -0
  16. package/dist_ts/proxies/smart-proxy/rust-metrics-adapter.d.ts +74 -0
  17. package/dist_ts/proxies/smart-proxy/rust-metrics-adapter.js +146 -0
  18. package/dist_ts/proxies/smart-proxy/rust-proxy-bridge.d.ts +49 -0
  19. package/dist_ts/proxies/smart-proxy/rust-proxy-bridge.js +259 -0
  20. package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +39 -157
  21. package/dist_ts/proxies/smart-proxy/smart-proxy.js +224 -621
  22. package/dist_ts/proxies/smart-proxy/socket-handler-server.d.ts +45 -0
  23. package/dist_ts/proxies/smart-proxy/socket-handler-server.js +253 -0
  24. package/dist_ts/routing/index.d.ts +1 -1
  25. package/dist_ts/routing/index.js +3 -3
  26. package/dist_ts/routing/models/http-types.d.ts +119 -4
  27. package/dist_ts/routing/models/http-types.js +93 -5
  28. package/package.json +1 -1
  29. package/readme.md +470 -219
  30. package/ts/00_commitinfo_data.ts +1 -1
  31. package/ts/index.ts +4 -12
  32. package/ts/protocols/common/fragment-handler.ts +4 -0
  33. package/ts/proxies/index.ts +1 -9
  34. package/ts/proxies/smart-proxy/index.ts +6 -13
  35. package/ts/proxies/smart-proxy/models/interfaces.ts +6 -4
  36. package/ts/proxies/smart-proxy/route-preprocessor.ts +122 -0
  37. package/ts/proxies/smart-proxy/rust-binary-locator.ts +112 -0
  38. package/ts/proxies/smart-proxy/rust-metrics-adapter.ts +161 -0
  39. package/ts/proxies/smart-proxy/rust-proxy-bridge.ts +310 -0
  40. package/ts/proxies/smart-proxy/smart-proxy.ts +282 -798
  41. package/ts/proxies/smart-proxy/socket-handler-server.ts +279 -0
  42. package/ts/routing/index.ts +2 -2
  43. package/ts/routing/models/http-types.ts +147 -4
  44. package/ts/proxies/http-proxy/connection-pool.ts +0 -228
  45. package/ts/proxies/http-proxy/context-creator.ts +0 -145
  46. package/ts/proxies/http-proxy/default-certificates.ts +0 -150
  47. package/ts/proxies/http-proxy/function-cache.ts +0 -279
  48. package/ts/proxies/http-proxy/handlers/index.ts +0 -5
  49. package/ts/proxies/http-proxy/http-proxy.ts +0 -669
  50. package/ts/proxies/http-proxy/http-request-handler.ts +0 -331
  51. package/ts/proxies/http-proxy/http2-request-handler.ts +0 -255
  52. package/ts/proxies/http-proxy/index.ts +0 -18
  53. package/ts/proxies/http-proxy/models/http-types.ts +0 -148
  54. package/ts/proxies/http-proxy/models/index.ts +0 -5
  55. package/ts/proxies/http-proxy/models/types.ts +0 -125
  56. package/ts/proxies/http-proxy/request-handler.ts +0 -878
  57. package/ts/proxies/http-proxy/security-manager.ts +0 -413
  58. package/ts/proxies/http-proxy/websocket-handler.ts +0 -581
  59. package/ts/proxies/smart-proxy/acme-state-manager.ts +0 -112
  60. package/ts/proxies/smart-proxy/cert-store.ts +0 -92
  61. package/ts/proxies/smart-proxy/certificate-manager.ts +0 -895
  62. package/ts/proxies/smart-proxy/connection-manager.ts +0 -809
  63. package/ts/proxies/smart-proxy/http-proxy-bridge.ts +0 -213
  64. package/ts/proxies/smart-proxy/metrics-collector.ts +0 -453
  65. package/ts/proxies/smart-proxy/nftables-manager.ts +0 -271
  66. package/ts/proxies/smart-proxy/port-manager.ts +0 -358
  67. package/ts/proxies/smart-proxy/route-connection-handler.ts +0 -1712
  68. package/ts/proxies/smart-proxy/route-orchestrator.ts +0 -297
  69. package/ts/proxies/smart-proxy/security-manager.ts +0 -269
  70. package/ts/proxies/smart-proxy/throughput-tracker.ts +0 -138
  71. package/ts/proxies/smart-proxy/timeout-manager.ts +0 -196
  72. package/ts/proxies/smart-proxy/tls-manager.ts +0 -171
@@ -1,271 +0,0 @@
1
- import * as plugins from '../../plugins.js';
2
- import { NfTablesProxy } from '../nftables-proxy/nftables-proxy.js';
3
- import type {
4
- NfTableProxyOptions,
5
- PortRange,
6
- NfTablesStatus
7
- } from '../nftables-proxy/models/interfaces.js';
8
- import type {
9
- IRouteConfig,
10
- TPortRange,
11
- INfTablesOptions
12
- } from './models/route-types.js';
13
- import type { SmartProxy } from './smart-proxy.js';
14
-
15
- /**
16
- * Manages NFTables rules based on SmartProxy route configurations
17
- *
18
- * This class bridges the gap between SmartProxy routes and the NFTablesProxy,
19
- * allowing high-performance kernel-level packet forwarding for routes that
20
- * specify NFTables as their forwarding engine.
21
- */
22
- export class NFTablesManager {
23
- private rulesMap: Map<string, NfTablesProxy> = new Map();
24
-
25
- /**
26
- * Creates a new NFTablesManager
27
- *
28
- * @param smartProxy The SmartProxy instance
29
- */
30
- constructor(private smartProxy: SmartProxy) {}
31
-
32
- /**
33
- * Provision NFTables rules for a route
34
- *
35
- * @param route The route configuration
36
- * @returns A promise that resolves to true if successful, false otherwise
37
- */
38
- public async provisionRoute(route: IRouteConfig): Promise<boolean> {
39
- // Generate a unique ID for this route
40
- const routeId = this.generateRouteId(route);
41
-
42
- // Skip if route doesn't use NFTables
43
- if (route.action.forwardingEngine !== 'nftables') {
44
- return true;
45
- }
46
-
47
- // Create NFTables options from route configuration
48
- const nftOptions = this.createNfTablesOptions(route);
49
-
50
- // Create and start an NFTablesProxy instance
51
- const proxy = new NfTablesProxy(nftOptions);
52
-
53
- try {
54
- await proxy.start();
55
- this.rulesMap.set(routeId, proxy);
56
- return true;
57
- } catch (err) {
58
- console.error(`Failed to provision NFTables rules for route ${route.name || 'unnamed'}: ${err.message}`);
59
- return false;
60
- }
61
- }
62
-
63
- /**
64
- * Remove NFTables rules for a route
65
- *
66
- * @param route The route configuration
67
- * @returns A promise that resolves to true if successful, false otherwise
68
- */
69
- public async deprovisionRoute(route: IRouteConfig): Promise<boolean> {
70
- const routeId = this.generateRouteId(route);
71
-
72
- const proxy = this.rulesMap.get(routeId);
73
- if (!proxy) {
74
- return true; // Nothing to remove
75
- }
76
-
77
- try {
78
- await proxy.stop();
79
- this.rulesMap.delete(routeId);
80
- return true;
81
- } catch (err) {
82
- console.error(`Failed to deprovision NFTables rules for route ${route.name || 'unnamed'}: ${err.message}`);
83
- return false;
84
- }
85
- }
86
-
87
- /**
88
- * Update NFTables rules when route changes
89
- *
90
- * @param oldRoute The previous route configuration
91
- * @param newRoute The new route configuration
92
- * @returns A promise that resolves to true if successful, false otherwise
93
- */
94
- public async updateRoute(oldRoute: IRouteConfig, newRoute: IRouteConfig): Promise<boolean> {
95
- // Remove old rules and add new ones
96
- await this.deprovisionRoute(oldRoute);
97
- return this.provisionRoute(newRoute);
98
- }
99
-
100
- /**
101
- * Generate a unique ID for a route
102
- *
103
- * @param route The route configuration
104
- * @returns A unique ID string
105
- */
106
- private generateRouteId(route: IRouteConfig): string {
107
- // Generate a unique ID based on route properties
108
- // Include the route name, match criteria, and a timestamp
109
- const matchStr = JSON.stringify({
110
- ports: route.match.ports,
111
- domains: route.match.domains
112
- });
113
-
114
- return `${route.name || 'unnamed'}-${matchStr}-${route.id || Date.now().toString()}`;
115
- }
116
-
117
- /**
118
- * Create NFTablesProxy options from a route configuration
119
- *
120
- * @param route The route configuration
121
- * @returns NFTableProxyOptions object
122
- */
123
- private createNfTablesOptions(route: IRouteConfig): NfTableProxyOptions {
124
- const { action } = route;
125
-
126
- // Ensure we have targets
127
- if (!action.targets || action.targets.length === 0) {
128
- throw new Error('Route must have targets to use NFTables forwarding');
129
- }
130
-
131
- // NFTables can only handle a single target, so we use the first target without match criteria
132
- // or the first target if all have match criteria
133
- const defaultTarget = action.targets.find(t => !t.match) || action.targets[0];
134
-
135
- // Convert port specifications
136
- const fromPorts = this.expandPortRange(route.match.ports);
137
-
138
- // Determine target port
139
- let toPorts: number | PortRange | Array<number | PortRange>;
140
-
141
- if (defaultTarget.port === 'preserve') {
142
- // 'preserve' means use the same ports as the source
143
- toPorts = fromPorts;
144
- } else if (typeof defaultTarget.port === 'function') {
145
- // For function-based ports, we can't determine at setup time
146
- // Use the "preserve" approach and let NFTables handle it
147
- toPorts = fromPorts;
148
- } else {
149
- toPorts = defaultTarget.port;
150
- }
151
-
152
- // Determine target host
153
- let toHost: string;
154
- if (typeof defaultTarget.host === 'function') {
155
- // Can't determine at setup time, use localhost as a placeholder
156
- // and rely on run-time handling
157
- toHost = 'localhost';
158
- } else if (Array.isArray(defaultTarget.host)) {
159
- // Use first host for now - NFTables will do simple round-robin
160
- toHost = defaultTarget.host[0];
161
- } else {
162
- toHost = defaultTarget.host;
163
- }
164
-
165
- // Create options
166
- const options: NfTableProxyOptions = {
167
- fromPort: fromPorts,
168
- toPort: toPorts,
169
- toHost: toHost,
170
- protocol: action.nftables?.protocol || 'tcp',
171
- preserveSourceIP: action.nftables?.preserveSourceIP !== undefined ?
172
- action.nftables.preserveSourceIP :
173
- this.smartProxy.settings.preserveSourceIP,
174
- useIPSets: action.nftables?.useIPSets !== false,
175
- useAdvancedNAT: action.nftables?.useAdvancedNAT,
176
- enableLogging: this.smartProxy.settings.enableDetailedLogging,
177
- deleteOnExit: true,
178
- tableName: action.nftables?.tableName || 'smartproxy'
179
- };
180
-
181
- // Add security-related options
182
- if (route.security?.ipAllowList?.length) {
183
- options.ipAllowList = route.security.ipAllowList;
184
- }
185
-
186
- if (route.security?.ipBlockList?.length) {
187
- options.ipBlockList = route.security.ipBlockList;
188
- }
189
-
190
- // Add QoS options
191
- if (action.nftables?.maxRate || action.nftables?.priority) {
192
- options.qos = {
193
- enabled: true,
194
- maxRate: action.nftables.maxRate,
195
- priority: action.nftables.priority
196
- };
197
- }
198
-
199
- return options;
200
- }
201
-
202
- /**
203
- * Expand port range specifications
204
- *
205
- * @param ports The port range specification
206
- * @returns Expanded port range
207
- */
208
- private expandPortRange(ports: TPortRange): number | PortRange | Array<number | PortRange> {
209
- // Process different port specifications
210
- if (typeof ports === 'number') {
211
- return ports;
212
- } else if (Array.isArray(ports)) {
213
- const result: Array<number | PortRange> = [];
214
-
215
- for (const item of ports) {
216
- if (typeof item === 'number') {
217
- result.push(item);
218
- } else if ('from' in item && 'to' in item) {
219
- result.push({ from: item.from, to: item.to });
220
- }
221
- }
222
-
223
- return result;
224
- } else if (typeof ports === 'object' && ports !== null && 'from' in ports && 'to' in ports) {
225
- return { from: (ports as any).from, to: (ports as any).to };
226
- }
227
-
228
- // Fallback to port 80 if something went wrong
229
- console.warn('Invalid port range specification, using port 80 as fallback');
230
- return 80;
231
- }
232
-
233
- /**
234
- * Get status of all managed rules
235
- *
236
- * @returns A promise that resolves to a record of NFTables status objects
237
- */
238
- public async getStatus(): Promise<Record<string, NfTablesStatus>> {
239
- const result: Record<string, NfTablesStatus> = {};
240
-
241
- for (const [routeId, proxy] of this.rulesMap.entries()) {
242
- result[routeId] = await proxy.getStatus();
243
- }
244
-
245
- return result;
246
- }
247
-
248
- /**
249
- * Check if a route is currently provisioned
250
- *
251
- * @param route The route configuration
252
- * @returns True if the route is provisioned, false otherwise
253
- */
254
- public isRouteProvisioned(route: IRouteConfig): boolean {
255
- const routeId = this.generateRouteId(route);
256
- return this.rulesMap.has(routeId);
257
- }
258
-
259
- /**
260
- * Stop all NFTables rules
261
- *
262
- * @returns A promise that resolves when all rules have been stopped
263
- */
264
- public async stop(): Promise<void> {
265
- // Stop all NFTables proxies
266
- const stopPromises = Array.from(this.rulesMap.values()).map(proxy => proxy.stop());
267
- await Promise.all(stopPromises);
268
-
269
- this.rulesMap.clear();
270
- }
271
- }
@@ -1,358 +0,0 @@
1
- import * as plugins from '../../plugins.js';
2
- import { logger } from '../../core/utils/logger.js';
3
- import { cleanupSocket } from '../../core/utils/socket-utils.js';
4
- import type { SmartProxy } from './smart-proxy.js';
5
-
6
- /**
7
- * PortManager handles the dynamic creation and removal of port listeners
8
- *
9
- * This class provides methods to add and remove listening ports at runtime,
10
- * allowing SmartProxy to adapt to configuration changes without requiring
11
- * a full restart.
12
- *
13
- * It includes a reference counting system to track how many routes are using
14
- * each port, so ports can be automatically released when they are no longer needed.
15
- */
16
- export class PortManager {
17
- private servers: Map<number, plugins.net.Server> = new Map();
18
- private isShuttingDown: boolean = false;
19
- // Track how many routes are using each port
20
- private portRefCounts: Map<number, number> = new Map();
21
-
22
- /**
23
- * Create a new PortManager
24
- *
25
- * @param smartProxy The SmartProxy instance
26
- */
27
- constructor(
28
- private smartProxy: SmartProxy
29
- ) {}
30
-
31
- /**
32
- * Start listening on a specific port
33
- *
34
- * @param port The port number to listen on
35
- * @returns Promise that resolves when the server is listening or rejects on error
36
- */
37
- public async addPort(port: number): Promise<void> {
38
- // Check if we're already listening on this port
39
- if (this.servers.has(port)) {
40
- // Port is already bound, just increment the reference count
41
- this.incrementPortRefCount(port);
42
- try {
43
- logger.log('debug', `PortManager: Port ${port} is already bound by SmartProxy, reusing binding`, {
44
- port,
45
- component: 'port-manager'
46
- });
47
- } catch (e) {
48
- console.log(`[DEBUG] PortManager: Port ${port} is already bound by SmartProxy, reusing binding`);
49
- }
50
- return;
51
- }
52
-
53
- // Initialize reference count for new port
54
- this.portRefCounts.set(port, 1);
55
-
56
- // Create a server for this port
57
- const server = plugins.net.createServer((socket) => {
58
- // Check if shutting down
59
- if (this.isShuttingDown) {
60
- cleanupSocket(socket, 'port-manager-shutdown', { immediate: true });
61
- return;
62
- }
63
-
64
- // Delegate to route connection handler
65
- this.smartProxy.routeConnectionHandler.handleConnection(socket);
66
- }).on('error', (err: Error) => {
67
- try {
68
- logger.log('error', `Server Error on port ${port}: ${err.message}`, {
69
- port,
70
- error: err.message,
71
- component: 'port-manager'
72
- });
73
- } catch (e) {
74
- console.error(`[ERROR] Server Error on port ${port}: ${err.message}`);
75
- }
76
- });
77
-
78
- // Start listening on the port
79
- return new Promise<void>((resolve, reject) => {
80
- server.listen(port, () => {
81
- const isHttpProxyPort = this.smartProxy.settings.useHttpProxy?.includes(port);
82
- try {
83
- logger.log('info', `SmartProxy -> OK: Now listening on port ${port}${
84
- isHttpProxyPort ? ' (HttpProxy forwarding enabled)' : ''
85
- }`, {
86
- port,
87
- isHttpProxyPort: !!isHttpProxyPort,
88
- component: 'port-manager'
89
- });
90
- } catch (e) {
91
- console.log(`[INFO] SmartProxy -> OK: Now listening on port ${port}${
92
- isHttpProxyPort ? ' (HttpProxy forwarding enabled)' : ''
93
- }`);
94
- }
95
-
96
- // Store the server reference
97
- this.servers.set(port, server);
98
- resolve();
99
- }).on('error', (err) => {
100
- // Check if this is an external conflict
101
- const { isConflict, isExternal } = this.isPortConflict(err);
102
-
103
- if (isConflict && !isExternal) {
104
- // This is an internal conflict (port already bound by SmartProxy)
105
- // This shouldn't normally happen because we check servers.has(port) above
106
- logger.log('warn', `Port ${port} binding conflict: already in use by SmartProxy`, {
107
- port,
108
- component: 'port-manager'
109
- });
110
- // Still increment reference count to maintain tracking
111
- this.incrementPortRefCount(port);
112
- resolve();
113
- return;
114
- }
115
-
116
- // Log the error and propagate it
117
- logger.log('error', `Failed to listen on port ${port}: ${err.message}`, {
118
- port,
119
- error: err.message,
120
- code: (err as any).code,
121
- component: 'port-manager'
122
- });
123
-
124
- // Clean up reference count since binding failed
125
- this.portRefCounts.delete(port);
126
-
127
- reject(err);
128
- });
129
- });
130
- }
131
-
132
- /**
133
- * Stop listening on a specific port
134
- *
135
- * @param port The port to stop listening on
136
- * @returns Promise that resolves when the server is closed
137
- */
138
- public async removePort(port: number): Promise<void> {
139
- // Decrement the reference count first
140
- const newRefCount = this.decrementPortRefCount(port);
141
-
142
- // If there are still references to this port, keep it open
143
- if (newRefCount > 0) {
144
- logger.log('debug', `PortManager: Port ${port} still has ${newRefCount} references, keeping open`, {
145
- port,
146
- refCount: newRefCount,
147
- component: 'port-manager'
148
- });
149
- return;
150
- }
151
-
152
- // Get the server for this port
153
- const server = this.servers.get(port);
154
- if (!server) {
155
- logger.log('warn', `PortManager: Not listening on port ${port}`, {
156
- port,
157
- component: 'port-manager'
158
- });
159
- // Ensure reference count is reset
160
- this.portRefCounts.delete(port);
161
- return;
162
- }
163
-
164
- // Close the server
165
- return new Promise<void>((resolve) => {
166
- server.close((err) => {
167
- if (err) {
168
- logger.log('error', `Error closing server on port ${port}: ${err.message}`, {
169
- port,
170
- error: err.message,
171
- component: 'port-manager'
172
- });
173
- } else {
174
- logger.log('info', `SmartProxy -> Stopped listening on port ${port}`, {
175
- port,
176
- component: 'port-manager'
177
- });
178
- }
179
-
180
- // Remove the server reference and clean up reference counting
181
- this.servers.delete(port);
182
- this.portRefCounts.delete(port);
183
- resolve();
184
- });
185
- });
186
- }
187
-
188
- /**
189
- * Add multiple ports at once
190
- *
191
- * @param ports Array of ports to add
192
- * @returns Promise that resolves when all servers are listening
193
- */
194
- public async addPorts(ports: number[]): Promise<void> {
195
- const uniquePorts = [...new Set(ports)];
196
- await Promise.all(uniquePorts.map(port => this.addPort(port)));
197
- }
198
-
199
- /**
200
- * Remove multiple ports at once
201
- *
202
- * @param ports Array of ports to remove
203
- * @returns Promise that resolves when all servers are closed
204
- */
205
- public async removePorts(ports: number[]): Promise<void> {
206
- const uniquePorts = [...new Set(ports)];
207
- await Promise.all(uniquePorts.map(port => this.removePort(port)));
208
- }
209
-
210
- /**
211
- * Update listening ports to match the provided list
212
- *
213
- * This will add any ports that aren't currently listening,
214
- * and remove any ports that are no longer needed.
215
- *
216
- * @param ports Array of ports that should be listening
217
- * @returns Promise that resolves when all operations are complete
218
- */
219
- public async updatePorts(ports: number[]): Promise<void> {
220
- const targetPorts = new Set(ports);
221
- const currentPorts = new Set(this.servers.keys());
222
-
223
- // Find ports to add and remove
224
- const portsToAdd = ports.filter(port => !currentPorts.has(port));
225
- const portsToRemove = Array.from(currentPorts).filter(port => !targetPorts.has(port));
226
-
227
- // Log the changes
228
- if (portsToAdd.length > 0) {
229
- console.log(`PortManager: Adding new listeners for ports: ${portsToAdd.join(', ')}`);
230
- }
231
-
232
- if (portsToRemove.length > 0) {
233
- console.log(`PortManager: Removing listeners for ports: ${portsToRemove.join(', ')}`);
234
- }
235
-
236
- // Add and remove ports
237
- await this.removePorts(portsToRemove);
238
- await this.addPorts(portsToAdd);
239
- }
240
-
241
- /**
242
- * Get all ports that are currently listening
243
- *
244
- * @returns Array of port numbers
245
- */
246
- public getListeningPorts(): number[] {
247
- return Array.from(this.servers.keys());
248
- }
249
-
250
- /**
251
- * Mark the port manager as shutting down
252
- */
253
- public setShuttingDown(isShuttingDown: boolean): void {
254
- this.isShuttingDown = isShuttingDown;
255
- }
256
-
257
- /**
258
- * Close all listening servers
259
- *
260
- * @returns Promise that resolves when all servers are closed
261
- */
262
- public async closeAll(): Promise<void> {
263
- const allPorts = Array.from(this.servers.keys());
264
- await this.removePorts(allPorts);
265
- }
266
-
267
- /**
268
- * Get all server instances (for testing or debugging)
269
- */
270
- public getServers(): Map<number, plugins.net.Server> {
271
- return new Map(this.servers);
272
- }
273
-
274
- /**
275
- * Check if a port is bound by this SmartProxy instance
276
- *
277
- * @param port The port number to check
278
- * @returns True if the port is currently bound by SmartProxy
279
- */
280
- public isPortBoundBySmartProxy(port: number): boolean {
281
- return this.servers.has(port);
282
- }
283
-
284
- /**
285
- * Get the current reference count for a port
286
- *
287
- * @param port The port number to check
288
- * @returns The number of routes using this port, 0 if none
289
- */
290
- public getPortRefCount(port: number): number {
291
- return this.portRefCounts.get(port) || 0;
292
- }
293
-
294
- /**
295
- * Increment the reference count for a port
296
- *
297
- * @param port The port number to increment
298
- * @returns The new reference count
299
- */
300
- public incrementPortRefCount(port: number): number {
301
- const currentCount = this.portRefCounts.get(port) || 0;
302
- const newCount = currentCount + 1;
303
- this.portRefCounts.set(port, newCount);
304
-
305
- logger.log('debug', `Port ${port} reference count increased to ${newCount}`, {
306
- port,
307
- refCount: newCount,
308
- component: 'port-manager'
309
- });
310
-
311
- return newCount;
312
- }
313
-
314
- /**
315
- * Decrement the reference count for a port
316
- *
317
- * @param port The port number to decrement
318
- * @returns The new reference count
319
- */
320
- public decrementPortRefCount(port: number): number {
321
- const currentCount = this.portRefCounts.get(port) || 0;
322
-
323
- if (currentCount <= 0) {
324
- logger.log('warn', `Attempted to decrement reference count for port ${port} below zero`, {
325
- port,
326
- component: 'port-manager'
327
- });
328
- return 0;
329
- }
330
-
331
- const newCount = currentCount - 1;
332
- this.portRefCounts.set(port, newCount);
333
-
334
- logger.log('debug', `Port ${port} reference count decreased to ${newCount}`, {
335
- port,
336
- refCount: newCount,
337
- component: 'port-manager'
338
- });
339
-
340
- return newCount;
341
- }
342
-
343
- /**
344
- * Determine if a port binding error is due to an external or internal conflict
345
- *
346
- * @param error The error object from a failed port binding
347
- * @returns Object indicating if this is a conflict and if it's external
348
- */
349
- private isPortConflict(error: any): { isConflict: boolean; isExternal: boolean } {
350
- if (error.code !== 'EADDRINUSE') {
351
- return { isConflict: false, isExternal: false };
352
- }
353
-
354
- // Check if we already have this port
355
- const isBoundInternally = this.servers.has(Number(error.port));
356
- return { isConflict: true, isExternal: !isBoundInternally };
357
- }
358
- }