Skip to content

Commit 9b3168f

Browse files
committed
fix(hub.js): prevent duplicate service registration in hub
- Added socket.io middleware to validate service registration - Implemented service name uniqueness check during connection - Added standardized error response for duplicate registration attempts - Implemented socket cleanup for rejected connections - Added proper logging for duplicate registration attempts - Implemented automatic rejection of duplicate socket connection requests This prevents multiple services from registering with the same name, ensuring message routing integrity and system stability. Any duplicate connection attempts are automatically rejected at the connection level, providing enhanced security against unauthorized service impersonation. fix #3
1 parent 2830776 commit 9b3168f

File tree

1 file changed

+67
-5
lines changed

1 file changed

+67
-5
lines changed

Diff for: src/hub.js

+67-5
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,67 @@ const Logger = require("./utils/logger"); // Import the logger
44
const PORT = process.env.PORT || 3000;
55
const io = new Server(Number(PORT), { cors: { origin: "*" } });
66

7-
const services = new Map(); // Stores connected services
7+
const services = new Map(); // Stores connected services (serviceName -> { socketId, lastHeartbeat })
88
const pendingRequests = new Map(); // Stores pending requests (requestId -> senderSocketId)
99

1010
const logger = new Logger(process.env.LOG_LEVEL || "debug"); // Initialize logger
1111

12+
/*
13+
Socket.io Middleware Setup
14+
15+
Socket.io Middleware to handle Duplicate Client Service Registration.
16+
17+
This middleware will check if a service is already registered with the same name.
18+
If so, it will reject the new connection request, log a warning and pass an error to the client.
19+
Error Structure: {
20+
code: "DUPLICATE_SERVICE_REGISTRATION",
21+
content: "Duplicate Service Registration Name! A service with the same name is already registered."
22+
}
23+
24+
Otherwise, it will proceed with the connection.
25+
26+
This is to prevent multiple instances of the same service from connecting to the hub.
27+
The hub will only keep the one connection for each service.
28+
*/
29+
io.use((socket, next) => {
30+
const serviceName = socket.handshake.query.serviceName;
31+
32+
// If the service is already registered, reject the connection and pass an error to the client
33+
if (services.has(serviceName)) {
34+
logger.warn(
35+
`Duplicate service registration attempt: ${serviceName} from Socket ID: ${socket.id}`
36+
);
37+
38+
const error = new Error("DUPLICATE_SERVICE_REGISTRATION");
39+
error.data = {
40+
code: "DUPLICATE_SERVICE_REGISTRATION",
41+
content:
42+
"Duplicate Service Registration Name! A service with the same name is already registered.",
43+
};
44+
45+
// Set a flag to indicate that the connection was rejected
46+
socket.rejected = true;
47+
48+
next(error);
49+
}
50+
51+
// If the service is not registered, proceed with the connection
52+
next();
53+
});
54+
1255
// Handle new connections
1356
io.on("connection", (socket) => {
1457
const serviceName = socket.handshake.query.serviceName;
58+
59+
// If the connection was rejected by the middleware, close the connection
60+
if (socket.rejected) {
61+
logger.warn(
62+
`Connection rejected for duplicate service registration: ${serviceName} from Socket ID: ${socket.id}`
63+
);
64+
socket.disconnect(true);
65+
return;
66+
}
67+
1568
logger.info(`${serviceName} connected: ${socket.id}`);
1669

1770
// Register the service
@@ -37,7 +90,6 @@ io.on("connection", (socket) => {
3790
// Notify the sender that the target service was not found
3891
logger.warn(`Service "${targetService}" not found`);
3992

40-
// socket.emit("response", { id: payload.id, error: `Service "${targetService}" not found` });
4193
socket.emit("response", {
4294
id: payload.id,
4395
// error: `Service "${targetService}" not found`, // This was causing a crash in the client response handler because it was expecting a "data" key
@@ -73,8 +125,11 @@ io.on("connection", (socket) => {
73125

74126
// Handle disconnections
75127
socket.on("disconnect", () => {
76-
services.delete(serviceName);
77-
logger.info(`${serviceName} disconnected: ${socket.id}`);
128+
// Only remove the service if the connection was not rejected
129+
if (!socket.rejected) {
130+
services.delete(serviceName);
131+
logger.info(`${serviceName} disconnected: ${socket.id}`);
132+
}
78133
});
79134
});
80135

@@ -84,7 +139,14 @@ setInterval(() => {
84139
for (const [serviceName, data] of services) {
85140
if (now - data.lastHeartbeat > 15000) {
86141
services.delete(serviceName);
87-
logger.info(`Removed inactive service: ${serviceName}`);
142+
143+
// Disconnect the inactive service socket
144+
const inactiveServiceSocket = io.sockets.sockets.get(data.socketId); // Disconnect the socket
145+
inactiveServiceSocket.disconnect(true);
146+
147+
logger.info(
148+
`Removed inactive service: ${serviceName} with Socket ID: ${inactiveServiceSocket.id}`
149+
);
88150
}
89151
}
90152
}, 5000);

0 commit comments

Comments
 (0)