@push.rocks/smartproxy 20.0.1 → 21.1.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 +26 -0
- package/dist_ts/core/utils/proxy-protocol.d.ts +5 -17
- package/dist_ts/core/utils/proxy-protocol.js +13 -97
- package/dist_ts/core/utils/websocket-utils.d.ts +6 -7
- package/dist_ts/core/utils/websocket-utils.js +10 -66
- package/dist_ts/detection/detectors/http-detector-v2.d.ts +33 -0
- package/dist_ts/detection/detectors/http-detector-v2.js +87 -0
- package/dist_ts/detection/detectors/http-detector.d.ts +33 -0
- package/dist_ts/detection/detectors/http-detector.js +89 -0
- package/dist_ts/detection/detectors/quick-detector.d.ts +28 -0
- package/dist_ts/detection/detectors/quick-detector.js +131 -0
- package/dist_ts/detection/detectors/routing-extractor.d.ts +28 -0
- package/dist_ts/detection/detectors/routing-extractor.js +122 -0
- package/dist_ts/detection/detectors/tls-detector-v2.d.ts +33 -0
- package/dist_ts/detection/detectors/tls-detector-v2.js +80 -0
- package/dist_ts/detection/detectors/tls-detector.d.ts +33 -0
- package/dist_ts/detection/detectors/tls-detector.js +106 -0
- package/dist_ts/detection/index.d.ts +17 -0
- package/dist_ts/detection/index.js +22 -0
- package/dist_ts/detection/models/detection-types.d.ts +87 -0
- package/dist_ts/detection/models/detection-types.js +5 -0
- package/dist_ts/detection/models/interfaces.d.ts +97 -0
- package/dist_ts/detection/models/interfaces.js +5 -0
- package/dist_ts/detection/protocol-detector-v2.d.ts +46 -0
- package/dist_ts/detection/protocol-detector-v2.js +116 -0
- package/dist_ts/detection/protocol-detector.d.ts +74 -0
- package/dist_ts/detection/protocol-detector.js +173 -0
- package/dist_ts/detection/utils/buffer-utils.d.ts +61 -0
- package/dist_ts/detection/utils/buffer-utils.js +127 -0
- package/dist_ts/detection/utils/fragment-manager.d.ts +31 -0
- package/dist_ts/detection/utils/fragment-manager.js +53 -0
- package/dist_ts/detection/utils/parser-utils.d.ts +42 -0
- package/dist_ts/detection/utils/parser-utils.js +63 -0
- package/dist_ts/index.d.ts +2 -1
- package/dist_ts/index.js +3 -2
- package/dist_ts/protocols/common/fragment-handler.d.ts +73 -0
- package/dist_ts/protocols/common/fragment-handler.js +117 -0
- package/dist_ts/protocols/common/index.d.ts +7 -0
- package/dist_ts/protocols/common/index.js +8 -0
- package/dist_ts/protocols/common/types.d.ts +68 -0
- package/dist_ts/protocols/common/types.js +7 -0
- package/dist_ts/protocols/http/constants.d.ts +119 -0
- package/dist_ts/protocols/http/constants.js +200 -0
- package/dist_ts/protocols/http/index.d.ts +7 -0
- package/dist_ts/protocols/http/index.js +8 -0
- package/dist_ts/protocols/http/parser.d.ts +58 -0
- package/dist_ts/protocols/http/parser.js +184 -0
- package/dist_ts/protocols/http/types.d.ts +62 -0
- package/dist_ts/protocols/http/types.js +5 -0
- package/dist_ts/protocols/index.d.ts +11 -0
- package/dist_ts/protocols/index.js +12 -0
- package/dist_ts/protocols/proxy/index.d.ts +6 -0
- package/dist_ts/protocols/proxy/index.js +7 -0
- package/dist_ts/protocols/proxy/parser.d.ts +44 -0
- package/dist_ts/protocols/proxy/parser.js +153 -0
- package/dist_ts/protocols/proxy/types.d.ts +47 -0
- package/dist_ts/protocols/proxy/types.js +6 -0
- package/dist_ts/protocols/tls/alerts/index.d.ts +4 -0
- package/dist_ts/protocols/tls/alerts/index.js +5 -0
- package/dist_ts/protocols/tls/alerts/tls-alert.d.ts +150 -0
- package/dist_ts/protocols/tls/alerts/tls-alert.js +226 -0
- package/dist_ts/protocols/tls/constants.d.ts +122 -0
- package/dist_ts/protocols/tls/constants.js +135 -0
- package/dist_ts/protocols/tls/index.d.ts +12 -0
- package/dist_ts/protocols/tls/index.js +27 -0
- package/dist_ts/protocols/tls/parser.d.ts +53 -0
- package/dist_ts/protocols/tls/parser.js +294 -0
- package/dist_ts/protocols/tls/sni/client-hello-parser.d.ts +100 -0
- package/dist_ts/protocols/tls/sni/client-hello-parser.js +463 -0
- package/dist_ts/protocols/tls/sni/index.d.ts +5 -0
- package/dist_ts/protocols/tls/sni/index.js +6 -0
- package/dist_ts/protocols/tls/sni/sni-extraction.d.ts +58 -0
- package/dist_ts/protocols/tls/sni/sni-extraction.js +275 -0
- package/dist_ts/protocols/tls/types.d.ts +65 -0
- package/dist_ts/protocols/tls/types.js +5 -0
- package/dist_ts/protocols/tls/utils/index.d.ts +4 -0
- package/dist_ts/protocols/tls/utils/index.js +5 -0
- package/dist_ts/protocols/tls/utils/tls-utils.d.ts +158 -0
- package/dist_ts/protocols/tls/utils/tls-utils.js +187 -0
- package/dist_ts/protocols/websocket/constants.d.ts +55 -0
- package/dist_ts/protocols/websocket/constants.js +58 -0
- package/dist_ts/protocols/websocket/index.d.ts +7 -0
- package/dist_ts/protocols/websocket/index.js +8 -0
- package/dist_ts/protocols/websocket/types.d.ts +47 -0
- package/dist_ts/protocols/websocket/types.js +5 -0
- package/dist_ts/protocols/websocket/utils.d.ts +25 -0
- package/dist_ts/protocols/websocket/utils.js +103 -0
- package/dist_ts/proxies/http-proxy/models/http-types.d.ts +25 -27
- package/dist_ts/proxies/http-proxy/models/http-types.js +24 -44
- package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +5 -0
- package/dist_ts/proxies/smart-proxy/models/route-types.js +1 -1
- package/dist_ts/proxies/smart-proxy/route-connection-handler.js +81 -61
- package/dist_ts/proxies/smart-proxy/tls-manager.js +2 -1
- package/dist_ts/proxies/smart-proxy/utils/index.d.ts +1 -2
- package/dist_ts/proxies/smart-proxy/utils/index.js +3 -4
- package/dist_ts/proxies/smart-proxy/utils/route-helpers.d.ts +112 -8
- package/dist_ts/proxies/smart-proxy/utils/route-helpers.js +231 -76
- package/dist_ts/tls/index.d.ts +5 -7
- package/dist_ts/tls/index.js +8 -11
- package/dist_ts/tls/sni/client-hello-parser.js +3 -2
- package/dist_ts/tls/sni/sni-handler.js +4 -4
- package/dist_ts/tls/utils/tls-utils.d.ts +1 -110
- package/dist_ts/tls/utils/tls-utils.js +4 -116
- package/package.json +17 -8
- package/readme.md +471 -2345
- package/readme.plan.md +0 -0
- package/ts/core/utils/proxy-protocol.ts +14 -131
- package/ts/core/utils/websocket-utils.ts +12 -60
- package/ts/detection/detectors/http-detector.ts +114 -0
- package/ts/detection/detectors/quick-detector.ts +148 -0
- package/ts/detection/detectors/routing-extractor.ts +147 -0
- package/ts/detection/detectors/tls-detector.ts +120 -0
- package/ts/detection/index.ts +25 -0
- package/ts/detection/models/detection-types.ts +102 -0
- package/ts/detection/models/interfaces.ts +115 -0
- package/ts/detection/protocol-detector.ts +230 -0
- package/ts/detection/utils/buffer-utils.ts +141 -0
- package/ts/detection/utils/fragment-manager.ts +64 -0
- package/ts/detection/utils/parser-utils.ts +77 -0
- package/ts/index.ts +3 -2
- package/ts/protocols/common/fragment-handler.ts +163 -0
- package/ts/protocols/common/index.ts +8 -0
- package/ts/protocols/common/types.ts +76 -0
- package/ts/protocols/http/constants.ts +219 -0
- package/ts/protocols/http/index.ts +8 -0
- package/ts/protocols/http/parser.ts +219 -0
- package/ts/protocols/http/types.ts +70 -0
- package/ts/protocols/index.ts +12 -0
- package/ts/protocols/proxy/index.ts +7 -0
- package/ts/protocols/proxy/parser.ts +183 -0
- package/ts/protocols/proxy/types.ts +53 -0
- package/ts/{tls → protocols/tls}/alerts/tls-alert.ts +1 -1
- package/ts/protocols/tls/index.ts +37 -0
- package/ts/protocols/tls/sni/index.ts +6 -0
- package/ts/{tls → protocols/tls}/utils/tls-utils.ts +1 -1
- package/ts/protocols/websocket/constants.ts +60 -0
- package/ts/protocols/websocket/index.ts +8 -0
- package/ts/protocols/websocket/types.ts +53 -0
- package/ts/protocols/websocket/utils.ts +98 -0
- package/ts/proxies/http-proxy/models/http-types.ts +29 -46
- package/ts/proxies/smart-proxy/models/interfaces.ts +7 -1
- package/ts/proxies/smart-proxy/models/route-types.ts +0 -1
- package/ts/proxies/smart-proxy/route-connection-handler.ts +91 -68
- package/ts/proxies/smart-proxy/tls-manager.ts +1 -0
- package/ts/proxies/smart-proxy/utils/index.ts +2 -13
- package/ts/proxies/smart-proxy/utils/route-helpers.ts +323 -86
- package/ts/tls/index.ts +8 -12
- package/ts/tls/sni/sni-handler.ts +3 -3
- package/ts/forwarding/config/forwarding-types.ts +0 -76
- package/ts/forwarding/config/index.ts +0 -26
- package/ts/forwarding/factory/forwarding-factory.ts +0 -189
- package/ts/forwarding/factory/index.ts +0 -5
- package/ts/forwarding/handlers/base-handler.ts +0 -155
- package/ts/forwarding/handlers/http-handler.ts +0 -163
- package/ts/forwarding/handlers/https-passthrough-handler.ts +0 -185
- package/ts/forwarding/handlers/https-terminate-to-http-handler.ts +0 -312
- package/ts/forwarding/handlers/https-terminate-to-https-handler.ts +0 -297
- package/ts/forwarding/handlers/index.ts +0 -9
- package/ts/forwarding/index.ts +0 -35
- package/ts/proxies/smart-proxy/utils/route-patterns.ts +0 -403
- /package/ts/{tls → protocols/tls}/alerts/index.ts +0 -0
- /package/ts/{tls → protocols/tls}/sni/client-hello-parser.ts +0 -0
- /package/ts/{tls → protocols/tls}/sni/sni-extraction.ts +0 -0
- /package/ts/{tls → protocols/tls}/utils/index.ts +0 -0
|
@@ -20,6 +20,8 @@
|
|
|
20
20
|
|
|
21
21
|
import * as plugins from '../../../plugins.js';
|
|
22
22
|
import type { IRouteConfig, IRouteMatch, IRouteAction, IRouteTarget, TPortRange, IRouteContext } from '../models/route-types.js';
|
|
23
|
+
import { mergeRouteConfigs } from './route-utils.js';
|
|
24
|
+
import { ProtocolDetector, HttpDetector } from '../../../detection/index.js';
|
|
23
25
|
|
|
24
26
|
/**
|
|
25
27
|
* Create an HTTP-only route configuration
|
|
@@ -211,26 +213,62 @@ export function createCompleteHttpsServer(
|
|
|
211
213
|
/**
|
|
212
214
|
* Create a load balancer route (round-robin between multiple backend hosts)
|
|
213
215
|
* @param domains Domain(s) to match
|
|
214
|
-
* @param
|
|
215
|
-
* @param
|
|
216
|
-
* @param options Additional route options
|
|
216
|
+
* @param backendsOrHosts Array of backend servers OR array of host strings (legacy)
|
|
217
|
+
* @param portOrOptions Port number (legacy) OR options object
|
|
218
|
+
* @param options Additional route options (legacy)
|
|
217
219
|
* @returns Route configuration object
|
|
218
220
|
*/
|
|
219
221
|
export function createLoadBalancerRoute(
|
|
220
222
|
domains: string | string[],
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
options: {
|
|
223
|
+
backendsOrHosts: Array<{ host: string; port: number }> | string[],
|
|
224
|
+
portOrOptions?: number | {
|
|
224
225
|
tls?: {
|
|
225
226
|
mode: 'passthrough' | 'terminate' | 'terminate-and-reencrypt';
|
|
226
227
|
certificate?: 'auto' | { key: string; cert: string };
|
|
227
228
|
};
|
|
229
|
+
useTls?: boolean;
|
|
230
|
+
certificate?: 'auto' | { key: string; cert: string };
|
|
231
|
+
algorithm?: 'round-robin' | 'least-connections' | 'ip-hash';
|
|
232
|
+
healthCheck?: {
|
|
233
|
+
path: string;
|
|
234
|
+
interval: number;
|
|
235
|
+
timeout: number;
|
|
236
|
+
unhealthyThreshold: number;
|
|
237
|
+
healthyThreshold: number;
|
|
238
|
+
};
|
|
228
239
|
[key: string]: any;
|
|
229
|
-
}
|
|
240
|
+
},
|
|
241
|
+
options?: {
|
|
242
|
+
tls?: {
|
|
243
|
+
mode: 'passthrough' | 'terminate' | 'terminate-and-reencrypt';
|
|
244
|
+
certificate?: 'auto' | { key: string; cert: string };
|
|
245
|
+
};
|
|
246
|
+
[key: string]: any;
|
|
247
|
+
}
|
|
230
248
|
): IRouteConfig {
|
|
249
|
+
// Handle legacy signature: (domains, hosts[], port, options)
|
|
250
|
+
let backends: Array<{ host: string; port: number }>;
|
|
251
|
+
let finalOptions: any;
|
|
252
|
+
|
|
253
|
+
if (Array.isArray(backendsOrHosts) && backendsOrHosts.length > 0 && typeof backendsOrHosts[0] === 'string') {
|
|
254
|
+
// Legacy signature
|
|
255
|
+
const hosts = backendsOrHosts as string[];
|
|
256
|
+
const port = portOrOptions as number;
|
|
257
|
+
backends = hosts.map(host => ({ host, port }));
|
|
258
|
+
finalOptions = options || {};
|
|
259
|
+
} else {
|
|
260
|
+
// New signature
|
|
261
|
+
backends = backendsOrHosts as Array<{ host: string; port: number }>;
|
|
262
|
+
finalOptions = (portOrOptions as any) || {};
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Extract hosts and ensure all backends use the same port
|
|
266
|
+
const port = backends[0].port;
|
|
267
|
+
const hosts = backends.map(backend => backend.host);
|
|
268
|
+
|
|
231
269
|
// Create route match
|
|
232
270
|
const match: IRouteMatch = {
|
|
233
|
-
ports:
|
|
271
|
+
ports: finalOptions.match?.ports || (finalOptions.tls || finalOptions.useTls ? 443 : 80),
|
|
234
272
|
domains
|
|
235
273
|
};
|
|
236
274
|
|
|
@@ -247,10 +285,18 @@ export function createLoadBalancerRoute(
|
|
|
247
285
|
};
|
|
248
286
|
|
|
249
287
|
// Add TLS configuration if provided
|
|
250
|
-
if (
|
|
288
|
+
if (finalOptions.tls || finalOptions.useTls) {
|
|
251
289
|
action.tls = {
|
|
252
|
-
mode:
|
|
253
|
-
certificate:
|
|
290
|
+
mode: finalOptions.tls?.mode || 'terminate',
|
|
291
|
+
certificate: finalOptions.tls?.certificate || finalOptions.certificate || 'auto'
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Add load balancing options
|
|
296
|
+
if (finalOptions.algorithm || finalOptions.healthCheck) {
|
|
297
|
+
action.loadBalancing = {
|
|
298
|
+
algorithm: finalOptions.algorithm || 'round-robin',
|
|
299
|
+
healthCheck: finalOptions.healthCheck
|
|
254
300
|
};
|
|
255
301
|
}
|
|
256
302
|
|
|
@@ -258,8 +304,8 @@ export function createLoadBalancerRoute(
|
|
|
258
304
|
return {
|
|
259
305
|
match,
|
|
260
306
|
action,
|
|
261
|
-
name:
|
|
262
|
-
...
|
|
307
|
+
name: finalOptions.name || `Load Balancer for ${Array.isArray(domains) ? domains.join(', ') : domains}`,
|
|
308
|
+
...finalOptions
|
|
263
309
|
};
|
|
264
310
|
}
|
|
265
311
|
|
|
@@ -339,34 +385,61 @@ export function createApiRoute(
|
|
|
339
385
|
/**
|
|
340
386
|
* Create a WebSocket route configuration
|
|
341
387
|
* @param domains Domain(s) to match
|
|
342
|
-
* @param
|
|
343
|
-
* @param
|
|
344
|
-
* @param options Additional route options
|
|
388
|
+
* @param targetOrPath Target server OR WebSocket path (legacy)
|
|
389
|
+
* @param targetOrOptions Target server (legacy) OR options
|
|
390
|
+
* @param options Additional route options (legacy)
|
|
345
391
|
* @returns Route configuration object
|
|
346
392
|
*/
|
|
347
393
|
export function createWebSocketRoute(
|
|
348
394
|
domains: string | string[],
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
options: {
|
|
395
|
+
targetOrPath: { host: string | string[]; port: number } | string,
|
|
396
|
+
targetOrOptions?: { host: string | string[]; port: number } | {
|
|
352
397
|
useTls?: boolean;
|
|
353
398
|
certificate?: 'auto' | { key: string; cert: string };
|
|
399
|
+
path?: string;
|
|
354
400
|
httpPort?: number | number[];
|
|
355
401
|
httpsPort?: number | number[];
|
|
356
402
|
pingInterval?: number;
|
|
357
403
|
pingTimeout?: number;
|
|
358
404
|
name?: string;
|
|
359
405
|
[key: string]: any;
|
|
360
|
-
}
|
|
406
|
+
},
|
|
407
|
+
options?: {
|
|
408
|
+
useTls?: boolean;
|
|
409
|
+
certificate?: 'auto' | { key: string; cert: string };
|
|
410
|
+
httpPort?: number | number[];
|
|
411
|
+
httpsPort?: number | number[];
|
|
412
|
+
pingInterval?: number;
|
|
413
|
+
pingTimeout?: number;
|
|
414
|
+
name?: string;
|
|
415
|
+
[key: string]: any;
|
|
416
|
+
}
|
|
361
417
|
): IRouteConfig {
|
|
418
|
+
// Handle different signatures
|
|
419
|
+
let target: { host: string | string[]; port: number };
|
|
420
|
+
let wsPath: string;
|
|
421
|
+
let finalOptions: any;
|
|
422
|
+
|
|
423
|
+
if (typeof targetOrPath === 'string') {
|
|
424
|
+
// Legacy signature: (domains, path, target, options)
|
|
425
|
+
wsPath = targetOrPath;
|
|
426
|
+
target = targetOrOptions as { host: string | string[]; port: number };
|
|
427
|
+
finalOptions = options || {};
|
|
428
|
+
} else {
|
|
429
|
+
// New signature: (domains, target, options)
|
|
430
|
+
target = targetOrPath;
|
|
431
|
+
finalOptions = (targetOrOptions as any) || {};
|
|
432
|
+
wsPath = finalOptions.path || '/ws';
|
|
433
|
+
}
|
|
434
|
+
|
|
362
435
|
// Normalize WebSocket path
|
|
363
436
|
const normalizedPath = wsPath.startsWith('/') ? wsPath : `/${wsPath}`;
|
|
364
437
|
|
|
365
438
|
// Create route match
|
|
366
439
|
const match: IRouteMatch = {
|
|
367
|
-
ports:
|
|
368
|
-
? (
|
|
369
|
-
: (
|
|
440
|
+
ports: finalOptions.useTls
|
|
441
|
+
? (finalOptions.httpsPort || 443)
|
|
442
|
+
: (finalOptions.httpPort || 80),
|
|
370
443
|
domains,
|
|
371
444
|
path: normalizedPath
|
|
372
445
|
};
|
|
@@ -377,16 +450,16 @@ export function createWebSocketRoute(
|
|
|
377
450
|
targets: [target],
|
|
378
451
|
websocket: {
|
|
379
452
|
enabled: true,
|
|
380
|
-
pingInterval:
|
|
381
|
-
pingTimeout:
|
|
453
|
+
pingInterval: finalOptions.pingInterval || 30000, // 30 seconds
|
|
454
|
+
pingTimeout: finalOptions.pingTimeout || 5000 // 5 seconds
|
|
382
455
|
}
|
|
383
456
|
};
|
|
384
457
|
|
|
385
458
|
// Add TLS configuration if using HTTPS
|
|
386
|
-
if (
|
|
459
|
+
if (finalOptions.useTls) {
|
|
387
460
|
action.tls = {
|
|
388
461
|
mode: 'terminate',
|
|
389
|
-
certificate:
|
|
462
|
+
certificate: finalOptions.certificate || 'auto'
|
|
390
463
|
};
|
|
391
464
|
}
|
|
392
465
|
|
|
@@ -394,9 +467,9 @@ export function createWebSocketRoute(
|
|
|
394
467
|
return {
|
|
395
468
|
match,
|
|
396
469
|
action,
|
|
397
|
-
name:
|
|
398
|
-
priority:
|
|
399
|
-
...
|
|
470
|
+
name: finalOptions.name || `WebSocket Route ${normalizedPath} for ${Array.isArray(domains) ? domains.join(', ') : domains}`,
|
|
471
|
+
priority: finalOptions.priority || 100, // Higher priority for WebSocket routes
|
|
472
|
+
...finalOptions
|
|
400
473
|
};
|
|
401
474
|
}
|
|
402
475
|
|
|
@@ -884,83 +957,91 @@ export const SocketHandlers = {
|
|
|
884
957
|
|
|
885
958
|
/**
|
|
886
959
|
* HTTP redirect handler
|
|
960
|
+
* Now uses the centralized detection module for HTTP parsing
|
|
887
961
|
*/
|
|
888
962
|
httpRedirect: (locationTemplate: string, statusCode: number = 301) => (socket: plugins.net.Socket, context: IRouteContext) => {
|
|
889
|
-
|
|
963
|
+
const connectionId = ProtocolDetector.createConnectionId({
|
|
964
|
+
socketId: context.connectionId || `${Date.now()}-${Math.random()}`
|
|
965
|
+
});
|
|
890
966
|
|
|
891
|
-
socket.once('data', (data) => {
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
const domain = context.domain || 'localhost';
|
|
899
|
-
const port = context.port;
|
|
967
|
+
socket.once('data', async (data) => {
|
|
968
|
+
// Use detection module for parsing
|
|
969
|
+
const detectionResult = await ProtocolDetector.detectWithConnectionTracking(
|
|
970
|
+
data,
|
|
971
|
+
connectionId,
|
|
972
|
+
{ extractFullHeaders: false } // We only need method and path
|
|
973
|
+
);
|
|
900
974
|
|
|
901
|
-
|
|
902
|
-
.
|
|
903
|
-
.
|
|
904
|
-
|
|
905
|
-
.
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
975
|
+
if (detectionResult.protocol === 'http' && detectionResult.connectionInfo.path) {
|
|
976
|
+
const method = detectionResult.connectionInfo.method || 'GET';
|
|
977
|
+
const path = detectionResult.connectionInfo.path || '/';
|
|
978
|
+
|
|
979
|
+
const domain = context.domain || 'localhost';
|
|
980
|
+
const port = context.port;
|
|
981
|
+
|
|
982
|
+
let finalLocation = locationTemplate
|
|
983
|
+
.replace('{domain}', domain)
|
|
984
|
+
.replace('{port}', String(port))
|
|
985
|
+
.replace('{path}', path)
|
|
986
|
+
.replace('{clientIp}', context.clientIp);
|
|
987
|
+
|
|
988
|
+
const message = `Redirecting to ${finalLocation}`;
|
|
989
|
+
const response = [
|
|
990
|
+
`HTTP/1.1 ${statusCode} ${statusCode === 301 ? 'Moved Permanently' : 'Found'}`,
|
|
991
|
+
`Location: ${finalLocation}`,
|
|
992
|
+
'Content-Type: text/plain',
|
|
993
|
+
`Content-Length: ${message.length}`,
|
|
994
|
+
'Connection: close',
|
|
995
|
+
'',
|
|
996
|
+
message
|
|
997
|
+
].join('\r\n');
|
|
998
|
+
|
|
999
|
+
socket.write(response);
|
|
1000
|
+
} else {
|
|
1001
|
+
// Not a valid HTTP request, close connection
|
|
1002
|
+
socket.write('HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n');
|
|
1003
|
+
}
|
|
917
1004
|
|
|
918
|
-
socket.write(response);
|
|
919
1005
|
socket.end();
|
|
1006
|
+
// Clean up detection state
|
|
1007
|
+
ProtocolDetector.cleanupConnections();
|
|
920
1008
|
});
|
|
921
1009
|
},
|
|
922
1010
|
|
|
923
1011
|
/**
|
|
924
1012
|
* HTTP server handler for ACME challenges and other HTTP needs
|
|
1013
|
+
* Now uses the centralized detection module for HTTP parsing
|
|
925
1014
|
*/
|
|
926
1015
|
httpServer: (handler: (req: { method: string; url: string; headers: Record<string, string>; body?: string }, res: { status: (code: number) => void; header: (name: string, value: string) => void; send: (data: string) => void; end: () => void }) => void) => (socket: plugins.net.Socket, context: IRouteContext) => {
|
|
927
|
-
let buffer = '';
|
|
928
1016
|
let requestParsed = false;
|
|
1017
|
+
const connectionId = ProtocolDetector.createConnectionId({
|
|
1018
|
+
socketId: context.connectionId || `${Date.now()}-${Math.random()}`
|
|
1019
|
+
});
|
|
929
1020
|
|
|
930
|
-
|
|
1021
|
+
const processData = async (data: Buffer) => {
|
|
931
1022
|
if (requestParsed) return; // Only handle the first request
|
|
932
1023
|
|
|
933
|
-
|
|
1024
|
+
// Use HttpDetector for parsing
|
|
1025
|
+
const detectionResult = await ProtocolDetector.detectWithConnectionTracking(
|
|
1026
|
+
data,
|
|
1027
|
+
connectionId,
|
|
1028
|
+
{ extractFullHeaders: true }
|
|
1029
|
+
);
|
|
934
1030
|
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
1031
|
+
if (detectionResult.protocol !== 'http' || !detectionResult.isComplete) {
|
|
1032
|
+
// Not a complete HTTP request yet
|
|
1033
|
+
return;
|
|
1034
|
+
}
|
|
938
1035
|
|
|
939
1036
|
requestParsed = true;
|
|
1037
|
+
const connInfo = detectionResult.connectionInfo;
|
|
940
1038
|
|
|
941
|
-
//
|
|
942
|
-
const headerPart = buffer.substring(0, headerEndIndex);
|
|
943
|
-
const bodyPart = buffer.substring(headerEndIndex + 4);
|
|
944
|
-
|
|
945
|
-
const lines = headerPart.split('\r\n');
|
|
946
|
-
const [method, url] = lines[0].split(' ');
|
|
947
|
-
|
|
948
|
-
const headers: Record<string, string> = {};
|
|
949
|
-
for (let i = 1; i < lines.length; i++) {
|
|
950
|
-
const colonIndex = lines[i].indexOf(':');
|
|
951
|
-
if (colonIndex > 0) {
|
|
952
|
-
const name = lines[i].substring(0, colonIndex).trim().toLowerCase();
|
|
953
|
-
const value = lines[i].substring(colonIndex + 1).trim();
|
|
954
|
-
headers[name] = value;
|
|
955
|
-
}
|
|
956
|
-
}
|
|
957
|
-
|
|
958
|
-
// Create request object
|
|
1039
|
+
// Create request object from detection result
|
|
959
1040
|
const req = {
|
|
960
|
-
method: method || 'GET',
|
|
961
|
-
url:
|
|
962
|
-
headers,
|
|
963
|
-
body:
|
|
1041
|
+
method: connInfo.method || 'GET',
|
|
1042
|
+
url: connInfo.path || '/',
|
|
1043
|
+
headers: connInfo.headers || {},
|
|
1044
|
+
body: detectionResult.remainingBuffer?.toString() || ''
|
|
964
1045
|
};
|
|
965
1046
|
|
|
966
1047
|
// Create response object
|
|
@@ -1021,12 +1102,168 @@ export const SocketHandlers = {
|
|
|
1021
1102
|
res.send('Internal Server Error');
|
|
1022
1103
|
}
|
|
1023
1104
|
}
|
|
1024
|
-
}
|
|
1105
|
+
};
|
|
1106
|
+
|
|
1107
|
+
socket.on('data', processData);
|
|
1025
1108
|
|
|
1026
1109
|
socket.on('error', () => {
|
|
1027
1110
|
if (!requestParsed) {
|
|
1028
1111
|
socket.end();
|
|
1029
1112
|
}
|
|
1030
1113
|
});
|
|
1114
|
+
|
|
1115
|
+
socket.on('close', () => {
|
|
1116
|
+
// Clean up detection state
|
|
1117
|
+
ProtocolDetector.cleanupConnections();
|
|
1118
|
+
});
|
|
1031
1119
|
}
|
|
1032
1120
|
};
|
|
1121
|
+
|
|
1122
|
+
/**
|
|
1123
|
+
* Create an API Gateway route pattern
|
|
1124
|
+
* @param domains Domain(s) to match
|
|
1125
|
+
* @param apiBasePath Base path for API endpoints (e.g., '/api')
|
|
1126
|
+
* @param target Target host and port
|
|
1127
|
+
* @param options Additional route options
|
|
1128
|
+
* @returns API route configuration
|
|
1129
|
+
*/
|
|
1130
|
+
export function createApiGatewayRoute(
|
|
1131
|
+
domains: string | string[],
|
|
1132
|
+
apiBasePath: string,
|
|
1133
|
+
target: { host: string | string[]; port: number },
|
|
1134
|
+
options: {
|
|
1135
|
+
useTls?: boolean;
|
|
1136
|
+
certificate?: 'auto' | { key: string; cert: string };
|
|
1137
|
+
addCorsHeaders?: boolean;
|
|
1138
|
+
[key: string]: any;
|
|
1139
|
+
} = {}
|
|
1140
|
+
): IRouteConfig {
|
|
1141
|
+
// Normalize apiBasePath to ensure it starts with / and doesn't end with /
|
|
1142
|
+
const normalizedPath = apiBasePath.startsWith('/')
|
|
1143
|
+
? apiBasePath
|
|
1144
|
+
: `/${apiBasePath}`;
|
|
1145
|
+
|
|
1146
|
+
// Add wildcard to path to match all API endpoints
|
|
1147
|
+
const apiPath = normalizedPath.endsWith('/')
|
|
1148
|
+
? `${normalizedPath}*`
|
|
1149
|
+
: `${normalizedPath}/*`;
|
|
1150
|
+
|
|
1151
|
+
// Create base route
|
|
1152
|
+
const baseRoute = options.useTls
|
|
1153
|
+
? createHttpsTerminateRoute(domains, target, {
|
|
1154
|
+
certificate: options.certificate || 'auto'
|
|
1155
|
+
})
|
|
1156
|
+
: createHttpRoute(domains, target);
|
|
1157
|
+
|
|
1158
|
+
// Add API-specific configurations
|
|
1159
|
+
const apiRoute: Partial<IRouteConfig> = {
|
|
1160
|
+
match: {
|
|
1161
|
+
...baseRoute.match,
|
|
1162
|
+
path: apiPath
|
|
1163
|
+
},
|
|
1164
|
+
name: options.name || `API Gateway: ${apiPath} -> ${Array.isArray(target.host) ? target.host.join(', ') : target.host}:${target.port}`,
|
|
1165
|
+
priority: options.priority || 100 // Higher priority for specific path matching
|
|
1166
|
+
};
|
|
1167
|
+
|
|
1168
|
+
// Add CORS headers if requested
|
|
1169
|
+
if (options.addCorsHeaders) {
|
|
1170
|
+
apiRoute.headers = {
|
|
1171
|
+
response: {
|
|
1172
|
+
'Access-Control-Allow-Origin': '*',
|
|
1173
|
+
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
|
|
1174
|
+
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
|
|
1175
|
+
'Access-Control-Max-Age': '86400'
|
|
1176
|
+
}
|
|
1177
|
+
};
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
return mergeRouteConfigs(baseRoute, apiRoute);
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
/**
|
|
1184
|
+
* Create a rate limiting route pattern
|
|
1185
|
+
* @param baseRoute Base route to add rate limiting to
|
|
1186
|
+
* @param rateLimit Rate limiting configuration
|
|
1187
|
+
* @returns Route with rate limiting
|
|
1188
|
+
*/
|
|
1189
|
+
export function addRateLimiting(
|
|
1190
|
+
baseRoute: IRouteConfig,
|
|
1191
|
+
rateLimit: {
|
|
1192
|
+
maxRequests: number;
|
|
1193
|
+
window: number; // Time window in seconds
|
|
1194
|
+
keyBy?: 'ip' | 'path' | 'header';
|
|
1195
|
+
headerName?: string; // Required if keyBy is 'header'
|
|
1196
|
+
errorMessage?: string;
|
|
1197
|
+
}
|
|
1198
|
+
): IRouteConfig {
|
|
1199
|
+
return mergeRouteConfigs(baseRoute, {
|
|
1200
|
+
security: {
|
|
1201
|
+
rateLimit: {
|
|
1202
|
+
enabled: true,
|
|
1203
|
+
maxRequests: rateLimit.maxRequests,
|
|
1204
|
+
window: rateLimit.window,
|
|
1205
|
+
keyBy: rateLimit.keyBy || 'ip',
|
|
1206
|
+
headerName: rateLimit.headerName,
|
|
1207
|
+
errorMessage: rateLimit.errorMessage || 'Rate limit exceeded. Please try again later.'
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
});
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
/**
|
|
1214
|
+
* Create a basic authentication route pattern
|
|
1215
|
+
* @param baseRoute Base route to add authentication to
|
|
1216
|
+
* @param auth Authentication configuration
|
|
1217
|
+
* @returns Route with basic authentication
|
|
1218
|
+
*/
|
|
1219
|
+
export function addBasicAuth(
|
|
1220
|
+
baseRoute: IRouteConfig,
|
|
1221
|
+
auth: {
|
|
1222
|
+
users: Array<{ username: string; password: string }>;
|
|
1223
|
+
realm?: string;
|
|
1224
|
+
excludePaths?: string[];
|
|
1225
|
+
}
|
|
1226
|
+
): IRouteConfig {
|
|
1227
|
+
return mergeRouteConfigs(baseRoute, {
|
|
1228
|
+
security: {
|
|
1229
|
+
basicAuth: {
|
|
1230
|
+
enabled: true,
|
|
1231
|
+
users: auth.users,
|
|
1232
|
+
realm: auth.realm || 'Restricted Area',
|
|
1233
|
+
excludePaths: auth.excludePaths || []
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
});
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
/**
|
|
1240
|
+
* Create a JWT authentication route pattern
|
|
1241
|
+
* @param baseRoute Base route to add JWT authentication to
|
|
1242
|
+
* @param jwt JWT authentication configuration
|
|
1243
|
+
* @returns Route with JWT authentication
|
|
1244
|
+
*/
|
|
1245
|
+
export function addJwtAuth(
|
|
1246
|
+
baseRoute: IRouteConfig,
|
|
1247
|
+
jwt: {
|
|
1248
|
+
secret: string;
|
|
1249
|
+
algorithm?: string;
|
|
1250
|
+
issuer?: string;
|
|
1251
|
+
audience?: string;
|
|
1252
|
+
expiresIn?: number; // Time in seconds
|
|
1253
|
+
excludePaths?: string[];
|
|
1254
|
+
}
|
|
1255
|
+
): IRouteConfig {
|
|
1256
|
+
return mergeRouteConfigs(baseRoute, {
|
|
1257
|
+
security: {
|
|
1258
|
+
jwtAuth: {
|
|
1259
|
+
enabled: true,
|
|
1260
|
+
secret: jwt.secret,
|
|
1261
|
+
algorithm: jwt.algorithm || 'HS256',
|
|
1262
|
+
issuer: jwt.issuer,
|
|
1263
|
+
audience: jwt.audience,
|
|
1264
|
+
expiresIn: jwt.expiresIn,
|
|
1265
|
+
excludePaths: jwt.excludePaths || []
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
});
|
|
1269
|
+
}
|
package/ts/tls/index.ts
CHANGED
|
@@ -1,22 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* TLS module
|
|
2
|
+
* TLS module for smartproxy
|
|
3
|
+
* Re-exports protocol components and provides smartproxy-specific functionality
|
|
3
4
|
*/
|
|
4
5
|
|
|
5
|
-
//
|
|
6
|
-
export * from '
|
|
6
|
+
// Re-export all protocol components from protocols/tls
|
|
7
|
+
export * from '../protocols/tls/index.js';
|
|
7
8
|
|
|
8
|
-
// Export SNI
|
|
9
|
+
// Export smartproxy-specific SNI handler
|
|
9
10
|
export * from './sni/sni-handler.js';
|
|
10
|
-
export * from './sni/sni-extraction.js';
|
|
11
|
-
export * from './sni/client-hello-parser.js';
|
|
12
|
-
|
|
13
|
-
// Export TLS utilities
|
|
14
|
-
export * from './utils/tls-utils.js';
|
|
15
11
|
|
|
16
12
|
// Create a namespace for SNI utilities
|
|
17
13
|
import { SniHandler } from './sni/sni-handler.js';
|
|
18
|
-
import { SniExtraction } from '
|
|
19
|
-
import { ClientHelloParser } from '
|
|
14
|
+
import { SniExtraction } from '../protocols/tls/sni/sni-extraction.js';
|
|
15
|
+
import { ClientHelloParser } from '../protocols/tls/sni/client-hello-parser.js';
|
|
20
16
|
|
|
21
17
|
// Export utility objects for convenience
|
|
22
18
|
export const SNI = {
|
|
@@ -30,4 +26,4 @@ export const SNI = {
|
|
|
30
26
|
// Convenience functions
|
|
31
27
|
extractSNI: SniHandler.extractSNI,
|
|
32
28
|
processTlsPacket: SniHandler.processTlsPacket,
|
|
33
|
-
};
|
|
29
|
+
};
|
|
@@ -4,15 +4,15 @@ import {
|
|
|
4
4
|
TlsHandshakeType,
|
|
5
5
|
TlsExtensionType,
|
|
6
6
|
TlsUtils
|
|
7
|
-
} from '
|
|
7
|
+
} from '../../protocols/tls/utils/tls-utils.js';
|
|
8
8
|
import {
|
|
9
9
|
ClientHelloParser,
|
|
10
10
|
type LoggerFunction
|
|
11
|
-
} from '
|
|
11
|
+
} from '../../protocols/tls/sni/client-hello-parser.js';
|
|
12
12
|
import {
|
|
13
13
|
SniExtraction,
|
|
14
14
|
type ConnectionInfo
|
|
15
|
-
} from '
|
|
15
|
+
} from '../../protocols/tls/sni/sni-extraction.js';
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* SNI (Server Name Indication) handler for TLS connections.
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import type * as plugins from '../../plugins.js';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* The primary forwarding types supported by SmartProxy
|
|
5
|
-
* Used for configuration compatibility
|
|
6
|
-
*/
|
|
7
|
-
export type TForwardingType =
|
|
8
|
-
| 'http-only' // HTTP forwarding only (no HTTPS)
|
|
9
|
-
| 'https-passthrough' // Pass-through TLS traffic (SNI forwarding)
|
|
10
|
-
| 'https-terminate-to-http' // Terminate TLS and forward to HTTP backend
|
|
11
|
-
| 'https-terminate-to-https'; // Terminate TLS and forward to HTTPS backend
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Event types emitted by forwarding handlers
|
|
15
|
-
*/
|
|
16
|
-
export enum ForwardingHandlerEvents {
|
|
17
|
-
CONNECTED = 'connected',
|
|
18
|
-
DISCONNECTED = 'disconnected',
|
|
19
|
-
ERROR = 'error',
|
|
20
|
-
DATA_FORWARDED = 'data-forwarded',
|
|
21
|
-
HTTP_REQUEST = 'http-request',
|
|
22
|
-
HTTP_RESPONSE = 'http-response',
|
|
23
|
-
CERTIFICATE_NEEDED = 'certificate-needed',
|
|
24
|
-
CERTIFICATE_LOADED = 'certificate-loaded'
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Base interface for forwarding handlers
|
|
29
|
-
*/
|
|
30
|
-
export interface IForwardingHandler extends plugins.EventEmitter {
|
|
31
|
-
initialize(): Promise<void>;
|
|
32
|
-
handleConnection(socket: plugins.net.Socket): void;
|
|
33
|
-
handleHttpRequest(req: plugins.http.IncomingMessage, res: plugins.http.ServerResponse): void;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// Route-based helpers are now available directly from route-patterns.ts
|
|
37
|
-
import {
|
|
38
|
-
createHttpRoute,
|
|
39
|
-
createHttpsTerminateRoute,
|
|
40
|
-
createHttpsPassthroughRoute,
|
|
41
|
-
createHttpToHttpsRedirect,
|
|
42
|
-
createCompleteHttpsServer,
|
|
43
|
-
createLoadBalancerRoute
|
|
44
|
-
} from '../../proxies/smart-proxy/utils/route-patterns.js';
|
|
45
|
-
|
|
46
|
-
export {
|
|
47
|
-
createHttpRoute,
|
|
48
|
-
createHttpsTerminateRoute,
|
|
49
|
-
createHttpsPassthroughRoute,
|
|
50
|
-
createHttpToHttpsRedirect,
|
|
51
|
-
createCompleteHttpsServer,
|
|
52
|
-
createLoadBalancerRoute
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
// Note: Legacy helper functions have been removed
|
|
56
|
-
// Please use the route-based helpers instead:
|
|
57
|
-
// - createHttpRoute
|
|
58
|
-
// - createHttpsTerminateRoute
|
|
59
|
-
// - createHttpsPassthroughRoute
|
|
60
|
-
// - createHttpToHttpsRedirect
|
|
61
|
-
import type { IRouteConfig } from '../../proxies/smart-proxy/models/route-types.js';
|
|
62
|
-
|
|
63
|
-
// For backward compatibility, kept only the basic configuration interface
|
|
64
|
-
export interface IForwardConfig {
|
|
65
|
-
type: TForwardingType;
|
|
66
|
-
target: {
|
|
67
|
-
host: string | string[];
|
|
68
|
-
port: number | 'preserve' | ((ctx: any) => number);
|
|
69
|
-
};
|
|
70
|
-
http?: any;
|
|
71
|
-
https?: any;
|
|
72
|
-
acme?: any;
|
|
73
|
-
security?: any;
|
|
74
|
-
advanced?: any;
|
|
75
|
-
[key: string]: any;
|
|
76
|
-
}
|