Line data Source code
1 : /*
2 : * Package : mqtt_client
3 : * Author : S. Hamblett <steve.hamblett@linux.com>
4 : * Date : 31/05/2017
5 : * Copyright : S.Hamblett
6 : */
7 :
8 : part of mqtt_client;
9 :
10 : /// Represents the Fixed Header of an MQTT message.
11 : class MqttHeader {
12 : /// Initializes a new instance of the MqttHeader class.
13 6 : MqttHeader();
14 :
15 : /// Initializes a new instance of MqttHeader" based on data contained within the supplied stream.
16 4 : MqttHeader.fromByteBuffer(MqttByteBuffer headerStream) {
17 4 : readFrom(headerStream);
18 : }
19 :
20 : /// Backing storage for the payload size.
21 : int _messageSize = 0;
22 :
23 : /// Gets or sets the type of the MQTT message.
24 : MqttMessageType messageType;
25 :
26 : /// Gets or sets a value indicating whether this MQTT Message is duplicate of a previous message.
27 : /// True if duplicate; otherwise, false.
28 : bool duplicate = false;
29 :
30 : /// Gets or sets the Quality of Service indicator for the message.
31 : MqttQos qos = MqttQos.atMostOnce;
32 :
33 : /// Gets or sets a value indicating whether this MQTT message should be retained by the message broker for transmission to new subscribers.
34 : /// True if message should be retained by the message broker; otherwise, false.
35 : /// </value>
36 : bool retain = false;
37 :
38 : /// Gets or sets the size of the variable header + payload section of the message.
39 : /// <value>The size of the variable header + payload.</value>
40 : /// <exception cref="Nmqtt.InvalidPayloadSizeException">The size of the variable header + payload exceeds the maximum allowed size.</exception>
41 2 : int get messageSize => _messageSize;
42 :
43 : set messageSize(int value) {
44 2 : if (value < 0 || value > Constants.maxMessageSize) {
45 1 : throw new InvalidPayloadSizeException(value, Constants.maxMessageSize);
46 : }
47 1 : _messageSize = value;
48 : }
49 :
50 : /// Writes the header to a supplied stream.
51 : void writeTo(int messageSize, MqttByteBuffer messageStream) {
52 4 : _messageSize = messageSize;
53 4 : final typed.Uint8Buffer headerBuff = headerBytes();
54 4 : messageStream.write(headerBuff);
55 : }
56 :
57 : /// Creates a new MqttHeader based on a list of bytes.
58 : void readFrom(MqttByteBuffer headerStream) {
59 8 : if (headerStream.length < 2) {
60 1 : throw new InvalidHeaderException(
61 : "The supplied header is invalid. Header must be at least 2 bytes long.");
62 : }
63 4 : final int firstHeaderByte = headerStream.readByte();
64 : // Pull out the first byte
65 12 : retain = ((firstHeaderByte & 1) == 1 ? true : false);
66 16 : qos = MqttQos.values[((firstHeaderByte & 6) >> 1)];
67 16 : duplicate = (((firstHeaderByte & 8) >> 3) == 1 ? true : false);
68 16 : messageType = MqttMessageType.values[((firstHeaderByte & 240) >> 4)];
69 :
70 : // Decode the remaining bytes as the remaining/payload size, input param is the 2nd to last byte of the header byte list
71 : try {
72 8 : _messageSize = readRemainingLength(headerStream);
73 : } catch (InvalidPayloadSizeException) {
74 0 : throw new InvalidHeaderException(
75 0 : "The header being processed contained an invalid size byte pattern." +
76 : "Message size must take a most 4 bytes, and the last byte must have bit 8 set to 0.");
77 : }
78 : }
79 :
80 : /// Gets the value of the Mqtt header as a byte array
81 : typed.Uint8Buffer headerBytes() {
82 4 : final typed.Uint8Buffer headerBytes = new typed.Uint8Buffer();
83 :
84 : // Build the bytes that make up the header. The first byte is a combination of message type, dup,
85 : // qos and retain, and the follow bytes (up to 4 of them) are the size of the payload + variable header.
86 12 : final int messageTypeLength = messageType.index << 4;
87 8 : final int duplicateLength = (duplicate ? 1 : 0) << 3;
88 12 : final int qosLength = qos.index << 1;
89 4 : final int retainLength = retain ? 1 : 0;
90 8 : final int firstByte = messageTypeLength + duplicateLength +
91 4 : qosLength + retainLength;
92 4 : headerBytes.add(firstByte);
93 8 : headerBytes.addAll(getRemainingLengthBytes());
94 : return headerBytes;
95 : }
96 :
97 : static int readRemainingLength(MqttByteBuffer headerStream) {
98 4 : final typed.Uint8Buffer lengthBytes = readLengthBytes(headerStream);
99 4 : return calculateLength(lengthBytes);
100 : }
101 :
102 : /// Reads the length bytes of an MqttHeader from the supplied stream.
103 : static typed.Uint8Buffer readLengthBytes(MqttByteBuffer headerStream) {
104 4 : final typed.Uint8Buffer lengthBytes = new typed.Uint8Buffer();
105 : // Read until we've got the entire size, or the 4 byte limit is reached
106 : int sizeByte;
107 : int byteCount = 0;
108 : do {
109 4 : sizeByte = headerStream.readByte();
110 4 : lengthBytes.add(sizeByte);
111 16 : } while (++byteCount <= 4 && (sizeByte & 0x80) == 0x80);
112 : return lengthBytes;
113 : }
114 :
115 : /// Calculates and return the bytes that represent the remaining length of the message.
116 : typed.Uint8Buffer getRemainingLengthBytes() {
117 4 : final typed.Uint8Buffer lengthBytes = new typed.Uint8Buffer();
118 4 : int payloadCalc = _messageSize;
119 :
120 : // Generate a byte array based on the message size, splitting it up into
121 : // 7 bit chunks, with the 8th bit being used to indicate "one more to come"
122 : do {
123 4 : int nextByteValue = payloadCalc % 128;
124 4 : payloadCalc = (payloadCalc ~/ 128);
125 4 : if (payloadCalc > 0) {
126 1 : nextByteValue = nextByteValue | 0x80;
127 : }
128 4 : lengthBytes.add(nextByteValue);
129 4 : } while (payloadCalc > 0);
130 :
131 : return lengthBytes;
132 : }
133 :
134 : /// Calculates the remaining length of an MqttMessage from the bytes that make up the length
135 : static int calculateLength(typed.Uint8Buffer lengthBytes) {
136 : var remainingLength = 0;
137 : var multiplier = 1;
138 :
139 8 : for (int currentByte in lengthBytes) {
140 12 : remainingLength += (currentByte & 0x7f) * multiplier;
141 4 : multiplier *= 0x80;
142 : }
143 : return remainingLength;
144 : }
145 :
146 : /// Sets the IsDuplicate flag of the header.
147 : MqttHeader isDuplicate() {
148 1 : this.duplicate = true;
149 : return this;
150 : }
151 :
152 : /// Sets the Qos of the message header.
153 : MqttHeader withQos(MqttQos qos) {
154 4 : this.qos = qos;
155 : return this;
156 : }
157 :
158 : /// Sets the type of the message identified in the header.
159 : MqttHeader asType(MqttMessageType messageType) {
160 6 : this.messageType = messageType;
161 : return this;
162 : }
163 :
164 : /// Defines that the message should be retained.
165 : MqttHeader shouldBeRetained() {
166 1 : this.retain = true;
167 : return this;
168 : }
169 :
170 : String toString() {
171 36 : return "Header: MessageType = $messageType, Duplicate = $duplicate, Retain = $retain, Qos = $qos, Size = $_messageSize";
172 : }
173 : }
|