A connect(B)
when connreq && connack === connreq
else when !connreq || connack !== connreq
B handleConnectRequest (connreqA, channelNonceA)
when !connreqA
else when !connreq || connreqA !== connack
else when connreqA === connack
else
A handleConnectAck (connackB, channelNonceB)
when connreq && connreq === connackB
- set connack = connreq
- set channelSendId = hash(connack, channelNonce, channelNonceB, sharedSecretAB)
- set channelReceiveId = hash(connack, channelNonceB, channelNonce, sharedSecretAB)
- subscribe topic to receive from B
- connackResolve()
B connect(A): vice versa
mqtt messages types by topicPath[3]
message topic publish -t message/
chunk: stream-id chunk-id
chunk-ack: to-id
response-end-ack: to-id
stream-ack: to-id
message flow:
A: request B: request-ack A: stream B: stream-ack A: chunk B: chunk-ack .. A: chunk A: stream-end B: stream-end-ack ..A: stream A: request-end B: request-end-ack B: response A: response-ack B: stream A: stream-ack B: chunk A: chunk-ack .. B: chunk B: stream-end A: stream-end-ack .. B: stream B: response-end A: response-end-ack
this is to browserify node Buffer
when browserify, use Node >=5.10.0 to get the correct version
Buffer.from() see: https://nodejs.org/docs/latest-v5.x/api/buffer.html
var EventEmitter = require('events');
options:
call super constructor.
MqttReqRes extends EventEmitter
expose EventEmitter
expose node Buffer
use sha512
remove all internal listeners
The Server MUST allow ClientIds which are between 1 and 23 UTF-8 encoded bytes in length, and that contain only the characters 0-9a-Z
use client.sharedSecret(function (clientId, cb){cb('my-secret');})
to set function
list of connections to other clients
connected client id e.g., 'WTwke7a4Yn5KobZbckgrRAj',
sharedSecret string
connreq null|string the connection request id set and sent by the client that initiated the connection
connack boolean|string the connection ack id
channel nonce: nonce for calculating channel ids
publish -t message/<this-client-id>/<channel-send-id>/
subscribe -t message/<connected-client-id>/<channel-receive-id>/#
<channel-receive-id>: the channel id of the channel this client listenes for messages sent by the other client.
<channel-send-id>: the channel id of the channel this client sends messages to the other client.
channel-receive-id
send a string|object|Buffer payload within an existing message
debug( '%s streamSend(%s, %s, %s)', this.clientId, connection.clientId, messageId, msgTargetProperty, payload );
Buffer
string instanceof Object -> false
debug('%s streamSend() type: %s', this.clientId, type);
payload as a JSON string
payload as a string
_mqtt-message-stream: message-id, stream-id, message-target-property
debug('%s sendStreamMessage() to %s', self.clientId, connection.clientId);
debug('%s sendChunks().nextChunk(): payloadLength %d remainingLength %d chunkno %d chunkSize %d eos %s', self.clientId, payloadLength, remainingLength, chunkno, chunkSize, eos );
chunk
not this stream
start next chunk request
_mqtt-message-stream-end: message-id, stream-id
debug('%s sendStreamEndMessage() to %s', self.clientId, connection.clientId);
send a chunk message string return Promise.resolve when connected, sent and acked
generate a chunk id
debug('%s streamSendChunk() to %s messageChunkStr "%s", connection:', this.clientId, connection.clientId, messageChunkStr, connection );
-mqtt-message-chunk: stream-id chunk-id payload
chunk id
debug( '%s sendAck(%s, %s, toId %s)', this.clientId, connection.clientId, ackMessageName, toId );
generic wait for ack message
debug( '%s waitForAck(%s, %s, %d)', this.clientId, ackMessageName, toId, timeout );
debug( '%s handleAck(%s), waits for: "%s" to id "%s", ackMessage:', self.clientId, ackMessage.toId, ackEventName, toId, ackMessage );
reject on timeout
generic mqtt publish
debug( '%s mqttPublish(%s, %s)', this.clientId, connection.clientId, subTopic // , messageStr );
pubish message
A: request
debug('%s request(%s)', this.clientId, toClientId);
meta object is optional
meta streamSend (connection, messageId, msgTargetProperty, payload)
payload streamSend (connection, messageId, msgTargetProperty, payload)
A: response-ack
debug('%s waitForResponse(toMessageId %s)', this.clientId, toMessageId);
A: response-ack
reject on timeout
A: response-end-ack
assume type 'string' or 'JSON'
A: stream-end-ack
debug('%s handleRequestMessage(%s)', this.clientId, topicPath.join('/'));
B: request-end-ack
no request handler defined, ignore message request
secret = this.getSharedSecret(connection);
call request handler
return Promise.reject(reason);
debug('%s sendResponse(%s, %s)', this.clientId, connection.clientId, respondToRequestId);
meta object is optional
meta streamSend (connection, messageId, msgTargetProperty, payload)
payload streamSend (connection, messageId, msgTargetProperty, payload)
debug('%s connect(%s)', this.clientId, toClientId);
connect to broker
connect to other client when toClientId is set
connect to broker
debug('%s connectToBroker()', this.clientId);
already connected to broker?
debug('%s connectToBroker() retry %d', self.clientId, retryCounter);
reset subsciption to connect flag
connect to broker debug('%s connectToBroker() call mqtt.connect()', self.clientId);
connack packet
subscribe to connect topic
subscribe to topic connect/<client-id>/#
debug('%s subscribeConnect()', this.clientId);
check for broker connection
check if already subscribed to topic connect
subscribe to connect topic
A connect(B)
handleConnectAck (ev{connack,channelNonce})
when connreq && connreq === connackB
debug('%s client connect in connectToClient()', self.clientId, toClientId);
- set connack = connreq
- set channelSendId = hash(connack, channelNonce, channelNonceB, sharedSecretAB)
reject on timeout
A - send connreq,channelNonce to B.handleConnectRequest()
- subscribe topic to receive from B
debug('failed to connect to client', connection);
debug('%s ping(%s)', this.clientId, toClientId);
check for broker connection
handle incoming messages
B: ping-ack
debug('%s handlePingRequest', this.clientId, message.messageId);
debug('%s handleMqttClientMessage(%s)', this.clientId, topic);
ignore message
is connect topic publish -t connect/to-device-id/from-device-id -m connack|connreq
else: ignore
is message topic publish -t message/
debug( '%s emitted %s , %s', this.clientId, topicPath.join('/'), '_mqtt-message-' + messageTypes[messageTypeIndex] // message );
ignore errors
-t connect/to-device-id/from-device-id -m channel-receive-id
B handleConnectRequest (reqMessage{connreqA, channelNonceA})
debug('%s handleConnectRequest(%s)', this.clientId, topicPath.join('/'));
validate topic
check if connecting to this client
not to this client
- ignore, done
no channel id for sending channel is set
invalid connect request
get connection
connection already established
debug('%s getSharedSecret(%s)', this.clientId, connection && connection.clientId);
debug('%s publishConnectAck(%s)', this.clientId, clientId);
get connection
A handleConnectAck (ackMessage{connackB, channelNonceB})
debug('%s handleConnectAck(%s)', this.clientId, topicPath.join('/'));
validate topic
check if connecting to this client
not to this client
invalid connect request
opens a message channel to enable other client to send messages
debug('%s subscribeReceiveChannel(%s)', this.clientId, clientId);
get connected client
build topic
subscribe -t message/
subscribe to connect topic -t message/<from-device-id>/<to-channel-id>/#
set topicReceive: subscribe -t 'message/<connected-client-id>/<channel-receive-id>/#'
set topicSend: publish -t 'message/<this-client-id>/<channel-send-id>/'
publish a connect message to the other client to let him know this client wants to connect
publish -t connect/to-device-id/from-device-id -m
debug('%s publishConnectRequest(%s)', this.clientId, clientId);
get connected client
disconnect from broker
remove from clients list
unsubscribe receive topic
MqttReqRes