Skip to content

Commit 6f0e351

Browse files
committed
feat: 🎸 improve connect packet serialization
1 parent 21b0fd7 commit 6f0e351

File tree

4 files changed

+185
-20
lines changed

4 files changed

+185
-20
lines changed

‎src/MqttDecoder.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -166,18 +166,19 @@ export class MqttDecoder {
166166
// const f = (ui32 & 0xFF0000) >> 16;
167167
// const k = (ui32 & 0xFFFF);
168168
// offset += 4;
169+
const isV5 = v === 5;
169170
let p: Properties = {};
170-
if (v === 5) {
171+
if (isV5) {
171172
const [props, propsSize] = parseProps(buf, offset);
172173
p = props;
173174
offset += propsSize;
174175
}
175176
const clientId = parseBinary(buf, offset);
176177
const id = clientId.toString('utf8');
177178
offset += 2 + clientId.byteLength;
178-
const packet = new PacketConnect(b, l, v, f, k, p, id);
179+
const packet = new PacketConnect(b, l, v, f, k, p, id)
179180
if (packet.willFlag()) {
180-
if (v === 5) {
181+
if (isV5) {
181182
const [props, propsSize] = parseProps(buf, offset);
182183
packet.wp = props;
183184
offset += propsSize;

‎src/packets/__tests__/connect.spec.ts

Lines changed: 173 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import {PROPERTY} from '../../../es6/enums';
22
import {PACKET_TYPE} from '../../enums';
33
import {PacketConnect} from '../connect';
4+
import {MqttDecoder} from '../../MqttDecoder';
45

56
test('can create a packet', () => {
6-
const packet = PacketConnect.create(5, 0, 30, {}, 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx');
7+
const packet = PacketConnect.create(5, 30, {}, 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx');
78
expect(packet.b >> 4).toBe(PACKET_TYPE.CONNECT);
89
expect(packet.id).toBe('xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx');
910
expect(packet.v).toBe(5);
@@ -13,7 +14,7 @@ test('can create a packet', () => {
1314
});
1415

1516
test('can set username', () => {
16-
const packet = PacketConnect.create(5, 0, 30, {}, 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx');
17+
const packet = PacketConnect.create(5, 30, {}, 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx');
1718
expect(packet.userNameFlag()).toBe(false);
1819
expect(packet.usr).toBe(undefined);
1920
packet.setUserName('streamich');
@@ -28,7 +29,7 @@ test('can set username', () => {
2829
});
2930

3031
test('can remove username', () => {
31-
const packet = PacketConnect.create(5, 0, 30, {}, 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx');
32+
const packet = PacketConnect.create(5, 30, {}, 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx');
3233
expect(packet.userNameFlag()).toBe(false);
3334
expect(packet.usr).toBe(undefined);
3435
packet.removeUserName();
@@ -43,7 +44,7 @@ test('can remove username', () => {
4344
});
4445

4546
test('can set password', () => {
46-
const packet = PacketConnect.create(5, 0, 30, {}, 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx');
47+
const packet = PacketConnect.create(5, 30, {}, 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx');
4748
expect(packet.passwordFlag()).toBe(false);
4849
expect(packet.usr).toBe(undefined);
4950
packet.setPassword(Buffer.from([1, 2, 3]));
@@ -58,7 +59,7 @@ test('can set password', () => {
5859
});
5960

6061
test('can remove password', () => {
61-
const packet = PacketConnect.create(5, 0, 30, {}, 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx');
62+
const packet = PacketConnect.create(5, 30, {}, 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx');
6263
expect(packet.passwordFlag()).toBe(false);
6364
expect(packet.pwd).toBe(undefined);
6465
packet.removePassword();
@@ -73,7 +74,7 @@ test('can remove password', () => {
7374
});
7475

7576
test('can change Clean Start flag', () => {
76-
const packet = PacketConnect.create(5, 0, 30, {}, 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx');
77+
const packet = PacketConnect.create(5, 30, {}, 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx');
7778
expect(packet.cleanStart()).toBe(false);
7879
packet.setCleanStart(false);
7980
expect(packet.cleanStart()).toBe(false);
@@ -86,7 +87,7 @@ test('can change Clean Start flag', () => {
8687
});
8788

8889
test('can set will', () => {
89-
const packet = PacketConnect.create(5, 0, 30, {}, 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx');
90+
const packet = PacketConnect.create(5, 30, {}, 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx');
9091
expect(packet.willFlag()).toBe(false);
9192
expect(packet.willQos()).toBe(0);
9293
expect(packet.willRetain()).toBe(false);
@@ -110,7 +111,7 @@ test('can set will', () => {
110111
});
111112

112113
test('can remove will', () => {
113-
const packet = PacketConnect.create(5, 0, 30, {}, 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx');
114+
const packet = PacketConnect.create(5, 30, {}, 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx');
114115
packet.removeWill();
115116
expect(packet.willFlag()).toBe(false);
116117
expect(packet.willQos()).toBe(0);
@@ -142,7 +143,7 @@ test('can remove will', () => {
142143
});
143144

144145
test('can manipulate multiple properties simultaneously', () => {
145-
const packet = PacketConnect.create(5, 0, 30, {}, 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx');
146+
const packet = PacketConnect.create(5, 30, {}, 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx');
146147

147148
expect(packet.qualityOfService()).toBe(0);
148149
packet.setQualityOfService(2);
@@ -214,3 +215,166 @@ test('can manipulate multiple properties simultaneously', () => {
214215
expect(packet.qualityOfService()).toBe(2);
215216
expect(packet.usr).toBe(undefined);
216217
});
218+
219+
test('can serialize packet', () => {
220+
const packet1 = PacketConnect.create(5, 30, {}, '');
221+
const buf = packet1.toBuffer();
222+
const decoder = new MqttDecoder();
223+
decoder.version = 5;
224+
decoder.push(buf);
225+
const packet2 = decoder.parse()! as PacketConnect;
226+
expect(packet2.b).toBe(packet1.b);
227+
expect(packet2.l).toBe(packet1.l);
228+
expect(packet2.v).toBe(packet1.v);
229+
expect(packet2.v).toBe(5);
230+
expect(packet2.f).toBe(packet1.f);
231+
expect(packet2.k).toBe(packet1.k);
232+
expect(packet2.k).toBe(30);
233+
expect(packet2.id).toBe(packet1.id);
234+
expect(packet2.id).toBe('');
235+
expect(packet2.p).toEqual(packet1.p);
236+
expect(packet2.p).toEqual({});
237+
expect(packet2.w).toEqual(undefined);
238+
expect(packet2.wp).toEqual(undefined);
239+
expect(packet2.wt).toEqual(undefined);
240+
expect(packet2.usr).toEqual(undefined);
241+
expect(packet2.pwd).toEqual(undefined);
242+
expect(packet2).toEqual(packet1);
243+
});
244+
245+
test('can serialize packet in MQTT 3.1.1', () => {
246+
const packet1 = PacketConnect.create(4, 30, {}, '');
247+
const buf = packet1.toBuffer();
248+
const decoder = new MqttDecoder();
249+
decoder.version = 4;
250+
decoder.push(buf);
251+
const packet2 = decoder.parse()! as PacketConnect;
252+
expect(packet2.b).toBe(packet1.b);
253+
expect(packet2.l).toBe(packet1.l);
254+
expect(packet2.v).toBe(packet1.v);
255+
expect(packet2.v).toBe(4);
256+
expect(packet2.f).toBe(packet1.f);
257+
expect(packet2.k).toBe(packet1.k);
258+
expect(packet2.k).toBe(30);
259+
expect(packet2.id).toBe(packet1.id);
260+
expect(packet2.id).toBe('');
261+
expect(packet2.p).toEqual(packet1.p);
262+
expect(packet2.p).toEqual({});
263+
expect(packet2.w).toEqual(undefined);
264+
expect(packet2.wp).toEqual(undefined);
265+
expect(packet2.wt).toEqual(undefined);
266+
expect(packet2.usr).toEqual(undefined);
267+
expect(packet2.pwd).toEqual(undefined);
268+
expect(packet2).toEqual(packet1);
269+
});
270+
271+
test('can serialize properties', () => {
272+
const packet1 = PacketConnect.create(5, 30, {
273+
[PROPERTY.ContentType]: 'test',
274+
[PROPERTY.UserProperty]: [
275+
['test', 'test'],
276+
],
277+
}, '');
278+
const buf = packet1.toBuffer();
279+
const decoder = new MqttDecoder();
280+
decoder.version = 5;
281+
decoder.push(buf);
282+
const packet2 = decoder.parse()! as PacketConnect;
283+
expect(packet2.p).toEqual({
284+
[PROPERTY.ContentType]: 'test',
285+
[PROPERTY.UserProperty]: [
286+
['test', 'test'],
287+
],
288+
});
289+
});
290+
291+
test('can serialize will', () => {
292+
const packet1 = PacketConnect.create(5, 30, {}, '');
293+
packet1.setWill(Buffer.from([1, 2, 3, 4, 5]), 'aha', {
294+
[PROPERTY.ContentType]: 'test',
295+
[PROPERTY.UserProperty]: [
296+
['test', 'test'],
297+
],
298+
}, 2, true)
299+
const buf = packet1.toBuffer();
300+
const decoder = new MqttDecoder();
301+
decoder.version = 5;
302+
decoder.push(buf);
303+
const packet2 = decoder.parse()! as PacketConnect;
304+
expect(packet2.willFlag()).toBe(true);
305+
expect(packet2.willQos()).toBe(2);
306+
expect(packet2.willRetain()).toBe(true);
307+
expect(packet2.w).toEqual(Buffer.from([1, 2, 3, 4, 5]));
308+
expect(packet2.wt).toBe('aha');
309+
expect(packet2.wp).toEqual({
310+
[PROPERTY.ContentType]: 'test',
311+
[PROPERTY.UserProperty]: [
312+
['test', 'test'],
313+
],
314+
});
315+
});
316+
317+
test('can serialize will and packet properties', () => {
318+
const packet1 = PacketConnect.create(5, 30, {
319+
[PROPERTY.ContentType]: 'test2',
320+
[PROPERTY.UserProperty]: [
321+
['test2', 'test2'],
322+
],
323+
}, '');
324+
packet1.setWill(Buffer.from([1, 2, 3, 4, 5]), 'aha', {
325+
[PROPERTY.ContentType]: 'test',
326+
[PROPERTY.UserProperty]: [
327+
['test', 'test'],
328+
],
329+
}, 0, false)
330+
const buf = packet1.toBuffer();
331+
const decoder = new MqttDecoder();
332+
decoder.version = 5;
333+
decoder.push(buf);
334+
const packet2 = decoder.parse()! as PacketConnect;
335+
expect(packet2.p).toEqual({
336+
[PROPERTY.ContentType]: 'test2',
337+
[PROPERTY.UserProperty]: [
338+
['test2', 'test2'],
339+
],
340+
});
341+
expect(packet2.willFlag()).toBe(true);
342+
expect(packet2.willQos()).toBe(0);
343+
expect(packet2.willRetain()).toBe(false);
344+
expect(packet2.w).toEqual(Buffer.from([1, 2, 3, 4, 5]));
345+
expect(packet2.wt).toBe('aha');
346+
expect(packet2.wp).toEqual({
347+
[PROPERTY.ContentType]: 'test',
348+
[PROPERTY.UserProperty]: [
349+
['test', 'test'],
350+
],
351+
});
352+
});
353+
354+
test('can serialize password', () => {
355+
const packet1 = PacketConnect.create(5, 30, {}, 'client-id');
356+
packet1.setPassword(Buffer.from('password'));
357+
const buf = packet1.toBuffer();
358+
const decoder = new MqttDecoder();
359+
decoder.version = 5;
360+
decoder.push(buf);
361+
const packet2 = decoder.parse()! as PacketConnect;
362+
expect(packet2).toEqual(packet1);
363+
expect(packet2.pwd).toEqual(Buffer.from('password'));
364+
expect(packet2.id).toBe('client-id');
365+
});
366+
367+
test('can serialize username', () => {
368+
const packet1 = PacketConnect.create(5, 30, {}, 'client-id');
369+
packet1.setUserName('streamich')
370+
packet1.setPassword(Buffer.from('password'));
371+
const buf = packet1.toBuffer();
372+
const decoder = new MqttDecoder();
373+
decoder.version = 5;
374+
decoder.push(buf);
375+
const packet2 = decoder.parse()! as PacketConnect;
376+
expect(packet2.usr).toBe('streamich');
377+
expect(packet2).toEqual(packet1);
378+
expect(packet2.pwd).toEqual(Buffer.from('password'));
379+
expect(packet2.id).toBe('client-id');
380+
});

‎src/packets/connect.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,19 +29,17 @@ export interface PacketConnectData extends PacketHeaderData {
2929
export class PacketConnect extends Packet implements PacketConnectData {
3030
/**
3131
* @param v Version, i.e. 5 or 4
32-
* @param f Connect flags
3332
* @param k Keep-alive
3433
* @param p Properties object in case of MQTT 5.0, or empty `{}` object otherwise.
3534
* @param id Connection ID
3635
*/
3736
static create(
3837
v: number,
39-
f: number,
4038
k: number,
4139
p: Properties,
4240
id: string,
4341
): PacketConnect {
44-
return new PacketConnect(PACKET_TYPE.CONNECT << 4, 0, v, f, k, p, id);
42+
return new PacketConnect(PACKET_TYPE.CONNECT << 4, 0, v, 0, k, p, id);
4543
}
4644

4745
public wp?: Properties;

‎src/packets/connect/encodeConnect.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import {genProps} from '../../util/genProps/v7';
22
import {PacketConnect} from '../connect';
33

44
// "MQTT" string with 2 byte length prefix
5-
const bufferMQTT = Buffer.from([0, 2, 0x4d, 0x51, 0x54, 0x54]);
5+
const bufferMQTT = Buffer.from([0, 4, 0x4d, 0x51, 0x54, 0x54]);
66

77
export const encodeConnect = (packet: PacketConnect): Buffer => {
88
const {b, f, id, v, k, p, w, wt, wp, usr, pwd} = packet;
@@ -13,8 +13,8 @@ export const encodeConnect = (packet: PacketConnect): Buffer => {
1313
const isV5 = v === 5;
1414
const props = isV5 ? genProps(p) : null;
1515
const propsLength = props ? props.length : 0;
16-
const willProps = isV5 && !!wp ? genProps(wp) : null;
17-
const willPropsLength = willProps ? willProps.length : 0;
16+
const willProps = (isV5 && hasWill) ? genProps(wp!) : null;
17+
const willPropsLength = hasWill ? willProps!.length : 0;
1818
const willTopicLength: number = hasWill ? Buffer.byteLength(wt!) : 0;
1919
const willPayloadLength: number = hasWill ? w!.length : 0;
2020
const lenClientId = Buffer.byteLength(id);
@@ -33,8 +33,8 @@ export const encodeConnect = (packet: PacketConnect): Buffer => {
3333
2 + willTopicLength + // Will topic
3434
2 + willPayloadLength // Will payload
3535
) : 0) +
36-
(hasUserName ? 2 + userNameLength : 0) + // Username
37-
(hasPassword ? 2 + passwordLength : 0); // Password
36+
(hasUserName ? (2 + userNameLength) : 0) + // Username
37+
(hasPassword ? (2 + passwordLength) : 0); // Password
3838

3939
packet.l = remainingLength;
4040
const remainingLengthSize = remainingLength < 128 ? 1 : remainingLength < 16_384 ? 2 : remainingLength < 2_097_152 ? 3 : 4;
@@ -83,6 +83,8 @@ export const encodeConnect = (packet: PacketConnect): Buffer => {
8383
offset+= propsLength;
8484
}
8585

86+
buf.writeUInt16BE(lenClientId, offset);
87+
offset += 2;
8688
buf.write(id, offset);
8789
offset += lenClientId;
8890

0 commit comments

Comments
 (0)