Getting Started #
Kuzzle has native support for the following network protocols: HTTP, MQTT (disabled by default), Websocket and Socket.io.
However, any number of protocols can be implemented, adding new network capabilities.
Protocols are entirely responsible of the network communication layer, which can be as simple as a UDP socket, all the way to a complete pub/sub message broker. Protocols can even decide to propose a dedicated message format and/or query syntax for the Kuzzle API.
Protocols are provided with objects to interact with Kuzzle:
- EntryPoint: base communication layer (declare user connections, forward API requests, ...)
- context: utilities and object constructors not directly related to network communications
Prerequisites #
Location #
Protocols are subdirectories that must be put at the following location: <kuzzle_install_dir>/protocols/enabled
.
The recommended way to install a protocol is to put it in protocols/available
, and to create a symbolic link pointing to it in the protocols/enabled
directory.
Node.js modules #
Kuzzle loads protocols as Node.js modules.
This means that a protocol directory must contain either:
- an
index.js
file
and/or:
- a valid package.json file. If the protocol's entrypoint is not the root
index.js
file, then the main property must be filled
manifest.json #
Kuzzle needs a few information to make your protocol work properly. These information must be provided in a manifest.json
file, in the protocol directory.
The following properties can be defined in this manifest.json
file:
name
(required): protocol unique identifier. This protocol name will be used by Kuzzle for statistics, logs, and to provide context to requestskuzzleVersion
: a non-empty string describing a semver range, limiting the range of Kuzzle versions supported by this protocol. If not set, a warning is displayed on the console, and Kuzzle assumes that the protocol is only compatible with Kuzzle v1.x
Interface #
To add new network capabilities, a protocol must implement a set of functions, to be called by Kuzzle:
If one or multiple of these functions are missing, Kuzzle fails to load the protocol, and refuses to start.
Channels #
channel
is a recurrent concept in the protocol interface, one that must be implemented by the protocol itself.
Simply put: it is the same channel
identifier returned to a user after a real-time subscription. Many users can share the same channel, as it is calculated from the provided subscription filters, after they are normalized (i.e. equivalent yet differently written filters still share the same identifier).
Kuzzle notifies protocols when one of their managed connection joins or leaves a channel.
Kuzzle has no opinion on how a protocol handles channels and their associated users. It simply asks protocols to broadcast, or to notify messages to listening users.
Configuration #
Protocols can be configured in the Kuzzle configuration file, under the server/protocols/<protocol name>
section.
Example #
{
"server": {
"protocols": {
"mqtt": {
"port": 1883,
"allowPubSub": true
}
}
}
}
The custom configuration can be found in the EntryPoint object provided to the init
function, under the following property: entryPoint.config.protocols.<protocol name>
Protocol example #
module.exports = class MyProtocol {
constructor () {
this.context = null;
this.entryPoint = null;
this.name = 'foobar';
// Example on how to maintain client connections
this.clients = {};
this.connections = {};
}
/**
* @param {EntryPoint} entryPoint - main protocol interface with Kuzzle
* @param {object} context - Constructors and utilities
*/
init (entryPoint, context) {
// plugin initialization
this.entryPoint = entryPoint;
this.context = context;
// user configuration can be retrieved from entryPoint.config[protocol name]
this.config = Object.assign({
default: 'value'
}, entryPoint.config[this.name] || {});
}
/*
This function is only an example showing how to interact with
clients and with Kuzzle. It does not implement any actual protocol.
The way a protocol plugins handles clients closely depends on the
implemented protocol.
*/
handleClient () {
// when a client connects
this.on('onClientConnect', client => {
const connection = new this.context.ClientConnection(
this.name,
[client.connection.stream.remoteAddress],
{some: 'header'}
);
this.entryPoint.newConnection(connection);
this.clients[connection.id] = client;
this.connections[client.id] = connection;
});
// when a client sends a request
this.on('onClientRequest', (client, data) => {
// Instantiates a Request object to be passed to Kuzzle
const
connection = this.connections[client.id],
request = new this.context.Request(data, { connection });
this.entryPoint.execute(request, response => {
// forward the response to the client
});
});
// whenever a client is disconnected
this.on('onClientDisconnect', client => {
const connection = this.connections[client.id];
this.entryPoint.removeConnection(connection.id);
delete this.clients[connection.id];
delete this.connections[client.id];
});
}
/*
Invoked by Kuzzle when a "data.payload" payload needs to be
broadcasted
*/
broadcast (channels, payload) {
for (const channel of channels) {
// send the payload to all connections having subscribed
// to that channel
}
}
/*
Invoked by Kuzzle when a payload needs to be sent to
a single connection
*/
notify (channels, connectionId, payload) {
for (const channel of channels) {
// send the payload to the connection
});
}
/*
Invoked by Kuzzle when a connection has subscribed to a channel
*/
joinChannel (channel, connectionId) {
// ...
}
/*
Invoked by Kuzzle when a connection leaves a channel
*/
leaveChannel (channel, connectionId) {
// ...
}
/*
Invoked by Kuzzle when it needs to force-close a client connection
*/
disconnect (connectionId) {
const client = this.clients[connectionId];
// close the client connection
}
}