jsdw_ios/Pods/CocoaMQTT/Source/FramePublish.swift

238 lines
5.8 KiB
Swift

//
// FramePublish.swift
// CocoaMQTT
//
// Created by JianBo on 2019/8/7.
// Copyright © 2019 emqx.io. All rights reserved.
//
import Foundation
// MQTT PUBLISH Frame
struct FramePublish: Frame {
// 3.3.1.4 Remaining Length
public var remainingLength: UInt32?
// 3.3.2.1 Topic Name
public var topicName: String?
// 3.3.2.2 Packet Identifier
public var packetIdentifier: UInt16?
// 3.3.2.3 PUBLISH Properties
public var publishProperties: MqttPublishProperties?
public var publishRecProperties: MqttDecodePublish?
var packetFixedHeaderType: UInt8 = FrameType.publish.rawValue
// --- Attributes
var msgid: UInt16
var topic: String = ""
var _payload: [UInt8] = []
var mqtt5Topic: String = ""
// --- Attributes End
init(topic: String, payload: [UInt8], qos: CocoaMQTTQoS = .qos0, msgid: UInt16 = 0) {
self.topic = topic
self._payload = payload
self.msgid = msgid
self.qos = qos
}
}
extension FramePublish {
func fixedHeader() -> [UInt8] {
var header = [UInt8]()
header += [FrameType.publish.rawValue]
return header
}
func variableHeader5() -> [UInt8] {
// 3.3.2.1 Topic Name
var header = self.topic.bytesWithLength
// 3.3.2.2 Packet Identifier qos1 or qos2
if qos > .qos0 {
header += msgid.hlBytes
// header.append(UInt8(0))
// header.append(QoS.rawValue)
}
// MQTT 5.0
header += beVariableByteInteger(length: self.properties().count)
return header
}
func payload5() -> [UInt8] { return _payload }
func properties() -> [UInt8] {
// Properties
return publishProperties?.properties ?? []
}
func allData() -> [UInt8] {
var allData = [UInt8]()
allData += fixedHeader()
allData += variableHeader5()
allData += properties()
allData += payload5()
return allData
}
func variableHeader() -> [UInt8] {
var header = topic.bytesWithLength
if qos > .qos0 {
header += msgid.hlBytes
}
return header
}
func payload() -> [UInt8] { return _payload }
}
extension FramePublish: InitialWithBytes {
init?(packetFixedHeaderType: UInt8, bytes: [UInt8]) {
guard packetFixedHeaderType & 0xF0 == FrameType.publish.rawValue else {
return nil
}
let recDup = ((packetFixedHeaderType & 0b0000_1000) >> 3) > 0
guard let recQos = CocoaMQTTQoS(rawValue: (packetFixedHeaderType & 0b0000_0110) >> 1) else {
return nil
}
let recRetain = (packetFixedHeaderType & 0b0000_0001) > 0
// Reserved
var flags: UInt8 = 0
if recRetain {
flags = flags | 0b0000_0001
} else {
flags = flags | 0b0000_0000
}
if recDup {
flags = flags | 0b0011_1000
} else {
flags = flags | 0b0011_0000
}
switch recQos {
case .qos0:
flags = flags | 0b0011_0000
case .qos1:
flags = flags | 0b0011_0010
case .qos2:
flags = flags | 0b0011_0100
case .FAILURE:
printDebug("FAILTURE")
}
self.packetFixedHeaderType = flags
// Packet Identifier
// The Packet Identifier field is only present in PUBLISH packets where the QoS level is 1 or 2.
// parse topic
if bytes.count < 2 {
return nil
}
let topicLength = Int(UInt16(bytes[0]) << 8 | UInt16(bytes[1]))
let topicStart = 2
let topicEnd = topicStart + topicLength
if bytes.count < topicEnd {
return nil
}
var pos = topicEnd
// msgid
if (packetFixedHeaderType & 0x06) >> 1 == CocoaMQTTQoS.qos0.rawValue {
msgid = 0
} else {
if bytes.count < pos + 2 {
return nil
}
msgid = UInt16(bytes[pos]) << 8 + UInt16(bytes[pos+1])
pos += 2
}
var protocolVersion = ""
if let storage = CocoaMQTTStorage() {
protocolVersion = storage.queryMQTTVersion()
}
if protocolVersion == "5.0" {
let data = MqttDecodePublish()
data.decodePublish(fixedHeader: packetFixedHeaderType, publishData: bytes)
pos = data.mqtt5DataIndex
if data.propertyLength != 0 {
pos += data.propertyLength!
}
if pos > bytes.count {
return nil
}
// MQTT 5.0: Topic Name may be empty only when Topic Alias is present.
if data.topic.isEmpty && data.topicAlias == nil {
return nil
}
// MQTT 5.0
self.mqtt5Topic = data.topic
self.topic = data.topic
self.packetIdentifier = data.packetIdentifier
if let packetIdentifier = data.packetIdentifier {
self.msgid = packetIdentifier
}
self.publishRecProperties = data
} else {
// MQTT 3.1.1
guard topicLength > 0 else {
return nil
}
guard let recTopic = String(bytes: bytes[topicStart..<topicEnd], encoding: .utf8) else {
return nil
}
topic = recTopic
}
// payload
if pos == bytes.count {
_payload = []
} else if pos < bytes.count {
_payload = [UInt8](bytes[pos..<bytes.count])
} else {
return nil
}
}
}
extension FramePublish: CustomStringConvertible {
var description: String {
return "PUBLISH(id: \(msgid), topic: \(topic), payload: \(_payload.summary))"
}
}