@push.rocks/smartproxy 19.4.1 → 19.5.2
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/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/proxies/http-proxy/handlers/index.d.ts +1 -2
- package/dist_ts/proxies/http-proxy/handlers/index.js +3 -3
- package/dist_ts/proxies/smart-proxy/certificate-manager.js +30 -25
- package/dist_ts/proxies/smart-proxy/models/route-types.d.ts +9 -40
- package/dist_ts/proxies/smart-proxy/models/route-types.js +1 -1
- package/dist_ts/proxies/smart-proxy/route-connection-handler.d.ts +2 -10
- package/dist_ts/proxies/smart-proxy/route-connection-handler.js +69 -43
- package/dist_ts/proxies/smart-proxy/utils/index.d.ts +2 -2
- package/dist_ts/proxies/smart-proxy/utils/index.js +3 -3
- package/dist_ts/proxies/smart-proxy/utils/route-helpers.d.ts +61 -20
- package/dist_ts/proxies/smart-proxy/utils/route-helpers.js +240 -45
- package/dist_ts/proxies/smart-proxy/utils/route-patterns.d.ts +0 -18
- package/dist_ts/proxies/smart-proxy/utils/route-patterns.js +4 -43
- package/dist_ts/proxies/smart-proxy/utils/route-utils.js +14 -15
- package/dist_ts/proxies/smart-proxy/utils/route-validators.js +10 -31
- package/package.json +7 -7
- package/readme.hints.md +38 -1
- package/readme.plan.md +314 -382
- package/readme.plan2.md +764 -0
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/proxies/http-proxy/handlers/index.ts +1 -2
- package/ts/proxies/smart-proxy/certificate-manager.ts +29 -23
- package/ts/proxies/smart-proxy/models/route-types.ts +12 -56
- package/ts/proxies/smart-proxy/route-connection-handler.ts +73 -60
- package/ts/proxies/smart-proxy/utils/index.ts +0 -2
- package/ts/proxies/smart-proxy/utils/route-helpers.ts +278 -61
- package/ts/proxies/smart-proxy/utils/route-patterns.ts +6 -56
- package/ts/proxies/smart-proxy/utils/route-utils.ts +12 -15
- package/ts/proxies/smart-proxy/utils/route-validators.ts +9 -31
- package/ts/proxies/http-proxy/handlers/redirect-handler.ts +0 -105
- package/ts/proxies/http-proxy/handlers/static-handler.ts +0 -261
package/ts/00_commitinfo_data.ts
CHANGED
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@push.rocks/smartproxy',
|
|
6
|
-
version: '19.
|
|
6
|
+
version: '19.5.2',
|
|
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
|
}
|
|
@@ -5,6 +5,7 @@ import type { IAcmeOptions } from './models/interfaces.js';
|
|
|
5
5
|
import { CertStore } from './cert-store.js';
|
|
6
6
|
import type { AcmeStateManager } from './acme-state-manager.js';
|
|
7
7
|
import { logger } from '../../core/utils/logger.js';
|
|
8
|
+
import { SocketHandlers } from './utils/route-helpers.js';
|
|
8
9
|
|
|
9
10
|
export interface ICertStatus {
|
|
10
11
|
domain: string;
|
|
@@ -693,22 +694,24 @@ export class SmartCertManager {
|
|
|
693
694
|
path: '/.well-known/acme-challenge/*'
|
|
694
695
|
},
|
|
695
696
|
action: {
|
|
696
|
-
type: '
|
|
697
|
-
|
|
697
|
+
type: 'socket-handler',
|
|
698
|
+
socketHandler: SocketHandlers.httpServer((req, res) => {
|
|
698
699
|
// Extract the token from the path
|
|
699
|
-
const token =
|
|
700
|
+
const token = req.url?.split('/').pop();
|
|
700
701
|
if (!token) {
|
|
701
|
-
|
|
702
|
+
res.status(404);
|
|
703
|
+
res.send('Not found');
|
|
704
|
+
return;
|
|
702
705
|
}
|
|
703
706
|
|
|
704
707
|
// Create mock request/response objects for SmartAcme
|
|
708
|
+
let responseData: any = null;
|
|
705
709
|
const mockReq = {
|
|
706
|
-
url:
|
|
707
|
-
method:
|
|
708
|
-
headers:
|
|
710
|
+
url: req.url,
|
|
711
|
+
method: req.method,
|
|
712
|
+
headers: req.headers
|
|
709
713
|
};
|
|
710
714
|
|
|
711
|
-
let responseData: any = null;
|
|
712
715
|
const mockRes = {
|
|
713
716
|
statusCode: 200,
|
|
714
717
|
setHeader: (name: string, value: string) => {},
|
|
@@ -718,24 +721,27 @@ export class SmartCertManager {
|
|
|
718
721
|
};
|
|
719
722
|
|
|
720
723
|
// Use SmartAcme's handler
|
|
721
|
-
const
|
|
724
|
+
const handleAcme = () => {
|
|
722
725
|
http01Handler.handleRequest(mockReq as any, mockRes as any, () => {
|
|
723
|
-
|
|
726
|
+
// Not handled by ACME
|
|
727
|
+
res.status(404);
|
|
728
|
+
res.send('Not found');
|
|
724
729
|
});
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
730
|
+
|
|
731
|
+
// Give it a moment to process, then send response
|
|
732
|
+
setTimeout(() => {
|
|
733
|
+
if (responseData) {
|
|
734
|
+
res.header('Content-Type', 'text/plain');
|
|
735
|
+
res.send(String(responseData));
|
|
736
|
+
} else {
|
|
737
|
+
res.status(404);
|
|
738
|
+
res.send('Not found');
|
|
739
|
+
}
|
|
740
|
+
}, 100);
|
|
741
|
+
};
|
|
728
742
|
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
status: mockRes.statusCode,
|
|
732
|
-
headers: { 'Content-Type': 'text/plain' },
|
|
733
|
-
body: responseData
|
|
734
|
-
};
|
|
735
|
-
} else {
|
|
736
|
-
return { status: 404, body: 'Not found' };
|
|
737
|
-
}
|
|
738
|
-
}
|
|
743
|
+
handleAcme();
|
|
744
|
+
})
|
|
739
745
|
}
|
|
740
746
|
};
|
|
741
747
|
|
|
@@ -2,11 +2,20 @@ import * as plugins from '../../../plugins.js';
|
|
|
2
2
|
// Certificate types removed - use local definition
|
|
3
3
|
import type { TForwardingType } from '../../../forwarding/config/forwarding-types.js';
|
|
4
4
|
import type { PortRange } from '../../../proxies/nftables-proxy/models/interfaces.js';
|
|
5
|
+
import type { IRouteContext } from '../../../core/models/route-context.js';
|
|
6
|
+
|
|
7
|
+
// Re-export IRouteContext for convenience
|
|
8
|
+
export type { IRouteContext };
|
|
5
9
|
|
|
6
10
|
/**
|
|
7
11
|
* Supported action types for route configurations
|
|
8
12
|
*/
|
|
9
|
-
export type TRouteActionType = 'forward' | '
|
|
13
|
+
export type TRouteActionType = 'forward' | 'socket-handler';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Socket handler function type
|
|
17
|
+
*/
|
|
18
|
+
export type TSocketHandler = (socket: plugins.net.Socket, context: IRouteContext) => void | Promise<void>;
|
|
10
19
|
|
|
11
20
|
/**
|
|
12
21
|
* TLS handling modes for route configurations
|
|
@@ -35,36 +44,6 @@ export interface IRouteMatch {
|
|
|
35
44
|
headers?: Record<string, string | RegExp>; // Match specific HTTP headers
|
|
36
45
|
}
|
|
37
46
|
|
|
38
|
-
/**
|
|
39
|
-
* Context provided to port and host mapping functions
|
|
40
|
-
*/
|
|
41
|
-
export interface IRouteContext {
|
|
42
|
-
// Connection information
|
|
43
|
-
port: number; // The matched incoming port
|
|
44
|
-
domain?: string; // The domain from SNI or Host header
|
|
45
|
-
clientIp: string; // The client's IP address
|
|
46
|
-
serverIp: string; // The server's IP address
|
|
47
|
-
path?: string; // URL path (for HTTP connections)
|
|
48
|
-
query?: string; // Query string (for HTTP connections)
|
|
49
|
-
headers?: Record<string, string>; // HTTP headers (for HTTP connections)
|
|
50
|
-
method?: string; // HTTP method (for HTTP connections)
|
|
51
|
-
|
|
52
|
-
// TLS information
|
|
53
|
-
isTls: boolean; // Whether the connection is TLS
|
|
54
|
-
tlsVersion?: string; // TLS version if applicable
|
|
55
|
-
|
|
56
|
-
// Route information
|
|
57
|
-
routeName?: string; // The name of the matched route
|
|
58
|
-
routeId?: string; // The ID of the matched route
|
|
59
|
-
|
|
60
|
-
// Target information (resolved from dynamic mapping)
|
|
61
|
-
targetHost?: string | string[]; // The resolved target host(s)
|
|
62
|
-
targetPort?: number; // The resolved target port
|
|
63
|
-
|
|
64
|
-
// Additional properties
|
|
65
|
-
timestamp: number; // The request timestamp
|
|
66
|
-
connectionId: string; // Unique connection identifier
|
|
67
|
-
}
|
|
68
47
|
|
|
69
48
|
/**
|
|
70
49
|
* Target configuration for forwarding
|
|
@@ -84,15 +63,6 @@ export interface IRouteAcme {
|
|
|
84
63
|
renewBeforeDays?: number; // Days before expiry to renew (default: 30)
|
|
85
64
|
}
|
|
86
65
|
|
|
87
|
-
/**
|
|
88
|
-
* Static route handler response
|
|
89
|
-
*/
|
|
90
|
-
export interface IStaticResponse {
|
|
91
|
-
status: number;
|
|
92
|
-
headers?: Record<string, string>;
|
|
93
|
-
body: string | Buffer;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
66
|
/**
|
|
97
67
|
* TLS configuration for route actions
|
|
98
68
|
*/
|
|
@@ -112,14 +82,6 @@ export interface IRouteTls {
|
|
|
112
82
|
sessionTimeout?: number; // TLS session timeout in seconds
|
|
113
83
|
}
|
|
114
84
|
|
|
115
|
-
/**
|
|
116
|
-
* Redirect configuration for route actions
|
|
117
|
-
*/
|
|
118
|
-
export interface IRouteRedirect {
|
|
119
|
-
to: string; // URL or template with {domain}, {port}, etc.
|
|
120
|
-
status: 301 | 302 | 307 | 308;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
85
|
/**
|
|
124
86
|
* Authentication options
|
|
125
87
|
*/
|
|
@@ -265,12 +227,6 @@ export interface IRouteAction {
|
|
|
265
227
|
// TLS handling
|
|
266
228
|
tls?: IRouteTls;
|
|
267
229
|
|
|
268
|
-
// For redirects
|
|
269
|
-
redirect?: IRouteRedirect;
|
|
270
|
-
|
|
271
|
-
// For static files
|
|
272
|
-
static?: IRouteStaticFiles;
|
|
273
|
-
|
|
274
230
|
// WebSocket support
|
|
275
231
|
websocket?: IRouteWebSocket;
|
|
276
232
|
|
|
@@ -295,8 +251,8 @@ export interface IRouteAction {
|
|
|
295
251
|
// NFTables-specific options
|
|
296
252
|
nftables?: INfTablesOptions;
|
|
297
253
|
|
|
298
|
-
//
|
|
299
|
-
|
|
254
|
+
// Socket handler function (when type is 'socket-handler')
|
|
255
|
+
socketHandler?: TSocketHandler;
|
|
300
256
|
}
|
|
301
257
|
|
|
302
258
|
/**
|
|
@@ -10,7 +10,6 @@ import { HttpProxyBridge } from './http-proxy-bridge.js';
|
|
|
10
10
|
import { TimeoutManager } from './timeout-manager.js';
|
|
11
11
|
import { RouteManager } from './route-manager.js';
|
|
12
12
|
import type { ForwardingHandler } from '../../forwarding/handlers/base-handler.js';
|
|
13
|
-
import { RedirectHandler, StaticHandler } from '../http-proxy/handlers/index.js';
|
|
14
13
|
|
|
15
14
|
/**
|
|
16
15
|
* Handles new connection processing and setup logic with support for route-based configuration
|
|
@@ -389,14 +388,13 @@ export class RouteConnectionHandler {
|
|
|
389
388
|
case 'forward':
|
|
390
389
|
return this.handleForwardAction(socket, record, route, initialChunk);
|
|
391
390
|
|
|
392
|
-
case '
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
this.handleStaticAction(socket, record, route, initialChunk);
|
|
391
|
+
case 'socket-handler':
|
|
392
|
+
logger.log('info', `Handling socket-handler action for route ${route.name}`, {
|
|
393
|
+
connectionId,
|
|
394
|
+
routeName: route.name,
|
|
395
|
+
component: 'route-handler'
|
|
396
|
+
});
|
|
397
|
+
this.handleSocketHandlerAction(socket, record, route, initialChunk);
|
|
400
398
|
return;
|
|
401
399
|
|
|
402
400
|
default:
|
|
@@ -710,70 +708,85 @@ export class RouteConnectionHandler {
|
|
|
710
708
|
}
|
|
711
709
|
|
|
712
710
|
/**
|
|
713
|
-
* Handle a
|
|
711
|
+
* Handle a socket-handler action for a route
|
|
714
712
|
*/
|
|
715
|
-
private
|
|
713
|
+
private async handleSocketHandlerAction(
|
|
716
714
|
socket: plugins.net.Socket,
|
|
717
715
|
record: IConnectionRecord,
|
|
718
|
-
route: IRouteConfig
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
716
|
+
route: IRouteConfig,
|
|
717
|
+
initialChunk?: Buffer
|
|
718
|
+
): Promise<void> {
|
|
719
|
+
const connectionId = record.id;
|
|
720
|
+
|
|
721
|
+
if (!route.action.socketHandler) {
|
|
722
|
+
logger.log('error', 'socket-handler action missing socketHandler function', {
|
|
723
|
+
connectionId,
|
|
724
|
+
routeName: route.name,
|
|
724
725
|
component: 'route-handler'
|
|
725
726
|
});
|
|
726
|
-
socket.
|
|
727
|
-
this.connectionManager.cleanupConnection(record, '
|
|
727
|
+
socket.destroy();
|
|
728
|
+
this.connectionManager.cleanupConnection(record, 'missing_handler');
|
|
728
729
|
return;
|
|
729
730
|
}
|
|
730
|
-
|
|
731
|
-
//
|
|
732
|
-
|
|
731
|
+
|
|
732
|
+
// Create route context for the handler
|
|
733
|
+
const routeContext = this.createRouteContext({
|
|
733
734
|
connectionId: record.id,
|
|
734
|
-
|
|
735
|
-
|
|
735
|
+
port: record.localPort,
|
|
736
|
+
domain: record.lockedDomain,
|
|
737
|
+
clientIp: record.remoteIP,
|
|
738
|
+
serverIp: socket.localAddress || '',
|
|
739
|
+
isTls: record.isTLS || false,
|
|
740
|
+
tlsVersion: record.tlsVersion,
|
|
741
|
+
routeName: route.name,
|
|
742
|
+
routeId: route.id,
|
|
736
743
|
});
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
744
|
+
|
|
745
|
+
try {
|
|
746
|
+
// Call the handler with socket AND context
|
|
747
|
+
const result = route.action.socketHandler(socket, routeContext);
|
|
748
|
+
|
|
749
|
+
// Handle async handlers properly
|
|
750
|
+
if (result instanceof Promise) {
|
|
751
|
+
result
|
|
752
|
+
.then(() => {
|
|
753
|
+
// Emit initial chunk after async handler completes
|
|
754
|
+
if (initialChunk && initialChunk.length > 0) {
|
|
755
|
+
socket.emit('data', initialChunk);
|
|
756
|
+
}
|
|
757
|
+
})
|
|
758
|
+
.catch(error => {
|
|
759
|
+
logger.log('error', 'Socket handler error', {
|
|
760
|
+
connectionId,
|
|
761
|
+
routeName: route.name,
|
|
762
|
+
error: error.message,
|
|
763
|
+
component: 'route-handler'
|
|
764
|
+
});
|
|
765
|
+
if (!socket.destroyed) {
|
|
766
|
+
socket.destroy();
|
|
767
|
+
}
|
|
768
|
+
this.connectionManager.cleanupConnection(record, 'handler_error');
|
|
769
|
+
});
|
|
770
|
+
} else {
|
|
771
|
+
// For sync handlers, emit on next tick
|
|
772
|
+
if (initialChunk && initialChunk.length > 0) {
|
|
773
|
+
process.nextTick(() => {
|
|
774
|
+
socket.emit('data', initialChunk);
|
|
775
|
+
});
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
} catch (error) {
|
|
779
|
+
logger.log('error', 'Socket handler error', {
|
|
751
780
|
connectionId,
|
|
752
|
-
routeName: route.name
|
|
781
|
+
routeName: route.name,
|
|
782
|
+
error: error.message,
|
|
753
783
|
component: 'route-handler'
|
|
754
784
|
});
|
|
785
|
+
if (!socket.destroyed) {
|
|
786
|
+
socket.destroy();
|
|
787
|
+
}
|
|
788
|
+
this.connectionManager.cleanupConnection(record, 'handler_error');
|
|
755
789
|
}
|
|
756
|
-
|
|
757
|
-
// Simply close the connection
|
|
758
|
-
socket.end();
|
|
759
|
-
this.connectionManager.initiateCleanupOnce(record, 'route_blocked');
|
|
760
|
-
}
|
|
761
|
-
|
|
762
|
-
/**
|
|
763
|
-
* Handle a static action for a route
|
|
764
|
-
*/
|
|
765
|
-
private async handleStaticAction(
|
|
766
|
-
socket: plugins.net.Socket,
|
|
767
|
-
record: IConnectionRecord,
|
|
768
|
-
route: IRouteConfig,
|
|
769
|
-
initialChunk?: Buffer
|
|
770
|
-
): Promise<void> {
|
|
771
|
-
// Delegate to HttpProxy's StaticHandler
|
|
772
|
-
await StaticHandler.handleStatic(socket, route, {
|
|
773
|
-
connectionId: record.id,
|
|
774
|
-
connectionManager: this.connectionManager,
|
|
775
|
-
settings: this.settings
|
|
776
|
-
}, record, initialChunk);
|
|
777
790
|
}
|
|
778
791
|
|
|
779
792
|
/**
|
|
@@ -19,7 +19,6 @@ import {
|
|
|
19
19
|
createWebSocketRoute as createWebSocketPatternRoute,
|
|
20
20
|
createLoadBalancerRoute as createLoadBalancerPatternRoute,
|
|
21
21
|
createApiGatewayRoute,
|
|
22
|
-
createStaticFileServerRoute,
|
|
23
22
|
addRateLimiting,
|
|
24
23
|
addBasicAuth,
|
|
25
24
|
addJwtAuth
|
|
@@ -29,7 +28,6 @@ export {
|
|
|
29
28
|
createWebSocketPatternRoute,
|
|
30
29
|
createLoadBalancerPatternRoute,
|
|
31
30
|
createApiGatewayRoute,
|
|
32
|
-
createStaticFileServerRoute,
|
|
33
31
|
addRateLimiting,
|
|
34
32
|
addBasicAuth,
|
|
35
33
|
addJwtAuth
|