Skip to content

Commit

Permalink
Enhance websocket options and documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
rolljee committed Oct 19, 2023
1 parent 06b0129 commit a043dd8
Show file tree
Hide file tree
Showing 9 changed files with 99 additions and 9 deletions.
6 changes: 6 additions & 0 deletions .kuzzlerc.sample
Original file line number Diff line number Diff line change
Expand Up @@ -486,11 +486,17 @@
// Disabled if set to 0.
// * realtimeNotifications:
// Set to "true" to enable realtime notifications like "TokenExpired" notifications
// * sendPingsAutomatically:
// Whether or not we should automatically send pings to uphold a stable connection given whatever idleTimeout.
// * resetIdleTimeoutOnSend:
// This one depends on kernel timeouts and is a bad default
"compression": false,
"enabled": true,
"idleTimeout": 60000,
"rateLimit": 0,
"realtimeNotifications": true
"sendPingsAutomatically": false,
"resetIdleTimeoutOnSend": false,
}
}
},
Expand Down
38 changes: 29 additions & 9 deletions doc/2/api/protocols/websocket/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,24 +37,44 @@ The listening port can be modified under the `server.port` section of the [confi
// Set to true to enable WebSocket support
"enabled": true,

// The maximum time (in milliseconds) without sending or receiving a
// message from a client. Once reached, the client's socket is
// forcibly closed.
// Contrary to heartbeats (see below), this is a passive check,
// forcing all clients to actively send either PINGs or messages to
// maintain their connection active.
// Set the value to 0 to disable this feature (should only be
// activated if heartbeat is disabled)
// The maximum time (in milliseconds) without sending or receiving a
// message from a client. Once reached, the client's socket is
// forcibly closed.
// If a client socket is inactive for too long, the server will send
// a PING request before closing the socket.
// Minimum value: 1000 (but it's strongly advised to not set a value
// this low to forcibly close idle client sockets)
"idleTimeout": 0,

// @Deprecated
// The time, in milliseconds, between the server's PING requests to
// clients, to make sure they are still active.
// Setting this value to 0 disables PING requests from the server
// (it will still respond with a PONG to PING requests from clients).
// If heartbeat is deactivated, then setting a non-zero value to
// idleTimeout is strongly recommended to detect and remove
// dead sockets.
"heartbeat": 60000
"heartbeat": 60000,

// Enable/Disable per message compression
"compression": false,

// The maximum number of messages per second a single socket can
// submit to the server.
// Requests exceeding that rate limit are rejected.
// Disabled if set to 0.
"rateLimit": 0,

// Set to "true" to enable realtime notifications like "TokenExpired"
// notifications
"realtimeNotifications": true,

// Whether or not we should automatically send pings to uphold a stable
// connection given whatever idleTimeout.
"sendPingsAutomatically": false,

// This one depends on kernel timeouts and is a bad default
"resetIdleTimeoutOnSend": false,
}
}
```
Expand Down
2 changes: 2 additions & 0 deletions lib/config/default.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,8 @@ const defaultConfig: KuzzleConfiguration = {
compression: false,
rateLimit: 0,
realtimeNotifications: true,
resetIdleTimeoutOnSend: false,
sendPingsAutomatically: false,
},
},
strictSdkVersion: true,
Expand Down
8 changes: 8 additions & 0 deletions lib/config/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,14 @@ function checkWebSocketOptions(config) {
typeof cfg.compression === "boolean",
`[websocket] "compression" parameter: invalid value "${cfg.compression}" (boolean value expected)`
);
assert(
typeof cfg.sendPingsAutomatically === "boolean",
`[websocket] "sendPingsAutomatically" parameter: invalid value "${cfg.sendPingsAutomatically}" (boolean value expected)`
);
assert(
typeof cfg.resetIdleTimeoutOnSend === "boolean",
`[websocket] "resetIdleTimeoutOnSend" parameter: invalid value "${cfg.resetIdleTimeoutOnSend}" (boolean value expected)`
);
}

function checkHttpOptions(config) {
Expand Down
2 changes: 2 additions & 0 deletions lib/core/network/protocols/httpwsProtocol.js
Original file line number Diff line number Diff line change
Expand Up @@ -1162,6 +1162,8 @@ class HttpWsProtocol extends Protocol {
compression,
idleTimeout: idleTimeoutInSecond,
maxPayloadLength: this.maxRequestSize,
resetIdleTimeoutOnSend: cfg.resetIdleTimeoutOnSend,
sendPingsAutomatically: cfg.sendPingsAutomatically,
},
rateLimit: cfg.rateLimit,
};
Expand Down
10 changes: 10 additions & 0 deletions lib/types/config/ServerConfiguration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,16 @@ export type ServerConfiguration = {
* @default true
*/
realtimeNotifications: boolean;

/**
* Whether or not we should automatically send pings to uphold a stable connection given whatever idleTimeout.
*/
sendPingsAutomatically: boolean;

/**
* Whether or not we should reset the idle timeout on every message received.
*/
resetIdleTimeoutOnSend: boolean;
};
};

Expand Down
38 changes: 38 additions & 0 deletions test/config/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,44 @@ describe("lib/config/index.js", () => {
);
}
});

it('should throw if "sendPingsAutomatically" is not a boolean', async () => {
for (const bad of [null, "foo", 123, 0, [], {}]) {
mockedConfigContent = getcfg({
server: {
protocols: {
websocket: {
sendPingsAutomatically: bad,
},
},
},
});

// eslint-disable-next-line no-loop-func
should(() => config.loadConfig()).throw(
`[websocket] "enabled" parameter: invalid value "${bad}" (boolean expected)`
);
}
});

it('should throw if "resetIdleTimeoutOnSend" is not a boolean', async () => {
for (const bad of [null, "foo", 123, 0, [], {}]) {
mockedConfigContent = getcfg({
server: {
protocols: {
websocket: {
resetIdleTimeoutOnSend: bad,
},
},
},
});

// eslint-disable-next-line no-loop-func
should(() => config.loadConfig()).throw(
`[websocket] "enabled" parameter: invalid value "${bad}" (boolean expected)`
);
}
});
});

describe("#preprocessHttpOptions", async () => {
Expand Down
2 changes: 2 additions & 0 deletions test/core/network/protocols/http.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ describe("core/network/protocols/http", () => {
idleTimeout: 60000,
compression: false,
rateLimit: 0,
sendPingsAutomatically: false,
resetIdleTimeoutOnSend: false,
},
},
});
Expand Down
2 changes: 2 additions & 0 deletions test/core/network/protocols/websocket.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@ describe("core/network/protocols/websocket", () => {
should(httpWs.server.ws).calledWithMatch("/*", {
compression: uWS.SHARED_COMPRESSOR,
idleTimeout: 12,
resetIdleTimeoutOnSend: false,
sendPingsAutomatically: false,
maxBackPressure: sinon.match.number,
maxPayloadLength: 1024,
upgrade: sinon.match.func,
Expand Down

0 comments on commit a043dd8

Please sign in to comment.