Skip to content

Commit

Permalink
fix: added ability to send back to clients the reason on closing at m…
Browse files Browse the repository at this point in the history
…ax connection limit (#2362) (#2363)

* fix: removed extra requestIdPrefix

Signed-off-by: Logan Nguyen <[email protected]>

* fix: aded ability to send back to client when closing connection in limiter

Signed-off-by: Logan Nguyen <[email protected]>

---------

Signed-off-by: Logan Nguyen <[email protected]>
  • Loading branch information
quiet-node authored Apr 18, 2024
1 parent bf77d2f commit 08cb690
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 14 deletions.
37 changes: 29 additions & 8 deletions packages/ws-server/src/utils/connectionLimiter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,24 +115,38 @@ export default class ConnectionLimiter {

public applyLimits(ctx) {
// Limit total connections
if (this.connectedClients > parseInt(process.env.WS_CONNECTION_LIMIT || '10')) {
const MAX_CONNECTION_LIMIT = process.env.WS_CONNECTION_LIMIT || '10';
if (this.connectedClients > parseInt(MAX_CONNECTION_LIMIT)) {
this.logger.info(
`Closing connection ${ctx.websocket.id} due to exceeded maximum connections (${process.env.WS_CONNECTION_LIMIT})`,
`Closing connection ${ctx.websocket.id} due to exceeded maximum connections (max_con=${MAX_CONNECTION_LIMIT})`,
);
this.connectionLimitCounter.inc();
ctx.websocket.send(
JSON.stringify({
jsonrpc: '2.0',
error: `Closing current connection due to exceeded maximum connections (max_con=${MAX_CONNECTION_LIMIT})`,
id: '1',
}),
);
ctx.websocket.close(CONNECTION_LIMIT_EXCEEDED.code, CONNECTION_LIMIT_EXCEEDED.message);
return;
}

const { ip } = ctx.request;

// Limit connections from a single IP address
const limitPerIp = parseInt(process.env.WS_CONNECTION_LIMIT_PER_IP || '10');
if (this.clientIps[ip] && this.clientIps[ip] > limitPerIp) {
const { ip } = ctx.request;
const MAX_CONNECTION_LIMIT_PER_IP = process.env.WS_CONNECTION_LIMIT_PER_IP || '10';
if (this.clientIps[ip] && this.clientIps[ip] > parseInt(MAX_CONNECTION_LIMIT_PER_IP)) {
this.logger.info(
`Closing connection ${ctx.websocket.id} due to exceeded maximum connections from a single IP (${this.clientIps[ip]}) for address ${ip}`,
`Closing connection ${ctx.websocket.id} due to exceeded maximum connections from a single IP: address ${ip} - ${this.clientIps[ip]} connections. (max_con=${MAX_CONNECTION_LIMIT_PER_IP})`,
);
this.ipConnectionLimitCounter.labels(ip).inc();
ctx.websocket.send(
JSON.stringify({
jsonrpc: '2.0',
error: `Closing current connection due to exceeded maximum connections from a single IP: address ${ip} - ${this.clientIps[ip]} connections. (max_con=${MAX_CONNECTION_LIMIT_PER_IP})`,
id: '1',
}),
);
ctx.websocket.close(CONNECTION_IP_LIMIT_EXCEEDED.code, CONNECTION_IP_LIMIT_EXCEEDED.message);
return;
}
Expand All @@ -159,9 +173,16 @@ export default class ConnectionLimiter {
websocket.inactivityTTL = setTimeout(() => {
if (websocket.readyState !== 3) {
// 3 = CLOSED, Avoid closing already closed connections
this.logger.debug(`Closing connection ${websocket.id} due to reaching TTL of ${maxInactivityTTL}ms`);
this.logger.debug(`Closing connection ${websocket.id} due to reaching TTL (${maxInactivityTTL}ms)`);
try {
this.inactivityTTLCounter.inc();
websocket.send(
JSON.stringify({
jsonrpc: '2.0',
error: `Closing current connection due to reaching TTL (${maxInactivityTTL}ms)`,
id: '1',
}),
);
websocket.close(TTL_EXPIRED.code, TTL_EXPIRED.message);
} catch (e) {
this.logger.error(`${websocket.id}: ${e}`);
Expand Down
9 changes: 3 additions & 6 deletions packages/ws-server/src/webSocketServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,15 +92,15 @@ app.ws.use(async (ctx) => {
ctx.websocket.id = relay.subs()?.generateId();
ctx.websocket.limiter = limiter;
const connectionIdPrefix = formatIdMessage('Connection ID', ctx.websocket.id);
const connectionRequestIdPrefix = formatIdMessage('Request ID', uuid());
const requestIdPrefix = formatIdMessage('Request ID', uuid());
logger.info(
`${connectionIdPrefix} ${connectionRequestIdPrefix} New connection established. Current active connections: ${ctx.app.server._connections}`,
`${connectionIdPrefix} ${requestIdPrefix} New connection established. Current active connections: ${ctx.app.server._connections}`,
);

// Close event handle
ctx.websocket.on('close', async (code, message) => {
logger.info(
`${connectionIdPrefix} ${connectionRequestIdPrefix} Closing connection ${ctx.websocket.id} | code: ${code}, message: ${message}`,
`${connectionIdPrefix} ${requestIdPrefix} Closing connection ${ctx.websocket.id} | code: ${code}, message: ${message}`,
);
await handleConnectionClose(ctx, relay, limiter);
});
Expand All @@ -116,9 +116,6 @@ app.ws.use(async (ctx) => {
// Reset the TTL timer for inactivity upon receiving a message from the client
limiter.resetInactivityTTLTimer(ctx.websocket);

// Format ID prefixes for logging purposes
const requestIdPrefix = formatIdMessage('Request ID', uuid());

// parse the received message from the client into a JSON object
let request;
try {
Expand Down

0 comments on commit 08cb690

Please sign in to comment.