mirror of
https://github.com/JasonYANG170/IOTConnect-Web.git
synced 2024-11-27 14:16:34 +00:00
292 lines
7.7 KiB
JavaScript
292 lines
7.7 KiB
JavaScript
|
/* Protocol - protocol constants */
|
||
|
const protocol = module.exports
|
||
|
const { Buffer } = require('buffer')
|
||
|
|
||
|
/* Command code => mnemonic */
|
||
|
protocol.types = {
|
||
|
0: 'reserved',
|
||
|
1: 'connect',
|
||
|
2: 'connack',
|
||
|
3: 'publish',
|
||
|
4: 'puback',
|
||
|
5: 'pubrec',
|
||
|
6: 'pubrel',
|
||
|
7: 'pubcomp',
|
||
|
8: 'subscribe',
|
||
|
9: 'suback',
|
||
|
10: 'unsubscribe',
|
||
|
11: 'unsuback',
|
||
|
12: 'pingreq',
|
||
|
13: 'pingresp',
|
||
|
14: 'disconnect',
|
||
|
15: 'auth'
|
||
|
}
|
||
|
|
||
|
protocol.requiredHeaderFlags = {
|
||
|
1: 0, // 'connect'
|
||
|
2: 0, // 'connack'
|
||
|
4: 0, // 'puback'
|
||
|
5: 0, // 'pubrec'
|
||
|
6: 2, // 'pubrel'
|
||
|
7: 0, // 'pubcomp'
|
||
|
8: 2, // 'subscribe'
|
||
|
9: 0, // 'suback'
|
||
|
10: 2, // 'unsubscribe'
|
||
|
11: 0, // 'unsuback'
|
||
|
12: 0, // 'pingreq'
|
||
|
13: 0, // 'pingresp'
|
||
|
14: 0, // 'disconnect'
|
||
|
15: 0 // 'auth'
|
||
|
}
|
||
|
|
||
|
protocol.requiredHeaderFlagsErrors = {}
|
||
|
for (const k in protocol.requiredHeaderFlags) {
|
||
|
const v = protocol.requiredHeaderFlags[k]
|
||
|
protocol.requiredHeaderFlagsErrors[k] = 'Invalid header flag bits, must be 0x' + v.toString(16) + ' for ' + protocol.types[k] + ' packet'
|
||
|
}
|
||
|
|
||
|
/* Mnemonic => Command code */
|
||
|
protocol.codes = {}
|
||
|
for (const k in protocol.types) {
|
||
|
const v = protocol.types[k]
|
||
|
protocol.codes[v] = k
|
||
|
}
|
||
|
|
||
|
/* Header */
|
||
|
protocol.CMD_SHIFT = 4
|
||
|
protocol.CMD_MASK = 0xF0
|
||
|
protocol.DUP_MASK = 0x08
|
||
|
protocol.QOS_MASK = 0x03
|
||
|
protocol.QOS_SHIFT = 1
|
||
|
protocol.RETAIN_MASK = 0x01
|
||
|
|
||
|
/* Length */
|
||
|
protocol.VARBYTEINT_MASK = 0x7F
|
||
|
protocol.VARBYTEINT_FIN_MASK = 0x80
|
||
|
protocol.VARBYTEINT_MAX = 268435455
|
||
|
|
||
|
/* Connack */
|
||
|
protocol.SESSIONPRESENT_MASK = 0x01
|
||
|
protocol.SESSIONPRESENT_HEADER = Buffer.from([protocol.SESSIONPRESENT_MASK])
|
||
|
protocol.CONNACK_HEADER = Buffer.from([protocol.codes.connack << protocol.CMD_SHIFT])
|
||
|
|
||
|
/* Connect */
|
||
|
protocol.USERNAME_MASK = 0x80
|
||
|
protocol.PASSWORD_MASK = 0x40
|
||
|
protocol.WILL_RETAIN_MASK = 0x20
|
||
|
protocol.WILL_QOS_MASK = 0x18
|
||
|
protocol.WILL_QOS_SHIFT = 3
|
||
|
protocol.WILL_FLAG_MASK = 0x04
|
||
|
protocol.CLEAN_SESSION_MASK = 0x02
|
||
|
protocol.CONNECT_HEADER = Buffer.from([protocol.codes.connect << protocol.CMD_SHIFT])
|
||
|
|
||
|
/* Properties */
|
||
|
protocol.properties = {
|
||
|
sessionExpiryInterval: 17,
|
||
|
willDelayInterval: 24,
|
||
|
receiveMaximum: 33,
|
||
|
maximumPacketSize: 39,
|
||
|
topicAliasMaximum: 34,
|
||
|
requestResponseInformation: 25,
|
||
|
requestProblemInformation: 23,
|
||
|
userProperties: 38,
|
||
|
authenticationMethod: 21,
|
||
|
authenticationData: 22,
|
||
|
payloadFormatIndicator: 1,
|
||
|
messageExpiryInterval: 2,
|
||
|
contentType: 3,
|
||
|
responseTopic: 8,
|
||
|
correlationData: 9,
|
||
|
maximumQoS: 36,
|
||
|
retainAvailable: 37,
|
||
|
assignedClientIdentifier: 18,
|
||
|
reasonString: 31,
|
||
|
wildcardSubscriptionAvailable: 40,
|
||
|
subscriptionIdentifiersAvailable: 41,
|
||
|
sharedSubscriptionAvailable: 42,
|
||
|
serverKeepAlive: 19,
|
||
|
responseInformation: 26,
|
||
|
serverReference: 28,
|
||
|
topicAlias: 35,
|
||
|
subscriptionIdentifier: 11
|
||
|
}
|
||
|
protocol.propertiesCodes = {}
|
||
|
for (const prop in protocol.properties) {
|
||
|
const id = protocol.properties[prop]
|
||
|
protocol.propertiesCodes[id] = prop
|
||
|
}
|
||
|
protocol.propertiesTypes = {
|
||
|
sessionExpiryInterval: 'int32',
|
||
|
willDelayInterval: 'int32',
|
||
|
receiveMaximum: 'int16',
|
||
|
maximumPacketSize: 'int32',
|
||
|
topicAliasMaximum: 'int16',
|
||
|
requestResponseInformation: 'byte',
|
||
|
requestProblemInformation: 'byte',
|
||
|
userProperties: 'pair',
|
||
|
authenticationMethod: 'string',
|
||
|
authenticationData: 'binary',
|
||
|
payloadFormatIndicator: 'byte',
|
||
|
messageExpiryInterval: 'int32',
|
||
|
contentType: 'string',
|
||
|
responseTopic: 'string',
|
||
|
correlationData: 'binary',
|
||
|
maximumQoS: 'int8',
|
||
|
retainAvailable: 'byte',
|
||
|
assignedClientIdentifier: 'string',
|
||
|
reasonString: 'string',
|
||
|
wildcardSubscriptionAvailable: 'byte',
|
||
|
subscriptionIdentifiersAvailable: 'byte',
|
||
|
sharedSubscriptionAvailable: 'byte',
|
||
|
serverKeepAlive: 'int16',
|
||
|
responseInformation: 'string',
|
||
|
serverReference: 'string',
|
||
|
topicAlias: 'int16',
|
||
|
subscriptionIdentifier: 'var'
|
||
|
}
|
||
|
|
||
|
function genHeader (type) {
|
||
|
return [0, 1, 2].map(qos => {
|
||
|
return [0, 1].map(dup => {
|
||
|
return [0, 1].map(retain => {
|
||
|
const buf = Buffer.alloc(1)
|
||
|
buf.writeUInt8(
|
||
|
protocol.codes[type] << protocol.CMD_SHIFT |
|
||
|
(dup ? protocol.DUP_MASK : 0) |
|
||
|
qos << protocol.QOS_SHIFT | retain, 0, true)
|
||
|
return buf
|
||
|
})
|
||
|
})
|
||
|
})
|
||
|
}
|
||
|
|
||
|
/* Publish */
|
||
|
protocol.PUBLISH_HEADER = genHeader('publish')
|
||
|
|
||
|
/* Subscribe */
|
||
|
protocol.SUBSCRIBE_HEADER = genHeader('subscribe')
|
||
|
protocol.SUBSCRIBE_OPTIONS_QOS_MASK = 0x03
|
||
|
protocol.SUBSCRIBE_OPTIONS_NL_MASK = 0x01
|
||
|
protocol.SUBSCRIBE_OPTIONS_NL_SHIFT = 2
|
||
|
protocol.SUBSCRIBE_OPTIONS_RAP_MASK = 0x01
|
||
|
protocol.SUBSCRIBE_OPTIONS_RAP_SHIFT = 3
|
||
|
protocol.SUBSCRIBE_OPTIONS_RH_MASK = 0x03
|
||
|
protocol.SUBSCRIBE_OPTIONS_RH_SHIFT = 4
|
||
|
protocol.SUBSCRIBE_OPTIONS_RH = [0x00, 0x10, 0x20]
|
||
|
protocol.SUBSCRIBE_OPTIONS_NL = 0x04
|
||
|
protocol.SUBSCRIBE_OPTIONS_RAP = 0x08
|
||
|
protocol.SUBSCRIBE_OPTIONS_QOS = [0x00, 0x01, 0x02]
|
||
|
|
||
|
/* Unsubscribe */
|
||
|
protocol.UNSUBSCRIBE_HEADER = genHeader('unsubscribe')
|
||
|
|
||
|
/* Confirmations */
|
||
|
protocol.ACKS = {
|
||
|
unsuback: genHeader('unsuback'),
|
||
|
puback: genHeader('puback'),
|
||
|
pubcomp: genHeader('pubcomp'),
|
||
|
pubrel: genHeader('pubrel'),
|
||
|
pubrec: genHeader('pubrec')
|
||
|
}
|
||
|
|
||
|
protocol.SUBACK_HEADER = Buffer.from([protocol.codes.suback << protocol.CMD_SHIFT])
|
||
|
|
||
|
/* Protocol versions */
|
||
|
protocol.VERSION3 = Buffer.from([3])
|
||
|
protocol.VERSION4 = Buffer.from([4])
|
||
|
protocol.VERSION5 = Buffer.from([5])
|
||
|
protocol.VERSION131 = Buffer.from([131])
|
||
|
protocol.VERSION132 = Buffer.from([132])
|
||
|
|
||
|
/* QoS */
|
||
|
protocol.QOS = [0, 1, 2].map(qos => {
|
||
|
return Buffer.from([qos])
|
||
|
})
|
||
|
|
||
|
/* Empty packets */
|
||
|
protocol.EMPTY = {
|
||
|
pingreq: Buffer.from([protocol.codes.pingreq << 4, 0]),
|
||
|
pingresp: Buffer.from([protocol.codes.pingresp << 4, 0]),
|
||
|
disconnect: Buffer.from([protocol.codes.disconnect << 4, 0])
|
||
|
}
|
||
|
|
||
|
protocol.MQTT5_PUBACK_PUBREC_CODES = {
|
||
|
0x00: 'Success',
|
||
|
0x10: 'No matching subscribers',
|
||
|
0x80: 'Unspecified error',
|
||
|
0x83: 'Implementation specific error',
|
||
|
0x87: 'Not authorized',
|
||
|
0x90: 'Topic Name invalid',
|
||
|
0x91: 'Packet identifier in use',
|
||
|
0x97: 'Quota exceeded',
|
||
|
0x99: 'Payload format invalid'
|
||
|
}
|
||
|
|
||
|
protocol.MQTT5_PUBREL_PUBCOMP_CODES = {
|
||
|
0x00: 'Success',
|
||
|
0x92: 'Packet Identifier not found'
|
||
|
}
|
||
|
|
||
|
protocol.MQTT5_SUBACK_CODES = {
|
||
|
0x00: 'Granted QoS 0',
|
||
|
0x01: 'Granted QoS 1',
|
||
|
0x02: 'Granted QoS 2',
|
||
|
0x80: 'Unspecified error',
|
||
|
0x83: 'Implementation specific error',
|
||
|
0x87: 'Not authorized',
|
||
|
0x8F: 'Topic Filter invalid',
|
||
|
0x91: 'Packet Identifier in use',
|
||
|
0x97: 'Quota exceeded',
|
||
|
0x9E: 'Shared Subscriptions not supported',
|
||
|
0xA1: 'Subscription Identifiers not supported',
|
||
|
0xA2: 'Wildcard Subscriptions not supported'
|
||
|
}
|
||
|
|
||
|
protocol.MQTT5_UNSUBACK_CODES = {
|
||
|
0x00: 'Success',
|
||
|
0x11: 'No subscription existed',
|
||
|
0x80: 'Unspecified error',
|
||
|
0x83: 'Implementation specific error',
|
||
|
0x87: 'Not authorized',
|
||
|
0x8F: 'Topic Filter invalid',
|
||
|
0x91: 'Packet Identifier in use'
|
||
|
}
|
||
|
|
||
|
protocol.MQTT5_DISCONNECT_CODES = {
|
||
|
0x00: 'Normal disconnection',
|
||
|
0x04: 'Disconnect with Will Message',
|
||
|
0x80: 'Unspecified error',
|
||
|
0x81: 'Malformed Packet',
|
||
|
0x82: 'Protocol Error',
|
||
|
0x83: 'Implementation specific error',
|
||
|
0x87: 'Not authorized',
|
||
|
0x89: 'Server busy',
|
||
|
0x8B: 'Server shutting down',
|
||
|
0x8D: 'Keep Alive timeout',
|
||
|
0x8E: 'Session taken over',
|
||
|
0x8F: 'Topic Filter invalid',
|
||
|
0x90: 'Topic Name invalid',
|
||
|
0x93: 'Receive Maximum exceeded',
|
||
|
0x94: 'Topic Alias invalid',
|
||
|
0x95: 'Packet too large',
|
||
|
0x96: 'Message rate too high',
|
||
|
0x97: 'Quota exceeded',
|
||
|
0x98: 'Administrative action',
|
||
|
0x99: 'Payload format invalid',
|
||
|
0x9A: 'Retain not supported',
|
||
|
0x9B: 'QoS not supported',
|
||
|
0x9C: 'Use another server',
|
||
|
0x9D: 'Server moved',
|
||
|
0x9E: 'Shared Subscriptions not supported',
|
||
|
0x9F: 'Connection rate exceeded',
|
||
|
0xA0: 'Maximum connect time',
|
||
|
0xA1: 'Subscription Identifiers not supported',
|
||
|
0xA2: 'Wildcard Subscriptions not supported'
|
||
|
}
|
||
|
|
||
|
protocol.MQTT5_AUTH_CODES = {
|
||
|
0x00: 'Success',
|
||
|
0x18: 'Continue authentication',
|
||
|
0x19: 'Re-authenticate'
|
||
|
}
|