Skip to content

Commit 9f64359

Browse files
committed
feat: 🎸 add publish packet
1 parent 12152f9 commit 9f64359

File tree

5 files changed

+74
-44
lines changed

5 files changed

+74
-44
lines changed

‎src/MqttDecoder.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
import BufferList from 'bl';
22
import {DECODER_STATE, PACKET_TYPE} from './enums';
3-
import {PacketSuback, PacketPublish} from './packet';
43
import {PacketConnack, parseConnack} from './packets/connack';
54
import {PacketConnect, parseConnect} from './packets/connect';
5+
import {PacketPublish, parsePublish} from './packets/publish';
66

77
export class MqttDecoder {
88
public state: DECODER_STATE = DECODER_STATE.HEADER;
99
public error: null | Error = null;
1010
public list = new BufferList();
1111
public b: number = 0;
1212
public l: number = 0;
13-
public version: number = 5; // MQTT protocol version.
13+
14+
// MQTT protocol version. Defaults to 4 as that is most popular version currently.
15+
public version: number = 4;
1416

1517
constructor() {}
1618

@@ -28,7 +30,7 @@ export class MqttDecoder {
2830
this.list = new BufferList();
2931
}
3032

31-
public parse(): null | PacketConnect | PacketConnack | PacketSuback | PacketPublish {
33+
public parse(): null | PacketConnect | PacketConnack | PacketPublish {
3234
this.parseFixedHeader();
3335
const data = this.parseVariableData();
3436
if (!data) return null;
@@ -44,11 +46,10 @@ export class MqttDecoder {
4446
return parseConnack(b, l, data, this.version);
4547
}
4648
case PACKET_TYPE.SUBACK: {
47-
const packet = new PacketSuback(b, l, data);
48-
return packet;
49+
return null;
4950
}
5051
case PACKET_TYPE.PUBLISH: {
51-
const packet = new PacketPublish(b, l, data);
52+
const packet = parsePublish(b, l, data, this.version);
5253
return packet;
5354
}
5455
default: {

‎src/__tests__/MqttDecoder.spec.ts

Lines changed: 22 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import {MqttDecoder} from '../MqttDecoder';
2-
import {connect, connectAck, connectWithClientId, publish1, subscribe, subscribeAck} from './util';
2+
import {connect, connectAck, connectWithClientId, publish3111, subscribe, subscribeAck} from './util';
33
import {PACKET_TYPE, PROPERTY} from '../enums';
44
import {PacketConnect} from '../packets/connect';
5-
import { PacketConnack } from '../packets/connack';
5+
import {PacketConnack} from '../packets/connack';
6+
import {PacketPublish} from '../packets/publish';
67

78
it('can instantiate', () => {
89
const decoder = new MqttDecoder();
@@ -122,6 +123,7 @@ describe('CONNACK', () => {
122123

123124
it('parses variable header', () => {
124125
const decoder = new MqttDecoder();
126+
decoder.version = 5;
125127
decoder.push(connectAck);
126128
const packet: PacketConnack = decoder.parse() as PacketConnack;
127129
expect(packet.f).toBe(0);
@@ -133,31 +135,26 @@ describe('CONNACK', () => {
133135
});
134136
});
135137

136-
it('can parse SUBACK packet fixed header', () => {
137-
const decoder = new MqttDecoder();
138-
decoder.push(subscribeAck);
139-
const packet = decoder.parse();
140-
expect(packet!.type()).toBe(PACKET_TYPE.SUBACK);
141-
expect(packet!.dup()).toBe(false);
142-
expect(packet!.qos()).toBe(0);
143-
expect(packet!.retain()).toBe(false);
138+
describe('PUBLISH', () => {
139+
it('parses PUBLISH variable data', () => {
140+
const decoder = new MqttDecoder();
141+
decoder.push(publish3111);
142+
const packet: PacketPublish = decoder.parse() as PacketPublish;
143+
expect(packet.b).toBe(publish3111.readUInt8(0));
144+
expect(packet.l).toBe(publish3111.readUInt8(1));
145+
expect(packet.t).toBe('zibel32/18fe34f1d68e/$name');
146+
expect(packet.i).toBe(23145);
147+
expect(packet.p).toEqual({});
148+
expect(packet.d.toString()).toMatchInlineSnapshot(`"bel32 Garage Door"`);
149+
});
144150
});
145151

146-
// it('parses CONNACK variable data', () => {
147-
// const decoder = new MqttDecoder();
148-
// decoder.push(connectAck);
149-
// const packet = decoder.parse();
150-
// expect(!!packet!.data!).toBe(true);
151-
// expect(packet!.l).toBe(connectAck.byteLength - 2);
152-
// expect(packet!.data!.length).toBe(connectAck.byteLength - 2);
153-
// expect(packet!.data!.readInt8(0)).toBe(0);
154-
// expect(packet!.data!.readInt8(1)).toBe(0);
155-
// });
156-
157-
// it('parses PUBLISH variable data', () => {
152+
// it('can parse SUBACK packet fixed header', () => {
158153
// const decoder = new MqttDecoder();
159-
// decoder.push(publish1);
154+
// decoder.push(subscribeAck);
160155
// const packet = decoder.parse();
161-
// expect(!!packet!.data!).toBe(true);
162-
// expect(packet!.l).toBe(publish1.byteLength - 2);
156+
// expect(packet!.type()).toBe(PACKET_TYPE.SUBACK);
157+
// expect(packet!.dup()).toBe(false);
158+
// expect(packet!.qos()).toBe(0);
159+
// expect(packet!.retain()).toBe(false);
163160
// });

‎src/__tests__/util.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,6 @@ export const subscribeAck = Buffer.from([0x90, 0x03, 0x00, 0x01, 0x00]);
1515
// prettier-ignore
1616
export const disconnect = Buffer.from([0x1c, 0xb0, 0x44, 0x94, 0xd8, 0xc0, 0x38, 0xf9, 0xd3, 0x96, 0x21, 0x14, 0x86, 0xdd, 0x60, 0x21, 0x42, 0x71, 0x00, 0x22, 0x06, 0x40, 0x2a, 0x02, 0x12, 0x0b, 0xc3, 0xc7, 0x94, 0xe0, 0xd8, 0xe2, 0x18, 0x15, 0xbc, 0xe6, 0xf2, 0xd9, 0x20, 0x01, 0x41, 0xd0, 0x00, 0x0a, 0xfe, 0xd0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf2, 0x75, 0x07, 0x5b, 0xc5, 0x94, 0x83, 0xc6, 0x7a, 0x0e, 0xdf, 0x70, 0x80, 0x18, 0x08, 0x00, 0x8d, 0xd9, 0x00, 0x00, 0x01, 0x01, 0x08, 0x0a, 0x35, 0x95, 0x49, 0xec, 0x75, 0xb0, 0xd8, 0xe0, 0xe0, 0x00]);
1717
// prettier-ignore
18-
export const publish1 = Buffer.from([0x31, 0x2f, 0x00, 0x1a, 0x7a, 0x69, 0x62, 0x65, 0x6c, 0x33, 0x32, 0x2f, 0x31, 0x38, 0x66, 0x65, 0x33, 0x34, 0x66, 0x31, 0x64, 0x36, 0x38, 0x65, 0x2f, 0x24, 0x6e, 0x61, 0x6d, 0x65, 0x5a, 0x69, 0x62, 0x65, 0x6c, 0x33, 0x32, 0x20, 0x47, 0x61, 0x72, 0x61, 0x67, 0x65, 0x20, 0x44, 0x6f, 0x6f, 0x72]);
18+
export const publish3111 = Buffer.from([0x31, 0x2f, 0x00, 0x1a, 0x7a, 0x69, 0x62, 0x65, 0x6c, 0x33, 0x32, 0x2f, 0x31, 0x38, 0x66, 0x65, 0x33, 0x34, 0x66, 0x31, 0x64, 0x36, 0x38, 0x65, 0x2f, 0x24, 0x6e, 0x61, 0x6d, 0x65, 0x5a, 0x69, 0x62, 0x65, 0x6c, 0x33, 0x32, 0x20, 0x47, 0x61, 0x72, 0x61, 0x67, 0x65, 0x20, 0x44, 0x6f, 0x6f, 0x72]);
1919
// prettier-ignore
2020
export const publish2 = Buffer.from([0x31, 0x2f, 0x00, 0x1a, 0x7a, 0x69, 0x62, 0x65, 0x6c, 0x33, 0x32, 0x2f, 0x31, 0x38, 0x66, 0x65, 0x33, 0x34, 0x66, 0x31, 0x64, 0x36, 0x38, 0x65, 0x2f, 0x24, 0x6e, 0x61, 0x6d, 0x65, 0x5a, 0x69, 0x62, 0x65, 0x6c, 0x33, 0x32, 0x20, 0x47, 0x61, 0x72, 0x61, 0x67, 0x65, 0x20, 0x44, 0x6f, 0x6f, 0x72, 0x31, 0x2b, 0x00, 0x1d, 0x7a, 0x69, 0x62, 0x65, 0x6c, 0x33, 0x32, 0x2f, 0x31, 0x38, 0x66, 0x65, 0x33, 0x34, 0x66, 0x31, 0x64, 0x36, 0x38, 0x65, 0x2f, 0x24, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x69, 0x70, 0x31, 0x39, 0x32, 0x2e, 0x31, 0x36, 0x38, 0x2e, 0x31, 0x2e, 0x31, 0x32, 0x31, 0x28, 0x00, 0x24, 0x7a, 0x69, 0x62, 0x65, 0x6c, 0x33, 0x32, 0x2f, 0x31, 0x38, 0x66, 0x65, 0x33, 0x34, 0x66, 0x31, 0x64, 0x36, 0x38, 0x65, 0x2f, 0x24, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x36, 0x30]);

‎src/packet.ts

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,3 @@ export class Packet implements PacketHeaderData {
3737
return !!(this.b & 0b1);
3838
}
3939
}
40-
41-
export class PacketSuback extends Packet {
42-
constructor(b: number, l: number, public readonly data: BufferList) {
43-
super(b, l);
44-
}
45-
}
46-
47-
export class PacketPublish extends Packet {
48-
constructor(b: number, l: number, public readonly data: BufferList) {
49-
super(b, l);
50-
}
51-
}

‎src/packets/publish.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import BufferList from 'bl';
2+
import {Packet, PacketHeaderData} from '../packet';
3+
import {Properties} from '../types';
4+
import {parseBinary, parseProps} from '../util/parse';
5+
6+
export interface PacketPublishData extends PacketHeaderData {
7+
/** Topic Name. */
8+
t: string;
9+
/** Packet Identifier. */
10+
i: number;
11+
/** Properties. */
12+
p: Properties;
13+
/** Payload. */
14+
d: Buffer;
15+
}
16+
17+
export class PacketPublish extends Packet implements PacketPublishData {
18+
constructor(
19+
b: number,
20+
l: number,
21+
public t: string,
22+
public i: number,
23+
public p: Properties,
24+
public d: Buffer,
25+
) {
26+
super(b, l);
27+
}
28+
}
29+
30+
export const parsePublish = (b: number, l: number, data: BufferList, version: number): PacketPublish => {
31+
const topic = parseBinary(data, 0);
32+
let offset = 2 + topic.byteLength;
33+
const i = data.readUInt16BE(offset);
34+
offset += 2;
35+
let p: Properties = {};
36+
if (version === 5) {
37+
const [props, size] = parseProps(data, offset);
38+
p = props;
39+
offset += size;
40+
}
41+
const d = data.slice(offset, data.length);
42+
const packet = new PacketPublish(b, l, topic.toString('utf8'), i, p, d);
43+
return packet;
44+
};

0 commit comments

Comments
 (0)