| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250 |
- //
- // SocketPacket.swift
- // Socket.IO-Client-Swift
- //
- // Created by Erik Little on 1/18/15.
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to deal
- // in the Software without restriction, including without limitation the rights
- // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- // copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- // THE SOFTWARE.
- //
- import Foundation
- /// A struct that represents a socket.io packet.
- public struct SocketPacket : CustomStringConvertible {
- // MARK: Properties
- private static let logType = "SocketPacket"
- /// The namespace for this packet.
- public let nsp: String
- /// If > 0 then this packet is using acking.
- public let id: Int
- /// The type of this packet.
- public let type: PacketType
- /// An array of binary data for this packet.
- public internal(set) var binary: [Data]
- /// The data for this event.
- ///
- /// Note: This includes all data inside of the socket.io packet payload array, which includes the event name for
- /// event type packets.
- public internal(set) var data: [Any]
- /// Returns the payload for this packet, minus the event name if this is an event or binaryEvent type packet.
- public var args: [Any] {
- if type == .event || type == .binaryEvent && data.count != 0 {
- return Array(data.dropFirst())
- } else {
- return data
- }
- }
- private let placeholders: Int
- /// A string representation of this packet.
- public var description: String {
- return "SocketPacket {type: \(String(type.rawValue)); data: " +
- "\(String(describing: data)); id: \(id); placeholders: \(placeholders); nsp: \(nsp)}"
- }
- /// The event name for this packet.
- public var event: String {
- return String(describing: data[0])
- }
- /// A string representation of this packet.
- public var packetString: String {
- return createPacketString()
- }
- init(type: PacketType, data: [Any] = [Any](), id: Int = -1, nsp: String, placeholders: Int = 0,
- binary: [Data] = [Data]()) {
- self.data = data
- self.id = id
- self.nsp = nsp
- self.type = type
- self.placeholders = placeholders
- self.binary = binary
- }
- mutating func addData(_ data: Data) -> Bool {
- if placeholders == binary.count {
- return true
- }
- binary.append(data)
- if placeholders == binary.count {
- fillInPlaceholders()
- return true
- } else {
- return false
- }
- }
- private func completeMessage(_ message: String) -> String {
- guard data.count != 0 else { return message + "[]" }
- guard let jsonSend = try? data.toJSON(), let jsonString = String(data: jsonSend, encoding: .utf8) else {
- DefaultSocketLogger.Logger.error("Error creating JSON object in SocketPacket.completeMessage",
- type: SocketPacket.logType)
- return message + "[]"
- }
- return message + jsonString
- }
- private func createPacketString() -> String {
- let typeString = String(type.rawValue)
- // Binary count?
- let binaryCountString = typeString + (type.isBinary ? "\(String(binary.count))-" : "")
- // Namespace?
- let nspString = binaryCountString + (nsp != "/" ? "\(nsp)," : "")
- // Ack number?
- let idString = nspString + (id != -1 ? String(id) : "")
- return completeMessage(idString)
- }
- // Called when we have all the binary data for a packet
- // calls _fillInPlaceholders, which replaces placeholders with the
- // corresponding binary
- private mutating func fillInPlaceholders() {
- data = data.map(_fillInPlaceholders)
- }
- // Helper method that looks for placeholders
- // If object is a collection it will recurse
- // Returns the object if it is not a placeholder or the corresponding
- // binary data
- private func _fillInPlaceholders(_ object: Any) -> Any {
- switch object {
- case let dict as JSON:
- if dict["_placeholder"] as? Bool ?? false {
- return binary[dict["num"] as! Int]
- } else {
- return dict.reduce(into: JSON(), {cur, keyValue in
- cur[keyValue.0] = _fillInPlaceholders(keyValue.1)
- })
- }
- case let arr as [Any]:
- return arr.map(_fillInPlaceholders)
- default:
- return object
- }
- }
- }
- public extension SocketPacket {
- // MARK: PacketType enum
- /// The type of packets.
- enum PacketType: Int {
- // MARK: Cases
- /// Connect: 0
- case connect
- /// Disconnect: 1
- case disconnect
- /// Event: 2
- case event
- /// Ack: 3
- case ack
- /// Error: 4
- case error
- /// Binary Event: 5
- case binaryEvent
- /// Binary Ack: 6
- case binaryAck
- // MARK: Properties
- /// Whether or not this type is binary
- public var isBinary: Bool {
- return self == .binaryAck || self == .binaryEvent
- }
- }
- }
- extension SocketPacket {
- private static func findType(_ binCount: Int, ack: Bool) -> PacketType {
- switch binCount {
- case 0 where !ack:
- return .event
- case 0 where ack:
- return .ack
- case _ where !ack:
- return .binaryEvent
- case _ where ack:
- return .binaryAck
- default:
- return .error
- }
- }
- static func packetFromEmit(_ items: [Any], id: Int, nsp: String, ack: Bool, checkForBinary: Bool = true) -> SocketPacket {
- if checkForBinary {
- let (parsedData, binary) = deconstructData(items)
- return SocketPacket(type: findType(binary.count, ack: ack), data: parsedData, id: id, nsp: nsp,
- binary: binary)
- } else {
- return SocketPacket(type: findType(0, ack: ack), data: items, id: id, nsp: nsp)
- }
- }
- }
- private extension SocketPacket {
- // Recursive function that looks for NSData in collections
- static func shred(_ data: Any, binary: inout [Data]) -> Any {
- let placeholder = ["_placeholder": true, "num": binary.count] as JSON
- switch data {
- case let bin as Data:
- binary.append(bin)
- return placeholder
- case let arr as [Any]:
- return arr.map({shred($0, binary: &binary)})
- case let dict as JSON:
- return dict.reduce(into: JSON(), {cur, keyValue in
- cur[keyValue.0] = shred(keyValue.1, binary: &binary)
- })
- default:
- return data
- }
- }
- // Removes binary data from emit data
- // Returns a type containing the de-binaryed data and the binary
- static func deconstructData(_ data: [Any]) -> ([Any], [Data]) {
- var binary = [Data]()
- return (data.map({ shred($0, binary: &binary) }), binary)
- }
- }
|