You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

250 lines
7.7 KiB

2 years ago
  1. //
  2. // SocketPacket.swift
  3. // Socket.IO-Client-Swift
  4. //
  5. // Created by Erik Little on 1/18/15.
  6. //
  7. // Permission is hereby granted, free of charge, to any person obtaining a copy
  8. // of this software and associated documentation files (the "Software"), to deal
  9. // in the Software without restriction, including without limitation the rights
  10. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11. // copies of the Software, and to permit persons to whom the Software is
  12. // furnished to do so, subject to the following conditions:
  13. //
  14. // The above copyright notice and this permission notice shall be included in
  15. // all copies or substantial portions of the Software.
  16. //
  17. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  20. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  22. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  23. // THE SOFTWARE.
  24. //
  25. import Foundation
  26. /// A struct that represents a socket.io packet.
  27. public struct SocketPacket : CustomStringConvertible {
  28. // MARK: Properties
  29. private static let logType = "SocketPacket"
  30. /// The namespace for this packet.
  31. public let nsp: String
  32. /// If > 0 then this packet is using acking.
  33. public let id: Int
  34. /// The type of this packet.
  35. public let type: PacketType
  36. /// An array of binary data for this packet.
  37. public internal(set) var binary: [Data]
  38. /// The data for this event.
  39. ///
  40. /// Note: This includes all data inside of the socket.io packet payload array, which includes the event name for
  41. /// event type packets.
  42. public internal(set) var data: [Any]
  43. /// Returns the payload for this packet, minus the event name if this is an event or binaryEvent type packet.
  44. public var args: [Any] {
  45. if type == .event || type == .binaryEvent && data.count != 0 {
  46. return Array(data.dropFirst())
  47. } else {
  48. return data
  49. }
  50. }
  51. private let placeholders: Int
  52. /// A string representation of this packet.
  53. public var description: String {
  54. return "SocketPacket {type: \(String(type.rawValue)); data: " +
  55. "\(String(describing: data)); id: \(id); placeholders: \(placeholders); nsp: \(nsp)}"
  56. }
  57. /// The event name for this packet.
  58. public var event: String {
  59. return String(describing: data[0])
  60. }
  61. /// A string representation of this packet.
  62. public var packetString: String {
  63. return createPacketString()
  64. }
  65. init(type: PacketType, data: [Any] = [Any](), id: Int = -1, nsp: String, placeholders: Int = 0,
  66. binary: [Data] = [Data]()) {
  67. self.data = data
  68. self.id = id
  69. self.nsp = nsp
  70. self.type = type
  71. self.placeholders = placeholders
  72. self.binary = binary
  73. }
  74. mutating func addData(_ data: Data) -> Bool {
  75. if placeholders == binary.count {
  76. return true
  77. }
  78. binary.append(data)
  79. if placeholders == binary.count {
  80. fillInPlaceholders()
  81. return true
  82. } else {
  83. return false
  84. }
  85. }
  86. private func completeMessage(_ message: String) -> String {
  87. guard data.count != 0 else { return message + "[]" }
  88. guard let jsonSend = try? data.toJSON(), let jsonString = String(data: jsonSend, encoding: .utf8) else {
  89. DefaultSocketLogger.Logger.error("Error creating JSON object in SocketPacket.completeMessage",
  90. type: SocketPacket.logType)
  91. return message + "[]"
  92. }
  93. return message + jsonString
  94. }
  95. private func createPacketString() -> String {
  96. let typeString = String(type.rawValue)
  97. // Binary count?
  98. let binaryCountString = typeString + (type.isBinary ? "\(String(binary.count))-" : "")
  99. // Namespace?
  100. let nspString = binaryCountString + (nsp != "/" ? "\(nsp)," : "")
  101. // Ack number?
  102. let idString = nspString + (id != -1 ? String(id) : "")
  103. return completeMessage(idString)
  104. }
  105. // Called when we have all the binary data for a packet
  106. // calls _fillInPlaceholders, which replaces placeholders with the
  107. // corresponding binary
  108. private mutating func fillInPlaceholders() {
  109. data = data.map(_fillInPlaceholders)
  110. }
  111. // Helper method that looks for placeholders
  112. // If object is a collection it will recurse
  113. // Returns the object if it is not a placeholder or the corresponding
  114. // binary data
  115. private func _fillInPlaceholders(_ object: Any) -> Any {
  116. switch object {
  117. case let dict as JSON:
  118. if dict["_placeholder"] as? Bool ?? false {
  119. return binary[dict["num"] as! Int]
  120. } else {
  121. return dict.reduce(into: JSON(), {cur, keyValue in
  122. cur[keyValue.0] = _fillInPlaceholders(keyValue.1)
  123. })
  124. }
  125. case let arr as [Any]:
  126. return arr.map(_fillInPlaceholders)
  127. default:
  128. return object
  129. }
  130. }
  131. }
  132. public extension SocketPacket {
  133. // MARK: PacketType enum
  134. /// The type of packets.
  135. enum PacketType: Int {
  136. // MARK: Cases
  137. /// Connect: 0
  138. case connect
  139. /// Disconnect: 1
  140. case disconnect
  141. /// Event: 2
  142. case event
  143. /// Ack: 3
  144. case ack
  145. /// Error: 4
  146. case error
  147. /// Binary Event: 5
  148. case binaryEvent
  149. /// Binary Ack: 6
  150. case binaryAck
  151. // MARK: Properties
  152. /// Whether or not this type is binary
  153. public var isBinary: Bool {
  154. return self == .binaryAck || self == .binaryEvent
  155. }
  156. }
  157. }
  158. extension SocketPacket {
  159. private static func findType(_ binCount: Int, ack: Bool) -> PacketType {
  160. switch binCount {
  161. case 0 where !ack:
  162. return .event
  163. case 0 where ack:
  164. return .ack
  165. case _ where !ack:
  166. return .binaryEvent
  167. case _ where ack:
  168. return .binaryAck
  169. default:
  170. return .error
  171. }
  172. }
  173. static func packetFromEmit(_ items: [Any], id: Int, nsp: String, ack: Bool, checkForBinary: Bool = true) -> SocketPacket {
  174. if checkForBinary {
  175. let (parsedData, binary) = deconstructData(items)
  176. return SocketPacket(type: findType(binary.count, ack: ack), data: parsedData, id: id, nsp: nsp,
  177. binary: binary)
  178. } else {
  179. return SocketPacket(type: findType(0, ack: ack), data: items, id: id, nsp: nsp)
  180. }
  181. }
  182. }
  183. private extension SocketPacket {
  184. // Recursive function that looks for NSData in collections
  185. static func shred(_ data: Any, binary: inout [Data]) -> Any {
  186. let placeholder = ["_placeholder": true, "num": binary.count] as JSON
  187. switch data {
  188. case let bin as Data:
  189. binary.append(bin)
  190. return placeholder
  191. case let arr as [Any]:
  192. return arr.map({shred($0, binary: &binary)})
  193. case let dict as JSON:
  194. return dict.reduce(into: JSON(), {cur, keyValue in
  195. cur[keyValue.0] = shred(keyValue.1, binary: &binary)
  196. })
  197. default:
  198. return data
  199. }
  200. }
  201. // Removes binary data from emit data
  202. // Returns a type containing the de-binaryed data and the binary
  203. static func deconstructData(_ data: [Any]) -> ([Any], [Data]) {
  204. var binary = [Data]()
  205. return (data.map({ shred($0, binary: &binary) }), binary)
  206. }
  207. }