Initial commit
This commit is contained in:
27
node_modules/mqtt-packet/.github/workflows/ci.yml
generated
vendored
Executable file
27
node_modules/mqtt-packet/.github/workflows/ci.yml
generated
vendored
Executable file
@@ -0,0 +1,27 @@
|
||||
name: ci
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [16.x, 18.x, 20.x]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
- name: Install
|
||||
run: |
|
||||
npm install
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
npm run ci
|
||||
27
node_modules/mqtt-packet/CONTRIBUTING.md
generated
vendored
Executable file
27
node_modules/mqtt-packet/CONTRIBUTING.md
generated
vendored
Executable file
@@ -0,0 +1,27 @@
|
||||
# mqtt-packet is an OPEN Open Source Project
|
||||
|
||||
-----------------------------------------
|
||||
|
||||
## What?
|
||||
|
||||
Individuals making significant and valuable contributions are given commit-access to the project to contribute as they see fit. This project is more like an open wiki than a standard guarded open source project.
|
||||
|
||||
## Rules
|
||||
|
||||
There are a few basic ground-rules for contributors:
|
||||
|
||||
1. **No `--force` pushes** or modifying the Git history in any way.
|
||||
1. **Non-master branches** ought to be used for ongoing work.
|
||||
1. **External API changes and significant modifications** ought to be subject to an **internal pull-request** to solicit feedback from other contributors.
|
||||
1. Internal pull-requests to solicit feedback are *encouraged* for any other non-trivial contribution but left to the discretion of the contributor.
|
||||
1. Contributors should attempt to adhere to the prevailing code-style.
|
||||
|
||||
## Releases
|
||||
|
||||
Declaring formal releases remains the prerogative of the project maintainer.
|
||||
|
||||
## Changes to this arrangement
|
||||
|
||||
This is an experiment and feedback is welcome! This document may also be subject to pull-requests or changes by contributors where you believe you have something valuable to add or change.
|
||||
|
||||
-----------------------------------------
|
||||
13
node_modules/mqtt-packet/LICENSE.md
generated
vendored
Executable file
13
node_modules/mqtt-packet/LICENSE.md
generated
vendored
Executable file
@@ -0,0 +1,13 @@
|
||||
The MIT License (MIT)
|
||||
=====================
|
||||
|
||||
Copyright (c) 2014-2017 mqtt-packet contributors
|
||||
---------------------------------------
|
||||
|
||||
*mqtt-packet contributors listed at <https://github.com/mqttjs/mqtt-packet#contributors>*
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
491
node_modules/mqtt-packet/README.md
generated
vendored
Executable file
491
node_modules/mqtt-packet/README.md
generated
vendored
Executable file
@@ -0,0 +1,491 @@
|
||||
mqtt-packet
|
||||
===========
|
||||
|
||||
Encode and Decode MQTT 3.1.1, 5.0 packets the node way.
|
||||
|
||||
[](https://github.com/feross/standard)
|
||||
|
||||
* <a href="#installation">Installation</a>
|
||||
* <a href="#examples">Examples</a>
|
||||
* <a href="#packets">Packets</a>
|
||||
* <a href="#api">API</a>
|
||||
* <a href="#contributing">Contributing</a>
|
||||
* <a href="#license">License & copyright</a>
|
||||
|
||||
This library is tested with node v6, v8, v10, v12 and v14. The last version to support
|
||||
older versions of node was mqtt-packet@4.1.2.
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
```bash
|
||||
npm install mqtt-packet --save
|
||||
```
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
### Generating
|
||||
|
||||
```js
|
||||
const mqtt = require('mqtt-packet');
|
||||
const object = {
|
||||
cmd: 'publish',
|
||||
retain: false,
|
||||
qos: 0,
|
||||
dup: false,
|
||||
length: 10,
|
||||
topic: 'test',
|
||||
payload: 'test' // Can also be a Buffer
|
||||
};
|
||||
const opts = { protocolVersion: 4 }; // default is 4. Usually, opts is a connect packet
|
||||
|
||||
console.log(mqtt.generate(object))
|
||||
// Prints:
|
||||
//
|
||||
// <Buffer 30 0a 00 04 74 65 73 74 74 65 73 74>
|
||||
//
|
||||
// Which is the same as:
|
||||
//
|
||||
// Buffer.from([
|
||||
// 48, 10, // Header (publish)
|
||||
// 0, 4, // Topic length
|
||||
// 116, 101, 115, 116, // Topic (test)
|
||||
// 116, 101, 115, 116 // Payload (test)
|
||||
// ])
|
||||
```
|
||||
|
||||
### Parsing
|
||||
|
||||
```js
|
||||
const mqtt = require('mqtt-packet');
|
||||
const opts = { protocolVersion: 4 }; // default is 4. Usually, opts is a connect packet
|
||||
const parser = mqtt.parser(opts);
|
||||
|
||||
// Synchronously emits all the parsed packets
|
||||
parser.on('packet', packet => {
|
||||
console.log(packet)
|
||||
// Prints:
|
||||
//
|
||||
// {
|
||||
// cmd: 'publish',
|
||||
// retain: false,
|
||||
// qos: 0,
|
||||
// dup: false,
|
||||
// length: 10,
|
||||
// topic: 'test',
|
||||
// payload: <Buffer 74 65 73 74>
|
||||
// }
|
||||
})
|
||||
|
||||
parser.parse(Buffer.from([
|
||||
48, 10, // Header (publish)
|
||||
0, 4, // Topic length
|
||||
116, 101, 115, 116, // Topic (test)
|
||||
116, 101, 115, 116 // Payload (test)
|
||||
]))
|
||||
// Returns the number of bytes left in the parser
|
||||
```
|
||||
|
||||
API
|
||||
---
|
||||
|
||||
* <a href="#generate"><code>mqtt#<b>generate()</b></code></a>
|
||||
* <a href="#writeToStream"><code>mqtt#<b>writeToStream()</b></code></a>
|
||||
* <a href="#parser"><code>mqtt#<b>parser()</b></code></a>
|
||||
|
||||
<a name="generate">
|
||||
|
||||
### mqtt.generate(object, [opts])
|
||||
|
||||
Generates a `Buffer` containing an MQTT packet.
|
||||
The object must be one of the ones specified by the [packets](#packets)
|
||||
section. Throws an `Error` if a packet cannot be generated.
|
||||
|
||||
<a name="writeToStream">
|
||||
|
||||
### mqtt.writeToStream(object, stream, [opts])
|
||||
|
||||
Writes the mqtt packet defined by `object` to the given stream.
|
||||
The object must be one of the ones specified by the [packets](#packets)
|
||||
section. Emits an `Error` on the stream if a packet cannot be generated.
|
||||
On node >= 0.12, this function automatically calls `cork()` on your stream,
|
||||
and then it calls `uncork()` on the next tick.
|
||||
By default cache for number buffers is enabled.
|
||||
It creates a list of buffers for faster write. To disable cache set `mqtt.writeToStream.cacheNumbers = false`.
|
||||
Should be set before any `writeToStream` calls.
|
||||
|
||||
<a name="parser">
|
||||
|
||||
### mqtt.parser([opts])
|
||||
|
||||
Returns a new `Parser` object. `Parser` inherits from `EventEmitter` and
|
||||
will emit:
|
||||
|
||||
* `packet`, when a new packet is parsed, according to
|
||||
[packets](#packets)
|
||||
* `error`, if an error happens
|
||||
|
||||
<a name="parse">
|
||||
|
||||
#### Parser.parse(buffer)
|
||||
|
||||
Parses a given `Buffer` and emits synchronously all the MQTT packets that
|
||||
are included. Returns the number of bytes left to parse.
|
||||
|
||||
If an error happens, an `error` event will be emitted, but no `packet` events
|
||||
will be emitted after that. Calling `parse()` again clears the error and
|
||||
previous buffer, as if you created a new `Parser`.
|
||||
|
||||
Packets
|
||||
-------
|
||||
|
||||
This section describes the format of all packets emitted by the `Parser`
|
||||
and that you can input to `generate`.
|
||||
|
||||
### Connect
|
||||
|
||||
```js
|
||||
{
|
||||
cmd: 'connect',
|
||||
protocolId: 'MQTT', // Or 'MQIsdp' in MQTT 3.1 and 5.0
|
||||
protocolVersion: 4, // Or 3 in MQTT 3.1, or 5 in MQTT 5.0
|
||||
clean: true, // Can also be false
|
||||
clientId: 'my-device',
|
||||
keepalive: 0, // Seconds which can be any positive number, with 0 as the default setting
|
||||
username: 'matteo',
|
||||
password: Buffer.from('collina'), // Passwords are buffers
|
||||
will: {
|
||||
topic: 'mydevice/status',
|
||||
payload: Buffer.from('dead'), // Payloads are buffers
|
||||
properties: { // MQTT 5.0
|
||||
willDelayInterval: 1234,
|
||||
payloadFormatIndicator: false,
|
||||
messageExpiryInterval: 4321,
|
||||
contentType: 'test',
|
||||
responseTopic: 'topic',
|
||||
correlationData: Buffer.from([1, 2, 3, 4]),
|
||||
userProperties: {
|
||||
'test': 'test'
|
||||
}
|
||||
}
|
||||
},
|
||||
properties: { // MQTT 5.0 properties
|
||||
sessionExpiryInterval: 1234,
|
||||
receiveMaximum: 432,
|
||||
maximumPacketSize: 100,
|
||||
topicAliasMaximum: 456,
|
||||
requestResponseInformation: true,
|
||||
requestProblemInformation: true,
|
||||
userProperties: {
|
||||
'test': 'test'
|
||||
},
|
||||
authenticationMethod: 'test',
|
||||
authenticationData: Buffer.from([1, 2, 3, 4])
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If `protocolVersion` is 3, `clientId` is mandatory and `generate` will throw if
|
||||
missing.
|
||||
|
||||
If `password` or `will.payload` are passed as strings, they will
|
||||
automatically be converted into a `Buffer`.
|
||||
|
||||
### Connack
|
||||
|
||||
```js
|
||||
{
|
||||
cmd: 'connack',
|
||||
returnCode: 0, // Or whatever else you see fit MQTT < 5.0
|
||||
sessionPresent: false, // Can also be true.
|
||||
reasonCode: 0, // reason code MQTT 5.0
|
||||
properties: { // MQTT 5.0 properties
|
||||
sessionExpiryInterval: 1234,
|
||||
receiveMaximum: 432,
|
||||
maximumQoS: 1,
|
||||
retainAvailable: true,
|
||||
maximumPacketSize: 100,
|
||||
assignedClientIdentifier: 'test',
|
||||
topicAliasMaximum: 456,
|
||||
reasonString: 'test',
|
||||
userProperties: {
|
||||
'test': 'test'
|
||||
},
|
||||
wildcardSubscriptionAvailable: true,
|
||||
subscriptionIdentifiersAvailable: true,
|
||||
sharedSubscriptionAvailable: false,
|
||||
serverKeepAlive: 1234,
|
||||
responseInformation: 'test',
|
||||
serverReference: 'test',
|
||||
authenticationMethod: 'test',
|
||||
authenticationData: Buffer.from([1, 2, 3, 4])
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The only mandatory argument is `returnCode`, as `generate` will throw if
|
||||
missing.
|
||||
|
||||
### Subscribe
|
||||
|
||||
```js
|
||||
{
|
||||
cmd: 'subscribe',
|
||||
messageId: 42,
|
||||
properties: { // MQTT 5.0 properties
|
||||
subscriptionIdentifier: 145,
|
||||
userProperties: {
|
||||
test: 'test'
|
||||
}
|
||||
}
|
||||
subscriptions: [{
|
||||
topic: 'test',
|
||||
qos: 0,
|
||||
nl: false, // no Local MQTT 5.0 flag
|
||||
rap: true, // Retain as Published MQTT 5.0 flag
|
||||
rh: 1 // Retain Handling MQTT 5.0
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
All properties are mandatory.
|
||||
|
||||
### Suback
|
||||
|
||||
```js
|
||||
{
|
||||
cmd: 'suback',
|
||||
messageId: 42,
|
||||
properties: { // MQTT 5.0 properties
|
||||
reasonString: 'test',
|
||||
userProperties: {
|
||||
'test': 'test'
|
||||
}
|
||||
}
|
||||
granted: [0, 1, 2, 128]
|
||||
}
|
||||
```
|
||||
|
||||
All the granted qos __must__ be < 256, as they are encoded as UInt8.
|
||||
All properties are mandatory.
|
||||
|
||||
### Unsubscribe
|
||||
|
||||
```js
|
||||
{
|
||||
cmd: 'unsubscribe',
|
||||
messageId: 42,
|
||||
properties: { // MQTT 5.0 properties
|
||||
userProperties: {
|
||||
'test': 'test'
|
||||
}
|
||||
}
|
||||
unsubscriptions: [
|
||||
'test',
|
||||
'a/topic'
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
All properties are mandatory.
|
||||
|
||||
### Unsuback
|
||||
|
||||
```js
|
||||
{
|
||||
cmd: 'unsuback',
|
||||
messageId: 42,
|
||||
properties: { // MQTT 5.0 properties
|
||||
reasonString: 'test',
|
||||
userProperties: {
|
||||
'test': 'test'
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
All properties are mandatory.
|
||||
|
||||
### Publish
|
||||
|
||||
```js
|
||||
{
|
||||
cmd: 'publish',
|
||||
messageId: 42,
|
||||
qos: 2,
|
||||
dup: false,
|
||||
topic: 'test',
|
||||
payload: Buffer.from('test'),
|
||||
retain: false,
|
||||
properties: { // optional properties MQTT 5.0
|
||||
payloadFormatIndicator: true,
|
||||
messageExpiryInterval: 4321,
|
||||
topicAlias: 100,
|
||||
responseTopic: 'topic',
|
||||
correlationData: Buffer.from([1, 2, 3, 4]),
|
||||
userProperties: {
|
||||
'test': 'test'
|
||||
},
|
||||
subscriptionIdentifier: 120, // can be an Array in message from broker, if message included in few another subscriptions
|
||||
contentType: 'test'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Only the `topic` property is mandatory.
|
||||
Both `topic` and `payload` can be `Buffer` objects instead of strings.
|
||||
`messageId` is mandatory for `qos > 0`.
|
||||
|
||||
### Puback
|
||||
|
||||
```js
|
||||
{
|
||||
cmd: 'puback',
|
||||
messageId: 42,
|
||||
reasonCode: 16, // only for MQTT 5.0
|
||||
properties: { // MQTT 5.0 properties
|
||||
reasonString: 'test',
|
||||
userProperties: {
|
||||
'test': 'test'
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The only mandatory property is `messageId`, as `generate` will throw if
|
||||
missing.
|
||||
|
||||
### Pubrec
|
||||
|
||||
```js
|
||||
{
|
||||
cmd: 'pubrec',
|
||||
messageId: 42,
|
||||
reasonCode: 16, // only for MQTT 5.0
|
||||
properties: { // properties MQTT 5.0
|
||||
reasonString: 'test',
|
||||
userProperties: {
|
||||
'test': 'test'
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The only mandatory property is `messageId`, as `generate` will throw if
|
||||
missing.
|
||||
|
||||
### Pubrel
|
||||
|
||||
```js
|
||||
{
|
||||
cmd: 'pubrel',
|
||||
messageId: 42,
|
||||
reasonCode: 16, // only for MQTT 5.0
|
||||
properties: { // properties MQTT 5.0
|
||||
reasonString: 'test',
|
||||
userProperties: {
|
||||
'test': 'test'
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The only mandatory property is `messageId`, as `generate` will throw if
|
||||
missing.
|
||||
|
||||
### Pubcomp
|
||||
|
||||
```js
|
||||
{
|
||||
cmd: 'pubcomp',
|
||||
messageId: 42,
|
||||
reasonCode: 16, // only for MQTT 5.0
|
||||
properties: { // properties MQTT 5.0
|
||||
reasonString: 'test',
|
||||
userProperties: {
|
||||
'test': 'test'
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The only mandatory property is `messageId`, as `generate` will throw if
|
||||
missing.
|
||||
|
||||
### Pingreq
|
||||
|
||||
```js
|
||||
{
|
||||
cmd: 'pingreq'
|
||||
}
|
||||
```
|
||||
|
||||
### Pingresp
|
||||
|
||||
```js
|
||||
{
|
||||
cmd: 'pingresp'
|
||||
}
|
||||
```
|
||||
|
||||
### Disconnect
|
||||
|
||||
```js
|
||||
{
|
||||
cmd: 'disconnect',
|
||||
reasonCode: 0, // MQTT 5.0 code
|
||||
properties: { // properties MQTT 5.0
|
||||
sessionExpiryInterval: 145,
|
||||
reasonString: 'test',
|
||||
userProperties: {
|
||||
'test': 'test'
|
||||
},
|
||||
serverReference: 'test'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Auth
|
||||
|
||||
```js
|
||||
{
|
||||
cmd: 'auth',
|
||||
reasonCode: 0, // MQTT 5.0 code
|
||||
properties: { // properties MQTT 5.0
|
||||
authenticationMethod: 'test',
|
||||
authenticationData: Buffer.from([0, 1, 2, 3]),
|
||||
reasonString: 'test',
|
||||
userProperties: {
|
||||
'test': 'test'
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<a name="contributing"></a>
|
||||
|
||||
Contributing
|
||||
------------
|
||||
|
||||
mqtt-packet is an **OPEN Open Source Project**. This means that:
|
||||
|
||||
> Individuals making significant and valuable contributions are given commit-access to the project to contribute as they see fit. This project is more like an open wiki than a standard guarded open source project.
|
||||
|
||||
See the [CONTRIBUTING.md](https://github.com/mqttjs/mqtt-packet/blob/master/CONTRIBUTING.md) file for more details.
|
||||
|
||||
### Contributors
|
||||
|
||||
mqtt-packet is only possible due to the excellent work of the following contributors:
|
||||
|
||||
<table><tbody>
|
||||
<tr><th align="left">Matteo Collina</th><td><a href="https://github.com/mcollina">GitHub/mcollina</a></td><td><a href="http://twitter.com/matteocollina">Twitter/@matteocollina</a></td></tr>
|
||||
<tr><th align="left">Adam Rudd</th><td><a href="https://github.com/adamvr">GitHub/adamvr</a></td><td><a href="http://twitter.com/adam_vr">Twitter/@adam_vr</a></td></tr>
|
||||
<tr><th align="left">Peter Sorowka</th><td><a href="https://github.com/psorowka">GitHub/psorowka</a></td><td><a href="http://twitter.com/psorowka">Twitter/@psorowka</a></td></tr>
|
||||
<tr><th align="left">Siarhei Buntsevich</th><td><a href="https://github.com/scarry1992">GitHub/scarry1992</a></td></tr>
|
||||
</tbody></table>
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
MIT
|
||||
26
node_modules/mqtt-packet/benchmarks/generate.js
generated
vendored
Executable file
26
node_modules/mqtt-packet/benchmarks/generate.js
generated
vendored
Executable file
@@ -0,0 +1,26 @@
|
||||
const mqtt = require('../')
|
||||
const max = 100000
|
||||
let i
|
||||
const buf = Buffer.from('test')
|
||||
|
||||
// initialize it
|
||||
mqtt.generate({
|
||||
cmd: 'publish',
|
||||
topic: 'test',
|
||||
payload: buf
|
||||
})
|
||||
|
||||
const start = Date.now()
|
||||
|
||||
for (i = 0; i < max; i++) {
|
||||
mqtt.generate({
|
||||
cmd: 'publish',
|
||||
topic: 'test',
|
||||
payload: buf
|
||||
})
|
||||
}
|
||||
|
||||
const time = Date.now() - start
|
||||
console.log('Total time', time)
|
||||
console.log('Total packets', max)
|
||||
console.log('Packet/s', max / time * 1000)
|
||||
50
node_modules/mqtt-packet/benchmarks/generateNet.js
generated
vendored
Executable file
50
node_modules/mqtt-packet/benchmarks/generateNet.js
generated
vendored
Executable file
@@ -0,0 +1,50 @@
|
||||
const mqtt = require('../')
|
||||
const max = 1000000
|
||||
let i = 0
|
||||
const start = Date.now()
|
||||
let time
|
||||
const buf = Buffer.allocUnsafe(10)
|
||||
const net = require('net')
|
||||
const server = net.createServer(handle)
|
||||
let dest
|
||||
|
||||
buf.fill('test')
|
||||
|
||||
function handle (sock) {
|
||||
sock.resume()
|
||||
}
|
||||
|
||||
server.listen(0, () => {
|
||||
dest = net.connect(server.address())
|
||||
|
||||
dest.on('connect', tickWait)
|
||||
dest.on('drain', tickWait)
|
||||
dest.on('finish', () => {
|
||||
time = Date.now() - start
|
||||
console.log('Total time', time)
|
||||
console.log('Total packets', max)
|
||||
console.log('Packet/s', max / time * 1000)
|
||||
server.close()
|
||||
})
|
||||
})
|
||||
|
||||
function tickWait () {
|
||||
// console.log('tickWait', i)
|
||||
let res = true
|
||||
// var toSend = new Buffer(5 + buf.length)
|
||||
|
||||
for (; i < max && res; i++) {
|
||||
res = dest.write(mqtt.generate({
|
||||
cmd: 'publish',
|
||||
topic: 'test',
|
||||
payload: buf
|
||||
}))
|
||||
// buf.copy(toSend, 5)
|
||||
// res = dest.write(toSend, 'buffer')
|
||||
// console.log(res)
|
||||
}
|
||||
|
||||
if (i >= max) {
|
||||
dest.end()
|
||||
}
|
||||
}
|
||||
19
node_modules/mqtt-packet/benchmarks/parse.js
generated
vendored
Executable file
19
node_modules/mqtt-packet/benchmarks/parse.js
generated
vendored
Executable file
@@ -0,0 +1,19 @@
|
||||
const mqtt = require('../')
|
||||
const parser = mqtt.parser()
|
||||
const max = 10000000
|
||||
let i
|
||||
const start = Date.now() / 1000
|
||||
|
||||
for (i = 0; i < max; i++) {
|
||||
parser.parse(Buffer.from([
|
||||
48, 10, // Header (publish)
|
||||
0, 4, // Topic length
|
||||
116, 101, 115, 116, // Topic (test)
|
||||
116, 101, 115, 116 // Payload (test)
|
||||
]))
|
||||
}
|
||||
|
||||
const time = Date.now() / 1000 - start
|
||||
console.log('Total packets', max)
|
||||
console.log('Total time', Math.round(time * 100) / 100)
|
||||
console.log('Packet/s', max / time)
|
||||
48
node_modules/mqtt-packet/benchmarks/writeToStream.js
generated
vendored
Executable file
48
node_modules/mqtt-packet/benchmarks/writeToStream.js
generated
vendored
Executable file
@@ -0,0 +1,48 @@
|
||||
const mqtt = require('../')
|
||||
const max = 1000000
|
||||
let i = 0
|
||||
const start = Date.now()
|
||||
let time
|
||||
const buf = Buffer.allocUnsafe(10)
|
||||
const net = require('net')
|
||||
const server = net.createServer(handle)
|
||||
let dest
|
||||
|
||||
function handle (sock) {
|
||||
sock.resume()
|
||||
}
|
||||
|
||||
buf.fill('test')
|
||||
|
||||
server.listen(0, () => {
|
||||
dest = net.connect(server.address())
|
||||
|
||||
dest.on('connect', tickWait)
|
||||
dest.on('drain', tickWait)
|
||||
dest.on('finish', () => {
|
||||
time = Date.now() - start
|
||||
console.log('Total time', time)
|
||||
console.log('Total packets', max)
|
||||
console.log('Packet/s', max / time * 1000)
|
||||
server.close()
|
||||
})
|
||||
})
|
||||
|
||||
function tickWait () {
|
||||
let res = true
|
||||
// var toSend = new Buffer(5)
|
||||
|
||||
for (; i < max && res; i++) {
|
||||
res = mqtt.writeToStream({
|
||||
cmd: 'publish',
|
||||
topic: 'test',
|
||||
payload: buf
|
||||
}, dest)
|
||||
// dest.write(toSend, 'buffer')
|
||||
// res = dest.write(buf, 'buffer')
|
||||
}
|
||||
|
||||
if (i >= max) {
|
||||
dest.end()
|
||||
}
|
||||
}
|
||||
291
node_modules/mqtt-packet/constants.js
generated
vendored
Executable file
291
node_modules/mqtt-packet/constants.js
generated
vendored
Executable file
@@ -0,0 +1,291 @@
|
||||
/* 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'
|
||||
}
|
||||
57
node_modules/mqtt-packet/generate.js
generated
vendored
Executable file
57
node_modules/mqtt-packet/generate.js
generated
vendored
Executable file
@@ -0,0 +1,57 @@
|
||||
const writeToStream = require('./writeToStream')
|
||||
const { EventEmitter } = require('events')
|
||||
const { Buffer } = require('buffer')
|
||||
|
||||
function generate (packet, opts) {
|
||||
const stream = new Accumulator()
|
||||
writeToStream(packet, stream, opts)
|
||||
return stream.concat()
|
||||
}
|
||||
|
||||
class Accumulator extends EventEmitter {
|
||||
constructor () {
|
||||
super()
|
||||
this._array = new Array(20)
|
||||
this._i = 0
|
||||
}
|
||||
|
||||
write (chunk) {
|
||||
this._array[this._i++] = chunk
|
||||
return true
|
||||
}
|
||||
|
||||
concat () {
|
||||
let length = 0
|
||||
const lengths = new Array(this._array.length)
|
||||
const list = this._array
|
||||
let pos = 0
|
||||
let i
|
||||
|
||||
for (i = 0; i < list.length && list[i] !== undefined; i++) {
|
||||
if (typeof list[i] !== 'string') lengths[i] = list[i].length
|
||||
else lengths[i] = Buffer.byteLength(list[i])
|
||||
|
||||
length += lengths[i]
|
||||
}
|
||||
|
||||
const result = Buffer.allocUnsafe(length)
|
||||
|
||||
for (i = 0; i < list.length && list[i] !== undefined; i++) {
|
||||
if (typeof list[i] !== 'string') {
|
||||
list[i].copy(result, pos)
|
||||
pos += lengths[i]
|
||||
} else {
|
||||
result.write(list[i], pos)
|
||||
pos += lengths[i]
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
destroy (err) {
|
||||
if (err) this.emit('error', err)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = generate
|
||||
3
node_modules/mqtt-packet/mqtt.js
generated
vendored
Executable file
3
node_modules/mqtt-packet/mqtt.js
generated
vendored
Executable file
@@ -0,0 +1,3 @@
|
||||
exports.parser = require('./parser').parser
|
||||
exports.generate = require('./generate')
|
||||
exports.writeToStream = require('./writeToStream')
|
||||
59
node_modules/mqtt-packet/numbers.js
generated
vendored
Executable file
59
node_modules/mqtt-packet/numbers.js
generated
vendored
Executable file
@@ -0,0 +1,59 @@
|
||||
const { Buffer } = require('buffer')
|
||||
const max = 65536
|
||||
const cache = {}
|
||||
|
||||
// in node 6 Buffer.subarray returns a Uint8Array instead of a Buffer
|
||||
// later versions return a Buffer
|
||||
// alternative is Buffer.slice but that creates a new buffer
|
||||
// creating new buffers takes time
|
||||
// SubOk is only false on node < 8
|
||||
const SubOk = Buffer.isBuffer(Buffer.from([1, 2]).subarray(0, 1))
|
||||
|
||||
function generateBuffer (i) {
|
||||
const buffer = Buffer.allocUnsafe(2)
|
||||
buffer.writeUInt8(i >> 8, 0)
|
||||
buffer.writeUInt8(i & 0x00FF, 0 + 1)
|
||||
|
||||
return buffer
|
||||
}
|
||||
|
||||
function generateCache () {
|
||||
for (let i = 0; i < max; i++) {
|
||||
cache[i] = generateBuffer(i)
|
||||
}
|
||||
}
|
||||
|
||||
function genBufVariableByteInt (num) {
|
||||
const maxLength = 4 // max 4 bytes
|
||||
let digit = 0
|
||||
let pos = 0
|
||||
const buffer = Buffer.allocUnsafe(maxLength)
|
||||
|
||||
do {
|
||||
digit = num % 128 | 0
|
||||
num = num / 128 | 0
|
||||
if (num > 0) digit = digit | 0x80
|
||||
|
||||
buffer.writeUInt8(digit, pos++)
|
||||
} while (num > 0 && pos < maxLength)
|
||||
|
||||
if (num > 0) {
|
||||
pos = 0
|
||||
}
|
||||
|
||||
return SubOk ? buffer.subarray(0, pos) : buffer.slice(0, pos)
|
||||
}
|
||||
|
||||
function generate4ByteBuffer (num) {
|
||||
const buffer = Buffer.allocUnsafe(4)
|
||||
buffer.writeUInt32BE(num, 0)
|
||||
return buffer
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
cache,
|
||||
generateCache,
|
||||
generateNumber: generateBuffer,
|
||||
genBufVariableByteInt,
|
||||
generate4ByteBuffer
|
||||
}
|
||||
48
node_modules/mqtt-packet/package.json
generated
vendored
Executable file
48
node_modules/mqtt-packet/package.json
generated
vendored
Executable file
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"name": "mqtt-packet",
|
||||
"version": "9.0.2",
|
||||
"description": "Parse and generate MQTT packets like a breeze",
|
||||
"main": "mqtt.js",
|
||||
"types": "types/index.d.ts",
|
||||
"contributors": [
|
||||
"Matteo Collina <matteo.collina@gmail.com> (https://github.com/mcollina)",
|
||||
"Adam Rudd <damvrr@gmail.com>",
|
||||
"Peter Sorowka (https://github.com/psorowka)",
|
||||
"Wouter Klijn <contact@wuhkuh.com> (https://github.com/wuhkuh)",
|
||||
"Siarhei Buntsevich (https://github.com/scarry1992)"
|
||||
],
|
||||
"scripts": {
|
||||
"test": "tape test.js | tap-spec && standard",
|
||||
"ci": "tape test.js && node testRandom && standard"
|
||||
},
|
||||
"pre-commit": "test",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/mqttjs/mqtt-packet.git"
|
||||
},
|
||||
"keywords": [
|
||||
"MQTT",
|
||||
"packet",
|
||||
"parse",
|
||||
"publish",
|
||||
"subscribe",
|
||||
"pubsub"
|
||||
],
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/mqttjs/mqtt-packet/issues"
|
||||
},
|
||||
"homepage": "https://github.com/mqttjs/mqtt-packet",
|
||||
"devDependencies": {
|
||||
"pre-commit": "^1.2.2",
|
||||
"readable-stream": "^4.4.2",
|
||||
"standard": "^17.1.0",
|
||||
"tap-spec": "^5.0.0",
|
||||
"tape": "^5.7.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"bl": "^6.0.8",
|
||||
"debug": "^4.3.4",
|
||||
"process-nextick-args": "^2.0.1"
|
||||
}
|
||||
}
|
||||
13
node_modules/mqtt-packet/packet.js
generated
vendored
Executable file
13
node_modules/mqtt-packet/packet.js
generated
vendored
Executable file
@@ -0,0 +1,13 @@
|
||||
class Packet {
|
||||
constructor () {
|
||||
this.cmd = null
|
||||
this.retain = false
|
||||
this.qos = 0
|
||||
this.dup = false
|
||||
this.length = -1
|
||||
this.topic = null
|
||||
this.payload = null
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Packet
|
||||
809
node_modules/mqtt-packet/parser.js
generated
vendored
Executable file
809
node_modules/mqtt-packet/parser.js
generated
vendored
Executable file
@@ -0,0 +1,809 @@
|
||||
const bl = require('bl')
|
||||
const { EventEmitter } = require('events')
|
||||
const Packet = require('./packet')
|
||||
const constants = require('./constants')
|
||||
const debug = require('debug')('mqtt-packet:parser')
|
||||
|
||||
class Parser extends EventEmitter {
|
||||
constructor () {
|
||||
super()
|
||||
this.parser = this.constructor.parser
|
||||
}
|
||||
|
||||
static parser (opt) {
|
||||
if (!(this instanceof Parser)) return (new Parser()).parser(opt)
|
||||
|
||||
this.settings = opt || {}
|
||||
|
||||
this._states = [
|
||||
'_parseHeader',
|
||||
'_parseLength',
|
||||
'_parsePayload',
|
||||
'_newPacket'
|
||||
]
|
||||
|
||||
this._resetState()
|
||||
return this
|
||||
}
|
||||
|
||||
_resetState () {
|
||||
debug('_resetState: resetting packet, error, _list, and _stateCounter')
|
||||
this.packet = new Packet()
|
||||
this.error = null
|
||||
this._list = bl()
|
||||
this._stateCounter = 0
|
||||
}
|
||||
|
||||
parse (buf) {
|
||||
if (this.error) this._resetState()
|
||||
|
||||
this._list.append(buf)
|
||||
debug('parse: current state: %s', this._states[this._stateCounter])
|
||||
while ((this.packet.length !== -1 || this._list.length > 0) &&
|
||||
this[this._states[this._stateCounter]]() &&
|
||||
!this.error) {
|
||||
this._stateCounter++
|
||||
debug('parse: state complete. _stateCounter is now: %d', this._stateCounter)
|
||||
debug('parse: packet.length: %d, buffer list length: %d', this.packet.length, this._list.length)
|
||||
if (this._stateCounter >= this._states.length) this._stateCounter = 0
|
||||
}
|
||||
debug('parse: exited while loop. packet: %d, buffer list length: %d', this.packet.length, this._list.length)
|
||||
return this._list.length
|
||||
}
|
||||
|
||||
_parseHeader () {
|
||||
// There is at least one byte in the buffer
|
||||
const zero = this._list.readUInt8(0)
|
||||
const cmdIndex = zero >> constants.CMD_SHIFT
|
||||
this.packet.cmd = constants.types[cmdIndex]
|
||||
const headerFlags = zero & 0xf
|
||||
const requiredHeaderFlags = constants.requiredHeaderFlags[cmdIndex]
|
||||
if (requiredHeaderFlags != null && headerFlags !== requiredHeaderFlags) {
|
||||
// Where a flag bit is marked as “Reserved” in Table 2.2 - Flag Bits, it is reserved for future use and MUST be set to the value listed in that table [MQTT-2.2.2-1]. If invalid flags are received, the receiver MUST close the Network Connection [MQTT-2.2.2-2]
|
||||
return this._emitError(new Error(constants.requiredHeaderFlagsErrors[cmdIndex]))
|
||||
}
|
||||
this.packet.retain = (zero & constants.RETAIN_MASK) !== 0
|
||||
this.packet.qos = (zero >> constants.QOS_SHIFT) & constants.QOS_MASK
|
||||
if (this.packet.qos > 2) {
|
||||
return this._emitError(new Error('Packet must not have both QoS bits set to 1'))
|
||||
}
|
||||
this.packet.dup = (zero & constants.DUP_MASK) !== 0
|
||||
debug('_parseHeader: packet: %o', this.packet)
|
||||
|
||||
this._list.consume(1)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
_parseLength () {
|
||||
// There is at least one byte in the list
|
||||
const result = this._parseVarByteNum(true)
|
||||
|
||||
if (result) {
|
||||
this.packet.length = result.value
|
||||
this._list.consume(result.bytes)
|
||||
}
|
||||
debug('_parseLength %d', result.value)
|
||||
return !!result
|
||||
}
|
||||
|
||||
_parsePayload () {
|
||||
debug('_parsePayload: payload %O', this._list)
|
||||
let result = false
|
||||
|
||||
// Do we have a payload? Do we have enough data to complete the payload?
|
||||
// PINGs have no payload
|
||||
if (this.packet.length === 0 || this._list.length >= this.packet.length) {
|
||||
this._pos = 0
|
||||
|
||||
switch (this.packet.cmd) {
|
||||
case 'connect':
|
||||
this._parseConnect()
|
||||
break
|
||||
case 'connack':
|
||||
this._parseConnack()
|
||||
break
|
||||
case 'publish':
|
||||
this._parsePublish()
|
||||
break
|
||||
case 'puback':
|
||||
case 'pubrec':
|
||||
case 'pubrel':
|
||||
case 'pubcomp':
|
||||
this._parseConfirmation()
|
||||
break
|
||||
case 'subscribe':
|
||||
this._parseSubscribe()
|
||||
break
|
||||
case 'suback':
|
||||
this._parseSuback()
|
||||
break
|
||||
case 'unsubscribe':
|
||||
this._parseUnsubscribe()
|
||||
break
|
||||
case 'unsuback':
|
||||
this._parseUnsuback()
|
||||
break
|
||||
case 'pingreq':
|
||||
case 'pingresp':
|
||||
// These are empty, nothing to do
|
||||
break
|
||||
case 'disconnect':
|
||||
this._parseDisconnect()
|
||||
break
|
||||
case 'auth':
|
||||
this._parseAuth()
|
||||
break
|
||||
default:
|
||||
this._emitError(new Error('Not supported'))
|
||||
}
|
||||
|
||||
result = true
|
||||
}
|
||||
debug('_parsePayload complete result: %s', result)
|
||||
return result
|
||||
}
|
||||
|
||||
_parseConnect () {
|
||||
debug('_parseConnect')
|
||||
let topic // Will topic
|
||||
let payload // Will payload
|
||||
let password // Password
|
||||
let username // Username
|
||||
const flags = {}
|
||||
const packet = this.packet
|
||||
|
||||
// Parse protocolId
|
||||
const protocolId = this._parseString()
|
||||
|
||||
if (protocolId === null) return this._emitError(new Error('Cannot parse protocolId'))
|
||||
if (protocolId !== 'MQTT' && protocolId !== 'MQIsdp') {
|
||||
return this._emitError(new Error('Invalid protocolId'))
|
||||
}
|
||||
|
||||
packet.protocolId = protocolId
|
||||
|
||||
// Parse constants version number
|
||||
if (this._pos >= this._list.length) return this._emitError(new Error('Packet too short'))
|
||||
|
||||
packet.protocolVersion = this._list.readUInt8(this._pos)
|
||||
|
||||
if (packet.protocolVersion >= 128) {
|
||||
packet.bridgeMode = true
|
||||
packet.protocolVersion = packet.protocolVersion - 128
|
||||
}
|
||||
|
||||
if (packet.protocolVersion !== 3 && packet.protocolVersion !== 4 && packet.protocolVersion !== 5) {
|
||||
return this._emitError(new Error('Invalid protocol version'))
|
||||
}
|
||||
|
||||
this._pos++
|
||||
|
||||
if (this._pos >= this._list.length) {
|
||||
return this._emitError(new Error('Packet too short'))
|
||||
}
|
||||
|
||||
if (this._list.readUInt8(this._pos) & 0x1) {
|
||||
// The Server MUST validate that the reserved flag in the CONNECT Control Packet is set to zero and disconnect the Client if it is not zero [MQTT-3.1.2-3]
|
||||
return this._emitError(new Error('Connect flag bit 0 must be 0, but got 1'))
|
||||
}
|
||||
// Parse connect flags
|
||||
flags.username = (this._list.readUInt8(this._pos) & constants.USERNAME_MASK)
|
||||
flags.password = (this._list.readUInt8(this._pos) & constants.PASSWORD_MASK)
|
||||
flags.will = (this._list.readUInt8(this._pos) & constants.WILL_FLAG_MASK)
|
||||
|
||||
const willRetain = !!(this._list.readUInt8(this._pos) & constants.WILL_RETAIN_MASK)
|
||||
const willQos = (this._list.readUInt8(this._pos) &
|
||||
constants.WILL_QOS_MASK) >> constants.WILL_QOS_SHIFT
|
||||
|
||||
if (flags.will) {
|
||||
packet.will = {}
|
||||
packet.will.retain = willRetain
|
||||
packet.will.qos = willQos
|
||||
} else {
|
||||
if (willRetain) {
|
||||
return this._emitError(new Error('Will Retain Flag must be set to zero when Will Flag is set to 0'))
|
||||
}
|
||||
if (willQos) {
|
||||
return this._emitError(new Error('Will QoS must be set to zero when Will Flag is set to 0'))
|
||||
}
|
||||
}
|
||||
|
||||
packet.clean = (this._list.readUInt8(this._pos) & constants.CLEAN_SESSION_MASK) !== 0
|
||||
this._pos++
|
||||
|
||||
// Parse keepalive
|
||||
packet.keepalive = this._parseNum()
|
||||
if (packet.keepalive === -1) return this._emitError(new Error('Packet too short'))
|
||||
|
||||
// parse properties
|
||||
if (packet.protocolVersion === 5) {
|
||||
const properties = this._parseProperties()
|
||||
if (Object.getOwnPropertyNames(properties).length) {
|
||||
packet.properties = properties
|
||||
}
|
||||
}
|
||||
// Parse clientId
|
||||
const clientId = this._parseString()
|
||||
if (clientId === null) return this._emitError(new Error('Packet too short'))
|
||||
packet.clientId = clientId
|
||||
debug('_parseConnect: packet.clientId: %s', packet.clientId)
|
||||
|
||||
if (flags.will) {
|
||||
if (packet.protocolVersion === 5) {
|
||||
const willProperties = this._parseProperties()
|
||||
if (Object.getOwnPropertyNames(willProperties).length) {
|
||||
packet.will.properties = willProperties
|
||||
}
|
||||
}
|
||||
// Parse will topic
|
||||
topic = this._parseString()
|
||||
if (topic === null) return this._emitError(new Error('Cannot parse will topic'))
|
||||
packet.will.topic = topic
|
||||
debug('_parseConnect: packet.will.topic: %s', packet.will.topic)
|
||||
|
||||
// Parse will payload
|
||||
payload = this._parseBuffer()
|
||||
if (payload === null) return this._emitError(new Error('Cannot parse will payload'))
|
||||
packet.will.payload = payload
|
||||
debug('_parseConnect: packet.will.paylaod: %s', packet.will.payload)
|
||||
}
|
||||
|
||||
// Parse username
|
||||
if (flags.username) {
|
||||
username = this._parseString()
|
||||
if (username === null) return this._emitError(new Error('Cannot parse username'))
|
||||
packet.username = username
|
||||
debug('_parseConnect: packet.username: %s', packet.username)
|
||||
}
|
||||
|
||||
// Parse password
|
||||
if (flags.password) {
|
||||
password = this._parseBuffer()
|
||||
if (password === null) return this._emitError(new Error('Cannot parse password'))
|
||||
packet.password = password
|
||||
}
|
||||
// need for right parse auth packet and self set up
|
||||
this.settings = packet
|
||||
debug('_parseConnect: complete')
|
||||
return packet
|
||||
}
|
||||
|
||||
_parseConnack () {
|
||||
debug('_parseConnack')
|
||||
const packet = this.packet
|
||||
|
||||
if (this._list.length < 1) return null
|
||||
const flags = this._list.readUInt8(this._pos++)
|
||||
if (flags > 1) {
|
||||
return this._emitError(new Error('Invalid connack flags, bits 7-1 must be set to 0'))
|
||||
}
|
||||
packet.sessionPresent = !!(flags & constants.SESSIONPRESENT_MASK)
|
||||
|
||||
if (this.settings.protocolVersion === 5) {
|
||||
if (this._list.length >= 2) {
|
||||
packet.reasonCode = this._list.readUInt8(this._pos++)
|
||||
} else {
|
||||
packet.reasonCode = 0
|
||||
}
|
||||
} else {
|
||||
if (this._list.length < 2) return null
|
||||
packet.returnCode = this._list.readUInt8(this._pos++)
|
||||
}
|
||||
|
||||
if (packet.returnCode === -1 || packet.reasonCode === -1) return this._emitError(new Error('Cannot parse return code'))
|
||||
// mqtt 5 properties
|
||||
if (this.settings.protocolVersion === 5) {
|
||||
const properties = this._parseProperties()
|
||||
if (Object.getOwnPropertyNames(properties).length) {
|
||||
packet.properties = properties
|
||||
}
|
||||
}
|
||||
debug('_parseConnack: complete')
|
||||
}
|
||||
|
||||
_parsePublish () {
|
||||
debug('_parsePublish')
|
||||
const packet = this.packet
|
||||
packet.topic = this._parseString()
|
||||
|
||||
if (packet.topic === null) return this._emitError(new Error('Cannot parse topic'))
|
||||
|
||||
// Parse messageId
|
||||
if (packet.qos > 0) if (!this._parseMessageId()) { return }
|
||||
|
||||
// Properties mqtt 5
|
||||
if (this.settings.protocolVersion === 5) {
|
||||
const properties = this._parseProperties()
|
||||
if (Object.getOwnPropertyNames(properties).length) {
|
||||
packet.properties = properties
|
||||
}
|
||||
}
|
||||
|
||||
packet.payload = this._list.slice(this._pos, packet.length)
|
||||
debug('_parsePublish: payload from buffer list: %o', packet.payload)
|
||||
}
|
||||
|
||||
_parseSubscribe () {
|
||||
debug('_parseSubscribe')
|
||||
const packet = this.packet
|
||||
let topic
|
||||
let options
|
||||
let qos
|
||||
let rh
|
||||
let rap
|
||||
let nl
|
||||
let subscription
|
||||
|
||||
packet.subscriptions = []
|
||||
|
||||
if (!this._parseMessageId()) { return }
|
||||
|
||||
// Properties mqtt 5
|
||||
if (this.settings.protocolVersion === 5) {
|
||||
const properties = this._parseProperties()
|
||||
if (Object.getOwnPropertyNames(properties).length) {
|
||||
packet.properties = properties
|
||||
}
|
||||
}
|
||||
|
||||
if (packet.length <= 0) { return this._emitError(new Error('Malformed subscribe, no payload specified')) }
|
||||
|
||||
while (this._pos < packet.length) {
|
||||
// Parse topic
|
||||
topic = this._parseString()
|
||||
if (topic === null) return this._emitError(new Error('Cannot parse topic'))
|
||||
if (this._pos >= packet.length) return this._emitError(new Error('Malformed Subscribe Payload'))
|
||||
|
||||
options = this._parseByte()
|
||||
|
||||
if (this.settings.protocolVersion === 5) {
|
||||
if (options & 0xc0) {
|
||||
return this._emitError(new Error('Invalid subscribe topic flag bits, bits 7-6 must be 0'))
|
||||
}
|
||||
} else {
|
||||
if (options & 0xfc) {
|
||||
return this._emitError(new Error('Invalid subscribe topic flag bits, bits 7-2 must be 0'))
|
||||
}
|
||||
}
|
||||
|
||||
qos = options & constants.SUBSCRIBE_OPTIONS_QOS_MASK
|
||||
if (qos > 2) {
|
||||
return this._emitError(new Error('Invalid subscribe QoS, must be <= 2'))
|
||||
}
|
||||
nl = ((options >> constants.SUBSCRIBE_OPTIONS_NL_SHIFT) & constants.SUBSCRIBE_OPTIONS_NL_MASK) !== 0
|
||||
rap = ((options >> constants.SUBSCRIBE_OPTIONS_RAP_SHIFT) & constants.SUBSCRIBE_OPTIONS_RAP_MASK) !== 0
|
||||
rh = (options >> constants.SUBSCRIBE_OPTIONS_RH_SHIFT) & constants.SUBSCRIBE_OPTIONS_RH_MASK
|
||||
|
||||
if (rh > 2) {
|
||||
return this._emitError(new Error('Invalid retain handling, must be <= 2'))
|
||||
}
|
||||
|
||||
subscription = { topic, qos }
|
||||
|
||||
// mqtt 5 options
|
||||
if (this.settings.protocolVersion === 5) {
|
||||
subscription.nl = nl
|
||||
subscription.rap = rap
|
||||
subscription.rh = rh
|
||||
} else if (this.settings.bridgeMode) {
|
||||
subscription.rh = 0
|
||||
subscription.rap = true
|
||||
subscription.nl = true
|
||||
}
|
||||
|
||||
// Push pair to subscriptions
|
||||
debug('_parseSubscribe: push subscription `%s` to subscription', subscription)
|
||||
packet.subscriptions.push(subscription)
|
||||
}
|
||||
}
|
||||
|
||||
_parseSuback () {
|
||||
debug('_parseSuback')
|
||||
const packet = this.packet
|
||||
this.packet.granted = []
|
||||
|
||||
if (!this._parseMessageId()) { return }
|
||||
|
||||
// Properties mqtt 5
|
||||
if (this.settings.protocolVersion === 5) {
|
||||
const properties = this._parseProperties()
|
||||
if (Object.getOwnPropertyNames(properties).length) {
|
||||
packet.properties = properties
|
||||
}
|
||||
}
|
||||
|
||||
if (packet.length <= 0) { return this._emitError(new Error('Malformed suback, no payload specified')) }
|
||||
|
||||
// Parse granted QoSes
|
||||
while (this._pos < this.packet.length) {
|
||||
const code = this._list.readUInt8(this._pos++)
|
||||
if (this.settings.protocolVersion === 5) {
|
||||
if (!constants.MQTT5_SUBACK_CODES[code]) {
|
||||
return this._emitError(new Error('Invalid suback code'))
|
||||
}
|
||||
} else {
|
||||
if (code > 2 && code !== 0x80) {
|
||||
return this._emitError(new Error('Invalid suback QoS, must be 0, 1, 2 or 128'))
|
||||
}
|
||||
}
|
||||
this.packet.granted.push(code)
|
||||
}
|
||||
}
|
||||
|
||||
_parseUnsubscribe () {
|
||||
debug('_parseUnsubscribe')
|
||||
const packet = this.packet
|
||||
|
||||
packet.unsubscriptions = []
|
||||
|
||||
// Parse messageId
|
||||
if (!this._parseMessageId()) { return }
|
||||
|
||||
// Properties mqtt 5
|
||||
if (this.settings.protocolVersion === 5) {
|
||||
const properties = this._parseProperties()
|
||||
if (Object.getOwnPropertyNames(properties).length) {
|
||||
packet.properties = properties
|
||||
}
|
||||
}
|
||||
|
||||
if (packet.length <= 0) { return this._emitError(new Error('Malformed unsubscribe, no payload specified')) }
|
||||
|
||||
while (this._pos < packet.length) {
|
||||
// Parse topic
|
||||
const topic = this._parseString()
|
||||
if (topic === null) return this._emitError(new Error('Cannot parse topic'))
|
||||
|
||||
// Push topic to unsubscriptions
|
||||
debug('_parseUnsubscribe: push topic `%s` to unsubscriptions', topic)
|
||||
packet.unsubscriptions.push(topic)
|
||||
}
|
||||
}
|
||||
|
||||
_parseUnsuback () {
|
||||
debug('_parseUnsuback')
|
||||
const packet = this.packet
|
||||
if (!this._parseMessageId()) return this._emitError(new Error('Cannot parse messageId'))
|
||||
|
||||
if ((this.settings.protocolVersion === 3 ||
|
||||
this.settings.protocolVersion === 4) && packet.length !== 2) {
|
||||
return this._emitError(new Error('Malformed unsuback, payload length must be 2'))
|
||||
}
|
||||
if (packet.length <= 0) { return this._emitError(new Error('Malformed unsuback, no payload specified')) }
|
||||
|
||||
// Properties mqtt 5
|
||||
if (this.settings.protocolVersion === 5) {
|
||||
const properties = this._parseProperties()
|
||||
if (Object.getOwnPropertyNames(properties).length) {
|
||||
packet.properties = properties
|
||||
}
|
||||
// Parse granted QoSes
|
||||
packet.granted = []
|
||||
|
||||
while (this._pos < this.packet.length) {
|
||||
const code = this._list.readUInt8(this._pos++)
|
||||
if (!constants.MQTT5_UNSUBACK_CODES[code]) {
|
||||
return this._emitError(new Error('Invalid unsuback code'))
|
||||
}
|
||||
this.packet.granted.push(code)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// parse packets like puback, pubrec, pubrel, pubcomp
|
||||
_parseConfirmation () {
|
||||
debug('_parseConfirmation: packet.cmd: `%s`', this.packet.cmd)
|
||||
const packet = this.packet
|
||||
|
||||
this._parseMessageId()
|
||||
|
||||
if (this.settings.protocolVersion === 5) {
|
||||
if (packet.length > 2) {
|
||||
// response code
|
||||
packet.reasonCode = this._parseByte()
|
||||
switch (this.packet.cmd) {
|
||||
case 'puback':
|
||||
case 'pubrec':
|
||||
if (!constants.MQTT5_PUBACK_PUBREC_CODES[packet.reasonCode]) {
|
||||
return this._emitError(new Error('Invalid ' + this.packet.cmd + ' reason code'))
|
||||
}
|
||||
break
|
||||
case 'pubrel':
|
||||
case 'pubcomp':
|
||||
if (!constants.MQTT5_PUBREL_PUBCOMP_CODES[packet.reasonCode]) {
|
||||
return this._emitError(new Error('Invalid ' + this.packet.cmd + ' reason code'))
|
||||
}
|
||||
break
|
||||
}
|
||||
debug('_parseConfirmation: packet.reasonCode `%d`', packet.reasonCode)
|
||||
} else {
|
||||
packet.reasonCode = 0
|
||||
}
|
||||
|
||||
if (packet.length > 3) {
|
||||
// properies mqtt 5
|
||||
const properties = this._parseProperties()
|
||||
if (Object.getOwnPropertyNames(properties).length) {
|
||||
packet.properties = properties
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// parse disconnect packet
|
||||
_parseDisconnect () {
|
||||
const packet = this.packet
|
||||
debug('_parseDisconnect')
|
||||
|
||||
if (this.settings.protocolVersion === 5) {
|
||||
// response code
|
||||
if (this._list.length > 0) {
|
||||
packet.reasonCode = this._parseByte()
|
||||
if (!constants.MQTT5_DISCONNECT_CODES[packet.reasonCode]) {
|
||||
this._emitError(new Error('Invalid disconnect reason code'))
|
||||
}
|
||||
} else {
|
||||
packet.reasonCode = 0
|
||||
}
|
||||
// properies mqtt 5
|
||||
const properties = this._parseProperties()
|
||||
if (Object.getOwnPropertyNames(properties).length) {
|
||||
packet.properties = properties
|
||||
}
|
||||
}
|
||||
|
||||
debug('_parseDisconnect result: true')
|
||||
return true
|
||||
}
|
||||
|
||||
// parse auth packet
|
||||
_parseAuth () {
|
||||
debug('_parseAuth')
|
||||
const packet = this.packet
|
||||
|
||||
if (this.settings.protocolVersion !== 5) {
|
||||
return this._emitError(new Error('Not supported auth packet for this version MQTT'))
|
||||
}
|
||||
|
||||
// response code
|
||||
packet.reasonCode = this._parseByte()
|
||||
if (!constants.MQTT5_AUTH_CODES[packet.reasonCode]) {
|
||||
return this._emitError(new Error('Invalid auth reason code'))
|
||||
}
|
||||
// properies mqtt 5
|
||||
const properties = this._parseProperties()
|
||||
if (Object.getOwnPropertyNames(properties).length) {
|
||||
packet.properties = properties
|
||||
}
|
||||
|
||||
debug('_parseAuth: result: true')
|
||||
return true
|
||||
}
|
||||
|
||||
_parseMessageId () {
|
||||
const packet = this.packet
|
||||
|
||||
packet.messageId = this._parseNum()
|
||||
|
||||
if (packet.messageId === null) {
|
||||
this._emitError(new Error('Cannot parse messageId'))
|
||||
return false
|
||||
}
|
||||
|
||||
debug('_parseMessageId: packet.messageId %d', packet.messageId)
|
||||
return true
|
||||
}
|
||||
|
||||
_parseString (maybeBuffer) {
|
||||
const length = this._parseNum()
|
||||
const end = length + this._pos
|
||||
|
||||
if (length === -1 || end > this._list.length || end > this.packet.length) return null
|
||||
|
||||
const result = this._list.toString('utf8', this._pos, end)
|
||||
this._pos += length
|
||||
debug('_parseString: result: %s', result)
|
||||
return result
|
||||
}
|
||||
|
||||
_parseStringPair () {
|
||||
debug('_parseStringPair')
|
||||
return {
|
||||
name: this._parseString(),
|
||||
value: this._parseString()
|
||||
}
|
||||
}
|
||||
|
||||
_parseBuffer () {
|
||||
const length = this._parseNum()
|
||||
const end = length + this._pos
|
||||
|
||||
if (length === -1 || end > this._list.length || end > this.packet.length) return null
|
||||
|
||||
const result = this._list.slice(this._pos, end)
|
||||
|
||||
this._pos += length
|
||||
debug('_parseBuffer: result: %o', result)
|
||||
return result
|
||||
}
|
||||
|
||||
_parseNum () {
|
||||
if (this._list.length - this._pos < 2) return -1
|
||||
|
||||
const result = this._list.readUInt16BE(this._pos)
|
||||
this._pos += 2
|
||||
debug('_parseNum: result: %s', result)
|
||||
return result
|
||||
}
|
||||
|
||||
_parse4ByteNum () {
|
||||
if (this._list.length - this._pos < 4) return -1
|
||||
|
||||
const result = this._list.readUInt32BE(this._pos)
|
||||
this._pos += 4
|
||||
debug('_parse4ByteNum: result: %s', result)
|
||||
return result
|
||||
}
|
||||
|
||||
_parseVarByteNum (fullInfoFlag) {
|
||||
debug('_parseVarByteNum')
|
||||
const maxBytes = 4
|
||||
let bytes = 0
|
||||
let mul = 1
|
||||
let value = 0
|
||||
let result = false
|
||||
let current
|
||||
const padding = this._pos ? this._pos : 0
|
||||
|
||||
while (bytes < maxBytes && (padding + bytes) < this._list.length) {
|
||||
current = this._list.readUInt8(padding + bytes++)
|
||||
value += mul * (current & constants.VARBYTEINT_MASK)
|
||||
mul *= 0x80
|
||||
|
||||
if ((current & constants.VARBYTEINT_FIN_MASK) === 0) {
|
||||
result = true
|
||||
break
|
||||
}
|
||||
if (this._list.length <= bytes) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (!result && bytes === maxBytes && this._list.length >= bytes) {
|
||||
this._emitError(new Error('Invalid variable byte integer'))
|
||||
}
|
||||
|
||||
if (padding) {
|
||||
this._pos += bytes
|
||||
}
|
||||
|
||||
if (result) {
|
||||
if (fullInfoFlag) {
|
||||
result = { bytes, value }
|
||||
} else {
|
||||
result = value
|
||||
}
|
||||
} else {
|
||||
result = false
|
||||
}
|
||||
|
||||
debug('_parseVarByteNum: result: %o', result)
|
||||
return result
|
||||
}
|
||||
|
||||
_parseByte () {
|
||||
let result
|
||||
if (this._pos < this._list.length) {
|
||||
result = this._list.readUInt8(this._pos)
|
||||
this._pos++
|
||||
}
|
||||
debug('_parseByte: result: %o', result)
|
||||
return result
|
||||
}
|
||||
|
||||
_parseByType (type) {
|
||||
debug('_parseByType: type: %s', type)
|
||||
switch (type) {
|
||||
case 'byte': {
|
||||
return this._parseByte() !== 0
|
||||
}
|
||||
case 'int8': {
|
||||
return this._parseByte()
|
||||
}
|
||||
case 'int16': {
|
||||
return this._parseNum()
|
||||
}
|
||||
case 'int32': {
|
||||
return this._parse4ByteNum()
|
||||
}
|
||||
case 'var': {
|
||||
return this._parseVarByteNum()
|
||||
}
|
||||
case 'string': {
|
||||
return this._parseString()
|
||||
}
|
||||
case 'pair': {
|
||||
return this._parseStringPair()
|
||||
}
|
||||
case 'binary': {
|
||||
return this._parseBuffer()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_parseProperties () {
|
||||
debug('_parseProperties')
|
||||
const length = this._parseVarByteNum()
|
||||
const start = this._pos
|
||||
const end = start + length
|
||||
const result = {}
|
||||
while (this._pos < end) {
|
||||
const type = this._parseByte()
|
||||
if (!type) {
|
||||
this._emitError(new Error('Cannot parse property code type'))
|
||||
return false
|
||||
}
|
||||
const name = constants.propertiesCodes[type]
|
||||
if (!name) {
|
||||
this._emitError(new Error('Unknown property'))
|
||||
return false
|
||||
}
|
||||
// user properties process
|
||||
if (name === 'userProperties') {
|
||||
if (!result[name]) {
|
||||
result[name] = Object.create(null)
|
||||
}
|
||||
const currentUserProperty = this._parseByType(constants.propertiesTypes[name])
|
||||
if (result[name][currentUserProperty.name]) {
|
||||
if (Array.isArray(result[name][currentUserProperty.name])) {
|
||||
result[name][currentUserProperty.name].push(currentUserProperty.value)
|
||||
} else {
|
||||
const currentValue = result[name][currentUserProperty.name]
|
||||
result[name][currentUserProperty.name] = [currentValue]
|
||||
result[name][currentUserProperty.name].push(currentUserProperty.value)
|
||||
}
|
||||
} else {
|
||||
result[name][currentUserProperty.name] = currentUserProperty.value
|
||||
}
|
||||
continue
|
||||
}
|
||||
if (result[name]) {
|
||||
if (Array.isArray(result[name])) {
|
||||
result[name].push(this._parseByType(constants.propertiesTypes[name]))
|
||||
} else {
|
||||
result[name] = [result[name]]
|
||||
result[name].push(this._parseByType(constants.propertiesTypes[name]))
|
||||
}
|
||||
} else {
|
||||
result[name] = this._parseByType(constants.propertiesTypes[name])
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
_newPacket () {
|
||||
debug('_newPacket')
|
||||
if (this.packet) {
|
||||
this._list.consume(this.packet.length)
|
||||
debug('_newPacket: parser emit packet: packet.cmd: %s, packet.payload: %s, packet.length: %d', this.packet.cmd, this.packet.payload, this.packet.length)
|
||||
this.emit('packet', this.packet)
|
||||
}
|
||||
debug('_newPacket: new packet')
|
||||
this.packet = new Packet()
|
||||
|
||||
this._pos = 0
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
_emitError (err) {
|
||||
debug('_emitError', err)
|
||||
this.error = err
|
||||
this.emit('error', err)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Parser
|
||||
3234
node_modules/mqtt-packet/test.js
generated
vendored
Executable file
3234
node_modules/mqtt-packet/test.js
generated
vendored
Executable file
File diff suppressed because it is too large
Load Diff
86
node_modules/mqtt-packet/testRandom.js
generated
vendored
Executable file
86
node_modules/mqtt-packet/testRandom.js
generated
vendored
Executable file
@@ -0,0 +1,86 @@
|
||||
const mqtt = require('./')
|
||||
const crypto = require('crypto')
|
||||
const max = 1E5
|
||||
const start = Date.now() / 1000
|
||||
let errors = 0
|
||||
let packets = 0
|
||||
let randomPacket
|
||||
const firstBytes = [
|
||||
16 * 1, // CONNECT
|
||||
16 * 2, // CONNACK
|
||||
16 * 3, // PUBLISH, QoS: 0, No Retain, No Dup
|
||||
16 * 3 + 1, // PUBLISH, QoS: 0, Retain, No Dup
|
||||
16 * 3 + 8, // PUBLISH, QoS: 0, No Retain, Dup
|
||||
16 * 3 + 1 + 8, // PUBLISH, QoS: 0, Retain, Dup
|
||||
16 * 3 + 2, // PUBLISH, QoS: 1, No Retain, No Dup
|
||||
16 * 3 + 2 + 1, // PUBLISH, QoS: 1, Retain, No Dup
|
||||
16 * 3 + 2 + 8, // PUBLISH, QoS: 1, No Retain, Dup
|
||||
16 * 3 + 2 + 1 + 8, // PUBLISH, QoS: 1, Retain, Dup
|
||||
16 * 3 + 4, // PUBLISH, QoS: 2, No Retain, No Dup
|
||||
16 * 3 + 4 + 1, // PUBLISH, QoS: 2, Retain, No Dup
|
||||
16 * 3 + 4 + 8, // PUBLISH, QoS: 2, No Retain, Dup
|
||||
16 * 3 + 4 + 1 + 8, // PUBLISH, QoS: 2, Retain, Dup
|
||||
16 * 4, // PUBACK
|
||||
16 * 5, // PUBREC
|
||||
16 * 6, // PUBREL
|
||||
16 * 7, // PUBCOMP
|
||||
16 * 8, // SUBSCRIBE
|
||||
16 * 9, // SUBACK
|
||||
16 * 10, // UNSUBSCRIBE
|
||||
16 * 11, // UNSUBACK
|
||||
16 * 12, // PINGREQ
|
||||
16 * 13, // PINGRESP
|
||||
16 * 14, // DISCONNECT
|
||||
16 * 15 // RESERVED
|
||||
]
|
||||
|
||||
function doParse () {
|
||||
const parser = mqtt.parser()
|
||||
|
||||
parser.on('error', onError)
|
||||
parser.on('packet', onPacket)
|
||||
randomPacket = crypto.randomBytes(Math.floor(Math.random() * 512))
|
||||
|
||||
// Increase probability to have a valid first byte in order to at least
|
||||
// enter the parser
|
||||
if (Math.random() > 0.2 && randomPacket.length > 0) randomPacket.writeUInt8(firstBytes[Math.floor(Math.random() * firstBytes.length)], 0)
|
||||
parser.parse(randomPacket)
|
||||
}
|
||||
|
||||
try {
|
||||
console.log('Starting benchmark')
|
||||
for (let i = 0; i < max; i++) {
|
||||
doParse()
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('Exception occurred at packet')
|
||||
console.log(randomPacket)
|
||||
console.log(e.message)
|
||||
console.log(e.stack)
|
||||
}
|
||||
|
||||
function onError () {
|
||||
errors++
|
||||
}
|
||||
|
||||
function onPacket () {
|
||||
packets++
|
||||
}
|
||||
|
||||
const delta = Math.abs(max - packets - errors)
|
||||
const time = Date.now() / 1000 - start
|
||||
console.log('Benchmark complete')
|
||||
console.log('==========================')
|
||||
console.log('Sent packets:', max)
|
||||
console.log('Total time:', Math.round(time * 100) / 100, 'seconds', '\r\n')
|
||||
|
||||
console.log('Valid packets:', packets)
|
||||
console.log('Erroneous packets:', errors)
|
||||
|
||||
if ((max - packets - errors) < 0) console.log('Excess packets:', delta, '\r\n')
|
||||
else console.log('Missing packets:', delta, '\r\n')
|
||||
|
||||
console.log('Total packets:', packets + errors)
|
||||
console.log('Total errors:', errors + delta)
|
||||
console.log('Error rate:', `${((errors + delta) / max * 100).toFixed(2)}%`)
|
||||
console.log('==========================')
|
||||
257
node_modules/mqtt-packet/types/index.d.ts
generated
vendored
Executable file
257
node_modules/mqtt-packet/types/index.d.ts
generated
vendored
Executable file
@@ -0,0 +1,257 @@
|
||||
import EventEmitter = NodeJS.EventEmitter
|
||||
import WritableStream = NodeJS.WritableStream
|
||||
|
||||
export declare type QoS = 0 | 1 | 2
|
||||
|
||||
export declare type PacketCmd = 'auth' |
|
||||
'connack' |
|
||||
'connect' |
|
||||
'disconnect' |
|
||||
'pingreq' |
|
||||
'pingresp' |
|
||||
'puback' |
|
||||
'pubcomp' |
|
||||
'publish' |
|
||||
'pubrel' |
|
||||
'pubrec' |
|
||||
'suback' |
|
||||
'subscribe' |
|
||||
'unsuback' |
|
||||
'unsubscribe'
|
||||
|
||||
export declare type UserProperties = {[index: string]: string | string[]}
|
||||
|
||||
export interface IPacket {
|
||||
cmd: PacketCmd
|
||||
messageId?: number
|
||||
length?: number
|
||||
}
|
||||
|
||||
export interface IAuthPacket extends IPacket {
|
||||
cmd: 'auth'
|
||||
reasonCode: number,
|
||||
properties?: {
|
||||
authenticationMethod?: string,
|
||||
authenticationData?: Buffer,
|
||||
reasonString?: string,
|
||||
userProperties?: UserProperties,
|
||||
}
|
||||
}
|
||||
|
||||
export interface IConnectPacket extends IPacket {
|
||||
cmd: 'connect'
|
||||
clientId: string
|
||||
protocolVersion?: 4 | 5 | 3
|
||||
protocolId?: 'MQTT' | 'MQIsdp'
|
||||
clean?: boolean
|
||||
keepalive?: number
|
||||
username?: string
|
||||
password?: Buffer
|
||||
will?: {
|
||||
topic: string
|
||||
payload: Buffer | string
|
||||
qos?: QoS
|
||||
retain?: boolean
|
||||
properties?: {
|
||||
willDelayInterval?: number,
|
||||
payloadFormatIndicator?: boolean,
|
||||
messageExpiryInterval?: number,
|
||||
contentType?: string,
|
||||
responseTopic?: string,
|
||||
correlationData?: Buffer,
|
||||
userProperties?: UserProperties
|
||||
}
|
||||
}
|
||||
properties?: {
|
||||
sessionExpiryInterval?: number,
|
||||
receiveMaximum?: number,
|
||||
maximumPacketSize?: number,
|
||||
topicAliasMaximum?: number,
|
||||
requestResponseInformation?: boolean,
|
||||
requestProblemInformation?: boolean,
|
||||
userProperties?: UserProperties,
|
||||
authenticationMethod?: string,
|
||||
authenticationData?: Buffer
|
||||
}
|
||||
}
|
||||
|
||||
export interface IPublishPacket extends IPacket {
|
||||
cmd: 'publish'
|
||||
qos: QoS
|
||||
dup: boolean
|
||||
retain: boolean
|
||||
topic: string
|
||||
payload: string | Buffer
|
||||
properties?: {
|
||||
payloadFormatIndicator?: boolean,
|
||||
messageExpiryInterval?: number,
|
||||
topicAlias?: number,
|
||||
responseTopic?: string,
|
||||
correlationData?: Buffer,
|
||||
userProperties?: UserProperties,
|
||||
subscriptionIdentifier?: number | number[],
|
||||
contentType?: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface IConnackPacket extends IPacket {
|
||||
cmd: 'connack'
|
||||
returnCode?: number,
|
||||
reasonCode?: number,
|
||||
sessionPresent: boolean
|
||||
properties?: {
|
||||
sessionExpiryInterval?: number,
|
||||
receiveMaximum?: number,
|
||||
maximumQoS?: number,
|
||||
retainAvailable?: boolean,
|
||||
maximumPacketSize?: number,
|
||||
assignedClientIdentifier?: string,
|
||||
topicAliasMaximum?: number,
|
||||
reasonString?: string,
|
||||
userProperties?: UserProperties,
|
||||
wildcardSubscriptionAvailable?: boolean,
|
||||
subscriptionIdentifiersAvailable?: boolean,
|
||||
sharedSubscriptionAvailable?: boolean,
|
||||
serverKeepAlive?: number,
|
||||
responseInformation?: string,
|
||||
serverReference?: string,
|
||||
authenticationMethod?: string,
|
||||
authenticationData?: Buffer
|
||||
}
|
||||
}
|
||||
|
||||
export interface ISubscription {
|
||||
topic: string
|
||||
qos: QoS,
|
||||
nl?: boolean,
|
||||
rap?: boolean,
|
||||
rh?: number
|
||||
}
|
||||
|
||||
export interface ISubscribePacket extends IPacket {
|
||||
cmd: 'subscribe'
|
||||
subscriptions: ISubscription[],
|
||||
properties?: {
|
||||
reasonString?: string,
|
||||
subscriptionIdentifier?: number,
|
||||
userProperties?: UserProperties
|
||||
}
|
||||
}
|
||||
|
||||
export interface ISubackPacket extends IPacket {
|
||||
cmd: 'suback',
|
||||
reasonCode?: number,
|
||||
properties?: {
|
||||
reasonString?: string,
|
||||
userProperties?: UserProperties
|
||||
},
|
||||
granted: number[] | Object[]
|
||||
}
|
||||
|
||||
export interface IUnsubscribePacket extends IPacket {
|
||||
cmd: 'unsubscribe',
|
||||
properties?: {
|
||||
reasonString?: string,
|
||||
userProperties?: UserProperties
|
||||
},
|
||||
unsubscriptions: string[]
|
||||
}
|
||||
|
||||
export interface IUnsubackPacket extends IPacket {
|
||||
cmd: 'unsuback',
|
||||
reasonCode?: number,
|
||||
properties?: {
|
||||
reasonString?: string,
|
||||
userProperties?: UserProperties
|
||||
},
|
||||
granted: number[]
|
||||
}
|
||||
|
||||
export interface IPubackPacket extends IPacket {
|
||||
cmd: 'puback',
|
||||
reasonCode?: number,
|
||||
properties?: {
|
||||
reasonString?: string,
|
||||
userProperties?: UserProperties
|
||||
}
|
||||
}
|
||||
|
||||
export interface IPubcompPacket extends IPacket {
|
||||
cmd: 'pubcomp',
|
||||
reasonCode?: number,
|
||||
properties?: {
|
||||
reasonString?: string,
|
||||
userProperties?: UserProperties
|
||||
}
|
||||
}
|
||||
|
||||
export interface IPubrelPacket extends IPacket {
|
||||
cmd: 'pubrel',
|
||||
reasonCode?: number,
|
||||
properties?: {
|
||||
reasonString?: string,
|
||||
userProperties?: UserProperties
|
||||
}
|
||||
}
|
||||
|
||||
export interface IPubrecPacket extends IPacket {
|
||||
cmd: 'pubrec',
|
||||
reasonCode?: number,
|
||||
properties?: {
|
||||
reasonString?: string,
|
||||
userProperties?: UserProperties
|
||||
}
|
||||
}
|
||||
|
||||
export interface IPingreqPacket extends IPacket {
|
||||
cmd: 'pingreq'
|
||||
}
|
||||
|
||||
export interface IPingrespPacket extends IPacket {
|
||||
cmd: 'pingresp'
|
||||
}
|
||||
|
||||
export interface IDisconnectPacket extends IPacket {
|
||||
cmd: 'disconnect',
|
||||
reasonCode?: number,
|
||||
properties?: {
|
||||
sessionExpiryInterval?: number,
|
||||
reasonString?: string,
|
||||
userProperties?: UserProperties,
|
||||
serverReference?: string
|
||||
}
|
||||
}
|
||||
|
||||
export declare type Packet = IConnectPacket |
|
||||
IPublishPacket |
|
||||
IConnackPacket |
|
||||
ISubscribePacket |
|
||||
ISubackPacket |
|
||||
IUnsubscribePacket |
|
||||
IUnsubackPacket |
|
||||
IPubackPacket |
|
||||
IPubcompPacket |
|
||||
IPubrelPacket |
|
||||
IPingreqPacket |
|
||||
IPingrespPacket |
|
||||
IDisconnectPacket |
|
||||
IPubrecPacket |
|
||||
IAuthPacket
|
||||
|
||||
export interface Parser extends EventEmitter {
|
||||
on(event: 'packet', callback: (packet: Packet) => void): this
|
||||
|
||||
on(event: 'error', callback: (error: any) => void): this
|
||||
|
||||
parse(buffer: Buffer, opts?: Object): number
|
||||
}
|
||||
|
||||
export declare function parser(opts?: Object): Parser
|
||||
|
||||
export declare function generate(packet: Packet, opts?: Object): Buffer
|
||||
|
||||
export declare function writeToStream(object: Packet, stream: WritableStream, opts?: Object): boolean
|
||||
|
||||
export declare namespace writeToStream {
|
||||
let cacheNumbers: boolean
|
||||
}
|
||||
1129
node_modules/mqtt-packet/writeToStream.js
generated
vendored
Executable file
1129
node_modules/mqtt-packet/writeToStream.js
generated
vendored
Executable file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user