feat(udp,http3): add UDP datagram handler relay support and stream HTTP/3 request bodies to backends

This commit is contained in:
2026-03-19 16:09:51 +00:00
parent bbe8b729ea
commit e890bda8fc
12 changed files with 660 additions and 61 deletions

View File

@@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@push.rocks/smartproxy',
version: '25.13.0',
version: '25.14.0',
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.'
}

View File

@@ -303,6 +303,21 @@ export class SmartProxy extends plugins.EventEmitter {
this.socketHandlerServer = null;
}
// Update datagram handler relay if datagram handler routes changed
const hasDatagramHandlers = newRoutes.some(
(r) => r.action.type === 'socket-handler' && r.action.datagramHandler
);
if (hasDatagramHandlers && !this.datagramHandlerServer) {
const dgPath = `/tmp/smartproxy-dgram-relay-${process.pid}.sock`;
this.datagramHandlerServer = new DatagramHandlerServer(dgPath, this.preprocessor);
await this.datagramHandlerServer.start();
await this.bridge.setDatagramHandlerRelay(this.datagramHandlerServer.getSocketPath());
} else if (!hasDatagramHandlers && this.datagramHandlerServer) {
await this.datagramHandlerServer.stop();
this.datagramHandlerServer = null;
}
// Update stored routes
this.settings.routes = newRoutes;

View File

@@ -123,10 +123,10 @@ export class RouteValidator {
errors.push(`Invalid action type: ${route.action.type}. Must be one of: ${this.VALID_ACTION_TYPES.join(', ')}`);
}
// Validate socket-handler
// Validate socket-handler (TCP socketHandler or UDP datagramHandler)
if (route.action.type === 'socket-handler') {
if (typeof route.action.socketHandler !== 'function') {
errors.push('socket-handler action requires a socketHandler function');
if (typeof route.action.socketHandler !== 'function' && typeof route.action.datagramHandler !== 'function') {
errors.push('socket-handler action requires a socketHandler or datagramHandler function');
}
}
@@ -620,10 +620,12 @@ export function validateRouteAction(action: IRouteAction): { valid: boolean; err
}
if (action.type === 'socket-handler') {
if (!action.socketHandler) {
errors.push('Socket handler function is required for socket-handler action');
} else if (typeof action.socketHandler !== 'function') {
if (!action.socketHandler && !action.datagramHandler) {
errors.push('Socket handler or datagram handler function is required for socket-handler action');
} else if (action.socketHandler && typeof action.socketHandler !== 'function') {
errors.push('Socket handler must be a function');
} else if (action.datagramHandler && typeof action.datagramHandler !== 'function') {
errors.push('Datagram handler must be a function');
}
}
@@ -714,7 +716,8 @@ export function hasRequiredPropertiesForAction(route: IRouteConfig, actionType:
route.action.targets.length > 0 &&
route.action.targets.every(t => t.host && t.port !== undefined);
case 'socket-handler':
return !!route.action.socketHandler && typeof route.action.socketHandler === 'function';
return (!!route.action.socketHandler && typeof route.action.socketHandler === 'function') ||
(!!route.action.datagramHandler && typeof route.action.datagramHandler === 'function');
default:
return false;
}