@push.rocks/smartproxy 3.25.4 → 3.28.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.
@@ -1,33 +1,259 @@
1
- import * as plugins from './plugins.js';
1
+ import * as http from 'http';
2
+ import * as url from 'url';
3
+ import * as tsclass from '@tsclass/tsclass';
2
4
  export class ProxyRouter {
3
- constructor() {
5
+ constructor(configs, logger) {
6
+ // Store original configs for reference
4
7
  this.reverseProxyConfigs = [];
8
+ // Store path patterns separately since they're not in the original interface
9
+ this.pathPatterns = new Map();
10
+ this.logger = logger || console;
11
+ if (configs) {
12
+ this.setNewProxyConfigs(configs);
13
+ }
5
14
  }
6
15
  /**
7
- * sets a new set of reverse configs to be routed to
8
- * @param reverseCandidatesArg
16
+ * Sets a new set of reverse configs to be routed to
17
+ * @param reverseCandidatesArg Array of reverse proxy configurations
9
18
  */
10
19
  setNewProxyConfigs(reverseCandidatesArg) {
11
- this.reverseProxyConfigs = reverseCandidatesArg;
20
+ this.reverseProxyConfigs = [...reverseCandidatesArg];
21
+ // Find default config if any (config with "*" as hostname)
22
+ this.defaultConfig = this.reverseProxyConfigs.find(config => config.hostName === '*');
23
+ this.logger.info(`Router initialized with ${this.reverseProxyConfigs.length} configs (${this.getHostnames().length} unique hosts)`);
12
24
  }
13
25
  /**
14
- * routes a request
26
+ * Routes a request based on hostname and path
27
+ * @param req The incoming HTTP request
28
+ * @returns The matching proxy config or undefined if no match found
15
29
  */
16
30
  routeReq(req) {
31
+ const result = this.routeReqWithDetails(req);
32
+ return result ? result.config : undefined;
33
+ }
34
+ /**
35
+ * Routes a request with detailed matching information
36
+ * @param req The incoming HTTP request
37
+ * @returns Detailed routing result including matched config and path information
38
+ */
39
+ routeReqWithDetails(req) {
40
+ // Extract and validate host header
17
41
  const originalHost = req.headers.host;
18
42
  if (!originalHost) {
19
- console.error('No host header found in request');
43
+ this.logger.error('No host header found in request');
44
+ return this.defaultConfig ? { config: this.defaultConfig } : undefined;
45
+ }
46
+ // Parse URL for path matching
47
+ const parsedUrl = url.parse(req.url || '/');
48
+ const urlPath = parsedUrl.pathname || '/';
49
+ // Extract hostname without port
50
+ const hostWithoutPort = originalHost.split(':')[0].toLowerCase();
51
+ // First try exact hostname match
52
+ const exactConfig = this.findConfigForHost(hostWithoutPort, urlPath);
53
+ if (exactConfig) {
54
+ return exactConfig;
55
+ }
56
+ // Try wildcard subdomain
57
+ if (hostWithoutPort.includes('.')) {
58
+ const domainParts = hostWithoutPort.split('.');
59
+ if (domainParts.length > 2) {
60
+ const wildcardDomain = `*.${domainParts.slice(1).join('.')}`;
61
+ const wildcardConfig = this.findConfigForHost(wildcardDomain, urlPath);
62
+ if (wildcardConfig) {
63
+ return wildcardConfig;
64
+ }
65
+ }
66
+ }
67
+ // Fall back to default config if available
68
+ if (this.defaultConfig) {
69
+ this.logger.warn(`No specific config found for host: ${hostWithoutPort}, using default`);
70
+ return { config: this.defaultConfig };
71
+ }
72
+ this.logger.error(`No config found for host: ${hostWithoutPort}`);
73
+ return undefined;
74
+ }
75
+ /**
76
+ * Find a config for a specific host and path
77
+ */
78
+ findConfigForHost(hostname, path) {
79
+ // Find all configs for this hostname
80
+ const configs = this.reverseProxyConfigs.filter(config => config.hostName.toLowerCase() === hostname.toLowerCase());
81
+ if (configs.length === 0) {
20
82
  return undefined;
21
83
  }
22
- // Strip port from host if present
23
- const hostWithoutPort = originalHost.split(':')[0];
24
- const correspodingReverseProxyConfig = this.reverseProxyConfigs.find((reverseConfig) => {
25
- return reverseConfig.hostName === hostWithoutPort;
84
+ // First try configs with path patterns
85
+ const configsWithPaths = configs.filter(config => this.pathPatterns.has(config));
86
+ // Sort by path pattern specificity - more specific first
87
+ configsWithPaths.sort((a, b) => {
88
+ const aPattern = this.pathPatterns.get(a) || '';
89
+ const bPattern = this.pathPatterns.get(b) || '';
90
+ // Exact patterns come before wildcard patterns
91
+ const aHasWildcard = aPattern.includes('*');
92
+ const bHasWildcard = bPattern.includes('*');
93
+ if (aHasWildcard && !bHasWildcard)
94
+ return 1;
95
+ if (!aHasWildcard && bHasWildcard)
96
+ return -1;
97
+ // Longer patterns are considered more specific
98
+ return bPattern.length - aPattern.length;
26
99
  });
27
- if (!correspodingReverseProxyConfig) {
28
- console.error(`No config found for host: ${hostWithoutPort}`);
100
+ // Check each config with path pattern
101
+ for (const config of configsWithPaths) {
102
+ const pathPattern = this.pathPatterns.get(config);
103
+ if (pathPattern) {
104
+ const pathMatch = this.matchPath(path, pathPattern);
105
+ if (pathMatch) {
106
+ return {
107
+ config,
108
+ pathMatch: pathMatch.matched,
109
+ pathParams: pathMatch.params,
110
+ pathRemainder: pathMatch.remainder
111
+ };
112
+ }
113
+ }
114
+ }
115
+ // If no path pattern matched, use the first config without a path pattern
116
+ const configWithoutPath = configs.find(config => !this.pathPatterns.has(config));
117
+ if (configWithoutPath) {
118
+ return { config: configWithoutPath };
119
+ }
120
+ return undefined;
121
+ }
122
+ /**
123
+ * Matches a URL path against a pattern
124
+ * Supports:
125
+ * - Exact matches: /users/profile
126
+ * - Wildcards: /api/* (matches any path starting with /api/)
127
+ * - Path parameters: /users/:id (captures id as a parameter)
128
+ *
129
+ * @param path The URL path to match
130
+ * @param pattern The pattern to match against
131
+ * @returns Match result with params and remainder, or null if no match
132
+ */
133
+ matchPath(path, pattern) {
134
+ // Handle exact match
135
+ if (path === pattern) {
136
+ return {
137
+ matched: pattern,
138
+ params: {},
139
+ remainder: ''
140
+ };
141
+ }
142
+ // Handle wildcard match
143
+ if (pattern.endsWith('/*')) {
144
+ const prefix = pattern.slice(0, -2);
145
+ if (path === prefix || path.startsWith(`${prefix}/`)) {
146
+ return {
147
+ matched: prefix,
148
+ params: {},
149
+ remainder: path.slice(prefix.length)
150
+ };
151
+ }
152
+ return null;
153
+ }
154
+ // Handle path parameters
155
+ const patternParts = pattern.split('/').filter(p => p);
156
+ const pathParts = path.split('/').filter(p => p);
157
+ // Too few path parts to match
158
+ if (pathParts.length < patternParts.length) {
159
+ return null;
160
+ }
161
+ const params = {};
162
+ // Compare each part
163
+ for (let i = 0; i < patternParts.length; i++) {
164
+ const patternPart = patternParts[i];
165
+ const pathPart = pathParts[i];
166
+ // Handle parameter
167
+ if (patternPart.startsWith(':')) {
168
+ const paramName = patternPart.slice(1);
169
+ params[paramName] = pathPart;
170
+ continue;
171
+ }
172
+ // Handle wildcard at the end
173
+ if (patternPart === '*' && i === patternParts.length - 1) {
174
+ break;
175
+ }
176
+ // Handle exact match for this part
177
+ if (patternPart !== pathPart) {
178
+ return null;
179
+ }
180
+ }
181
+ // Calculate the remainder - the unmatched path parts
182
+ const remainderParts = pathParts.slice(patternParts.length);
183
+ const remainder = remainderParts.length ? '/' + remainderParts.join('/') : '';
184
+ // Calculate the matched path
185
+ const matchedParts = patternParts.map((part, i) => {
186
+ return part.startsWith(':') ? pathParts[i] : part;
187
+ });
188
+ const matched = '/' + matchedParts.join('/');
189
+ return {
190
+ matched,
191
+ params,
192
+ remainder
193
+ };
194
+ }
195
+ /**
196
+ * Gets all currently active proxy configurations
197
+ * @returns Array of all active configurations
198
+ */
199
+ getProxyConfigs() {
200
+ return [...this.reverseProxyConfigs];
201
+ }
202
+ /**
203
+ * Gets all hostnames that this router is configured to handle
204
+ * @returns Array of hostnames
205
+ */
206
+ getHostnames() {
207
+ const hostnames = new Set();
208
+ for (const config of this.reverseProxyConfigs) {
209
+ if (config.hostName !== '*') {
210
+ hostnames.add(config.hostName.toLowerCase());
211
+ }
212
+ }
213
+ return Array.from(hostnames);
214
+ }
215
+ /**
216
+ * Adds a single new proxy configuration
217
+ * @param config The configuration to add
218
+ * @param pathPattern Optional path pattern for route matching
219
+ */
220
+ addProxyConfig(config, pathPattern) {
221
+ this.reverseProxyConfigs.push(config);
222
+ // Store path pattern if provided
223
+ if (pathPattern) {
224
+ this.pathPatterns.set(config, pathPattern);
225
+ }
226
+ }
227
+ /**
228
+ * Sets a path pattern for an existing config
229
+ * @param config The existing configuration
230
+ * @param pathPattern The path pattern to set
231
+ * @returns Boolean indicating if the config was found and updated
232
+ */
233
+ setPathPattern(config, pathPattern) {
234
+ const exists = this.reverseProxyConfigs.includes(config);
235
+ if (exists) {
236
+ this.pathPatterns.set(config, pathPattern);
237
+ return true;
238
+ }
239
+ return false;
240
+ }
241
+ /**
242
+ * Removes a proxy configuration by hostname
243
+ * @param hostname The hostname to remove
244
+ * @returns Boolean indicating whether any configs were removed
245
+ */
246
+ removeProxyConfig(hostname) {
247
+ const initialCount = this.reverseProxyConfigs.length;
248
+ // Find configs to remove
249
+ const configsToRemove = this.reverseProxyConfigs.filter(config => config.hostName === hostname);
250
+ // Remove them from the patterns map
251
+ for (const config of configsToRemove) {
252
+ this.pathPatterns.delete(config);
29
253
  }
30
- return correspodingReverseProxyConfig;
254
+ // Filter them out of the configs array
255
+ this.reverseProxyConfigs = this.reverseProxyConfigs.filter(config => config.hostName !== hostname);
256
+ return this.reverseProxyConfigs.length !== initialCount;
31
257
  }
32
258
  }
33
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5yb3V0ZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9jbGFzc2VzLnJvdXRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQUV4QyxNQUFNLE9BQU8sV0FBVztJQUF4QjtRQUNTLHdCQUFtQixHQUFrRCxFQUFFLENBQUM7SUE2QmpGLENBQUM7SUEzQkM7OztPQUdHO0lBQ0ksa0JBQWtCLENBQUMsb0JBQW1FO1FBQzNGLElBQUksQ0FBQyxtQkFBbUIsR0FBRyxvQkFBb0IsQ0FBQztJQUNsRCxDQUFDO0lBRUQ7O09BRUc7SUFDSSxRQUFRLENBQUMsR0FBaUM7UUFDL0MsTUFBTSxZQUFZLEdBQUcsR0FBRyxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUM7UUFDdEMsSUFBSSxDQUFDLFlBQVksRUFBRSxDQUFDO1lBQ2xCLE9BQU8sQ0FBQyxLQUFLLENBQUMsaUNBQWlDLENBQUMsQ0FBQztZQUNqRCxPQUFPLFNBQVMsQ0FBQztRQUNuQixDQUFDO1FBQ0Qsa0NBQWtDO1FBQ2xDLE1BQU0sZUFBZSxHQUFHLFlBQVksQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDbkQsTUFBTSw4QkFBOEIsR0FBRyxJQUFJLENBQUMsbUJBQW1CLENBQUMsSUFBSSxDQUFDLENBQUMsYUFBYSxFQUFFLEVBQUU7WUFDckYsT0FBTyxhQUFhLENBQUMsUUFBUSxLQUFLLGVBQWUsQ0FBQztRQUNwRCxDQUFDLENBQUMsQ0FBQztRQUNILElBQUksQ0FBQyw4QkFBOEIsRUFBRSxDQUFDO1lBQ3BDLE9BQU8sQ0FBQyxLQUFLLENBQUMsNkJBQTZCLGVBQWUsRUFBRSxDQUFDLENBQUM7UUFDaEUsQ0FBQztRQUNELE9BQU8sOEJBQThCLENBQUM7SUFDeEMsQ0FBQztDQUNGIn0=
259
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5yb3V0ZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9jbGFzc2VzLnJvdXRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssSUFBSSxNQUFNLE1BQU0sQ0FBQztBQUM3QixPQUFPLEtBQUssR0FBRyxNQUFNLEtBQUssQ0FBQztBQUMzQixPQUFPLEtBQUssT0FBTyxNQUFNLGtCQUFrQixDQUFDO0FBbUI1QyxNQUFNLE9BQU8sV0FBVztJQWV0QixZQUNFLE9BQStDLEVBQy9DLE1BS0M7UUFyQkgsdUNBQXVDO1FBQy9CLHdCQUFtQixHQUEwQyxFQUFFLENBQUM7UUFHeEUsNkVBQTZFO1FBQ3JFLGlCQUFZLEdBQXFELElBQUksR0FBRyxFQUFFLENBQUM7UUFrQmpGLElBQUksQ0FBQyxNQUFNLEdBQUcsTUFBTSxJQUFJLE9BQU8sQ0FBQztRQUNoQyxJQUFJLE9BQU8sRUFBRSxDQUFDO1lBQ1osSUFBSSxDQUFDLGtCQUFrQixDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQ25DLENBQUM7SUFDSCxDQUFDO0lBRUQ7OztPQUdHO0lBQ0ksa0JBQWtCLENBQUMsb0JBQTJEO1FBQ25GLElBQUksQ0FBQyxtQkFBbUIsR0FBRyxDQUFDLEdBQUcsb0JBQW9CLENBQUMsQ0FBQztRQUVyRCwyREFBMkQ7UUFDM0QsSUFBSSxDQUFDLGFBQWEsR0FBRyxJQUFJLENBQUMsbUJBQW1CLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUMsTUFBTSxDQUFDLFFBQVEsS0FBSyxHQUFHLENBQUMsQ0FBQztRQUV0RixJQUFJLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQywyQkFBMkIsSUFBSSxDQUFDLG1CQUFtQixDQUFDLE1BQU0sYUFBYSxJQUFJLENBQUMsWUFBWSxFQUFFLENBQUMsTUFBTSxnQkFBZ0IsQ0FBQyxDQUFDO0lBQ3RJLENBQUM7SUFFRDs7OztPQUlHO0lBQ0ksUUFBUSxDQUFDLEdBQXlCO1FBQ3ZDLE1BQU0s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
@@ -1,9 +1,10 @@
1
+ import { EventEmitter } from 'events';
1
2
  import * as http from 'http';
2
3
  import * as https from 'https';
3
4
  import * as net from 'net';
4
5
  import * as tls from 'tls';
5
6
  import * as url from 'url';
6
- export { http, https, net, tls, url };
7
+ export { EventEmitter, http, https, net, tls, url };
7
8
  import * as tsclass from '@tsclass/tsclass';
8
9
  export { tsclass };
9
10
  import * as lik from '@push.rocks/lik';
@@ -12,8 +13,9 @@ import * as smartpromise from '@push.rocks/smartpromise';
12
13
  import * as smartrequest from '@push.rocks/smartrequest';
13
14
  import * as smartstring from '@push.rocks/smartstring';
14
15
  export { lik, smartdelay, smartrequest, smartpromise, smartstring };
16
+ import * as acme from 'acme-client';
15
17
  import prettyMs from 'pretty-ms';
16
18
  import * as ws from 'ws';
17
19
  import wsDefault from 'ws';
18
20
  import { minimatch } from 'minimatch';
19
- export { prettyMs, ws, wsDefault, minimatch };
21
+ export { acme, prettyMs, ws, wsDefault, minimatch };
@@ -1,10 +1,11 @@
1
1
  // node native scope
2
+ import { EventEmitter } from 'events';
2
3
  import * as http from 'http';
3
4
  import * as https from 'https';
4
5
  import * as net from 'net';
5
6
  import * as tls from 'tls';
6
7
  import * as url from 'url';
7
- export { http, https, net, tls, url };
8
+ export { EventEmitter, http, https, net, tls, url };
8
9
  // tsclass scope
9
10
  import * as tsclass from '@tsclass/tsclass';
10
11
  export { tsclass };
@@ -16,9 +17,10 @@ import * as smartrequest from '@push.rocks/smartrequest';
16
17
  import * as smartstring from '@push.rocks/smartstring';
17
18
  export { lik, smartdelay, smartrequest, smartpromise, smartstring };
18
19
  // third party scope
20
+ import * as acme from 'acme-client';
19
21
  import prettyMs from 'pretty-ms';
20
22
  import * as ws from 'ws';
21
23
  import wsDefault from 'ws';
22
24
  import { minimatch } from 'minimatch';
23
- export { prettyMs, ws, wsDefault, minimatch };
24
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGx1Z2lucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3BsdWdpbnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsb0JBQW9CO0FBQ3BCLE9BQU8sS0FBSyxJQUFJLE1BQU0sTUFBTSxDQUFDO0FBQzdCLE9BQU8sS0FBSyxLQUFLLE1BQU0sT0FBTyxDQUFDO0FBQy9CLE9BQU8sS0FBSyxHQUFHLE1BQU0sS0FBSyxDQUFDO0FBQzNCLE9BQU8sS0FBSyxHQUFHLE1BQU0sS0FBSyxDQUFDO0FBQzNCLE9BQU8sS0FBSyxHQUFHLE1BQU0sS0FBSyxDQUFDO0FBRTNCLE9BQU8sRUFBRSxJQUFJLEVBQUUsS0FBSyxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLENBQUM7QUFFdEMsZ0JBQWdCO0FBQ2hCLE9BQU8sS0FBSyxPQUFPLE1BQU0sa0JBQWtCLENBQUM7QUFFNUMsT0FBTyxFQUFFLE9BQU8sRUFBRSxDQUFDO0FBRW5CLGtCQUFrQjtBQUNsQixPQUFPLEtBQUssR0FBRyxNQUFNLGlCQUFpQixDQUFDO0FBQ3ZDLE9BQU8sS0FBSyxVQUFVLE1BQU0sd0JBQXdCLENBQUM7QUFDckQsT0FBTyxLQUFLLFlBQVksTUFBTSwwQkFBMEIsQ0FBQztBQUN6RCxPQUFPLEtBQUssWUFBWSxNQUFNLDBCQUEwQixDQUFDO0FBQ3pELE9BQU8sS0FBSyxXQUFXLE1BQU0seUJBQXlCLENBQUM7QUFFdkQsT0FBTyxFQUFFLEdBQUcsRUFBRSxVQUFVLEVBQUUsWUFBWSxFQUFFLFlBQVksRUFBRSxXQUFXLEVBQUUsQ0FBQztBQUVwRSxvQkFBb0I7QUFDcEIsT0FBTyxRQUFRLE1BQU0sV0FBVyxDQUFDO0FBQ2pDLE9BQU8sS0FBSyxFQUFFLE1BQU0sSUFBSSxDQUFDO0FBQ3pCLE9BQU8sU0FBUyxNQUFNLElBQUksQ0FBQztBQUMzQixPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0sV0FBVyxDQUFDO0FBRXRDLE9BQU8sRUFBRSxRQUFRLEVBQUUsRUFBRSxFQUFFLFNBQVMsRUFBRSxTQUFTLEVBQUUsQ0FBQyJ9
25
+ export { acme, prettyMs, ws, wsDefault, minimatch };
26
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGx1Z2lucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3BsdWdpbnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsb0JBQW9CO0FBQ3BCLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxRQUFRLENBQUM7QUFDdEMsT0FBTyxLQUFLLElBQUksTUFBTSxNQUFNLENBQUM7QUFDN0IsT0FBTyxLQUFLLEtBQUssTUFBTSxPQUFPLENBQUM7QUFDL0IsT0FBTyxLQUFLLEdBQUcsTUFBTSxLQUFLLENBQUM7QUFDM0IsT0FBTyxLQUFLLEdBQUcsTUFBTSxLQUFLLENBQUM7QUFDM0IsT0FBTyxLQUFLLEdBQUcsTUFBTSxLQUFLLENBQUM7QUFHM0IsT0FBTyxFQUFFLFlBQVksRUFBRSxJQUFJLEVBQUUsS0FBSyxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLENBQUM7QUFFcEQsZ0JBQWdCO0FBQ2hCLE9BQU8sS0FBSyxPQUFPLE1BQU0sa0JBQWtCLENBQUM7QUFFNUMsT0FBTyxFQUFFLE9BQU8sRUFBRSxDQUFDO0FBRW5CLGtCQUFrQjtBQUNsQixPQUFPLEtBQUssR0FBRyxNQUFNLGlCQUFpQixDQUFDO0FBQ3ZDLE9BQU8sS0FBSyxVQUFVLE1BQU0sd0JBQXdCLENBQUM7QUFDckQsT0FBTyxLQUFLLFlBQVksTUFBTSwwQkFBMEIsQ0FBQztBQUN6RCxPQUFPLEtBQUssWUFBWSxNQUFNLDBCQUEwQixDQUFDO0FBQ3pELE9BQU8sS0FBSyxXQUFXLE1BQU0seUJBQXlCLENBQUM7QUFFdkQsT0FBTyxFQUFFLEdBQUcsRUFBRSxVQUFVLEVBQUUsWUFBWSxFQUFFLFlBQVksRUFBRSxXQUFXLEVBQUUsQ0FBQztBQUVwRSxvQkFBb0I7QUFDcEIsT0FBTyxLQUFLLElBQUksTUFBTSxhQUFhLENBQUM7QUFDcEMsT0FBTyxRQUFRLE1BQU0sV0FBVyxDQUFDO0FBQ2pDLE9BQU8sS0FBSyxFQUFFLE1BQU0sSUFBSSxDQUFDO0FBQ3pCLE9BQU8sU0FBUyxNQUFNLElBQUksQ0FBQztBQUMzQixPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0sV0FBVyxDQUFDO0FBRXRDLE9BQU8sRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFLEVBQUUsRUFBRSxTQUFTLEVBQUUsU0FBUyxFQUFFLENBQUMifQ==
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@push.rocks/smartproxy",
3
- "version": "3.25.4",
3
+ "version": "3.28.0",
4
4
  "private": false,
5
5
  "description": "A powerful proxy package that effectively handles high traffic, with features such as SSL/TLS support, port proxying, WebSocket handling, and dynamic routing with authentication options.",
6
6
  "main": "dist_ts/index.js",
package/readme.md CHANGED
@@ -193,12 +193,14 @@ sequenceDiagram
193
193
  - **HTTPS Reverse Proxy** - Route traffic to backend services based on hostname with TLS termination
194
194
  - **WebSocket Support** - Full WebSocket proxying with heartbeat monitoring
195
195
  - **TCP Port Forwarding** - Advanced port forwarding with SNI inspection and domain-based routing
196
+ - **Enhanced TLS Handling** - Robust TLS handshake processing with improved certificate error handling
196
197
  - **HTTP to HTTPS Redirection** - Automatically redirect HTTP requests to HTTPS
197
198
  - **Let's Encrypt Integration** - Automatic certificate management using ACME protocol
198
199
  - **IP Filtering** - Control access with IP allow/block lists using glob patterns
199
200
  - **IPTables Integration** - Direct manipulation of iptables for low-level port forwarding
200
201
  - **Basic Authentication** - Support for basic auth on proxied routes
201
- - **Connection Management** - Intelligent connection tracking and cleanup
202
+ - **Connection Management** - Intelligent connection tracking and cleanup with configurable timeouts
203
+ - **Browser Compatibility** - Optimized for modern browsers with fixes for common TLS handshake issues
202
204
 
203
205
  ## Installation
204
206
 
@@ -275,18 +277,38 @@ const portProxy = new PortProxy({
275
277
  toPort: 8443,
276
278
  targetIP: 'localhost', // Default target host
277
279
  sniEnabled: true, // Enable SNI inspection
280
+
281
+ // Enhanced reliability settings
282
+ initialDataTimeout: 60000, // 60 seconds for initial TLS handshake
283
+ socketTimeout: 3600000, // 1 hour socket timeout
284
+ maxConnectionLifetime: 3600000, // 1 hour connection lifetime
285
+ inactivityTimeout: 3600000, // 1 hour inactivity timeout
286
+ maxPendingDataSize: 10 * 1024 * 1024, // 10MB buffer for large TLS handshakes
287
+
288
+ // Browser compatibility enhancement
289
+ enableTlsDebugLogging: false, // Enable for troubleshooting TLS issues
290
+
291
+ // Port and IP configuration
278
292
  globalPortRanges: [{ from: 443, to: 443 }],
279
293
  defaultAllowedIPs: ['*'], // Allow all IPs by default
294
+
295
+ // Socket optimizations for better connection stability
296
+ noDelay: true, // Disable Nagle's algorithm
297
+ keepAlive: true, // Enable TCP keepalive
298
+ enableKeepAliveProbes: true, // Enhanced keepalive for stability
299
+
300
+ // Domain-specific routing configuration
280
301
  domainConfigs: [
281
302
  {
282
303
  domains: ['example.com', '*.example.com'], // Glob patterns for matching domains
283
304
  allowedIPs: ['192.168.1.*'], // Restrict access by IP
284
305
  blockedIPs: ['192.168.1.100'], // Block specific IPs
285
306
  targetIPs: ['10.0.0.1', '10.0.0.2'], // Round-robin between multiple targets
286
- portRanges: [{ from: 443, to: 443 }]
307
+ portRanges: [{ from: 443, to: 443 }],
308
+ connectionTimeout: 7200000 // Domain-specific timeout (2 hours)
287
309
  }
288
310
  ],
289
- maxConnectionLifetime: 3600000, // 1 hour in milliseconds
311
+
290
312
  preserveSourceIP: true
291
313
  });
292
314
 
@@ -333,19 +355,31 @@ acmeHandler.addDomain('api.example.com');
333
355
 
334
356
  ### PortProxy Settings
335
357
 
336
- | Option | Description | Default |
337
- |--------------------------|--------------------------------------------------------|-------------|
338
- | `fromPort` | Port to listen on | - |
339
- | `toPort` | Destination port to forward to | - |
340
- | `targetIP` | Default destination IP if not specified in domainConfig | 'localhost' |
341
- | `sniEnabled` | Enable SNI inspection for TLS connections | false |
342
- | `defaultAllowedIPs` | IP patterns allowed by default | - |
343
- | `defaultBlockedIPs` | IP patterns blocked by default | - |
344
- | `preserveSourceIP` | Preserve the original client IP | false |
345
- | `maxConnectionLifetime` | Maximum time in ms to keep a connection open | 600000 |
346
- | `globalPortRanges` | Array of port ranges to listen on | - |
347
- | `forwardAllGlobalRanges` | Forward all global range connections to targetIP | false |
348
- | `gracefulShutdownTimeout`| Time in ms to wait during shutdown | 30000 |
358
+ | Option | Description | Default |
359
+ |---------------------------|--------------------------------------------------------|-------------|
360
+ | `fromPort` | Port to listen on | - |
361
+ | `toPort` | Destination port to forward to | - |
362
+ | `targetIP` | Default destination IP if not specified in domainConfig | 'localhost' |
363
+ | `sniEnabled` | Enable SNI inspection for TLS connections | false |
364
+ | `defaultAllowedIPs` | IP patterns allowed by default | - |
365
+ | `defaultBlockedIPs` | IP patterns blocked by default | - |
366
+ | `preserveSourceIP` | Preserve the original client IP | false |
367
+ | `maxConnectionLifetime` | Maximum time in ms to keep a connection open | 3600000 |
368
+ | `initialDataTimeout` | Timeout for initial data/handshake in ms | 60000 |
369
+ | `socketTimeout` | Socket inactivity timeout in ms | 3600000 |
370
+ | `inactivityTimeout` | Connection inactivity check timeout in ms | 3600000 |
371
+ | `inactivityCheckInterval` | How often to check for inactive connections in ms | 60000 |
372
+ | `maxPendingDataSize` | Maximum bytes to buffer during connection setup | 10485760 |
373
+ | `globalPortRanges` | Array of port ranges to listen on | - |
374
+ | `forwardAllGlobalRanges` | Forward all global range connections to targetIP | false |
375
+ | `gracefulShutdownTimeout` | Time in ms to wait during shutdown | 30000 |
376
+ | `noDelay` | Disable Nagle's algorithm | true |
377
+ | `keepAlive` | Enable TCP keepalive | true |
378
+ | `keepAliveInitialDelay` | Initial delay before sending keepalive probes in ms | 30000 |
379
+ | `enableKeepAliveProbes` | Enable enhanced TCP keep-alive probes | false |
380
+ | `enableTlsDebugLogging` | Enable detailed TLS handshake debugging | false |
381
+ | `enableDetailedLogging` | Enable detailed connection logging | false |
382
+ | `enableRandomizedTimeouts`| Randomize timeouts slightly to prevent thundering herd | true |
349
383
 
350
384
  ### IPTablesProxy Settings
351
385
 
@@ -359,14 +393,37 @@ acmeHandler.addDomain('api.example.com');
359
393
 
360
394
  ## Advanced Features
361
395
 
396
+ ### TLS Handshake Optimization
397
+
398
+ The enhanced `PortProxy` implementation includes significant improvements for TLS handshake handling:
399
+
400
+ - Robust SNI extraction with improved error handling
401
+ - Increased buffer size for complex TLS handshakes (10MB)
402
+ - Longer initial handshake timeout (60 seconds)
403
+ - Detection and tracking of TLS connection states
404
+ - Optional detailed TLS debug logging for troubleshooting
405
+ - Browser compatibility fixes for Chrome certificate errors
406
+
407
+ ```typescript
408
+ // Example configuration to solve Chrome certificate errors
409
+ const portProxy = new PortProxy({
410
+ // ... other settings
411
+ initialDataTimeout: 60000, // Give browser more time for handshake
412
+ maxPendingDataSize: 10 * 1024 * 1024, // Larger buffer for complex handshakes
413
+ enableTlsDebugLogging: true, // Enable when troubleshooting
414
+ });
415
+ ```
416
+
362
417
  ### Connection Management and Monitoring
363
418
 
364
419
  The `PortProxy` class includes built-in connection tracking and monitoring:
365
420
 
366
- - Automatic cleanup of idle connections
421
+ - Automatic cleanup of idle connections with configurable timeouts
367
422
  - Timeouts for connections that exceed maximum lifetime
368
423
  - Detailed logging of connection states
369
424
  - Termination statistics
425
+ - Randomized timeouts to prevent "thundering herd" problems
426
+ - Per-domain timeout configuration
370
427
 
371
428
  ### WebSocket Support
372
429
 
@@ -385,6 +442,39 @@ The `PortProxy` class can inspect the SNI (Server Name Indication) field in TLS
385
442
  - Domain-specific allowed IP ranges
386
443
  - Protection against SNI renegotiation attacks
387
444
 
445
+ ## Troubleshooting
446
+
447
+ ### Browser Certificate Errors
448
+
449
+ If you experience certificate errors in browsers, especially in Chrome, try these solutions:
450
+
451
+ 1. **Increase Initial Data Timeout**: Set `initialDataTimeout` to 60 seconds or higher
452
+ 2. **Increase Buffer Size**: Set `maxPendingDataSize` to 10MB or higher
453
+ 3. **Enable TLS Debug Logging**: Set `enableTlsDebugLogging: true` to troubleshoot handshake issues
454
+ 4. **Enable Keep-Alive Probes**: Set `enableKeepAliveProbes: true` for better connection stability
455
+ 5. **Check Certificate Chain**: Ensure your certificate chain is complete and in the correct order
456
+
457
+ ```typescript
458
+ // Configuration to fix Chrome certificate errors
459
+ const portProxy = new PortProxy({
460
+ // ... other settings
461
+ initialDataTimeout: 60000,
462
+ maxPendingDataSize: 10 * 1024 * 1024,
463
+ enableTlsDebugLogging: true,
464
+ enableKeepAliveProbes: true
465
+ });
466
+ ```
467
+
468
+ ### Connection Stability
469
+
470
+ For improved connection stability in high-traffic environments:
471
+
472
+ 1. **Set Appropriate Timeouts**: Use longer timeouts for long-lived connections
473
+ 2. **Use Domain-Specific Timeouts**: Configure per-domain timeouts for different types of services
474
+ 3. **Enable TCP Keep-Alive**: Ensure `keepAlive` is set to `true`
475
+ 4. **Monitor Connection Statistics**: Enable detailed logging to track termination reasons
476
+ 5. **Fine-tune Inactivity Checks**: Adjust `inactivityCheckInterval` based on your traffic patterns
477
+
388
478
  ## License and Legal Information
389
479
 
390
480
  This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository.
@@ -402,4 +492,4 @@ Registered at District court Bremen HRB 35230 HB, Germany
402
492
 
403
493
  For any legal inquiries or if you require further information, please contact us via email at hello@task.vc.
404
494
 
405
- By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.
495
+ By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@push.rocks/smartproxy',
6
- version: '3.25.4',
6
+ version: '3.28.0',
7
7
  description: 'A powerful proxy package that effectively handles high traffic, with features such as SSL/TLS support, port proxying, WebSocket handling, and dynamic routing with authentication options.'
8
8
  }